Browse Source

[search] Admin UI

Refined UI for core editing
Enrico Berti 12 years ago
parent
commit
139a1393ea

+ 8 - 2
apps/search/src/search/models.py

@@ -56,6 +56,12 @@ class Facet(models.Model):
     if 'fields' in post_data and post_data['fields']:
       data_dict['fields'] = json.loads(post_data['fields'])
 
+    if 'ranges' in post_data and post_data['ranges']:
+      data_dict['ranges'] = json.loads(post_data['ranges'])
+
+    if 'dates' in post_data and post_data['dates']:
+      data_dict['dates'] = json.loads(post_data['dates'])
+
     self.data = json.dumps(data_dict)
 
   def get_query_params(self):
@@ -69,7 +75,7 @@ class Facet(models.Model):
     )
 
     if 'fields' in data_dict and data_dict['fields']:
-      field_facets = tuple([('facet.field', field_facet) for field_facet in data_dict['fields']])
+      field_facets = tuple([('facet.field', field_facet['field']) for field_facet in data_dict['fields']])
       params += field_facets
 
     return params
@@ -143,7 +149,7 @@ class Query(object): pass
 def temp_fixture_hook():
   #Core.objects.all().delete()
   if not Core.objects.exists():
-    facets = Facet.objects.create(data=json.dumps({'fields': ['id']}))
+    facets = Facet.objects.create(data=json.dumps({'fields': ['id'], 'ranges': [], 'dates': []}))
     result = Result.objects.create()
     sorting = Sorting.objects.create()
 

+ 210 - 56
apps/search/src/search/templates/admin_core_facets.mako

@@ -32,75 +32,229 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
     ${ layout.sidebar(hue_core.name, 'facets') }
   </%def>
   <%def name="content()">
-    <form method="POST" id="facets" data-bind="submit: submit">
+    <form method="POST" class="form-horizontal" data-bind="submit: submit">
 
-    <div class="well"><h3>${_('Field Facets')}</h3></div>
+      <div class="section">
+        <div class="alert alert-info"><h4>${_('Field Facets')}</h4></div>
+        <div data-bind="visible: fieldFacets().length == 0" style="padding-left: 10px;margin-bottom: 20px">
+          <em>${_('There are currently no field Facets defined.')}</em>
+        </div>
+        <div data-bind="foreach: fieldFacets">
+          <div class="bubble">
+            <strong><span data-bind="text: field"></span></strong>
+            <a class="btn btn-small" data-bind="click: $root.removeFieldFacet"><i class="icon-trash"></i></a>
+          </div>
+        </div>
+        <div class="clearfix"></div>
+        <div class="miniform">
+          ${_('Field')}
+          <select id="select-field-facet" data-bind="options: fields, value: selectedFieldFacet"></select>
+          <a class="btn" data-bind="click: $root.addFieldFacet"><i class="icon-plus"></i> ${_('Add field to Field Facets')}</a>
+          &nbsp;<span id="field-facet-error" class="label label-important hide">${_('The field you are trying to add is already in the list.')}</span>
+        </div>
+      </div>
+
+      <div class="section">
+        <div class="alert alert-info" style="margin-top: 60px"><h4>${_('Range Facets')}</h4></div>
+        <div data-bind="visible: rangeFacets().length == 0" style="padding-left: 10px;margin-bottom: 20px">
+          <em>${_('There are currently no Range Facets defined.')}</em>
+        </div>
+        <div data-bind="foreach: rangeFacets">
+          <div class="bubble">
+            <strong><span data-bind="text: field"></span></strong>
+            <span style="color:#666;font-size: 12px">
+              (<span data-bind="text: start"></span> <i class="icon-double-angle-right"></i> <span data-bind="text: end"></span>,
+              <i class="icon-resize-horizontal"></i> <span data-bind="text: gap"></span>)
+            </span>
+            <a class="btn btn-small" data-bind="click: $root.removeRangeFacet"><i class="icon-trash"></i></a>
+          </div>
+        </div>
+        <div class="clearfix"></div>
+        <div class="miniform">
+          ${_('Field')}
+          <select data-bind="options: fields, value: selectedRangeFacet"></select>
+          &nbsp;${_('Start')}
+          <input type="text" data-bind="value: selectedRangeStartFacet" class="input-mini" />
+          &nbsp;${_('End')}
+          <input type="text" data-bind="value: selectedRangeEndFacet" class="input-mini" />
+          &nbsp;${_('Gap')}
+          <input type="text" data-bind="value: selectedRangeGapFacet" class="input-mini" />
+          <a class="btn" data-bind="click: $root.addRangeFacet"><i class="icon-plus"></i> ${_('Add field to Range Facets')}</a>
+        </div>
+      </div>
 
