[ckan-changes] [okfn/ckan] 5f5ecc: [2285] Hide query controls using css; and move the...

GitHub noreply at github.com
Tue Apr 24 14:58:40 UTC 2012


  Branch: refs/heads/feature-2285-embeddable-data-viewer
  Home:   https://github.com/okfn/ckan
  Commit: 5f5ecc87e1d27ac76eabe674960ce5007f6ae9b9
      https://github.com/okfn/ckan/commit/5f5ecc87e1d27ac76eabe674960ce5007f6ae9b9
  Author: Ian Murray <ian.murray at okfn.org>
  Date:   2012-04-23 (Mon, 23 Apr 2012)

  Changed paths:
    M ckan/templates/package/resource_embedded_dataviewer.html

  Log Message:
  -----------
  [2285] Hide query controls using css; and move the viewer to the top-left

... of the embeddable page.


diff --git a/ckan/templates/package/resource_embedded_dataviewer.html b/ckan/templates/package/resource_embedded_dataviewer.html
index fdc5a8d..6f9b4bf 100644
--- a/ckan/templates/package/resource_embedded_dataviewer.html
+++ b/ckan/templates/package/resource_embedded_dataviewer.html
@@ -14,41 +14,33 @@
     <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/graph-flot.css')}" />
     <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/map.css')}" />
     <style type="text/css">
-      .recline-query-editor form, .recline-query-editor .text-query {
-        height: 28px;
-      }
 
