- Make the "Filter" link above FlattenerGrids a button and not a link.
- Instead of IDs as links in some grid columns, have the ID show up in
plain text and have links with a more descriptive name sit next to the
ID.
- Correct the settings for saving grid columns on the Select URLs and
Review Attempt interfaces.
- Tiny i18n fixes (page titles)
- Fix lack of horizontal scrollbar on Select URLs interface, and also
fix the way that if you clicked on said scrollbar in a case where
your grid was taller than your browser window, the page would
automatically scroll up to focus on your grid header row, and you
couldn't actually manipulate the horizontal scrollbar. We sadly
pay for our horiz scrollbar with a doubled vertical scrollbar, but
possibly someone can figure the Right way to fix such layout
problems, which actually occur widely in similar interfaces in
Evergreen.
- Add buttons to download CSV on Select URLs and Review Attempt
interfaces.
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Signed-off-by: Mike Rylander <mrylander@gmail.com>
);
INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
- 'url_verify.select_urls',
- 'url_verify',
+ 'ui.grid_columns.url_verify.select_urls',
+ 'gui',
FALSE,
oils_i18n_gettext(
- 'url_verify.select_urls',
+ 'ui.grid_columns.url_verify.select_urls',
'Link Checker''s URL Selection interface''s saved columns',
'cust',
'label'
),
oils_i18n_gettext(
- 'url_verify.select_urls',
+ 'ui.grid_columns.url_verify.select_urls',
'Link Checker''s URL Selection interface''s saved columns',
'cust',
'description'
);
INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
- 'url_verify.review_attempt',
- 'url_verify',
+ 'ui.grid_columns.url_verify.review_attempt',
+ 'gui',
FALSE,
oils_i18n_gettext(
- 'url_verify.review_attempt',
+ 'ui.grid_columns.url_verify.review_attempt',
'Link Checker''s Review Attempt interface''s saved columns',
'cust',
'label'
),
oils_i18n_gettext(
- 'url_verify.review_attempt',
+ 'ui.grid_columns.url_verify.review_attempt',
'Link Checker''s Review Attempt interface''s saved columns',
'cust',
'description'
INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
- 'url_verify.select_urls',
- 'url_verify',
+ 'ui.grid_columns.url_verify.select_urls',
+ 'gui',
FALSE,
oils_i18n_gettext(
- 'url_verify.select_urls',
+ 'ui.grid_columns.url_verify.select_urls',
'Link Checker''s URL Selection interface''s saved columns',
'cust',
'label'
),
oils_i18n_gettext(
- 'url_verify.select_urls',
+ 'ui.grid_columns.url_verify.select_urls',
'Link Checker''s URL Selection interface''s saved columns',
'cust',
'description'
);
INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
- 'url_verify.review_attempt',
- 'url_verify',
+ 'ui.grid_columns.url_verify.review_attempt',
+ 'gui',
FALSE,
oils_i18n_gettext(
- 'url_verify.review_attempt',
+ 'ui.grid_columns.url_verify.review_attempt',
'Link Checker''s Review Attempt interface''s saved columns',
'cust',
'label'
),
oils_i18n_gettext(
- 'url_verify.review_attempt',
+ 'ui.grid_columns.url_verify.review_attempt',
'Link Checker''s Review Attempt interface''s saved columns',
'cust',
'description'
[% WRAPPER base.tt2 %]
-[% ctx.page_title = "Link Checker - Create Session" %]
+[% ctx.page_title = l("Link Checker - Create Session") %]
<script type="text/javascript">
dojo.require("dijit.form.Button");
dojo.require("dijit.form.CheckBox");
[% WRAPPER base.tt2 %]
-[% ctx.page_title = "Link Checker - Review Verification Attempt" %]
+[% ctx.page_title = l("Link Checker - Review Verification Attempt") %]
<script type="text/javascript">
dojo.require("dijit.form.Button");
dojo.require("openils.widget.FlattenerGrid");
<button dojoType="dijit.form.Button" onClick="grid.print();">
[% l("Print verification results") %]
</button>
+ <button dojoType="dijit.form.Button"
+ onClick="grid.downloadCSV('[% l("link-checker-results") %]',
+ progress_dialog);">[% l("Download CSV") %]</button>
</div>
</div>
<div class="oils-acq-basic-roomy">
-[% WRAPPER base.tt2 no_content_pane=1 %]
-[% ctx.page_title = "Link Checker - Select URLs" %]
+[% WRAPPER base.tt2 %]
+[% ctx.page_title = l("Link Checker - Select URLs") %]
<script type="text/javascript">
dojo.require("dijit.form.Button");
dojo.require("openils.widget.FlattenerGrid");
.url-verify-attempt-info { font-style: italic; }
#session-name-here { font-weight: normal; font-size: 90%; }
</style>
-<div class="oils-header-panel" dojoType="dijit.layout.ContentPane" layoutAlign="top">
- <div>[% ctx.page_title %] - <span id="session-name-here"></span></div>
- <div class="url-verify-button">
- <button dojoType="dijit.form.Button"
- onClick="grid.print();">[%
- l("Print URLs")
- %]</button>
- <button dojoType="dijit.form.Button"
- onClick="module.verify_selected();">[%
- l("Verify Selected URLs")
- %]</button>
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+ <div dojoType="dijit.layout.ContentPane"
+ layoutAlign="top" class="oils-header-panel">
+ <div>[% ctx.page_title %] - <span id="session-name-here"></span></div>
+ <div class="url-verify-button">
+ <button dojoType="dijit.form.Button"
+ onClick="grid.print();">[%
+ l("Print URLs")
+ %]</button>
+ <button dojoType="dijit.form.Button"
+ onClick="grid.downloadCSV('[% l("link-checker-urls") %]',
+ progress_dialog);">[% l("Download CSV") %]</button>
+ <button dojoType="dijit.form.Button"
+ onClick="module.verify_selected();">[%
+ l("Verify Selected URLs")
+ %]</button>
+ </div>
</div>
-</div>
-<div dojoType="dijit.layout.ContentPane" layoutAlign="bottom" style="height: 85%;">
+ <div class="oils-acq-basic-roomy"><!-- XXX keep for layout reasons --></div>
<table jsid="grid"
dojoType="openils.widget.FlattenerGrid"
columnPersistKey="'url_verify.select_urls'"
<th field="author" fpath="item.target_biblio_record_entry.simple_record.author"></th>
<th field="isbn" fpath="item.target_biblio_record_entry.simple_record.isbn" _visible="false"></th>
<th field="issn" fpath="item.target_biblio_record_entry.simple_record.issn" _visible="false"></th>
- <th field="bib_id" fpath="item.target_biblio_record_entry.id" _visible="false"></th>
+ <th style="text-align: center;" field="bib_id" fpath="item.target_biblio_record_entry.id" _visible="false"></th>
<!-- You do NOT want to add the "verifications" column to this
table with ffilter="true". That introduces a left join to a
table linked to the core table in has-many relationship. When
[% WRAPPER base.tt2 no_content_pane=1 %]
-[% ctx.page_title = "Link Checker" %]
+[% ctx.page_title = l("Link Checker") %]
<script type="text/javascript">
dojo.require("dijit.form.Button");
dojo.require("openils.widget.FlattenerGrid");
req.queryOptions = req.queryOptions || {};
req.abort = function() { console.warn("[unimplemented] abort()"); };
- /* If we were asked to fetch without any sort order specified (as
- * will happen when coming from fetchToPrint(), try to use the
- * last cached sort order, if any. */
+ /* If we were asked to fetch without any sort order specified,
+ * try to use the last cached sort order, if any. */
req.sort = req.sort || this._last_fetch_sort;
this._last_fetch_sort = req.sort;
});
},
- /* *** Nonstandard but public API - Please think hard about doing
- * things the Dojo Way whenever possible before extending the API
- * here. *** */
-
- /* fetchToPrint() acts like a lot like fetch(), but doesn't call
- * onBegin or onComplete. */
- "fetchToPrint": function(req) {
- var callback_scope = req.scope || dojo.global;
- var post_params;
-
- try {
- post_params = this._fetch_prepare(req);
- } catch (E) {
- if (typeof req.onError == "function")
- req.onError.call(callback_scope, E);
- else
- throw E;
- }
-
- var process_fetch_all = dojo.hitch(
- this, function(text) {
- this._retried_map_key_already = false;
-
- if (typeof req.onComplete == "function")
- req.onComplete.call(callback_scope, text, req);
- }
- );
-
- var process_error = dojo.hitch(
- this, function(response, ioArgs) {
- this._on_http_error(response, ioArgs, req, "fetchToPrint");
- }
- );
-
- this._fetch_execute(
- post_params,
- "text",
- "text/html",
- process_fetch_all,
- process_error
- );
-
- return req;
- },
-
/* *** Begin dojo.data.api.Read methods *** */
"getValue": function(
var callback_scope = req.scope || dojo.global;
var post_params;
+ /* Special options to support special operations (print and csv): */
+ req.flattenerOptions = dojo.mixin(
+ {}, /* target object */
+ { /* default values */
+ "handleAs": "json",
+ "contentType": "application/json"
+ },
+ req.flattenerOptions /* optional input */
+ );
+
try {
post_params = this._fetch_prepare(req);
} catch (E) {
req.onBegin.call(callback_scope, might_be_a_lie, req);
}
- console.debug(
- "about to call onItem for " + obj.length +
- " elements in the obj array"
- );
- dojo.forEach(
- obj,
- function(item) {
- /* Cache items internally. */
- self._current_items[item[self.fmIdentifier]] = item;
-
- if (typeof req.onItem == "function")
- req.onItem.call(callback_scope, item, req);
- }
- );
+ if (req.flattenerOptions.handleAs == "json") {
+ dojo.forEach(
+ obj,
+ function(item) {
+ /* Cache items internally. */
+ self._current_items[item[self.fmIdentifier]] = item;
+
+ if (typeof req.onItem == "function")
+ req.onItem.call(callback_scope, item, req);
+ }
+ );
+ }
if (typeof req.onComplete == "function")
req.onComplete.call(callback_scope, obj, req);
this._fetch_execute(
post_params,
- "json",
- "application/json",
+ req.flattenerOptions.handleAs,
+ req.flattenerOptions.contentType,
function(obj) { process_fetch(obj, fetch_time); },
process_error
);
module.format_bib_id = function(id) {
if (!id) return "";
- return "<a title='" + localeStrings.MARC_EDITOR_LINK +
+ return id + " [<a title='" + localeStrings.MARC_EDITOR_LINK +
"' href='javascript:void(0);' " +
"onclick='openils.URLVerify.ReviewAttempt.open_marc_editor(" +
- id + "); return false;'>" + id + "</a>";
+ id + "); return false;'>" + localeStrings.EDIT + "</a>]";
};
}());
if (!str)
return "";
- return "<a href='select_urls?session_id=" + str + "' title='" +
- localeStrings.REREVIEW + "'>" + str +
- "</a> <a href='create_session?clone=" + str + "' title='" +
+ return str + " [<a href='select_urls?session_id=" + str + "' title='" +
+ localeStrings.REREVIEW + "'>" + localeStrings.REREVIEW +
+ "</a>] [<a href='create_session?clone=" + str + "' title='" +
localeStrings.CLONE_SESSION + "'>" +
- localeStrings.CLONE_SESSION + "</a>";
+ localeStrings.CLONE_SESSION + "</a>]";
};
module.format_attempts = function(list) {
list, function(id) {
if (isNaN(id))
return "";
- return "<a title='" + localeStrings.REVIEW_ATTEMPT +
+ return id + " [<a title='" + localeStrings.REVIEW_ATTEMPT +
"' href='review_attempt?attempt_id=" + id + "'>" +
- id + "</a>";
+ localeStrings.REREVIEW + "</a>]";
}
- ).join(", ");
+ ).join(" / ");
};
}());
"NOTHING_SELECTED": "No rows are selected, so no action will be taken.",
"REVIEW_ATTEMPT": "Review this verification attempt",
"CLONE_SESSION": "Clone",
- "REREVIEW": "Review / Verify",
+ "REREVIEW": "Open",
"CLONING": "Cloning existing session ...",
"CLONE_SESSION_NAME": "Copy of ${0}",
"XPATH": "XPath",
"SELECT_MORE": "Click here to review all session URLs and/or select other URLs to verify",
"MARC_EDITOR_LINK": "Click to open MARC Editor for this record",
"MARC_EDITOR_TITLE": "Record ID #${0}",
- "MARC_EDITOR_SAVE_RECORD": "Save Record"
+ "MARC_EDITOR_SAVE_RECORD": "Save Record",
+ "EDIT": "Edit"
}
"iface": Components.interfaces.nsIFileOutputStream,
"cls": "@mozilla.org/network/file-output-stream;1"
},
+ "COS": {
+ "iface": Components.interfaces.nsIConverterOutputStream,
+ "cls": "@mozilla.org/intl/converter-output-stream;1"
+ },
"create": function(key) {
return Components.classes[this[key].cls].
createInstance(this[key].iface);
result == api.FP.iface.returnReplace)) {
if (!picker.file.exists())
picker.file.create(0, 0644); /* XXX hardcoded = bad */
+
var fos = api.create("FOS");
fos.init(picker.file, 42 /* WRONLY | CREAT | TRUNCATE */, 0644, 0);
- return fos.write(content, content.length);
+
+ var cos = api.create("COS");
+ cos.init(fos, "UTF-8", 0, 0); /* It's the 21st century. You don't
+ use ISO-8859-*. */
+ cos.writeString(content);
+ return cos.close();
} else {
return 0;
}
if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
dojo.provide("openils.widget.FlattenerGrid");
+ dojo.requireLocalization("openils.widget", "FlattenerGrid");
+
dojo.require("DojoSRF");
dojo.require("dojox.grid.DataGrid");
dojo.require("openils.FlattenerStore");
dojo.require("openils.widget.GridColumnPicker");
dojo.require("openils.widget.EditDialog"); /* includes EditPane */
dojo.require("openils.widget._GridHelperColumns");
+ dojo.require("openils.XUL");
dojo.declare(
"openils.widget.FlattenerGrid",
[dojox.grid.DataGrid, openils.widget._GridHelperColumns], {
+ /* Later, might think about whether this should really be an
+ * "object" property like this or a "class" one (in dojo speak,
+ * since those terms don't really apply in pure JS)... */
+ "localeStrings": dojo.i18n.getLocalization(
+ "openils.widget", "FlattenerGrid"
+ ),
+
/* These potential constructor arguments are useful to
* FlattenerGrid in their own right */
"columnReordering": true,
}
this.inherited(arguments);
+
+ this.focus.focusHeader = function() {
+ /* This prevents an unwanted automatic scroll of the
+ * user's browser to the header row of the grid whenever
+ * you touch the horizontal scrollbar. The prevented
+ * behavior was absolutely hateful, since if your grid was
+ * larger than your window, touching the horizontal scroll-
+ * bar meant scrolling up so that the same scrollbar was
+ * now off your screen, and you could not manipulate it.
+ *
+ * There may be a more targeted way to fix the problem,
+ * but this will do. */
+ console.log("focusHeader() suppressed");
+ };
},
"canSort": function(idx, skip_structure /* API abuse */) {
this.filterSemaphoreCallback();
}
if (!this.filterAlwaysInDiv) {
- dojo.create(
- "a", {
- "innerHTML": "Filter", /* XXX i18n */
- "href": "javascript:void(0);",
- "onclick": dojo.hitch(this, function() {
- this.filterUi.show();
- })
- }, this.linkHolder.domNode
+ new dijit.form.Button(
+ {
+ "label": "Filter", /* XXX i18n */
+ "onClick": dojo.hitch(
+ this, function() { this.filterUi.show(); }
+ )
+ },
+ dojo.create("span", null, this.linkHolder.domNode)
);
}
}
).length == 0;
},
+ "downloadCSV": function(filename_prefix, progress_dialog) {
+ filename_prefix = filename_prefix || "grid";
+ var localeStrings = this.localeStrings;
+
+ var mapkey_for_filename =
+ this.store.mapKey ? this.store.mapKey.substr(-8, 8) : "X";
+
+ var dispositionArgs = {
+ "defaultString": filename_prefix + "-" +
+ mapkey_for_filename + ".csv",
+ "defaultExtension": ".csv",
+ "filterName": localeStrings.CSV_FILTER_NAME,
+ "filterExtension": "*.csv",
+ "filterAll": true
+ };
+
+ var coal = this._columnOrderingAndLabels();
+ var req = {
+ "query": this.query,
+ "queryOptions": {
+ "columns": coal.columns,
+ "labels": coal.labels,
+ "all": true
+ },
+ "flattenerOptions": {
+ "contentType": "text/csv",
+ "handleAs": "text"
+ },
+ "onComplete": function(text) {
+ if (progress_dialog)
+ progress_dialog.attr("title", "");
+ progress_dialog.hide();
+ openils.XUL.contentToFileSaveDialog(
+ text, localeStrings.CSV_SAVE_DIALOG, dispositionArgs
+ );
+ }
+ };
+
+ if (progress_dialog) {
+ progress_dialog.attr("title", localeStrings.FETCHING_CSV);
+ progress_dialog.show(true);
+ }
+ this.store.fetch(req);
+ },
+
/* Print the same data that the Flattener is feeding to the
* grid, sorted the same way too. Remove limit and offset (i.e.,
* print it all) unless those are passed in to the print() method.
"columns": coal.columns,
"labels": coal.labels
},
+ "flattenerOptions": {
+ "handleAs": "text", "contentType": "text/html"
+ },
"onComplete": function(text) {
openils.Util.printHtmlString(text);
}
req.queryOptions.all = true;
}
- this.store.fetchToPrint(req);
+ this.store.fetch(req);
},
"printSelected": function() {
--- /dev/null
+{
+ "FILTER": "Filter",
+ "CSV_SAVE_DIALOG": "Save CSV Output As",
+ "CSV_FILTER_NAME": "CSV Files",
+ "FETCHING_CSV": "Retrieving CSV data from server ..."
+}