-    <table class="table table-striped">
-      <thead data-bind="visible: fieldsFacets().length == 0">
-      <tr>
-        <td colspan="3">
-          <div class="alert">
-          ${_('There are currently no field Facets defined. Please add at least one from the bottom.')}
+      <div class="section">
+        <div class="alert alert-info" style="margin-top: 60px"><h4>${_('Date Facets')}</h4></div>
+        <div data-bind="visible: dateFacets().length == 0" style="padding-left: 10px;margin-bottom: 20px">
+          <em>${_('There are currently no Date Facets defined.')}</em>
+        </div>
+        <div data-bind="foreach: dateFacets">
+          <div class="bubble">
+            <strong><span data-bind="text: field"></span></strong>
+            <span style="color:#666;font-size: 12px">
+              (<span data-bind="text: start"></span> <i class="icon-double-angle-right"></i> <span data-bind="text: end"></span>,
+              <i class="icon-resize-horizontal"></i> <span data-bind="text: gap"></span>)
+            </span>
+            <a class="btn btn-small" data-bind="click: $root.removeDateFacet"><i class="icon-trash"></i></a>
           </div>
-        </td>
-      </tr>
-      </thead>
-      <tbody id="fields" data-bind="foreach: fieldsFacets">
-      <tr data-bind="attr: {'data-field': $data}">
-        <td><span data-bind="text: $data"></span></td>
-        <td width="30"><a class="btn btn-small" data-bind="click: $root.removeFieldsFacets"><i class="icon-trash"></i></a></td>
-      </tr>
-      </tbody>
-      <tfoot>
-      <tr style="padding-top: 20px">
-        <td><select data-bind="options: fields, value: selectedField" style="width:100%"></select></td>
-        <td width="30"><a class="btn btn-small" data-bind="click: $root.addFieldsFacets"><i class="icon-plus"></i></a></td>
-      </tr>
-      </tfoot>
-    </table>
-
-    <div class="form-actions">
-      <button type="submit" class="btn btn-primary btn-large" id="save-facets">${_('Save Facets')}</button>
-    </div>
+        </div>
+        <div class="clearfix"></div>
+        <div class="miniform">
+        ${_('Field')}
+          <select data-bind="options: fields, value: selectedDateFacet"></select>
+          &nbsp;${_('Start')}
+          <input id="dp-start" class="input-small" type="text" data-bind="value: selectedDateStartFacet" />
+          &nbsp;${_('End')}
+          <input id="dp-end" class="input-small" type="text" data-bind="value: selectedDateEndFacet" />
+          &nbsp;${_('Gap')}
+          <input type="text" data-bind="value: selectedDateGapFacet" class="input-mini" />
+          <a class="btn" data-bind="click: $root.addDateFacet"><i class="icon-plus"></i> ${_('Add field to Date Facets')}</a>
+        </div>
+      </div>
+
+
+      <div class="form-actions" style="margin-top: 80px">
+        <button type="submit" class="btn btn-primary">${_('Save Facets')}</button>
+        <a class="btn" href="${ url('search:admin') }"><i class="icon-list"></i> ${ _('Return to Core list') }</a>
+        <a class="btn" href="${ url('search:index') }"><i class="icon-search"></i> ${ _('Back to Search') }</a>
+      </div>
     </form>
   </%def>
 </%layout:skeleton>
 
+<link rel="stylesheet" href="/static/ext/css/bootstrap-datepicker.min.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+
 <script src="/static/ext/js/knockout-2.1.0.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/bootstrap-datepicker.min.js" type="text/javascript" charset="utf-8"></script>
 
 <script type="text/javascript">