-      .recline-query-editor .pagination ul {
-        margin: 0;
-        padding: 0;
+      /* Hide the query controls */
+      .header {
+        display: none;
       }
 
-      /* needed for Chrome but not FF */
-      .header .recline-query-editor .add-on {
-        margin-left: -27px;
+      /* Hide CKAN footer */
+      .footer.outer {
+        display: none;
       }
 
-      /* needed for FF but not chrome */
-      .header .recline-query-editor .input-prepend {
-        vertical-align: top;
-      }
-    </style>
-    <!-- /data preview -->
-    <style type="text/css">
-      .resource-actions {
-        margin-right: 0;
+      /* Don't center the main container. And provide a little space to the
+         left and above the viewer.  This is for the graph-view, which, if a
+         small amount of room is not given, the y-axis' labels are uncomfortably
+         close to the edge of the viewport.
+      */
+      #main.container {
+        width: auto;
+        margin-left: 2px;
+        margin-top: 2px;
       }
-      .resource-actions .btn {
-        position: relative;
-        bottom: 3px;
-        padding: 8px 10px;
-      }
-      .resource-actions .download {
-        display: inline;
-      }
-      .resource-actions .download img {
-        margin: 0px 4px -4px 0;
+
+      /* Remove the border from the right-hand-side */
+      #content {
+        border: 0px;
       }
+
     </style>
     <script type="text/javascript">
       var preload_resource = ${h.literal(c.resource_json)};
@@ -63,8 +55,6 @@
 
   <div py:match="content">
     <div class="resource-preview">
-      <h3>Preview</h3>
-      <a class="permalink" href="">Permalink</a>
       <div id="ckanext-datapreview"></div>
     </div>
   </div>


================================================================
  Commit: 921c9a3c1f82adb3cb632bd00c537708a8233934
      https://github.com/okfn/ckan/commit/921c9a3c1f82adb3cb632bd00c537708a8233934
  Author: Ian Murray <ian.murray at okfn.org>
  Date:   2012-04-23 (Mon, 23 Apr 2012)

  Changed paths:
    M ckan/public/scripts/vendor/recline/css/data-explorer.css
    R ckan/public/scripts/vendor/recline/css/graph-flot.css
    A ckan/public/scripts/vendor/recline/css/graph.css
    A ckan/public/scripts/vendor/recline/css/grid.css
    M ckan/templates/package/resource_embedded_dataviewer.html
    M ckan/templates/package/resource_read.html

  Log Message:
  -----------
  [2285] Updated the recline stylesheets.

 - graph-flot.css renamed to graph.css
 - grid.css is new (style split out from data-explorerer.css)

This pulls in the recline read-only styles for the grid view.


diff --git a/ckan/public/scripts/vendor/recline/css/data-explorer.css b/ckan/public/scripts/vendor/recline/css/data-explorer.css
index 6cc5b0a..766a91e 100644
--- a/ckan/public/scripts/vendor/recline/css/data-explorer.css
+++ b/ckan/public/scripts/vendor/recline/css/data-explorer.css
@@ -78,320 +78,3 @@
   display: inline-block;
 }
 
-
-/**********************************************************
-  * Data Table
-  *********************************************************/
-
-.recline-grid .btn-group .dropdown-toggle {
-  padding: 1px 3px;
-  line-height: auto;
-}
-
-.recline-grid-container {
-  overflow: auto;
-  height: 550px;
-}
-
-.recline-grid {
-  border: 1px solid #ccc;
-  width: 100%;
-}
-
-.recline-grid td, .recline-grid th {
-  border-left: 1px solid #ccc;
-  padding: 3px 4px;
-  text-align: left;
-}
-
-.recline-grid tr td:first-child, .recline-grid tr th:first-child {
-  width: 20px;
-}
-
-/* direct borrowing from twitter buttons */
-.recline-grid th,
-.transform-column-view .expression-preview-table-wrapper th
-{
-  background-color: #e6e6e6;
-  background-repeat: no-repeat;
-  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));
-  background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
-  background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);
-  background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
-  background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
-  background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
-  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
-  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
-  color: #333;
-  border: 1px solid #ccc;
-  border-bottom-color: #bbb;
-  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-  -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-  -webkit-transition: 0.1s linear all;
-  -moz-transition: 0.1s linear all;
-  -ms-transition: 0.1s linear all;
-  -o-transition: 0.1s linear all;
-  transition: 0.1s linear all;
-}
-
-
-/**********************************************************
-  * Data Table Menus
-  *********************************************************/
-
-.column-header-menu, a.root-header-menu {
-  float: right;
-}
-
-.read-only a.row-header-menu {
-  display: none;
-}
-
-div.data-table-cell-content {
-  line-height: 1.2;
-  color: #222;
-  position: relative;
-}
-
-div.data-table-cell-content-numeric {
-  text-align: right;
-}
-
-a.data-table-cell-edit {
-  position: absolute;
-  top: 0;
-  right: 0;
-  display: block;
-  width: 25px;
-  height: 16px;
-  text-decoration: none;
-  background-image: url(images/edit-map.png);
-  background-repeat: no-repeat;
-  visibility: hidden;
-}
-
-a.data-table-cell-edit:hover {
-  background-position: -25px 0px;
-}
-
-.recline-grid td:hover .data-table-cell-edit {
-  visibility: visible;
-}
-
-div.data-table-cell-content-numeric > a.data-table-cell-edit {
-  left: 0px;
-  right: auto;
-}
-
-.data-table-value-nonstring {
-  color: #282;
-}
-
-.data-table-error {
-  color: red;
-}
-
-.data-table-cell-editor-editor {
-  overflow: hidden;
-  display: block;
-  width: 98%;
-  height: 3em;
-  font-family: monospace;
-  margin: 3px 0;
-}
-
-.data-table-cell-copypaste-editor {
-  overflow: hidden;
-  display: block;
-  width: 98%;
-  height: 10em;
-  font-family: monospace;
-  margin: 3px 0;
-}
-
-.data-table-cell-editor-action {
-  float: left;
-  vertical-align: bottom;
-  text-align: center;
-}
-
-.data-table-cell-editor-key {
-  font-size: 0.8em;
-  color: #999;
-}
-
-
-/**********************************************************
-  * Dialogs
-  *********************************************************/
-
-.dialog-overlay {
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  background: #666;
-  opacity: 0.5;
-}
-
-.dialog {
-  position: fixed;
-  left: 0;
-  width: 100%;
-  text-align: center;
-}
-
-.dialog-frame {
-  margin: 0 auto;
-  text-align: left;
-  background: white;
-  border: 1px solid #3a5774;
-}
-
-.dialog-border {
-  border: 4px solid #c1d9ff;
-}
-
-.dialog-header {
-  background: #e0edfe;
-  padding: 10px;
-  font-weight: bold;
-  font-size: 1.6em;
-  color: #000;
-  cursor: move;
-}
-
-.dialog-body {
-  overflow: auto;
-  font-size: 1.3em;
-  padding: 15px;
-}
-
-.dialog-instruction {
-  padding: 0 0 7px;
-}
-
-.dialog-footer {
-  font-size: 1.3em;
-  background: #eee;
-  padding: 10px;
-}
-
-.dialog-busy {
-  width: 400px;
-  border: none;
-  -moz-border-radius: 5px;
-  -webkit-border-radius: 5px;
-  border-radius: 5px;
-}
-
-/**********************************************************
-  * Transform Dialog
-  *********************************************************/
-
-#expression-preview-tabs .ui-tabs-nav li a {
-  padding: 0.15em 1em;
-}
-
-textarea.expression-preview-code {
-  font-family: monospace;
-  height: 5em;
-  vertical-align: top;
-}
-
-.expression-preview-parsing-status {
-  color: #999;
-}
-
-.expression-preview-parsing-status.error {
-  color: red;
-}
-
-#expression-preview-tabs-preview,
-#expression-preview-tabs-help,
-#expression-preview-tabs-history,
-#expression-preview-tabs-starred {
-  padding: 5px;
-  overflow: hidden;
-}
-
-#expression-preview-tabs-preview > div,
-#expression-preview-tabs-help > div,
-#expression-preview-tabs-history > div,
-#expression-preview-tabs-starred {
-  height: 200px;
-  overflow: auto;
-}
-
-#expression-preview-tabs-preview td, #expression-preview-tabs-preview th,
-#expression-preview-tabs-help td, #expression-preview-tabs-help th,
-#expression-preview-tabs-history td, #expression-preview-tabs-history th,
-#expression-preview-tabs-starred td, #expression-preview-tabs-starred th {
-  padding: 5px;
-}
-
-.expression-preview-table-wrapper {
-  padding: 7px;
-}
-
-.expression-preview-container td {
-  padding: 2px 5px;
-  border-top: 1px solid #ccc;
-}
-
-td.expression-preview-heading {
-  border-top: none;
-  background: #ddd;
-  font-weight: bold;
-}
-
-td.expression-preview-value {
-  max-width: 250px !important;
-  overflow-x: hidden;
-}
-
-.expression-preview-special-value {
-  color: #aaa;
-}
-
-.expression-preview-help-container h3 {
-  margin-top: 15px;
-  margin-bottom: 7px;
-  border-bottom: 1px solid #999;
-}
-
-.expression-preview-doc-item-title {
-  font-weight: bold;
-  text-align: right;
-}
-
-.expression-preview-doc-item-params {
-}
-
-.expression-preview-doc-item-returns {
-}
-
-.expression-preview-doc-item-desc {
-  color: #666;
-}
-
-
-/**********************************************************
-  * Read-only mode
-  *********************************************************/
-
-.read-only .no-hidden .recline-grid tr td:first-child,
-.read-only .no-hidden .recline-grid tr th:first-child
-{
-  display: none;
-}
-
-
-.read-only .write-op,
-.read-only a.data-table-cell-edit
-{
-  display: none;
-}
-
diff --git a/ckan/public/scripts/vendor/recline/css/graph-flot.css b/ckan/public/scripts/vendor/recline/css/graph-flot.css
deleted file mode 100644
index 88acf5f..0000000
--- a/ckan/public/scripts/vendor/recline/css/graph-flot.css
+++ /dev/null
@@ -1,50 +0,0 @@
-.recline-graph .graph {
-  height: 500px;
-  margin-right: 200px;
-}
-
-.recline-graph .legend table {
-  width: auto;
-  margin-bottom: 0;
-}
-
-.recline-graph .legend td {
-  padding: 5px;
-  line-height: 13px;
-}
-
-/**********************************************************
-  * Editor
-  *********************************************************/
-
-.recline-graph .editor {
-  float: right;
-  width: 200px;
-  padding-left: 0px;
-}
-
-.recline-graph .editor-info {
-  padding-left: 4px;
-}
-
-.recline-graph .editor-info {
-	cursor: pointer;
-}
-
-.recline-graph .editor form {
-  padding-left: 4px;
-}
-
-.recline-graph .editor select {
-	width: 100%;
-}
-
-.recline-graph .editor-info {
-	border-bottom: 1px solid #ddd;
-	margin-bottom: 10px;
-}
-
-.recline-graph .editor-hide-info p {
-	display: none;
-}
-
diff --git a/ckan/public/scripts/vendor/recline/css/graph.css b/ckan/public/scripts/vendor/recline/css/graph.css
new file mode 100644
index 0000000..88acf5f
--- /dev/null
+++ b/ckan/public/scripts/vendor/recline/css/graph.css
@@ -0,0 +1,50 @@
+.recline-graph .graph {
+  height: 500px;
+  margin-right: 200px;
+}
+
+.recline-graph .legend table {
+  width: auto;
+  margin-bottom: 0;
+}
+
+.recline-graph .legend td {
+  padding: 5px;
+  line-height: 13px;
+}
+
+/**********************************************************
+  * Editor
+  *********************************************************/
+
+.recline-graph .editor {
+  float: right;
+  width: 200px;
+  padding-left: 0px;
+}
+
+.recline-graph .editor-info {
+  padding-left: 4px;
+}
+
+.recline-graph .editor-info {
+	cursor: pointer;
+}
+
+.recline-graph .editor form {
+  padding-left: 4px;
+}
+
+.recline-graph .editor select {
+	width: 100%;
+}
+
+.recline-graph .editor-info {
+	border-bottom: 1px solid #ddd;
+	margin-bottom: 10px;
+}
+
+.recline-graph .editor-hide-info p {
+	display: none;
+}
+
diff --git a/ckan/public/scripts/vendor/recline/css/grid.css b/ckan/public/scripts/vendor/recline/css/grid.css
new file mode 100644
index 0000000..aeb9984
--- /dev/null
+++ b/ckan/public/scripts/vendor/recline/css/grid.css
@@ -0,0 +1,319 @@
+/**********************************************************
+  * (Data) Grid
+  *********************************************************/
+
+.recline-grid .btn-group .dropdown-toggle {
+  padding: 1px 3px;
+  line-height: auto;
+}
+
+.recline-grid-container {
+  overflow: auto;
+  height: 550px;
+}
+
+.recline-grid {
+  border: 1px solid #ccc;
+  width: 100%;
+}
+
+.recline-grid td, .recline-grid th {
+  border-left: 1px solid #ccc;
+  padding: 3px 4px;
+  text-align: left;
+}
+
+.recline-grid td {
+  vertical-align: top;
+}
+
+.recline-grid tr td:first-child, .recline-grid tr th:first-child {
+  width: 20px;
+}
+
+/* direct borrowing from twitter buttons */
+.recline-grid th,
+.transform-column-view .expression-preview-table-wrapper th
+{
+  background-color: #e6e6e6;
+  background-repeat: no-repeat;
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));
+  background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
+  background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);
+  background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
+  background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
+  background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
+  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+  color: #333;
+  border: 1px solid #ccc;
+  border-bottom-color: #bbb;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+  -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+  -webkit-transition: 0.1s linear all;
+  -moz-transition: 0.1s linear all;
+  -ms-transition: 0.1s linear all;
+  -o-transition: 0.1s linear all;
+  transition: 0.1s linear all;
+}
+
+
+/**********************************************************
+  * Data Table Menus
+  *********************************************************/
+
+.column-header-menu, a.root-header-menu {
+  float: right;
+}
+
+div.data-table-cell-content {
+  line-height: 1.2;
+  color: #222;
+  position: relative;
+}
+
+div.data-table-cell-content-numeric {
+  text-align: right;
+}
+
+a.data-table-cell-edit {
+  position: absolute;
+  top: 0;
+  right: 0;
+  display: block;
+  width: 25px;
+  height: 16px;
+  text-decoration: none;
+  background-image: url(images/edit-map.png);
+  background-repeat: no-repeat;
+  visibility: hidden;
+}
+
+a.data-table-cell-edit:hover {
+  background-position: -25px 0px;
+}
+
+.recline-grid td:hover .data-table-cell-edit {
+  visibility: visible;
+}
+
+div.data-table-cell-content-numeric > a.data-table-cell-edit {
+  left: 0px;
+  right: auto;
+}
+
+.data-table-value-nonstring {
+  color: #282;
+}
+
+.data-table-error {
+  color: red;
+}
+
+.data-table-cell-editor-editor {
+  overflow: hidden;
+  display: block;
+  width: 98%;
+  height: 3em;
+  font-family: monospace;
+  margin: 3px 0;
+}
+
+.data-table-cell-copypaste-editor {
+  overflow: hidden;
+  display: block;
+  width: 98%;
+  height: 10em;
+  font-family: monospace;
+  margin: 3px 0;
+}
+
+.data-table-cell-editor-action {
+  float: left;
+  vertical-align: bottom;
+  text-align: center;
+}
+
+.data-table-cell-editor-key {
+  font-size: 0.8em;
+  color: #999;
+}
+
+
+/**********************************************************
+  * Dialogs
+  *********************************************************/
+
+.dialog-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: #666;
+  opacity: 0.5;
+}
+
+.dialog {
+  position: fixed;
+  left: 0;
+  width: 100%;
+  text-align: center;
+}
+
+.dialog-frame {
+  margin: 0 auto;
+  text-align: left;
+  background: white;
+  border: 1px solid #3a5774;
+}
+
+.dialog-border {
+  border: 4px solid #c1d9ff;
+}
+
+.dialog-header {
+  background: #e0edfe;
+  padding: 10px;
+  font-weight: bold;
+  font-size: 1.6em;
+  color: #000;
+  cursor: move;
+}
+
+.dialog-body {
+  overflow: auto;
+  font-size: 1.3em;
+  padding: 15px;
+}
+
+.dialog-instruction {
+  padding: 0 0 7px;
+}
+
+.dialog-footer {
+  font-size: 1.3em;
+  background: #eee;
+  padding: 10px;
+}
+
+.dialog-busy {
+  width: 400px;
+  border: none;
+  -moz-border-radius: 5px;
+  -webkit-border-radius: 5px;
+  border-radius: 5px;
+}
+
+/**********************************************************
+  * Transform Dialog
+  *********************************************************/
+
+#expression-preview-tabs .ui-tabs-nav li a {
+  padding: 0.15em 1em;
+}
+
+textarea.expression-preview-code {
+  font-family: monospace;
+  height: 5em;
+  vertical-align: top;
+}
+
+.expression-preview-parsing-status {
+  color: #999;
+}
+
+.expression-preview-parsing-status.error {
+  color: red;
+}
+
+#expression-preview-tabs-preview,
+#expression-preview-tabs-help,
+#expression-preview-tabs-history,
+#expression-preview-tabs-starred {
+  padding: 5px;
+  overflow: hidden;
+}
+
+#expression-preview-tabs-preview > div,
+#expression-preview-tabs-help > div,
+#expression-preview-tabs-history > div,
+#expression-preview-tabs-starred {
+  height: 200px;
+  overflow: auto;
+}
+
+#expression-preview-tabs-preview td, #expression-preview-tabs-preview th,
+#expression-preview-tabs-help td, #expression-preview-tabs-help th,
+#expression-preview-tabs-history td, #expression-preview-tabs-history th,
+#expression-preview-tabs-starred td, #expression-preview-tabs-starred th {
+  padding: 5px;
+}
+
+.expression-preview-table-wrapper {
+  padding: 7px;
+}
+
+.expression-preview-container td {
+  padding: 2px 5px;
+  border-top: 1px solid #ccc;
+}
+
+td.expression-preview-heading {
+  border-top: none;
+  background: #ddd;
+  font-weight: bold;
+}
+
+td.expression-preview-value {
+  max-width: 250px !important;
+  overflow-x: hidden;
+}
+
+.expression-preview-special-value {
+  color: #aaa;
+}
+
+.expression-preview-help-container h3 {
+  margin-top: 15px;
+  margin-bottom: 7px;
+  border-bottom: 1px solid #999;
+}
+
+.expression-preview-doc-item-title {
+  font-weight: bold;
+  text-align: right;
+}
+
+.expression-preview-doc-item-params {
+}
+
+.expression-preview-doc-item-returns {
+}
+
+.expression-preview-doc-item-desc {
+  color: #666;
+}
+
+
+/**********************************************************
+  * Read-only mode
+  *********************************************************/
+
+.recline-read-only .no-hidden .recline-grid tr td:first-child,
+.recline-read-only .no-hidden .recline-grid tr th:first-child
+{
+  display: none;
+}
+
+.recline-read-only .recline-grid .write-op,
+.recline-read-only .recline-grid a.data-table-cell-edit
+{
+  display: none;
+}
+
+.recline-read-only a.row-header-menu {
+  display: none;
+}
+
diff --git a/ckan/templates/package/resource_embedded_dataviewer.html b/ckan/templates/package/resource_embedded_dataviewer.html
index 6f9b4bf..974b2f1 100644
--- a/ckan/templates/package/resource_embedded_dataviewer.html
+++ b/ckan/templates/package/resource_embedded_dataviewer.html
@@ -11,8 +11,9 @@
     <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/leaflet/0.3.1/leaflet.ie.css')}" />
     <![endif]-->
     <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/data-explorer.css')}" />
