1 {% extends basetemplate %}
3 {% load i18n user_tags extra_tags extra_filters admin_static %}
6 <script type="text/javascript">
8 var $form = $('#changelist-search');
10 $('#all-node-type-link').click(function() {
11 $('#type-filter-container').find('input').remove();
15 $('.node-type-link').click(function() {
16 var link_type = $(this).attr('href').substring(1);
18 if ($('#type-filter-container').find('input[value=' + link_type + ']').length == 0) {
19 $('#type-filter-container').append($("<input name=\"node_type\" type=\"hidden\" value=\"" + link_type + "\" />"));
21 $('#type-filter-container').find('input[value=' + link_type + ']').remove();
27 $('#all-state-link').click(function() {
28 $('#state-filter-container').find('input').remove();
32 $('.state-type-link').click(function() {
33 var state_type = $(this).attr('href').substring(1);
35 if ($('#state-filter-container').find('input[value=' + state_type + ']').length == 0) {
36 $('#state-filter-container').append($("<input name=\"state_type\" type=\"hidden\" value=\"" + state_type + "\" />"));
38 $('#state-filter-container').find('input[value=' + state_type + ']').remove();
44 $('.action-select').change(function() {
45 $('#action-toggle').removeAttr('checked');
46 var $tr = $(this).parents('tr');
47 if ($(this).attr('checked')) {
48 $tr.addClass('selected');
50 $tr.removeClass('selected');
54 $('#action-toggle').change(function() {
55 var $rows = $('#result_list').find('tbody').find('tr');
56 var $boxes = $('#result_list').find('tbody').find('input');
58 if ($(this).attr('checked')) {
59 $rows.addClass('selected');
60 $boxes.attr('checked', 'checked')
62 $rows.removeClass('selected');
63 $boxes.removeAttr('checked');
67 $('#author-selector').autocomplete('{% url "matching_users" %}', {
72 formatItem: function(row, i, max, value) {
73 return row[1] + ' (' + row[2] + ' {% trans "rep" %})';
76 formatResult: function(row, i, max, value){
81 $('#author-selector').result(function(event, data, formatted) {
82 if ($('#author-filter-container').find('input[value=' + data[0] + ']').length == 0) {
83 $('#author-filter-container').append($("<input name=\"authors\" type=\"hidden\" value=\"" + data[0] + "\" />"));
88 $('.author-filter-remover').click(function() {
89 var id = $(this).attr('rel');
90 if ($('#author-filter-container').find('input[value=' + id + ']').length > 0) {
91 $('#author-filter-container').find('input[value=' + id + ']').remove();
96 $('#tag-selector').autocomplete('{% url "matching_tags" %}', {
101 formatItem: function(row, i, max, value) {
102 return row[1] + ' (' + row[2] + ' {% trans "uses" %})';
105 formatResult: function(row, i, max, value){
110 $('#tag-selector').result(function(event, data, formatted) {
111 if ($('#tag-filter-container').find('input[value=' + data[0] + ']').length == 0) {
112 $('#tag-filter-container').append($("<input name=\"tags\" type=\"hidden\" value=\"" + data[0] + "\" />"));
117 $('.tag-filter-remover').click(function() {
118 var id = $(this).attr('rel');
119 if ($('#tag-filter-container').find('input[value=' + id + ']').length > 0) {
120 $('#tag-filter-container').find('input[value=' + id + ']').remove();
125 $('#filter-name-box').one('focus', function() {
127 $(this).css('color', 'black');
130 $('#filter-name-box').keyup(function() {
131 if ($(this).val().trim().length > 0) {
132 $('#save-filter-button').removeAttr('disabled');
133 $('#save-filter-button').css('color', 'black');
135 $('#save-filter-button').css('color', '#AAA');
136 $('#save-filter-button').attr('disabled', 'disabled');
140 var resize_data = null;
142 $('.col-resizer').mousedown(function(e) {
143 var $to_resize = $(this).prev();
147 to_resize: $to_resize,
148 start_width: $to_resize.innerWidth(),
153 $('body').mousemove(function(e) {
154 if (resize_data != null) {
155 var new_size = (resize_data.start_width - (resize_data.x_start - e.pageX)) + 'px';
156 resize_data.to_resize.css({'max-width': new_size, 'min-width': new_size})
157 resize_data.resizer.css('max-width', '3px');
161 $('body').mouseup(function() {
162 if (resize_data != null)
166 $('#filter-panel-header').click(function() {
167 $('#filter-panel').slideToggle();
170 $('#state-filter-type').change(function() {
171 $('#state-filter-type-hidden').val($(this).val());
175 $('#reset-text-filter').click(function() {
176 $('#text-filter-input').val('');
184 list-style-type: none;
189 #result_list tr td.deleted {
190 background-color: #FDD;
191 border-bottom: 1px solid #a9a9a9;
194 #result_list tr td.accepted {
195 background-color: #DFD;
196 border-bottom: 1px solid #a9a9a9;
199 span.question-deleted {
200 text-decoration: line-through;
211 <link href="{% static 'admin/css/base.css' %}" rel="stylesheet" type="text/css" media="screen" />
212 <script type="text/javascript">
213 /* gettext identity library */
215 function gettext(msgid) { return msgid; }
216 function ngettext(singular, plural, count) { return (count == 1) ? singular : plural; }
217 function gettext_noop(msgid) { return msgid; }
219 function interpolate(fmt, obj, named) {
221 return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
223 return fmt.replace(/%s/g, function(match){return String(obj.shift())});
227 /* formatting library */
229 var formats = new Array();
231 formats['DATETIME_FORMAT'] = 'N j, Y, P';
232 formats['DATE_FORMAT'] = 'N j, Y';
233 formats['DECIMAL_SEPARATOR'] = '.';
234 formats['MONTH_DAY_FORMAT'] = 'F j';
235 formats['NUMBER_GROUPING'] = '0';
236 formats['TIME_FORMAT'] = 'P';
237 formats['FIRST_DAY_OF_WEEK'] = '0';
238 formats['TIME_INPUT_FORMATS'] = ['%H:%M:%S', '%H:%M'];
239 formats['THOUSAND_SEPARATOR'] = ',';
240 formats['DATE_INPUT_FORMATS'] = ['%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', '%b %d %Y', '%b %d, %Y', '%d %b %Y', '%d %b, %Y', '%B %d %Y', '%B %d, %Y', '%d %B %Y', '%d %B, %Y'];
241 formats['YEAR_MONTH_FORMAT'] = 'F Y';
242 formats['SHORT_DATE_FORMAT'] = 'm/d/Y';
243 formats['SHORT_DATETIME_FORMAT'] = 'm/d/Y P';
244 formats['DATETIME_INPUT_FORMATS'] = ['%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', '%Y-%m-%d', '%m/%d/%Y %H:%M:%S', '%m/%d/%Y %H:%M', '%m/%d/%Y', '%m/%d/%y %H:%M:%S', '%m/%d/%y %H:%M', '%m/%d/%y'];
246 function get_format(format_type) {
247 var value = formats[format_type];
248 if (typeof(value) == 'undefined') {
256 <script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
260 {% trans "Node manager" %}
262 {% block description %}
263 {% trans "Nodes bulk management" %}
266 {% block admincontent %}
267 <div id="changelist" class="module filtered">
269 <form method="get" action="" id="changelist-search">
273 <label><img alt="Search" src="{% static 'admin/img/admin/icon_searchbox.png' %}"></label>
274 <input type="text" size="40" name="text" id="text-filter-input" value="{{ text }}">
275 <input type="submit" value="{% trans "Search" %}">
277 <small><a href="#" id="reset-text-filter">{% trans "reset text filter" %}</a></small>
283 <input type="radio" name="text_in" value="title"{% ifequal text_in "title" %} checked="checked"{% endifequal %} />
284 {% trans "Title" %}</label>
288 <input type="radio" name="text_in" value="body"{% ifequal text_in "body" %} checked="checked"{% endifequal %} />
289 {% trans "Body" %}</label>
293 <input type="radio" name="text_in" value="both"{% ifequal text_in "both" %} checked="checked"{% endifequal %} />
294 {% trans "Title and Body" %}</label>
299 <input type="hidden" name="sort" value="{{ nodes.paginator.current_sort }}" />
300 <input type="hidden" id="state-filter-type-hidden" name="state_filter_type" value="" />
301 <div style="display: none;" id="author-filter-container">
302 {% for u in authors %}
303 <input name="authors" type="hidden" value="{{ u.id }}" />
306 <div style="display: none;" id="tag-filter-container">
308 <input name="tags" type="hidden" value="{{ t.id }}" />
311 <div id="type-filter-container" style="display: none;">
312 {% for type in type_filter %}
313 <input name="node_type" type="hidden" value="{{ type }}" />
316 <div id="state-filter-container" style="display: none;">
317 {% for type in state_filter %}
318 <input name="state_type" type="hidden" value="{{ type }}" />
323 <div id="changelist-filter">
324 <h2 id="filter-panel-header">{% trans "Filter" %}<small> ({% trans "Click to show/hide" %})</small></h2>
325 <div id="filter-panel">
326 <h3>{% trans "By type" %}</h3>
328 <li {% if not type_filter %} class="selected"{% endif %}>
329 <a id="all-node-type-link" href="#all" title="{% trans "click to clear the type filter" %}">{% trans "all" %}</a>
331 {% for type, name in node_types %}
332 <li{% if type|contained_in:type_filter %} class="selected" title="{% trans "click to remove from the filter" %}"{% else %} title="{% trans "click to add to the filter" %}"{% endif %}>
333 <a class="node-type-link" href="#{{ type }}">{{ name }}</a>
337 <h3>{% trans "By state" %}</h3>
339 <li {% if not state_filter %} class="selected"{% endif %}>
340 <a id="all-state-link" href="#any" title="{% trans "click to clear the state filter" %}">{% trans "any" %}</a>
342 {% for state_type in state_types %}
343 <li{% if state_type|contained_in:state_filter %} class="selected" title="{% trans "click to remove from the filter" %}"{% else %} title="{% trans "click to add to the filter" %}"{% endif %}>
344 <a class="state-type-link" href="#{{ state_type }}">{{ state_type }}</a>
348 <select id="state-filter-type">
349 <option value="any"{% ifequal state_filter_type "any" %} selected="selected"{% endifequal %}>{% trans "Match any selected" %}</option>
350 <option value="all"{% ifequal state_filter_type "all" %} selected="selected"{% endifequal %}>{% trans "Match all selected" %}</option>
354 <h3>{% trans "By author(s)" %}</h3>
355 {% if not authors.count %}
356 <small>{% trans "No users selected, use the box bellow to add users to the filter." %}</small>
359 {% for u in authors %}
360 <li class="selected">
361 <img class="author-filter-remover" rel="{{ u.id }}" src="{% media "/media/images/close-small-dark.png" %}">
362 {{ u.decorated_name }} ({{ u.reputation }})
366 <small>{% trans "Click on the cross next to a user name to remove it from the filter." %}</small>
368 <input type="text" size="20" autocomplete="off" id="author-selector" />
370 <h3>{% trans "By tag(s)" %}</h3>
371 {% if not tags.count %}
372 <small>{% trans "No tags selected, use the box bellow to add tags to the filter." %}</small>
376 <li class="selected">
377 <img class="tag-filter-remover" rel="{{ t.id }}" src="{% media "/media/images/close-small-dark.png" %}">
378 {{ t.name }} ({{ t.used_count }})
382 <small>{% trans "Click on the cross next to a tag name to remove it from the filter." %}</small>
384 <input type="text" size="20" autocomplete="off" id="tag-selector" />
386 <h3>{% trans "Pre defined" %}</h3>
387 {% if not settings.NODE_MAN_FILTERS %}
388 <small>{% trans "There are no saved filters. Click bellow to add." %}</small>
390 <ul id="pre-filter-container">
391 {% for name, uri in settings.NODE_MAN_FILTERS %}
392 <li class="selected"><a href="{% url "admin_tools" "nodeman" %}?{{ uri }}">{{ name }}</a></li>
395 <form action="" method="POST">
397 <input name="filter_name" type="text" size="20" id="filter-name-box" style="color: #AAA;" value="{% trans "Filter name..." %}" />
398 <button name="save_filter" value="0" style="color: #AAA;" title="{% trans "Click to save the current filter" %}" id="save-filter-button" disabled="disabled" class="button">{% trans "Save" %}</button>
401 {% comment %}<h3>{% trans "Show" %}</h3>
402 <form action="" method="get">
404 <div>{{ show_form.show }}</div>
405 <input type="submit" value="{% trans "Refresh" %}" />
406 </form>{% endcomment %}
409 <form id="changelist-form" method="POST" action="">
411 <div class="actions">
413 {% trans "Action" %}:
414 <select name="action">
415 <option selected="selected" value="">---------</option>
416 <option value="delete_selected">{% trans "Mark deleted" %}</option>
417 <option value="undelete_selected">{% trans "Undelete" %}</option>
418 <option value="hard_delete_selected">{% trans "Delete completely" %}</option>
419 <option value="close_selected">{% trans "Close (questions only)" %}</option>
422 <button value="0" name="execute" title="{% trans "Run the selected action" %}" class="button" type="submit">{% trans "Go" %}</button>
424 <table id="result_list" cellspacing="0">
428 current_sort = nodes.paginator.current_sort
429 added_at = current_sort == "added_at" and "ascending" or (current_sort == "added_at_asc" and "descending" or "")
430 author = current_sort == "author" and "ascending" or (current_sort == "author_asc" and "descending" or "")
431 score = current_sort == "score" and "ascending" or (current_sort == "score_asc" and "descending" or "")
432 act_at = current_sort == "act_at" and "ascending" or (current_sort == "act_at_asc" and "descending" or "")
433 act_by = current_sort == "act_by" and "ascending" or (current_sort == "act_by_asc" and "descending" or "")
435 added_at_link = current_sort == "added_at" and nodes.paginator.added_at_asc_sort_link or nodes.paginator.added_at_sort_link
436 author_link = current_sort == "author_asc" and nodes.paginator.author_sort_link or nodes.paginator.author_asc_sort_link
437 act_at_link = current_sort == "act_at" and nodes.paginator.act_at_asc_sort_link or nodes.paginator.act_at_sort_link
438 act_by_link = current_sort == "act_by_asc" and nodes.paginator.act_by_sort_link or nodes.paginator.act_by_asc_sort_link
441 <th class="action-checkbox-column">
442 <input type="checkbox" id="action-toggle" style="display: inline;" />
444 <th>{% trans "Type" %}</th>
445 <th>{% trans "Summary" %}</th>
446 <th class="col-resizer"></th>
447 <th>{% trans "State" %}</th>
448 <th class="sorted {{ author }}">
449 <a href="{{ author_link }}">{% trans "Author" %}</a>
451 <th class="sorted {{ added_at }}">
452 <a href="{{ added_at_link }}">{% trans "Added at" %}</a>
455 <th class="sorted {{ score }}">
456 <a href="{{ score_link }}">{% trans "Score" %}</a>
459 <th class="sorted {{ act_by }}">
460 <a href="{{ act_by_link }}">{% trans "Last activity by" %}</a>
462 <th class="sorted {{ act_at }}">
463 <a href="{{ act_at_link }}">{% trans "Last activity at" %}</a>
465 <th>{% trans "Tags" %}</th>
470 {% with filter_form.state_type.data as state_type %}
471 {% for node in nodes.paginator.page %}
472 <tr class="{% cycle 'row1' 'row2' %}">
473 <td><input type="checkbox" name="_selected_node" value="{{ node.id }}" class="action-select"></td>
474 <td>{{ node.friendly_name }}</td>
476 is_root = node.abs_parent == None
477 title = is_root and node.title or node.abs_parent.title
479 anchor = "<strong>%s</strong>" % html.hyperlink(node.get_absolute_url(), title)
480 anchor = ((not is_root) and node.abs_parent.nis.deleted) and "<span class=\"question-deleted\">%s</span>" % anchor or anchor
481 anchor = is_root and anchor or "(%s)" % anchor
482 anchor = html.mark_safe(anchor)
485 td_class = node.nis.accepted and "accepted" or td_class
486 td_class = node.nis.deleted and "deleted" or td_class
488 <td class="{{ td_class }}" colspan="2">
493 {% for state in node.states.all %}
494 <b>{{ state.state_type }}</b> {% diff_date state.action.at %} {% trans "by" %}
495 <a target="_blank" href="{{ state.action.by.get_absolute_url }}">{{ state.action.by.decorated_name }}</a><br />
498 <td><a href="{{ node.author.get_absolute_url }}">{{ node.author.decorated_name }}</a></td>
499 <td>{% diff_date node.added_at %}</td>
500 <!--<td>{{ node.score }}</td>-->
501 <td><a href="{{ node.last_activity_by.get_absolute_url }}">{{ node.last_activity_by.decorated_name }}</a></td>
502 <td>{% diff_date node.last_activity_at %}</td>
504 {% for t in node.tags.all %}
505 {% if t|contained_in:tags %}<b>{{ t.name }}</b>
506 {% else %}{{ t.name }}{% endif %}
514 {{ nodes.paginator.page_numbers }}