-  $(document).ready(function () {
-    function ViewModel() {
-      var self = this;
-      self.fields = ko.observableArray(${ hue_core.fields | n,unicode });
-      self.fieldsFacets = ko.observableArray(${ hue_core.facets.data | n,unicode }.fields
-    )
-      ;
-      self.selectedField = ko.observable();
-
-      self.removeFieldsFacets = function (facet) {
-        self.fieldsFacets.remove(facet);
-      };
-
-      self.addFieldsFacets = function () {
-        self.fieldsFacets.push(self.selectedField());
-      };
-
-      self.submit = function () {
-        $.ajax("${ url('search:admin_core_facets', core=hue_core.name) }", {
-          data: {'fields': ko.utils.stringifyJson(self.fieldsFacets)},
-          contentType: 'application/json',
-          type: 'POST',
-          complete: function (data) {
-            location.reload();
-          }
-        });
-      };
+
+  var DATE_FORMAT = "mm-dd-yyyy";
+
+  var Facet = function (type, field, start, end, gap) {
+    return {
+      type: type,
+      field:field,
+      start: start,
+      end: end,
+      gap: gap
+    }
+  }
+
+  var FieldFacet = function (field) {
+    return new Facet("field", field);
+  }
+
+  var RangeFacet = function (field, start, end, gap) {
+    return new Facet("range", field, start, end, gap);
+  }
+
+  var DateFacet = function (field, start, end, gap) {
+    return new Facet("date", field, start, end, gap);
+  }
+
+  function ViewModel() {
+    var self = this;
+    self.fields = ko.observableArray(${ hue_core.fields | n,unicode });
+
+    self.fieldFacets = ko.observableArray(ko.utils.arrayMap(${ hue_core.facets.data | n,unicode }.fields, function (obj) {
+      return new FieldFacet(obj.field);
+    }));
+
+    self.rangeFacets = ko.observableArray(ko.utils.arrayMap(${ hue_core.facets.data | n,unicode }.ranges, function (obj) {
+      return new RangeFacet(obj.field, obj.start, obj.end, obj.gap);
+    }));
+
+    self.dateFacets = ko.observableArray(ko.utils.arrayMap(${ hue_core.facets.data | n,unicode }.dates, function (obj) {
+      return new DateFacet(obj.field, obj.start, obj.end, obj.gap);
+    }));
+
+    self.selectedFieldFacet = ko.observable();
+    self.selectedRangeFacet = ko.observable();
+    self.selectedRangeStartFacet = ko.observable("");
+    self.selectedRangeEndFacet = ko.observable("");
+    self.selectedRangeGapFacet = ko.observable("");
+    self.selectedDateFacet = ko.observable();
+    self.selectedDateStartFacet = ko.observable("");
+    self.selectedDateEndFacet = ko.observable("");
+    self.selectedDateGapFacet = ko.observable("");
+
+    self.removeFieldFacet = function (facet) {
+      self.fieldFacets.remove(facet);
+    };
+
+    self.removeRangeFacet = function (facet) {
+      self.rangeFacets.remove(facet);
     };
 
-    ko.applyBindings(new ViewModel());
+    self.removeDateFacet = function (facet) {
+      self.dateFacets.remove(facet);
+    };
+
+    self.addFieldFacet = function () {
+      var found = false;
+      ko.utils.arrayForEach(self.fieldFacets(), function(facet) {
+        if (facet.field == self.selectedFieldFacet()){
+          found = true;
+        }
+      });
+      if (!found){
+        self.fieldFacets.push(new FieldFacet(self.selectedFieldFacet()));
+      }
+      else {
+        $("#field-facet-error").show();
+      }
+    };
 
+    self.addRangeFacet = function () {
+      self.rangeFacets.push(new RangeFacet(self.selectedRangeFacet(), self.selectedRangeStartFacet(), self.selectedRangeEndFacet(), self.selectedRangeGapFacet()));
+      self.selectedRangeStartFacet("");
+      self.selectedRangeEndFacet("");
+      self.selectedRangeGapFacet("");
+    };
+
+    self.addDateFacet = function () {
+      self.dateFacets.push(new DateFacet(self.selectedDateFacet(), self.selectedDateStartFacet(), self.selectedDateEndFacet(), self.selectedDateGapFacet()));
+      self.selectedDateStartFacet("");
+      self.selectedDateEndFacet("");
+      self.selectedDateGapFacet("");
+    };
+
+    self.submit = function () {
+      console.log(ko.utils.stringifyJson(self.fieldFacets));
+      $.ajax("${ url('search:admin_core_facets', core=hue_core.name) }", {
+        data: {
+          'fields': ko.utils.stringifyJson(self.fieldFacets),
+          'ranges': ko.utils.stringifyJson(self.rangeFacets),
+          'dates': ko.utils.stringifyJson(self.dateFacets)
+        },
+        contentType: 'application/json',
+        type: 'POST',
+        complete: function (data) {
+          location.reload();
+        }
+      });
+    };
+  };
+
+  var viewModel = new ViewModel();
+
+  $(document).ready(function () {
+    $(".btn-primary").button("reset");
+    ko.applyBindings(viewModel);
+    $("#select-field-facet").click(function(){
+      $("#field-facet-error").hide();
+    });
+    $("#dp-start").datepicker({
+      format: DATE_FORMAT
+    }).on("changeDate", function(e){
+      viewModel.selectedDateStartFacet($(this).val());
+    });
+    $("#dp-end").datepicker({
+      format: DATE_FORMAT
+    }).on("changeDate", function(e){
+      viewModel.selectedDateEndFacet($(this).val());
+    });
   });
 </script>
 

+ 106 - 94
apps/search/src/search/templates/admin_core_properties.mako

@@ -43,102 +43,114 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
     ${ layout.sidebar(hue_core.name, 'properties') }
   </%def>
   <%def name="content()">
-    <h3>${_('Index properties')}</h3>
-    <table class="table">
-      <thead>
-      <tr>
-        <th width="20%">${_('Property')}</th>
-        <th>${_('Value')}</th>
-      </tr>
-      </thead>
-      <tbody>
-        <tr>
-          <td>sizeInBytes</td>
-          <td>${ indexProperty('sizeInBytes') }</td>
-        </tr>
-        <tr>
-          <td>segmentCount</td>
-          <td>${ indexProperty('segmentCount') }</td>
-        </tr>
-        <tr>
-          <td>maxDoc</td>
-          <td>${ indexProperty('maxDoc') }</td>
-        </tr>
-        <tr>
-          <td>lastModified</td>
-          <td>${ indexProperty('lastModified') }</td>
-        </tr>
-        <tr>
-          <td>current</td>
-          <td>${ indexProperty('current') }</td>
-        </tr>
-        <tr>
-          <td>version</td>
-          <td>${ indexProperty('version') }</td>
-        </tr>
-        <tr>
-          <td>directory</td>
-          <td>${ indexProperty('directory') }</td>
-        </tr>
-        <tr>
-          <td>numDocs</td>
-          <td>${ indexProperty('numDocs') }</td>
-        </tr>
-        <tr>
-          <td>hasDeletions</td>
-          <td>${ indexProperty('hasDeletions') }</td>
-        </tr>
-        <tr>
-          <td>size</td>
-          <td>${ indexProperty('size') }</td>
-        </tr>
-      </tbody>
-    </table>
+    <ul class="nav nav-tabs">
+      <li class="active"><a href="#index" data-toggle="tab">${_('Index properties')}</a></li>
+      <li><a href="#core" data-toggle="tab">${_('Core properties')}</a></li>
+    </ul>
+    <div class="tab-content">
+      <div class="tab-pane active" id="index">
+        <table class="table">
+          <thead>
+          <tr>
+            <th width="20%">${_('Property')}</th>
+            <th>${_('Value')}</th>
+          </tr>
+          </thead>
+          <tbody>
+          <tr>
+            <td>sizeInBytes</td>
+            <td>${ indexProperty('sizeInBytes') }</td>
+          </tr>
+          <tr>
+            <td>segmentCount</td>
+            <td>${ indexProperty('segmentCount') }</td>
+          </tr>
+          <tr>
+            <td>maxDoc</td>
+            <td>${ indexProperty('maxDoc') }</td>
+          </tr>
+          <tr>
+            <td>lastModified</td>
+            <td>${ indexProperty('lastModified') }</td>
+          </tr>
+          <tr>
+            <td>current</td>
+            <td>${ indexProperty('current') }</td>
+          </tr>
+          <tr>
+            <td>version</td>
+            <td>${ indexProperty('version') }</td>
+          </tr>
+          <tr>
+            <td>directory</td>
+            <td>${ indexProperty('directory') }</td>
+          </tr>
+          <tr>
+            <td>numDocs</td>
+            <td>${ indexProperty('numDocs') }</td>
+          </tr>
+          <tr>
+            <td>hasDeletions</td>
+            <td>${ indexProperty('hasDeletions') }</td>
+          </tr>
+          <tr>
+            <td>size</td>
+            <td>${ indexProperty('size') }</td>
+          </tr>
+          </tbody>
+        </table>
+      </div>
+      <div class="tab-pane" id="core">
+        <table class="table">
+          <thead>
+          <tr>
+            <th width="20%">${_('Property')}</th>
+            <th>${_('Value')}</th>
+          </tr>
+          </thead>
+          <tbody>
+          <tr>
+            <td>uptime</td>
+            <td>${ coreProperty('uptime') }</td>
+          </tr>
+          <tr>
+            <td>name</td>
+            <td>${ coreProperty('name') }</td>
+          </tr>
+          <tr>
+            <td>isDefaultCore</td>
+            <td>${ coreProperty('isDefaultCore') }</td>
+          </tr>
+          <tr>
+            <td>dataDir</td>
+            <td>${ coreProperty('dataDir') }</td>
+          </tr>
+          <tr>
+            <td>instanceDir</td>
+            <td>${ coreProperty('instanceDir') }</td>
+          </tr>
+          <tr>
+            <td>startTime</td>
+            <td>${ coreProperty('startTime') }</td>
+          </tr>
+          <tr>
+            <td>config</td>
+            <td>${ coreProperty('config') }</td>
+          </tr>
+          <tr>
+            <td>schema</td>
+            <td>${ coreProperty('schema') }</td>
+          </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
 
-    <h3>${_('Core properties')}</h3>
-    <table class="table">
-      <thead>
-      <tr>
-        <th width="20%">${_('Property')}</th>
-        <th>${_('Value')}</th>
-      </tr>
-      </thead>
-      <tbody>
-      <tr>
-        <td>uptime</td>
-        <td>${ coreProperty('uptime') }</td>
-      </tr>
-      <tr>
-        <td>name</td>
-        <td>${ coreProperty('name') }</td>
-      </tr>
-      <tr>
-        <td>isDefaultCore</td>
-        <td>${ coreProperty('isDefaultCore') }</td>
-      </tr>
-      <tr>
-        <td>dataDir</td>
-        <td>${ coreProperty('dataDir') }</td>
-      </tr>
-      <tr>
-        <td>instanceDir</td>
-        <td>${ coreProperty('instanceDir') }</td>
-      </tr>
-      <tr>
-        <td>startTime</td>
-        <td>${ coreProperty('startTime') }</td>
-      </tr>
-      <tr>
-        <td>config</td>
-        <td>${ coreProperty('config') }</td>
-      </tr>
-      <tr>
-        <td>schema</td>
-        <td>${ coreProperty('schema') }</td>
-      </tr>
-      </tbody>
-    </table>
 
+    <div class="form-actions">
+      <a class="btn" href="${ url('search:admin') }"><i class="icon-list"></i> ${ _('Return to Core list') }</a>
+      <a class="btn" href="${ url('search:index') }"><i class="icon-search"></i> ${ _('Back to Search') }</a>
+    </div>
 
   </%def>
 </%layout:skeleton>

+ 5 - 1
apps/search/src/search/templates/admin_core_schema.mako

@@ -35,6 +35,10 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
     <textarea id="schema">
     ${ solr_schema.decode('utf-8') }
     </textarea>
+    <div class="form-actions">
+      <a class="btn" href="${ url('search:admin') }"><i class="icon-list"></i> ${ _('Return to Core list') }</a>
+      <a class="btn" href="${ url('search:index') }"><i class="icon-search"></i> ${ _('Back to Search') }</a>
+    </div>
   </%def>
 </%layout:skeleton>
 
@@ -56,7 +60,7 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
       lineNumbers: true
     });
 
-    codeMirror.setSize("100%", $(document).height() - 150);
+    codeMirror.setSize("100%", $(document).height() - 150 - $(".form-actions").outerHeight());
 
   });
 </script>