-    <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/graph-flot.css')}" />
+    <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/graph.css')}" />
     <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/map.css')}" />
+    <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/grid.css')}" />
     <style type="text/css">
 
       /* Hide the query controls */
diff --git a/ckan/templates/package/resource_read.html b/ckan/templates/package/resource_read.html
index e5809c7..d16f150 100644
--- a/ckan/templates/package/resource_read.html
+++ b/ckan/templates/package/resource_read.html
@@ -20,8 +20,9 @@
     <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/leaflet/0.3.1/leaflet.ie.css')}" />
     <![endif]-->
     <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/data-explorer.css')}" />
-    <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/graph-flot.css')}" />
+    <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/graph.css')}" />
     <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/map.css')}" />
+    <link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/grid.css')}" />
     <style type="text/css">
       .recline-query-editor form, .recline-query-editor .text-query {
         height: 28px;


================================================================
  Commit: 952306c214a171f8779207773b863ebadaf696b3
      https://github.com/okfn/ckan/commit/952306c214a171f8779207773b863ebadaf696b3
  Author: Ian Murray <ian.murray at okfn.org>
  Date:   2012-04-24 (Tue, 24 Apr 2012)

  Changed paths:
    M ckan/public/scripts/application.js
    A ckan/templates/_snippet/data-viewer-embed-dialog.html
    M ckan/templates/package/resource_read.html

  Log Message:
  -----------
  [2285] Added embeddable iframe using a modal dialog


diff --git a/ckan/public/scripts/application.js b/ckan/public/scripts/application.js
index 637dc7b..1690982 100644
--- a/ckan/public/scripts/application.js
+++ b/ckan/public/scripts/application.js
@@ -1305,11 +1305,11 @@ CKAN.DataPreview = function ($, my) {
     Backbone.history.start();
   };
 
-  my.makePermalink = function(explorerState) {
+  my.makeEmbedLink = function(explorerState) {
     var qs = recline.View.composeQueryString({
-			state:         explorerState.toJSON(),
-			state_version: 1
-		});
+      state:         explorerState.toJSON(),
+      state_version: 1
+    });
     return window.location.origin + window.location.pathname + '/embed' + qs;
   };
 