+ 82 - 72
apps/search/src/search/templates/admin_core_sorting.mako

@@ -32,36 +32,42 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
     ${ layout.sidebar(hue_core.name, 'sorting') }
   </%def>
   <%def name="content()">
-    <div class="well"><h3>${_('Fields')}</h3></div>
-
-    <table class="table table-striped">
-      <thead data-bind="visible: fieldsSorting().length == 0">
-        <tr>
-          <td colspan="3">
-            <div class="alert">
-            ${_('There are currently no Sorting fields defined. Please add at least one from the bottom.')}
-            </div>
-          </td>
-        </tr>
-      </thead>
-      <tbody id="fields" data-bind="foreach: fieldsSorting">
-        <tr data-bind="attr: {'data-field': $data}">
-          <td width="30"><i class="icon-list"></i></td>
-          <td><span data-bind="text: $data"></span></td>
-          <td width="30"><a class="btn btn-small" data-bind="click: $root.removeFieldSorting"><i class="icon-trash"></i></a></td>
-        </tr>
-      </tbody>
-      <tfoot>
-        <tr style="padding-top: 20px">
-          <td colspan="2"><select data-bind="options: fields, value: selectedField" style="width:100%"></select></td>
-          <td width="30"><a class="btn btn-small" data-bind="click: $root.addFieldSorting"><i class="icon-plus"></i></a></td>
-        </tr>
-      </tfoot>
-    </table>
-
-    <div class="form-actions">
-      <a class="btn btn-primary btn-large" id="save-sorting">${_('Save Sorting')}</a>
-    </div>
+    <form method="POST" class="form-horizontal" data-bind="submit: submit">
+      <div class="section">
+        <div class="alert alert-info"><h4>${_('Sorting Fields')}</h4></div>
+        <div data-bind="visible: sortingFields().length == 0" style="padding-left: 10px;margin-bottom: 20px">
+          <em>${_('There are currently no Sorting Fields defined. Please add at least one from the bottom.')}</em>
+        </div>
+        <div data-bind="foreach: sortingFields">
+          <div class="bubble">
+            <strong><span data-bind="text: label"></span></strong>
+            <span style="color:#666;font-size: 12px">
+              (<span data-bind="text: field"></span> <i class="icon-arrow-up" data-bind="visible: asc == true"></i><i class="icon-arrow-down" data-bind="visible: asc == false"></i> )
+            </span>
+            <a class="btn btn-small" data-bind="click: $root.removeSortingField"><i class="icon-trash"></i></a>
+          </div>
+        </div>
+        <div class="clearfix"></div>
+        <div class="miniform">
+          ${_('Field')}
+          <select data-bind="options: fields, value: newFieldSelect"></select>
+          &nbsp;${_('Label')}
+          <input type="text" data-bind="value: newFieldLabel" class="input" />
+          &nbsp;${_('Sorting')}
+          <div class="btn-group" style="display: inline">
+            <button id="newFieldAsc" type="button" data-bind="css: {'active': newFieldAscDesc() == 'asc', 'btn': true}"><i class="icon-arrow-up"></i></button>
+            <button id="newFieldDesc" type="button" data-bind="css: {'active': newFieldAscDesc() == 'desc', 'btn': true}"><i class="icon-arrow-down"></i></button>
+          </div>
+          &nbsp;&nbsp;<a class="btn" data-bind="click: $root.addSortingField"><i class="icon-plus"></i> ${_('Add field to Sorting Fields')}</a>
+        </div>
+      </div>
+
+      <div class="form-actions" style="margin-top: 80px">
+        <button type="submit" class="btn btn-primary">${_('Save Sorting')}</button>
+        <a class="btn" href="${ url('search:admin') }"><i class="icon-list"></i> ${ _('Return to Core list') }</a>
+        <a class="btn" href="${ url('search:index') }"><i class="icon-search"></i> ${ _('Back to Search') }</a>
+      </div>
+    </form>
   </%def>
 </%layout:skeleton>
 
@@ -92,54 +98,58 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
 <script src="/static/ext/js/jquery/plugins/jquery-ui-draggable-droppable-sortable-1.8.23.min.js"></script>
 
 <script type="text/javascript">
-  $(document).ready(function () {
-    function ViewModel() {
-      var self = this;
-      self.fields = ko.observableArray(${ hue_core.fields | n,unicode });
-      self.fieldsSorting = ko.observableArray();
-
-      self.selectedField = ko.observable();
-
-      self.removeFieldSorting = function (sort) {
-        self.fieldsSorting.remove(sort);
-      };
-
-      self.addFieldSorting = function () {
-        self.fieldsSorting.push(self.selectedField());
-      };
-
-      self.submit = function () {
-        $.ajax("${ url('search:admin_core_sorting', core=hue_core.name) }", {
-          data: {'fields': ko.utils.stringifyJson(self.fieldsSorting)},
-          contentType: 'application/json',
-          type: 'POST',
-          complete: function (data) {
-            location.reload();
-          }
-        });
-      };
+
+  var SortingField = function (field, label, asc) {
+    return {
+      field:field,
+      label: label,
+      asc: asc
+    }
+  }
+
+  function ViewModel() {
+    var self = this;
+    self.fields = ko.observableArray(${ hue_core.fields | n,unicode });
+
+    self.sortingFields = ko.observableArray(ko.utils.arrayMap([], function (obj) {
+      return new SortingField(obj.field, obj.label, obj.asc);
+    }));
+
+    self.newFieldSelect = ko.observable();
+    self.newFieldLabel = ko.observable("");
+    self.newFieldAscDesc = ko.observable("asc");
+
+    self.removeSortingField = function (field) {
+      self.sortingFields.remove(field);
+    };
+
+    self.addSortingField = function () {
+      if (self.newFieldLabel() == ""){
+        self.newFieldLabel(self.newFieldSelect());
+      }
+      self.sortingFields.push(new SortingField(self.newFieldSelect(), self.newFieldLabel(), self.newFieldAscDesc()=="asc"));
+      self.newFieldLabel("");
+      self.newFieldAscDesc("asc");
+    };
+
+    self.submit = function () {
+      ##TODO: the sorting fields are in self.sortingFields
+      console.log(ko.utils.stringifyJson(self.sortingFields));
     };
+  };
+
+  var viewModel = new ViewModel();
+
+  $(document).ready(function () {
 
-    var viewModel = new ViewModel();
     ko.applyBindings(viewModel);
 
-    $("#fields").sortable({
-      placeholder: "placeholder",
-      update: function (event, ui) {
-        var reorderedFields = [];
-        $("#fields tr").each(function () {
-          reorderedFields.push($(this).data("field"));
-        });
-        viewModel.fieldsSorting([]);
-        viewModel.fieldsSorting(reorderedFields);
-      }
+    $("#newFieldAsc").click(function(){
+      viewModel.newFieldAscDesc("asc");
     });
-    $("#fields").disableSelection();
 
-    $("#save-sorting").click(function () {
-      ##TODO: save sorting
-      ## you can access to the fields like this: viewModel.fieldsSorting()
-      ## console.log(viewModel.fieldsSorting());
+    $("#newFieldDesc").click(function(){
+      viewModel.newFieldAscDesc("desc");
     });
 
   });

+ 76 - 3
apps/search/src/search/templates/admin_core_template.mako

@@ -29,9 +29,28 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
     padding: 0;
     padding-left:10px;
   }
+  .available-fields ul {
+    list-style: none;
+    margin-left: 0;
+  }
+  .available-fields ul li {
+    color: #666;
+  }
   .preview-row:nth-child(odd) {
     background-color:#f9f9f9;
   }
+  .tmpl {
+    border: 1px solid #CCC;
+    margin: 10px;
+    height: 80px;
+    cursor: pointer;
+  }
+  .tmpl:hover {
+    border: 1px solid #999;
+  }
+  .tmpl.selected {
+    border: 2px solid #999;
+  }
 </style>
 
 <%layout:skeleton>
@@ -55,6 +74,7 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
           <div class="span9">
             <div id="toolbar"></div>
             <div id="content-editor" class="clear">${_('Add your content here...')}</div>
+            <div id="load-template" class="btn-group"><a title="Load template" class="btn toolbar-btn toolbar-cmd"><i class="icon-paste" style="margin-top:2px;"></i></a></div>
           </div>
 
           <div class="span3">
@@ -67,7 +87,6 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
           </div>
         </div>
 
-
       </div>
       <div class="tab-pane" id="source">
         <div class="row-fluid">
@@ -93,9 +112,39 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
     </div>
 
     <div class="form-actions">
-      <a class="btn btn-primary btn-large" id="save-template">${_('Save Template')}</a>
+      <a class="btn btn-primary" id="save-template">${_('Save Template')}</a>
+      <a class="btn" href="${ url('search:admin') }"><i class="icon-list"></i> ${ _('Return to Core list') }</a>
+      <a class="btn" href="${ url('search:index') }"><i class="icon-search"></i> ${ _('Back to Search') }</a>
+    </div>
+
+    <div id="load-template-modal" class="modal hide fade">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal">&times;</button>
+        <h3>${_('Load template')}</h3>
+      </div>
+      <div class="modal-body">
+        <div class="tmpl">
+          <div class="row-fluid">
+            <div class="span1"><img src="http://twitter.com/api/users/profile_image/{{user_screen_name}}" style="margin:20px"></div>
+            <div class="span9">
+              <h5>{{user_name}}</h5>
+              {{text}}
+            </div>
+            <div class="span2"><br><a class="btn" href="https://twitter.com/{{user_screen_name}}/status/{{id}}" target="_blank"><i class="icon-twitter"></i></a></div>
+          </div>
+        </div>
+        <div class="tmpl">
+          <h5>{{user_name}} <span style="color:#999">({{user_screen_name}})</span></h5>
+          <p>{{text}}</p>
+        </div>
+      </div>
+      <div class="modal-footer">
+        <a href="#" class="btn" data-dismiss="modal">${_('Cancel')}</a>
+        <button type="button" id="load-template-btn" href="#" class="btn btn-primary" disabled="disabled">${_('Load template')}</button>
+      </div>
     </div>
 
+
   </%def>
 </%layout:skeleton>
 
@@ -136,7 +185,7 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
       lineNumbers: true
     });
 
-    $("#content-editor").freshereditor({toolbar_selector: "#toolbar", excludes: ['strikethrough', 'removeFormat', 'backcolor', 'insertorderedlist', 'insertheading1', 'insertheading2', 'superscript', 'subscript']});
+    $("#content-editor").freshereditor({toolbar_selector: "#toolbar", excludes: ['strikethrough', 'removeFormat', 'backcolor', 'insertorderedlist', 'justifyfull', 'insertheading1', 'insertheading2', 'superscript', 'subscript']});
     $("#content-editor").freshereditor("edit", true);
 
     // force refresh on tab change
@@ -161,6 +210,30 @@ ${ commonheader(_('Search'), "search", user) | n,unicode }
       }, 300);
     });
 