@@ -1358,11 +1358,17 @@ CKAN.DataPreview = function ($, my) {
         }
       });
 
-      var permalink = $('.permalink');
-      dataExplorer.state.bind('change', function() {
-        permalink.attr('href', my.makePermalink(dataExplorer.state));
-      });
-      permalink.attr('href', my.makePermalink(dataExplorer.state));
+      var embedLink = $('.embedLink');
+      var embedIframeText = $('.embedIframeText');
+
+      function updateLink() {
+        var link = my.makeEmbedLink(dataExplorer.state);
+        embedIframeText.val($.mustache('<iframe src="{{link}}"></iframe>', {link: link.replace(/"/g, '"')}));
+        embedLink.attr('href', link);
+      }
+
+      dataExplorer.state.bind('change', updateLink);
+      updateLink();
 
       // will have to refactor if this can get called multiple times
       Backbone.history.start();
diff --git a/ckan/templates/_snippet/data-viewer-embed-dialog.html b/ckan/templates/_snippet/data-viewer-embed-dialog.html
new file mode 100644
index 0000000..eb3e577
--- /dev/null
+++ b/ckan/templates/_snippet/data-viewer-embed-dialog.html
@@ -0,0 +1,26 @@
+<html
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:i18n="http://genshi.edgewall.org/i18n"
+  xmlns:py="http://genshi.edgewall.org/"
+  xmlns:xi="http://www.w3.org/2001/XInclude"
+  py:strip=""
+  >
+
+<div class="modal-data-viewer-embed-dialog modal fade in" style="display: none;"
+  py:def="data_viewer_embed_dialog()">
+  <div class="modal-header">
+    <a class="close" data-dismiss="modal">×</a>
+    <h3>
+      Embed Data Viewer
+    </h3>
+  </div>
+  <div class="modal-body">
+    <div>
+      <p><strong>Embed this view</strong> by copying this into your webpage:</p>
+      <textarea class="embedIframeText" style="width: 100%; height: 300px;">Stuff</textarea>
+    </div>
+    <a class="embedLink" href="">Preview</a>
+  </div>
+</div>
+
+</html>
diff --git a/ckan/templates/package/resource_read.html b/ckan/templates/package/resource_read.html
index d16f150..6adb52b 100644
--- a/ckan/templates/package/resource_read.html
+++ b/ckan/templates/package/resource_read.html
@@ -12,6 +12,7 @@
   py:strip="">
 
   <xi:include href="../_snippet/data-api-help.html" />
+  <xi:include href="../_snippet/data-viewer-embed-dialog.html" />
 
   <py:def function="optional_head">
     <!-- data preview -->
@@ -59,6 +60,19 @@
       .resource-actions .download img {
         margin: 0px 4px -4px 0;
       }
+      .preview-header {
+        padding-bottom: 13px;
+        padding-top: 0px;
+      }
+      .preview-header h3 {
+        display: inline;
+      }
+      .preview-header .btn {
+        float: right;
+        position: relative;
+        bottom: 6px;
+        padding: 8px 15px;
+      }
     </style>
     <script type="text/javascript">
       var preload_resource = ${h.literal(c.resource_json)};
@@ -105,6 +119,8 @@
     ${data_api_help(c.datastore_api)}
   </py:if>
 
+  ${data_viewer_embed_dialog()}
+
     <div class="quick-info">
       <dl>
         <dt>Last updated</dt>
@@ -152,8 +168,10 @@
     </div>
 
     <div class="resource-preview">
-      <h3>Preview</h3>
-      <a class="permalink" href="">Permalink</a>
+      <div class="preview-header">
+        <h3>Preview</h3>
+        <a class="btn btn-primary" data-toggle="modal" href=".modal-data-viewer-embed-dialog">Embed</a>
+      </div>
       <div id="ckanext-datapreview"></div>
     </div>
 


================================================================
  Commit: 9928c10c7f3da91d14435e992f878eb4a5161847
      https://github.com/okfn/ckan/commit/9928c10c7f3da91d14435e992f878eb4a5161847
  Author: Ian Murray <ian.murray at okfn.org>
  Date:   2012-04-24 (Tue, 24 Apr 2012)

  Changed paths:
    M ckan/controllers/package.py
    M ckan/public/scripts/application.js
    M ckan/templates/_snippet/data-viewer-embed-dialog.html
    M ckan/templates/package/resource_embedded_dataviewer.html

  Log Message:
  -----------
  [2285] Configurable width and height of the embedded viewer


diff --git a/ckan/controllers/package.py b/ckan/controllers/package.py
index 4d2edcf..ea23594 100644
--- a/ckan/controllers/package.py
+++ b/ckan/controllers/package.py
@@ -772,6 +772,10 @@ def resource_embedded_dataviewer(self, id, resource_id):
         
         c.recline_state = json.dumps(recline_state)
 
+        c.width = max(int(request.params.get('width', 500)), 100)
+        c.height = max(int(request.params.get('height', 500)), 100)
+        c.embedded = True
+
         return render('package/resource_embedded_dataviewer.html')
 
     def _parse_recline_state(self, state_version, raw_state):
diff --git a/ckan/public/scripts/application.js b/ckan/public/scripts/application.js
index 1690982..da94cfe 100644
--- a/ckan/public/scripts/application.js
+++ b/ckan/public/scripts/application.js
@@ -1361,13 +1361,26 @@ CKAN.DataPreview = function ($, my) {
       var embedLink = $('.embedLink');
       var embedIframeText = $('.embedIframeText');
 
+      var iframeWidth = $('.iframe-width');
+      var iframeHeight = $('.iframe-height');
+
       function updateLink() {
         var link = my.makeEmbedLink(dataExplorer.state);
-        embedIframeText.val($.mustache('<iframe src="{{link}}"></iframe>', {link: link.replace(/"/g, '"')}));
+        var width = iframeWidth.val();
+        var height = iframeHeight.val();
+        link += '&width='+width+'&height='+height;
+        embedIframeText.val($.mustache('<iframe width="{{width}}" height="{{height}}" src="{{link}}"></iframe>',
+                                       {
+                                         link: link.replace(/"/g, '"'),
+                                         width: width,
+                                         height: height
+                                       }));
         embedLink.attr('href', link);
       }
 
       dataExplorer.state.bind('change', updateLink);
+      iframeWidth.change(updateLink);
+      iframeHeight.change(updateLink);
       updateLink();
 
       // will have to refactor if this can get called multiple times
diff --git a/ckan/templates/_snippet/data-viewer-embed-dialog.html b/ckan/templates/_snippet/data-viewer-embed-dialog.html
index eb3e577..b0b382d 100644
--- a/ckan/templates/_snippet/data-viewer-embed-dialog.html
+++ b/ckan/templates/_snippet/data-viewer-embed-dialog.html
@@ -17,7 +17,12 @@
   <div class="modal-body">
     <div>
       <p><strong>Embed this view</strong> by copying this into your webpage:</p>
-      <textarea class="embedIframeText" style="width: 100%; height: 300px;">Stuff</textarea>
+      <textarea class="embedIframeText" style="width: 100%; height: 200px;"></textarea>
+      <p>Choose width and height in pixels:</p>
+      <label for="iframe-width">Width:</label>
+      <input class="iframe-width" name="iframe-width" value="800"/>
+      <label for="iframe-height">Height:</label>
+      <input class="iframe-height" name="iframe-height" value="500"/>
     </div>
     <a class="embedLink" href="">Preview</a>
   </div>
diff --git a/ckan/templates/package/resource_embedded_dataviewer.html b/ckan/templates/package/resource_embedded_dataviewer.html
index 974b2f1..dc54947 100644
--- a/ckan/templates/package/resource_embedded_dataviewer.html
+++ b/ckan/templates/package/resource_embedded_dataviewer.html
@@ -34,7 +34,6 @@
       #main.container {
         width: auto;
         margin-left: 2px;
-        margin-top: 2px;
       }
 
       /* Remove the border from the right-hand-side */
@@ -42,6 +41,23 @@
         border: 0px;
       }
 
+      #ckanext-datapreview {
+        width: ${c.width}px;
+        height: ${c.height}px;
+      }
+
+      .recline-grid-container {
+        height: ${c.height}px;
+      }
+
+      .recline-graph .graph {
+        height: ${c.height}px;
+      }
+
+      .recline-map .map {
+        height: ${c.height}px;
+      }
+
     </style>
     <script type="text/javascript">
       var preload_resource = ${h.literal(c.resource_json)};


================================================================
  Commit: 8eb071c70d9b2ca75022c3ff0b04cd64e115b467
      https://github.com/okfn/ckan/commit/8eb071c70d9b2ca75022c3ff0b04cd64e115b467
  Author: Ian Murray <ian.murray at okfn.org>
  Date:   2012-04-24 (Tue, 24 Apr 2012)

  Changed paths:
    A ckan/templates/_snippet/data-viewer-embed-branded-link.html
    M ckan/templates/package/resource_embedded_dataviewer.html

  Log Message:
  -----------
  [2285] Added branded link back to resource page.


diff --git a/ckan/templates/_snippet/data-viewer-embed-branded-link.html b/ckan/templates/_snippet/data-viewer-embed-branded-link.html
new file mode 100644
index 0000000..68d3453
--- /dev/null
+++ b/ckan/templates/_snippet/data-viewer-embed-branded-link.html
@@ -0,0 +1,17 @@
+<html
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:i18n="http://genshi.edgewall.org/i18n"
+  xmlns:py="http://genshi.edgewall.org/"
+  xmlns:xi="http://www.w3.org/2001/XInclude"
+  py:strip=""
+  >
+
+<p>Powered by <a href="${h.url_for(controller='package', action='resource_read', id=c.package.id, resource_id=c.resource.id)}">
+
+${g.site_title}
+<img width="64" src="${h.url_for_static(g.site_logo)}" alt="${g.site_title} Logo" title="${g.site_title} Logo" id="logo" />
+</a>
+</p>
+
+</html>
+
diff --git a/ckan/templates/package/resource_embedded_dataviewer.html b/ckan/templates/package/resource_embedded_dataviewer.html
index dc54947..7504862 100644
--- a/ckan/templates/package/resource_embedded_dataviewer.html
+++ b/ckan/templates/package/resource_embedded_dataviewer.html
@@ -74,6 +74,7 @@
     <div class="resource-preview">
       <div id="ckanext-datapreview"></div>
     </div>
+    <xi:include href="../_snippet/data-viewer-embed-branded-link.html" />
   </div>
 
   <py:def function="optional_footer">


================================================================
  Commit: 111187e4250e7425b963f747438b1ab55484e45d
      https://github.com/okfn/ckan/commit/111187e4250e7425b963f747438b1ab55484e45d
  Author: Ian Murray <ian.murray at okfn.org>
  Date:   2012-04-24 (Tue, 24 Apr 2012)

  Changed paths:
    M ckan/public/scripts/application.js
    M ckan/templates/package/resource_read.html

  Log Message:
  -----------
  [2285] Only show embed button when the dataexplorer is available


diff --git a/ckan/public/scripts/application.js b/ckan/public/scripts/application.js
index da94cfe..97af2f3 100644
--- a/ckan/public/scripts/application.js
+++ b/ckan/public/scripts/application.js
@@ -1383,6 +1383,8 @@ CKAN.DataPreview = function ($, my) {
       iframeHeight.change(updateLink);
       updateLink();
 
+      $('.preview-header .btn').show();
+
       // will have to refactor if this can get called multiple times
       Backbone.history.start();
     }
diff --git a/ckan/templates/package/resource_read.html b/ckan/templates/package/resource_read.html
index 6adb52b..f9cbf4d 100644
--- a/ckan/templates/package/resource_read.html
+++ b/ckan/templates/package/resource_read.html
@@ -170,7 +170,7 @@
     <div class="resource-preview">
       <div class="preview-header">
         <h3>Preview</h3>
-        <a class="btn btn-primary" data-toggle="modal" href=".modal-data-viewer-embed-dialog">Embed</a>
+        <a style="display: none;" class="btn btn-primary" data-toggle="modal" href=".modal-data-viewer-embed-dialog">Embed</a>
       </div>
       <div id="ckanext-datapreview"></div>
     </div>


================================================================
  Commit: 7e2d796c8d6fc1966e8ba11347638e1837e32866
      https://github.com/okfn/ckan/commit/7e2d796c8d6fc1966e8ba11347638e1837e32866
  Author: Ian Murray <ian.murray at okfn.org>
  Date:   2012-04-24 (Tue, 24 Apr 2012)

  Changed paths:
    M ckan/public/scripts/vendor/recline/css/map.css
    M ckan/public/scripts/vendor/recline/recline.js

  Log Message:
  -----------
  [2285] Pulled in latest changes from recline


diff --git a/ckan/public/scripts/vendor/recline/css/map.css b/ckan/public/scripts/vendor/recline/css/map.css
index 829d0c8..f1f2da2 100644
--- a/ckan/public/scripts/vendor/recline/css/map.css
+++ b/ckan/public/scripts/vendor/recline/css/map.css
@@ -18,6 +18,11 @@
 }
 
 .recline-map .editor select {
-	width: 100%;
+  width: 100%;
 }
 
+.recline-map .editor .editor-options {
+  margin-top: 10px;
+  border-top: 1px solid gray;
+  padding: 5px 0;
+}
diff --git a/ckan/public/scripts/vendor/recline/recline.js b/ckan/public/scripts/vendor/recline/recline.js
index e404d78..391296a 100644
--- a/ckan/public/scripts/vendor/recline/recline.js
+++ b/ckan/public/scripts/vendor/recline/recline.js
@@ -285,7 +285,8 @@ my.DocumentList = Backbone.Collection.extend({
 // * format: (optional) used to indicate how the data should be formatted. For example:
 //   * type=date, format=yyyy-mm-dd
 //   * type=float, format=percentage
-//   * type=float, format='###,###.##'
+//   * type=string, format=link (render as hyperlink)
+//   * type=string, format=markdown (render as markdown if Showdown available)
 // * is_derived: (default: false) attribute indicating this field has no backend data but is just derived from other fields (see below).
 // 
 // Following additional instance properties:
@@ -341,6 +342,22 @@ my.Field = Backbone.Model.extend({
       if (format === 'percentage') {
         return val + '%';
       }
+      return val;
+    },
+    'string': function(val, field, doc) {
+      var format = field.get('format');
+      if (format === 'link') {
+        return '<a href="VAL">VAL</a>'.replace(/VAL/g, val);
+      } else if (format === 'markdown') {
+        if (typeof Showdown !== 'undefined') {
+          var showdown = new Showdown.converter();
+          out = showdown.makeHtml(val);
+          return out;
+        } else {
+          return val;
+        }
+      }
+      return val;
     }
   }
 });
@@ -1626,9 +1643,12 @@ my.Map = Backbone.View.extend({
 
     if (!(docs instanceof Array)) docs = [docs];
 
+    var count = 0;
+    var wrongSoFar = 0;
     _.every(docs,function(doc){
+      count += 1;
       var feature = self._getGeometryFromDocument(doc);
-      if (typeof feature === 'undefined'){
+      if (typeof feature === 'undefined' || feature === null){
         // Empty field
         return true;
       } else if (feature instanceof Object){
@@ -1645,16 +1665,20 @@ my.Map = Backbone.View.extend({
         feature.properties.cid = doc.cid;
 
         try {
-            self.features.addGeoJSON(feature);
+          self.features.addGeoJSON(feature);
         } catch (except) {
-            var msg = 'Wrong geometry value';
-            if (except.message) msg += ' (' + except.message + ')';
+          wrongSoFar += 1;
+          var msg = 'Wrong geometry value';
+          if (except.message) msg += ' (' + except.message + ')';
+          if (wrongSoFar <= 10) {
             my.notify(msg,{category:'error'});
-            return false;
+          }
         }
       } else {
-        my.notify('Wrong geometry value',{category:'error'});
-        return false;
+        wrongSoFar += 1
+        if (wrongSoFar <= 10) {
+          my.notify('Wrong geometry value',{category:'error'});
+        }
       }
       return true;
     });
@@ -1687,13 +1711,17 @@ my.Map = Backbone.View.extend({
         return doc.attributes[this.state.get('geomField')];
       } else if (this.state.get('lonField') && this.state.get('latField')){
         // We'll create a GeoJSON like point object from the two lat/lon fields
-        return {
-          type: 'Point',
-          coordinates: [
-            doc.attributes[this.state.get('lonField')],
-            doc.attributes[this.state.get('latField')]
-            ]
-        };
+        var lon = doc.get(this.state.get('lonField'));
+        var lat = doc.get(this.state.get('latField'));
+        if (lon && lat) {
+          return {
+            type: 'Point',
+            coordinates: [
+              doc.attributes[this.state.get('lonField')],
+              doc.attributes[this.state.get('latField')]
+              ]
+          };
+        }
       }
       return null;
     }
@@ -1705,12 +1733,16 @@ my.Map = Backbone.View.extend({
   // If not found, the user can define them via the UI form.
   _setupGeometryField: function(){
     var geomField, latField, lonField;
-    this.state.set({
-      geomField: this._checkField(this.geometryFieldNames),
-      latField: this._checkField(this.latitudeFieldNames),
-      lonField: this._checkField(this.longitudeFieldNames)
-    });
     this.geomReady = (this.state.get('geomField') || (this.state.get('latField') && this.state.get('lonField')));
+    // should not overwrite if we have already set this (e.g. explicitly via state)
+    if (!this.geomReady) {
+      this.state.set({
+        geomField: this._checkField(this.geometryFieldNames),
+        latField: this._checkField(this.latitudeFieldNames),
+        lonField: this._checkField(this.longitudeFieldNames)
+      });
+      this.geomReady = (this.state.get('geomField') || (this.state.get('latField') && this.state.get('lonField')));
+    }
   },
 
   // Private: Check if a field in the current model exists in the provided
@@ -2172,8 +2204,8 @@ my.DataExplorer = Backbone.View.extend({
   initialize: function(options) {
     var self = this;
     this.el = $(this.el);
-    // Hash of 'page' views (i.e. those for whole page) keyed by page name
     this._setupState(options.state);
+    // Hash of 'page' views (i.e. those for whole page) keyed by page name
     if (options.views) {
       this.pageViews = options.views;
     } else {
@@ -2772,6 +2804,13 @@ this.recline.Backend = this.recline.Backend || {};
     // backends (see recline.Model.Dataset.initialize).
     __type__: 'base',
 
+
+    // ### readonly
+    //
+    // Class level attribute indicating that this backend is read-only (that
+    // is, cannot be written to).
+    readonly: true,
+
     // ### sync
     //
     // An implementation of Backbone.sync that will be used to override
@@ -2832,6 +2871,32 @@ this.recline.Backend = this.recline.Backend || {};
     query: function(model, queryObj) {
     },
 
+    // ### _makeRequest
+    // 
+    // Just $.ajax but in any headers in the 'headers' attribute of this
+    // Backend instance. Example:
+    //
+    // <pre>
+    // var jqxhr = this._makeRequest({
+    //   url: the-url
+    // });
+    // </pre>
+    _makeRequest: function(data) {
+      var headers = this.get('headers');
+      var extras = {};
+      if (headers) {
+        extras = {
+          beforeSend: function(req) {
+            _.each(headers, function(value, key) {
+              req.setRequestHeader(key, value);
+            });
+          }
+        };
+      }
+      var data = _.extend(extras, data);
+      return $.ajax(data);
+    },
+
     // convenience method to convert simple set of documents / rows to a QueryResult
     _docsToQueryResult: function(rows) {
       var hits = _.map(rows, function(row) {
@@ -2891,6 +2956,7 @@ this.recline.Backend = this.recline.Backend || {};
   // Note that this is a **read-only** backend.
   my.DataProxy = my.Base.extend({
     __type__: 'dataproxy',
+    readonly: true,
     defaults: {
       dataproxy_url: 'http://jsonpdataproxy.appspot.com'
     },
@@ -2955,36 +3021,39 @@ this.recline.Backend = this.recline.Backend || {};
   //
   // Connecting to [ElasticSearch](http://www.elasticsearch.org/).
   //
-  // To use this backend ensure your Dataset has one of the following
-  // attributes (first one found is used):
+  // Usage:
+  //
+  // <pre>
+  // var backend = new recline.Backend.ElasticSearch({
+  //   // optional as can also be provided by Dataset/Document
+  //   url: {url to ElasticSearch endpoint i.e. ES 'type/table' url - more info below}
+  //   // optional
+  //   headers: {dict of headers to add to each request}
+  // });
+  //
+  // @param {String} url: url for ElasticSearch type/table, e.g. for ES running
+  // on localhost:9200 with index // twitter and type tweet it would be:
+  // 
+  // <pre>http://localhost:9200/twitter/tweet</pre>
+  //
+  // This url is optional since the ES endpoint url may be specified on the the
+  // dataset (and on a Document by the document having a dataset attribute) by
+  // having one of the following (see also `_getESUrl` function):
   //
   // <pre>
   // elasticsearch_url
   // webstore_url
   // url
   // </pre>
-  //
-  // This should point to the ES type url. E.G. for ES running on
-  // localhost:9200 with index twitter and type tweet it would be
-  //
-  // <pre>http://localhost:9200/twitter/tweet</pre>
   my.ElasticSearch = my.Base.extend({
     __type__: 'elasticsearch',
-    _getESUrl: function(dataset) {
-      var out = dataset.get('elasticsearch_url');
-      if (out) return out;
-      out = dataset.get('webstore_url');
-      if (out) return out;
-      out = dataset.get('url');
-      return out;
-    },
+    readonly: false,
     sync: function(method, model, options) {
       var self = this;
       if (method === "read") {
         if (model.__type__ == 'Dataset') {
-          var base = self._getESUrl(model);
-          var schemaUrl = base + '/_mapping';
-          var jqxhr = $.ajax({
+          var schemaUrl = self._getESUrl(model) + '/_mapping';
+          var jqxhr = this._makeRequest({
             url: schemaUrl,
             dataType: 'jsonp'
           });
@@ -3003,11 +3072,77 @@ this.recline.Backend = this.recline.Backend || {};
             dfd.reject(arguments);
           });
           return dfd.promise();
+        } else if (model.__type__ == 'Document') {
+          var base = this._getESUrl(model.dataset) + '/' + model.id;
+          return this._makeRequest({
+            url: base,
+            dataType: 'json'
+          });
+        }
+      } else if (method === 'update') {
+        if (model.__type__ == 'Document') {
+          return this.upsert(model.toJSON(), this._getESUrl(model.dataset));
+        }
+      } else if (method === 'delete') {
+        if (model.__type__ == 'Document') {
+          var url = this._getESUrl(model.dataset);
+          return this.delete(model.id, url);
         }
-      } else {
-        alert('This backend currently only supports read operations');
       }
     },
+
+    // ### upsert
+    //
+    // create / update a document to ElasticSearch backend
+    //
+    // @param {Object} doc an object to insert to the index.
+    // @param {string} url (optional) url for ElasticSearch endpoint (if not
+    // defined called this._getESUrl()
+    upsert: function(doc, url) {
+      var data = JSON.stringify(doc);
+      url = url ? url : this._getESUrl();
+      if (doc.id) {
+        url += '/' + doc.id;
+      }
+      return this._makeRequest({
+        url: url,
+        type: 'POST',
+        data: data,
+        dataType: 'json'
+      });
+    },
+
+    // ### delete
+    //
+    // Delete a document from the ElasticSearch backend.
+    //
+    // @param {Object} id id of object to delete
+    // @param {string} url (optional) url for ElasticSearch endpoint (if not
+    // provided called this._getESUrl()
+    delete: function(id, url) {
+      url = url ? url : this._getESUrl();
+      url += '/' + id;
+      return this._makeRequest({
+        url: url,
+        type: 'DELETE',
+        dataType: 'json'
+      });
+    },
+
+    // ### _getESUrl
+    //
+    // get url to ElasticSearch endpoint (see above)
+    _getESUrl: function(dataset) {
+      if (dataset) {
+        var out = dataset.get('elasticsearch_url');
+        if (out) return out;
+        out = dataset.get('webstore_url');
+        if (out) return out;
+        out = dataset.get('url');
+        return out;
+      }
+      return this.get('url');
+    },
     _normalizeQuery: function(queryObj) {
       var out = queryObj.toJSON ? queryObj.toJSON() : _.extend({}, queryObj);
       if (out.q !== undefined && out.q.trim() === '') {
@@ -3044,7 +3179,7 @@ this.recline.Backend = this.recline.Backend || {};
       var queryNormalized = this._normalizeQuery(queryObj);
       var data = {source: JSON.stringify(queryNormalized)};
       var base = this._getESUrl(model);
-      var jqxhr = $.ajax({
+      var jqxhr = this._makeRequest({
         url: base + '/_search',
         data: data,
         dataType: 'jsonp'
@@ -3088,6 +3223,7 @@ this.recline.Backend = this.recline.Backend || {};
   // </pre>
   my.GDoc = my.Base.extend({
     __type__: 'gdoc',
+    readonly: true,
     getUrl: function(dataset) {
       var url = dataset.get('url');
       if (url.indexOf('feeds/list') != -1) {
@@ -3450,6 +3586,7 @@ this.recline.Backend = this.recline.Backend || {};
   //  </pre>
   my.Memory = my.Base.extend({
     __type__: 'memory',
+    readonly: false,
     initialize: function() {
       this.datasets = {};
     },
@@ -3537,7 +3674,8 @@ this.recline.Backend = this.recline.Backend || {};
           _.each(terms, function(term) {
             var foundmatch = false;
             dataset.fields.each(function(field) {
-              var value = rawdoc[field.id].toString();
+              var value = rawdoc[field.id];
+              if (value !== null) { value = value.toString(); }
               // TODO regexes?
               foundmatch = foundmatch || (value === term);
               // TODO: early out (once we are true should break to spare unnecessary testing)


================================================================
Compare: https://github.com/okfn/ckan/compare/b41095e...7e2d796


More information about the ckan-changes mailing list