+    $("#load-template").appendTo($("#toolbar .btn-toolbar")).removeClass("hide");
+
+    $("#load-template-modal").modal({
+      show: false
+    });
+
+    $("#load-template .btn").click(function(){
+      $(".tmpl.selected").removeClass("selected");
+      $("#load-template-modal").modal("show");
+      $("#load-template-btn").attr("disabled", "disabled");
+    });
+
+    $(".tmpl").click(function(){
+      $(".tmpl.selected").removeClass("selected");
+      $(this).addClass("selected");
+      $("#load-template-btn").removeAttr("disabled");
+    });
+
+    $("#load-template-btn").click(function(){
+      $("#load-template-modal").modal("hide");
+      $("#content-editor").html($(".tmpl.selected").html());
+    });
+
+
     $("#save-template").click(function () {
       ##TODO: save template
       ## you can access it with: $("#content-editor").html()

+ 0 - 3
apps/search/src/search/templates/layout.mako

@@ -24,9 +24,6 @@
   <link rel="stylesheet" href="/search/static/css/admin.css">
 
   <div class="container-fluid">
-    <div class="pull-right">
-      <a class="btn" href="${ url('search:admin') }"><i class="icon-list"></i> ${ _('Core list') }</a>
-    </div>
     %if hasattr(caller, "title"):
       ${caller.title()}
     %else:

+ 29 - 0
apps/search/static/css/admin.css

@@ -15,3 +15,32 @@
 .availableFields ul li {
   cursor: pointer;
 }
+
+.form-actions {
+  padding-left: 20px!important;
+}
+
+.bubble {
+  float: left;
+  background-color: #EEEEEE;
+  border: 1px solid #E0E0E0;
+  padding: 8px;
+  margin: 4px;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+
+.section {
+  background-color: #FAFAFA;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+
+.miniform {
+  margin-top: 20px;
+  background-color: #F3F3F3;
+  padding: 10px;
+  border-top: 1px solid #DDD;
+}