<field reporter:label="Notes" name="notes" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="URI Maps" name="uri_maps" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="URIs" name="uris" oils_persist:virtual="true" reporter:datatype="link"/>
- <field reporter:label="Sort Key" name="label_sortkey" reporter:datatype="text"/>
+ <field reporter:label="Call Number Sort Key" name="label_sortkey" reporter:datatype="text"/>
<field reporter:label="Classification Scheme" name="label_class" reporter:datatype="link"/>
<field reporter:label="Prefix" name="prefix" reporter:datatype="link"/>
<field reporter:label="Suffix" name="suffix" reporter:datatype="link"/>
<link field="billing_location" reltype="has_a" key="id" map="" class="aou"/>
</links>
</class>
- <class id="au" controller="open-ils.cstore" oils_obj:fieldmapper="actor::user" oils_persist:tablename="actor.usr" reporter:core="true" reporter:label="ILS User">
+ <class id="au" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::user" oils_persist:tablename="actor.usr" reporter:core="true" reporter:label="ILS User">
<fields oils_persist:primary="id" oils_persist:sequence="actor.usr_id_seq">
<field reporter:label="All Addresses" name="addresses" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="All Library Cards" name="cards" oils_persist:virtual="true" reporter:datatype="link"/>
<link field="reservations" reltype="has_many" key="usr" map="" class="bresv"/>
<link field="usr_activity" reltype="has_many" key="usr" map="" class="auact"/>
</links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <retrieve permission="VIEW_USER" context_field="home_ou" />
+ </actions>
+ </permacrud>
</class>
<class id="cuat" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::usr_activity_type" oils_persist:tablename="config.usr_activity_type" reporter:label="User Activity Type">
<fields oils_persist:primary="id" oils_persist:sequence="config.usr_activity_type_id_seq">
</permacrud>
</class>
- <!-- A note: Please update alhr when updating ahr -->
+ <!-- A note: Please update alhr and ahopl when updating ahr -->
<class id="ahr" controller="open-ils.cstore" oils_obj:fieldmapper="action::hold_request" oils_persist:tablename="action.hold_request" reporter:core="true" reporter:label="Hold Request">
<fields oils_persist:primary="id" oils_persist:sequence="action.hold_request_id_seq">
<field reporter:label="Status" name="status" oils_persist:virtual="true" />
<field reporter:label="Hold ID" name="id" reporter:datatype="id" />
<field reporter:label="Notifications Phone Number" name="phone_notify" reporter:datatype="text"/>
<field reporter:label="Notifications SMS Number" name="sms_notify" reporter:datatype="text"/>
- <field reporter:label="Notifications SMS Carrier" name="sms_carrier" reporter:datatype="text"/>
+ <field reporter:label="Notifications SMS Carrier" name="sms_carrier" reporter:datatype="link"/>
<field reporter:label="Pickup Library" name="pickup_lib" reporter:datatype="org_unit"/>
<field reporter:label="Last Targeting Date/Time" name="prev_check_time" reporter:datatype="timestamp"/>
<field reporter:label="Requesting Library" name="request_lib" reporter:datatype="org_unit"/>
<link field="cancel_cause" reltype="might_have" key="id" map="" class="ahrcc"/>
<link field="notes" reltype="has_many" key="hold" map="" class="ahrn"/>
<link field="current_shelf_lib" reltype="has_a" key="id" map="" class="aou"/>
- <link field="sms_carrier" reltype="might_have" key="code" map="" class="csc"/>
+ <link field="sms_carrier" reltype="has_a" key="id" map="" class="csc"/>
</links>
</class>
+ <class id="ahopl" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::hold_on_pull_list" reporter:label="Hold On Pull List" oils_persist:readonly="true">
+ <oils_persist:source_definition><![CDATA[
+ SELECT
+ ahr.*,
+ COALESCE(acplo.position, 999) AS
+ copy_location_order_position,
+ CASE WHEN au.alias IS NOT NULL THEN
+ au.alias
+ ELSE
+ REGEXP_REPLACE(ARRAY_TO_STRING(ARRAY[
+ COALESCE(au.family_name, ''),
+ COALESCE(au.suffix, ''),
+ ', ',
+ COALESCE(au.prefix, ''),
+ COALESCE(au.first_given_name, ''),
+ COALESCE(au.second_given_name, '')
+ ], ' '), E'\\s+,', ',')
+ END AS usr_display_name,
+ TRIM(acnp.label || ' ' || acn.label || ' ' || acns.label)
+ AS call_number_label,
+ siss.label AS issuance_label,
+ (ahr.usr <> ahr.requestor) AS is_staff_hold
+ FROM action.hold_request ahr
+ JOIN asset.copy acp ON (acp.id = ahr.current_copy)
+ JOIN asset.call_number acn ON (acp.call_number = acn.id)
+ JOIN asset.call_number_prefix acnp ON (acn.prefix = acnp.id)
+ JOIN asset.call_number_suffix acns ON (acn.suffix = acns.id)
+ JOIN actor.usr au ON (au.id = ahr.usr)
+ LEFT JOIN serial.issuance siss
+ ON (ahr.hold_type = 'I' AND siss.id = ahr.target)
+ LEFT JOIN asset.copy_location_order acplo
+ ON (acp.location = acplo.location AND
+ acp.circ_lib = acplo.org)
+ WHERE
+ ahr.capture_time IS NULL AND
+ ahr.cancel_time IS NULL AND
+ (ahr.expire_time is NULL OR ahr.expire_time > NOW())
+ ]]></oils_persist:source_definition>
+ <fields oils_persist:primary="id">
+ <field reporter:label="Status" name="status" oils_persist:virtual="true" />
+ <field reporter:label="Transit" name="transit" oils_persist:virtual="true" />
+ <field reporter:label="Capture Date/Time" name="capture_time" reporter:datatype="timestamp"/>
+ <field reporter:label="Currently Targeted Copy" name="current_copy" />
+ <field reporter:label="Notify by Email?" name="email_notify" reporter:datatype="bool"/>
+ <field reporter:label="Hold Expire Date/Time" name="expire_time" reporter:datatype="timestamp"/>
+ <field reporter:label="Fulfilling Library" name="fulfillment_lib" reporter:datatype="org_unit"/>
+ <field reporter:label="Fulfilling Staff" name="fulfillment_staff" />
+ <field reporter:label="Fulfillment Date/Time" name="fulfillment_time" reporter:datatype="timestamp"/>
+ <field reporter:label="Hold Type" name="hold_type" reporter:datatype="text"/>
+ <field reporter:label="Holdable Formats (for M-type hold)" name="holdable_formats" reporter:datatype="text"/>
+ <field reporter:label="Hold ID" name="id" reporter:datatype="id" />
+ <field reporter:label="Notifications Phone Number" name="phone_notify" reporter:datatype="text"/>
+ <field reporter:label="Notifications SMS Number" name="sms_notify" reporter:datatype="text"/>
+ <field reporter:label="Notifications SMS Carrier" name="sms_carrier" reporter:datatype="link"/>
+ <field reporter:label="Pickup Library" name="pickup_lib" reporter:datatype="org_unit"/>
+ <field reporter:label="Last Targeting Date/Time" name="prev_check_time" reporter:datatype="timestamp"/>
+ <field reporter:label="Requesting Library" name="request_lib" reporter:datatype="org_unit"/>
+ <field reporter:label="Request Date/Time" name="request_time" reporter:datatype="timestamp"/>
+ <field reporter:label="Requesting User" name="requestor" reporter:datatype="link"/>
+ <field reporter:label="Item Selection Depth" name="selection_depth" />
+ <field reporter:label="Selection Locus" name="selection_ou" reporter:datatype="org_unit"/>
+ <field reporter:label="Target Object ID" name="target" reporter:datatype="link"/>
+ <field reporter:label="Hold User" name="usr" reporter:datatype="link"/>
+ <field reporter:label="Hold Cancel Date/Time" name="cancel_time" reporter:datatype="timestamp"/>
+ <field reporter:label="Notify Time" name="notify_time" oils_persist:virtual="true" reporter:datatype="timestamp"/>
+ <field reporter:label="Notify Count" name="notify_count" oils_persist:virtual="true" reporter:datatype="int" />
+ <field reporter:label="Notifications" name="notifications" oils_persist:virtual="true" reporter:datatype="link"/>
+ <field reporter:label="Bib Record link" name="bib_rec" oils_persist:virtual="true" reporter:datatype="link"/>
+ <field reporter:label="Eligible Copies" name="eligible_copies" oils_persist:virtual="true" reporter:datatype="link"/>
+ <field reporter:label="Currently Frozen" name="frozen" reporter:datatype="bool"/>
+ <field reporter:label="Thaw Date (if frozen)" name="thaw_date" reporter:datatype="timestamp"/>
+ <field reporter:label="Shelf Time" name="shelf_time" reporter:datatype="timestamp"/>
+ <field reporter:label="Cancelation cause" name="cancel_cause" reporter:datatype="link" />
+ <field reporter:label="Cancelation note" name="cancel_note" reporter:datatype="text" />
+ <field reporter:label="Top of Queue" name="cut_in_line" reporter:datatype="bool" />
+ <field reporter:label="Is Mint Condition" name="mint_condition" reporter:datatype="bool" />
+ <field reporter:label="Shelf Expire Time" name="shelf_expire_time" reporter:datatype="timestamp"/>
+ <field reporter:label="Notes" name="notes" reporter:datatype="link" oils_persist:virtual="true"/>
+ <field reporter:label="Current Shelf Lib" name="current_shelf_lib" reporter:datatype="org_unit"/>
+ <field reporter:label="Copy Location Sort Order" name="copy_location_order_position" reporter:datatype="int" />
+ <field reporter:label="User Display Name" name="usr_display_name" reporter:datatype="text" />
+ <field reporter:label="Call Number Label" name="call_number_label" reporter:datatype="text" />
+ <field reporter:label="Issuance Label" name="issuance_label" reporter:datatype="text" />
+ <field reporter:label="Is Staff Hold?" name="is_staff_hold" reporter:datatype="bool" />
+ </fields>
+ <links>
+ <link field="fulfillment_lib" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="fulfillment_staff" reltype="has_a" key="id" map="" class="au"/>
+ <link field="pickup_lib" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="selection_ou" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="requestor" reltype="has_a" key="id" map="" class="au"/>
+ <link field="current_copy" reltype="has_a" key="id" map="" class="acp"/>
+ <link field="usr" reltype="has_a" key="id" map="" class="au"/>
+ <link field="request_lib" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="transit" reltype="might_have" key="hold" map="" class="ahtc"/>
+ <link field="notifications" reltype="has_many" key="hold" map="" class="ahn"/>
+ <link field="eligible_copies" reltype="has_many" key="hold" map="target_copy" class="ahcm"/>
+ <link field="bib_rec" reltype="might_have" key="id" map="" class="rhrr"/>
+ <link field="cancel_cause" reltype="might_have" key="id" map="" class="ahrcc"/>
+ <link field="notes" reltype="has_many" key="hold" map="" class="ahrn"/>
+ <link field="current_shelf_lib" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="sms_carrier" reltype="has_a" key="id" map="" class="csc"/>
+ </links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <retrieve permission="VIEW_HOLD" context_field="pickup_lib" />
+ </actions>
+ </permacrud>
+ </class>
<class id="alhr" controller="open-ils.cstore" oils_obj:fieldmapper="action::last_hold_request" reporter:label="Last Captured Hold Request" oils_persist:readonly="true">
<oils_persist:source_definition>
SELECT ahr.* FROM action.hold_request ahr JOIN (SELECT current_copy, MAX(capture_time) AS capture_time FROM action.hold_request WHERE capture_time IS NOT NULL GROUP BY current_copy)x USING (current_copy, capture_time)
<link field="entries" reltype="has_many" key="stat_cat" map="" class="asce"/>
</links>
</class>
- <class id="ac" controller="open-ils.cstore" oils_obj:fieldmapper="actor::card" oils_persist:tablename="actor.card" reporter:label="Library Card">
+ <class id="ac" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::card" oils_persist:tablename="actor.card" reporter:label="Library Card">
<fields oils_persist:primary="id" oils_persist:sequence="actor.card_id_seq">
<field reporter:label="IsActive?" name="active" reporter:datatype="bool"/>
<field reporter:label="Barcode" name="barcode" reporter:datatype="text"/>
<links>
<link field="usr" reltype="has_a" key="id" map="" class="au"/>
</links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <retrieve permission="VIEW_USER">
+ <context link="usr" field="home_ou" />
+ </retrieve>
+ </actions>
+ </permacrud>
</class>
<class id="actscsf" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::stat_cat_sip_fields" oils_persist:tablename="actor.stat_cat_sip_fields" reporter:label="SIP Statistical Category Field Identifier">
<fields oils_persist:primary="field">
<link field="folder" reltype="has_a" key="id" map="" class="rof"/>
</links>
</class>
- <class id="rmsr" controller="open-ils.reporter-store open-ils.cstore" oils_obj:fieldmapper="reporter::materialized_simple_record" oils_persist:tablename="reporter.materialized_simple_record" reporter:label="Fast Simple Record Extracts">
+ <class id="rmsr" controller="open-ils.reporter-store open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="reporter::materialized_simple_record" oils_persist:tablename="reporter.materialized_simple_record" reporter:label="Fast Simple Record Extracts">
<fields oils_persist:primary="id">
<field reporter:label="Record ID" name="id" reporter:datatype="id" />
<field reporter:label="Fingerprint" name="fingerprint" reporter:datatype="text"/>
<links>
<link field="biblio_record" reltype="might_have" key="id" map="" class="bre"/>
</links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <retrieve />
+ </actions>
+ </permacrud>
</class>
<class id="rssr" controller="open-ils.reporter-store" oils_obj:fieldmapper="reporter::super_simple_record" oils_persist:tablename="reporter.super_simple_record" reporter:label="Simple Record Extracts">
<fields oils_persist:primary="id">
my $new_join;
if ($reltype eq "has_a") {
$new_join = {
+ type => "left",
class => $hint,
fkey => $piece,
field => $field
};
} elsif ($reltype eq "has_many" or $reltype eq "might_have") {
$new_join = {
+ type => "left",
class => $hint,
fkey => $last_ident,
field => $field
my $reltype = get_attribute( $attribute_list, 'reltype' );
my $key = get_attribute( $attribute_list, 'key' );
my $class = get_attribute( $attribute_list, 'class' );
+ my $map = get_attribute( $attribute_list, 'map' );
$$fieldmap{$fm}{links}{ $field } =
{ class => $class,
reltype => $reltype,
key => $key,
+ map => $map
};
}
}
"prio" => 0,
"code" => sub {
$_[0]->content_type("text/html; charset=utf-8");
- print html_ish_output( @_, 'FlatFielder2HTML.xsl' );
- return Apache2::Const::OK;
+ return html_ish_output( @_, 'FlatFielder2HTML.xsl' );
}
},
"application/xml" => {
$fs->setAttribute("FS_key", $args->{key}) if $args->{key};
$dom->setDocumentElement($fs);
+ my @columns;
+ my %column_labels;
+ if (@{$args->{columns}}) {
+ @columns = @{$args->{columns}};
+ if (@{$args->{labels}}) {
+ my @labels = @{$args->{labels}};
+ $column_labels{$columns[$_]} = $labels[$_] for (0..$#labels);
+ }
+ }
+
my $rownum = 1;
for my $i (@{$$args{data}}) {
my $item = $dom->createElement("row");
$item->setAttribute('ordinal', $rownum);
$rownum++;
- for my $k (keys %$i) {
+ @columns = keys %$i unless @columns;
+ for my $k (@columns) {
my $val = $dom->createElement('column');
- $val->setAttribute('name', $k);
- $val->appendText($i->{$k});
+ my $datum = $i->{$k};
+ $datum = join(" ", @$datum) if ref $datum eq 'ARRAY';
+
+ $val->setAttribute('name', $column_labels{$k} || $k);
+ $val->appendText($datum);
$item->addChild($val);
}
$fs->addChild($item);
$args{key} = $cgi->param('key');
$args{id_field} = $cgi->param('identifier');
$args{label_field} = $cgi->param('label');
+ $args{columns} = [ $cgi->param('columns') ];
+ $args{labels} = [ $cgi->param('labels') ];
my $fielder = OpenSRF::AppSession->create('open-ils.fielder');
if ($args{map}) {
),
'string'
);
+
+INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
+ 'ui.grid_columns.circ.hold_pull_list',
+ 'gui',
+ FALSE,
+ oils_i18n_gettext(
+ 'ui.grid_columns.circ.hold_pull_list',
+ 'Hold Pull List',
+ 'cust',
+ 'label'
+ ),
+ oils_i18n_gettext(
+ 'ui.grid_columns.circ.hold_pull_list',
+ 'Hold Pull List Saved Column Settings',
+ 'cust',
+ 'description'
+ ),
+ 'string'
+);
+
SELECT setval( 'config.sms_carrier_id_seq', 1000 );
INSERT INTO config.sms_carrier VALUES
--- /dev/null
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
+ 'ui.grid_columns.circ.hold_pull_list',
+ 'gui',
+ FALSE,
+ oils_i18n_gettext(
+ 'ui.grid_columns.circ.hold_pull_list',
+ 'Hold Pull List',
+ 'cust',
+ 'label'
+ ),
+ oils_i18n_gettext(
+ 'ui.grid_columns.circ.hold_pull_list',
+ 'Hold Pull List Saved Column Settings',
+ 'cust',
+ 'description'
+ ),
+ 'string'
+);
+
+COMMIT;
--- /dev/null
+[% WRAPPER base.tt2 %]
+[% ctx.page_title = 'Hold Pull List' %]
+<script type="text/javascript">
+ dojo.require("dijit.form.Button");
+ dojo.require("openils.widget.OrgUnitFilteringSelect");
+ dojo.require("openils.widget.FlattenerGrid");
+
+ var map_extras = {
+ "copy_circ_lib": {
+ "path": "current_copy.circ_lib",
+ "filter": true
+ },
+ "call_number_sort_key": {
+ "path": "current_copy.call_number.label_sortkey",
+ "sort" :true
+ }
+ };
+
+ function set_grid_query_from_org_selector() {
+ grid.query = {
+ "copy_circ_lib": org_selector.attr("value")
+ };
+ grid.refresh();
+ }
+
+ function prepare_org_selector(perm) {
+ new openils.User().buildPermOrgSelector(
+ perm, org_selector, null,
+ function() {
+ dojo.connect(
+ org_selector, "onChange", set_grid_query_from_org_selector
+ );
+ set_grid_query_from_org_selector();
+ }
+ );
+ }
+
+ openils.Util.addOnLoad(
+ function() {
+ prepare_org_selector("VIEW_HOLD");
+ }
+ );
+
+</script>
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+ <div dojoType="dijit.layout.ContentPane"
+ layoutAlign="top" class="oils-header-panel">
+ <div>Hold Pull List</div>
+ <div>
+ <button dojoType="dijit.form.Button"
+ onClick="grid.print();">Print Pull List</button>
+ </div>
+ </div>
+ <div class="oils-acq-basic-roomy">
+ <label for="org_selector">Show the pull list for:</label>
+ <select
+ id="org_selector" jsId="org_selector"
+ dojoType="openils.widget.OrgUnitFilteringSelect"
+ searchAttr="name" labelAttr="name">
+ </select>
+ </div>
+ <table
+ jsid="grid"
+ dojoType="openils.widget.FlattenerGrid"
+ columnPersistKey='"circ.hold_pull_list"'
+ autoHeight="10"
+ editOnEnter="false"
+ hideSelector="true"
+ autoCoreFields="true"
+ autoFieldFields="['current_copy','current_copy.call_number.record.simple_record']"
+ editStyle="pane"
+ showLoadFilter="true"
+ fmClass="'ahopl'"
+ defaultSort="['copy_location_sort_order','call_number_sort_key']"
+ mapExtras="map_extras"
+ sortFieldReMap="{call_number_label: 'call_number_sort_key'}"
+ fetchLock="true"
+ query="{}">
+ <thead>
+ <tr>
+ <th field="shelving_loc" fpath="current_copy.location.name" ffilter="true">Shelving Location</th>
+ <th field="call_number_label" fpath="call_number_label"></th>
+ <th field="author" fpath="current_copy.call_number.record.simple_record.author">Author</th>
+ <th field="title" fpath="current_copy.call_number.record.simple_record.title">Title</th>
+ <th field="barcode" fpath="current_copy.barcode"></th>
+ <th field="parts" fpath="current_copy.parts.label" fsort="false">Parts</th>
+ <th field="notes" fpath="notes.body" fsort="false" _visible="false">Hold Notes</th>
+ <th field="patron_barcode" fpath="usr.card.barcode" _visible="false">Patron Barcode</th>
+ <th field="pickup_lib_name" fpath="pickup_lib.name" _visible="false">Pickup Library</th>
+ <th field="pickup_lib_shortname" fpath="pickup_lib.shortname" _visible="false">Pickup Library (Shortname)</th>
+ <th field="request_lib_name" fpath="request_lib.name" _visible="false">Request Library</th>
+ <th field="request_lib_shortname" fpath="request_lib.shortname" _visible="false">Request Library (Shortname)</th>
+ <th field="selection_ou" fpath="selection_ou.shortname" _visible="false">Selection Locus</th>
+ <th field="sms_carrier_name" fpath="sms_carrier.name" _visible="false">SMS Carrier</th>
+ </tr>
+ </thead>
+ </table>
+</div>
+[% END %]
"offset": 0,
"baseSort": null,
"defaultSort": null,
+ "sortFieldReMap": null,
"constructor": function(/* object */ args) {
dojo.mixin(this, args);
);
},
- "_prepare_flattener_params": function(req) {
+ "_remap_sort": function(prepared_sort) {
+ if (this.sortFieldReMap) {
+ return prepared_sort.map(
+ dojo.hitch(
+ this, function(exp) {
+ if (typeof exp == "object") {
+ var key;
+ for (key in exp)
+ break;
+ var newkey = (key in this.sortFieldReMap) ?
+ this.sortFieldReMap[key] : key;
+ var o = {};
+ o[newkey] = exp[key];
+ return o;
+ } else {
+ return (exp in this.sortFieldReMap) ?
+ this.sortFieldReMap[exp] : exp;
+ }
+ }
+ )
+ );
+ } else {
+ return prepared_sort;
+ }
+ },
+
+ "_build_flattener_params": function(req) {
var params = {
"hint": this.fmClass,
"ses": openils.User.authtoken
params.where = dojo.toJson(where);
} else {
- var limit = (!isNaN(req.count) && req.count != Infinity) ?
- req.count : this.limit;
- var offset = (!isNaN(req.start) && req.start != Infinity) ?
- req.start : this.offset;
-
- dojo.mixin(
- params, {
- "where": dojo.toJson(req.query),
- "slo": dojo.toJson({
- "sort": this._prepare_sort(req.sort),
- "limit": limit,
- "offset": offset
- })
- }
- );
+ params.where = dojo.toJson(req.query);
+
+ var slo = {
+ "sort": this._remap_sort(this._prepare_sort(req.sort))
+ };
+
+ if (!req.queryOptions.all) {
+ slo.limit =
+ (!isNaN(req.count) && req.count != Infinity) ?
+ req.count : this.limit;
+
+ slo.offset =
+ (!isNaN(req.start) && req.start != Infinity) ?
+ req.start : this.offset;
+ }
+
+ if (req.queryOptions.columns)
+ params.columns = req.queryOptions.columns;
+ if (req.queryOptions.labels)
+ params.labels = req.queryOptions.labels;
+
+ params.slo = dojo.toJson(slo);
}
- if (this.mapKey) { /* XXX TODO, get a map key */
+ if (this.mapKey) {
params.key = this.mapKey;
} else {
params.map = dojo.toJson(this.mapClause);
}
- for (var key in params)
- console.debug("flattener param " + key + " -> " + params[key]);
+// for (var key in params)
+// console.debug("flattener param " + key + " -> " + params[key]);
return params;
},
);
},
+ "_on_http_error": function(response, ioArgs, req, retry_method) {
+ if (response.status == 402) { /* 'Payment Required' stands
+ in for cache miss */
+ if (this._retried_map_key_already) {
+ var e = new FlattenerStoreError(
+ "Server won't cache flattener map?"
+ );
+ if (typeof req.onError == "function")
+ req.onError.call(callback_scope, e);
+ else
+ throw e;
+ } else {
+ this._retried_map_key_already = true;
+ delete this.mapKey;
+ if (retry_method)
+ return this[retry_method](req);
+ }
+ }
+ },
+
+ "_fetch_prepare": function(req) {
+ req.queryOptions = req.queryOptions || {};
+ req.abort = function() { console.warn("[unimplemented] abort()"); };
+
+ if (!this.mapKey)
+ this._get_map_key();
+
+ return this._build_flattener_params(req);
+ },
+
+ "_fetch_execute": function(params,handle_as,mime_type,onload,onerror) {
+ dojo.xhrPost({
+ "url": this._flattener_url,
+ "content": params,
+ "handleAs": handle_as,
+ "sync": false,
+ "preventCache": true,
+ "headers": {"Accept": mime_type},
+ "load": onload,
+ "error": onerror
+ });
+ },
+
+ /* *** 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(
// onItem a callback that takes each item as we get it
// onComplete a callback that takes the list of items
// after they're all fetched
- //
- // The onError callback is ignored for now (haven't thought
- // of anything useful to do with it yet).
- //
- // The Read API also charges this method with adding an abort
- // callback to the *req* object for the caller's use, but
- // the one we provide does nothing but issue an alert().
- //console.log("fetch(" + dojo.toJson(req) + ")");
var self = this;
var callback_scope = req.scope || dojo.global;
-
- if (!this.mapKey) {
- try {
- this._get_map_key();
- } catch (E) {
- if (req.onError)
- req.onError.call(callback_scope, E);
- else
- throw E;
- }
- }
-
- var post_params = this._prepare_flattener_params(req);
-
- if (!post_params) {
- if (typeof req.onComplete == "function")
- req.onComplete.call(callback_scope, [], req);
- return;
+ 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 = function(obj, when) {
req.onComplete.call(callback_scope, obj, req);
};
- req.abort = function() {
- throw new FlattenerStoreError(
- "The 'abort' operation is not supported"
- );
- };
+ var process_error = dojo.hitch(
+ this, function(response, ioArgs) {
+ this._on_http_error(response, ioArgs, req, "fetch");
+ }
+ );
var fetch_time = this._last_fetch = (new Date().getTime());
- dojo.xhrPost({
- "url": this._flattener_url,
- "content": post_params,
- "handleAs": "json",
- "sync": false,
- "preventCache": true,
- "headers": {"Accept": "application/json"},
- "load": function(obj) { process_fetch(obj, fetch_time); },
- "error": function(response, ioArgs) {
- if (response.status == 402) { /* 'Payment Required' stands
- in for cache miss */
- if (self._retried_map_key_already) {
- var e = new FlattenerStoreError(
- "Server won't cache flattener map?"
- );
- if (typeof req.onError == "function")
- req.onError.call(callback_scope, e);
- else
- throw e;
- } else {
- self._retried_map_key_already = true;
- delete self.mapKey;
- return self.fetch(req);
- }
- }
- }
- });
+ this._fetch_execute(
+ post_params,
+ "json",
+ "application/json",
+ function(obj) { process_fetch(obj, fetch_time); },
+ process_error
+ );
return req;
},
return;
}
- var post_params = this._prepare_flattener_params(keywordArgs);
+ var post_params;
+ try {
+ post_params = this._fetch_prepare(keywordArgs);
+ } catch (E) {
+ if (typeof keywordArgs.onError == "function")
+ keywordArgs.onError.call(callback_scope, E);
+ else
+ throw E;
+ }
var process_fetch_one = dojo.hitch(
this, function(obj, when) {
}
);
+ var process_error = dojo.hitch(
+ this, function(response, ioArgs) {
+ this._on_http_error(
+ response, ioArgs, keywordArgs, "fetchItemByIdentity"
+ );
+ }
+ );
+
var fetch_time = this._last_fetch = (new Date().getTime());
- dojo.xhrPost({
- "url": this._flattener_url,
- "content": post_params,
- "handleAs": "json",
- "sync": false,
- "preventCache": true,
- "headers": {"Accept": "application/json"},
- "load": function(obj){ process_fetch_one(obj, fetch_time); }
- });
+ this._fetch_execute(
+ post_params,
+ "json",
+ "application/json",
+ function(obj) { process_fetch_one(obj, fetch_time); },
+ process_error
+ );
},
/* dojo.data.api.Write - only very partially implemented, because
* FlattenerGrid in their own right */
"columnReordering": true,
"columnPersistKey": null,
+ "autoCoreFields": false,
+ "autoFieldFields": null,
"showLoadFilter": false, /* use FlattenerFilterDialog */
+ "fetchLock": false,
/* These potential constructor arguments maybe useful to
* FlattenerGrid in their own right, and are passed to
"fmClass": null,
"fmIdentifier": null,
"mapExtras": null,
+ "sortFieldReMap": null,
"defaultSort": null, /* whatever any part of the UI says will
/replace/ this */
"baseSort": null, /* will contains what the columnpicker
/* These are the fields defined in thead -> tr -> [th,th,...].
* For purposes of building the map, where each field has
* three boolean attributes "display", "sort" and "filter",
- * assume "display" and "sort" are always true for these.
+ * assume "display" is always true for these.
* That doesn't mean that at the UI level we can't hide a
* column later.
*
* If you need extra fields in the map for which display
- * or sort should *not* be true, use mapExtras.
+ * should *not* be true, use mapExtras.
*/
dojo.forEach(
fields, function(field) {
map[field.field] = {
"display": true,
"filter": (field.ffilter || false),
- "sort": true,
+ "sort": field.fsort,
"path": field.fpath || field.field
};
/* The following attribute is not for the flattener
return clean;
},
- /* The FlattenerStore doesn't need this, but it has at least two
- * uses: 1) FlattenerFilterDialog, 2) setting column header labels
- * to IDL defaults.
- *
- * To call these 'Terminii' can be misleading. In certain
- * (actually probably common) cases, they won't really be the last
- * field in a path, but the next-to-last. Read on. */
- "_calculateMapTerminii": function() {
- function _fm_is_selector_for_class(hint, field) {
- var cl = fieldmapper.IDL.fmclasses[hint];
+ /* Given the hint of a class to start at, follow path to the end
+ * and return information on the last field. */
+ "_followPathToEnd": function(hint, path, allow_selector_backoff) {
+ function _fm_is_selector_for_class(h, field) {
+ var cl = fieldmapper.IDL.fmclasses[h];
return (cl.field_map[cl.pkey].selector == field);
}
- function _follow_to_end(hint, path) {
- var last_field, last_hint;
- var orig_path = dojo.clone(path);
- var field;
-
- while (field = path.shift()) {
- /* XXX this assumes we have the whole IDL loaded. I
- * guess we could teach this to work by loading classes
- * on demand when we don't have the whole IDL loaded. */
- var field_def =
- fieldmapper.IDL.fmclasses[hint].field_map[field];
-
- if (field_def["class"] && path.length) {
- last_field = field;
- last_hint = hint;
-
- hint = field_def["class"];
- } else if (path.length) {
- /* There are more fields left but we can't follow
- * the chain via IDL any further. */
- throw new Error(
- "_calculateMapTerminii can't parse path " +
- orig_path + " (at " + field + ")"
- );
- } else {
- break; /* keeps field defined after loop */
- }
+ var last_field, last_hint;
+ var orig_path = dojo.clone(path);
+ var field, field_def;
+
+ while (field = path.shift()) {
+ /* XXX this assumes we have the whole IDL loaded. I
+ * guess we could teach this to work by loading classes
+ * on demand when we don't have the whole IDL loaded. */
+ field_def =
+ fieldmapper.IDL.fmclasses[hint].field_map[field];
+
+ if (!field_def) {
+ /* This can be ok in some cases. Columns following
+ * IDL paths involving links with a nonempty "map"
+ * attribute can be used for display only (no
+ * sort, no filter). */
+ console.info(
+ "Lost our way in IDL at hint " + hint +
+ ", field " + field + "; may be ok"
+ );
+ return null;
}
- var datatype = field_def.datatype;
- var indirect = false;
- /* Back off the last field in the path if it's a selector
- * for its class, because the preceding field will be
- * a better thing to hand to AutoFieldWidget.
- */
- if (orig_path.length > 1 &&
- _fm_is_selector_for_class(hint, field)) {
- hint = last_hint;
- field = last_field;
- datatype = "link";
- indirect = true;
+ if (field_def["class"]) {
+ last_field = field;
+ last_hint = hint;
+
+ hint = field_def["class"];
+ } else if (path.length) {
+ /* There are more fields left but we can't follow
+ * the chain via IDL any further. */
+ throw new Error(
+ "_calculateMapTerminii can't parse path " +
+ orig_path + " (at " + field + ")"
+ );
}
+ }
- return {
- "fmClass": hint,
- "name": field,
- "label": field_def.label,
- "datatype": datatype,
- "indirect": indirect
- };
+ var datatype = field_def.datatype;
+ var indirect = false;
+ /* If allowed, back off the last field in the path if it's a
+ * selector for its class, because the preceding field will be
+ * a better thing to hand to AutoFieldWidget.
+ */
+ if (orig_path.length > 1 && allow_selector_backoff &&
+ _fm_is_selector_for_class(hint, field_def.name)) {
+ hint = last_hint;
+ field = last_field;
+ datatype = "link";
+ indirect = true;
+ } else {
+ field = field_def.name;
}
+ return {
+ "fmClass": hint,
+ "name": field,
+ "label": field_def.label,
+ "datatype": datatype,
+ "indirect": indirect
+ };
+ },
+
+ /* The FlattenerStore doesn't need this, but it has at least two
+ * uses: 1) FlattenerFilterDialog, 2) setting column header labels
+ * to IDL defaults.
+ *
+ * To call these 'Terminii' can be misleading. In certain
+ * (actually probably common) cases, they won't really be the last
+ * field in a path, but the next-to-last. Read on. */
+ "_calculateMapTerminii": function() {
this.mapTerminii = [];
for (var column in this.mapClause) {
+ var end = this._followPathToEnd(
+ this.fmClass,
+ this.mapClause[column].path.split(/\./),
+ true /* allow selector backoff */
+ );
+ if (!end)
+ continue;
var terminus = dojo.mixin(
- _follow_to_end(
- this.fmClass,
- this.mapClause[column].path.split(/\./)
- ), {
+ end, {
"simple_name": column,
"isfilter": this.mapClause[column].filter
}
},
"_supplementHeaderNames": function() {
- /* You'd be surprised how rarely this make sense in Flattener
- * use cases, but if we didn't give a particular header cell
+ /* If we didn't give a particular header cell
* (<th>) a display name (the innerHTML of that <th>), then
* use the IDL to provide the label of the terminus of the
* flattener path for that column. It may be better than using
);
},
+ "_columnOrderingAndLabels": function() {
+ var labels = [];
+ var columns = [];
+
+ this.views.views[0].structure.cells[0].forEach(
+ function(c) {
+ if (!c.field.match(/^\+/)) {
+ labels.push(c.name);
+ columns.push(c.field);
+ }
+ }
+ );
+
+ return {"labels": labels, "columns": columns};
+ },
+
+ "_getAutoFieldFields": function(fmclass) {
+ return dojo.clone(
+ fieldmapper.IDL.fmclasses[fmclass].fields)
+ .filter(
+ function(field) {
+ return !field.virtual && field.datatype != "link";
+ }
+ ).sort(
+ function(a, b) { return a.label > b.label ? 1 : -1; }
+ );
+ },
+
+ /* Take our core class (this.fmClass) and add table columns for
+ * any field we don't already have covered by actual hard-coded
+ * <th> columns. */
+ "_addAutoCoreFields": function() {
+ var cell_list = this.structure[0].cells[0];
+ var fields = dojo.clone(
+ fieldmapper.IDL.fmclasses[this.fmClass].fields
+ ).sort(
+ function(a, b) { return a.label > b.label ? 1 : -1; }
+ );
+
+ dojo.forEach(
+ fields, function(f) {
+ if (f.datatype == "link" || f.virtual)
+ return;
+
+ if (cell_list.filter(
+ function(c) {
+ if (!c.fpath) return false;
+ return c.fpath.split(/\./)[0] == f.name;
+ }
+ ).length)
+ return;
+
+ cell_list.push({
+ "field": f.name,
+ "name": f.label,
+ "fsort": true,
+ "_visible": false
+ });
+ }
+ );
+ },
+
+ "_addAutoFieldFields": function(paths) {
+ var self = this;
+ var n = 0;
+
+ dojo.forEach(
+ paths, function(path) {
+ /* The beginning is the end. */
+ var beginning = self._followPathToEnd(
+ self.fmClass, path.split(/\./), false
+ );
+ if (!beginning) {
+ return;
+ } else {
+ dojo.forEach(
+ self._getAutoFieldFields(beginning.fmClass),
+ function(field) {
+ var would_be_path =
+ path + "." + field.name;
+ var wbp_re =
+ new RegExp("^" + would_be_path);
+ if (!self.structure[0].cells[0].filter(
+ function(c) {
+ return c.fpath &&
+ c.fpath.match(wbp_re);
+ }
+ ).length) {
+ console.info("adding auto field" + would_be_path);
+ self.structure[0].cells[0].push({
+ "field": "AUTO_" + beginning.name +
+ "_" + field.name,
+ "name": beginning.label + " - " +
+ field.label,
+ "fsort": true,
+ "fpath": would_be_path,
+ "_visible": false
+ });
+ }
+ }
+ );
+ }
+ }
+ );
+ },
+
+ "_addAutoFields": function() {
+ if (this.autoCoreFields)
+ this._addAutoCoreFields();
+
+ if (dojo.isArray(this.autoFieldFields))
+ this._addAutoFieldFields(this.autoFieldFields);
+
+ this.setStructure(this.structure);
+ },
+
"constructor": function(args) {
dojo.mixin(this, args);
},
"startup": function() {
-
/* Save original query for further filtering later */
this._baseQuery = dojo.clone(this.query);
+
+ this._addAutoFields();
+
this._startupGridHelperColumns();
+ this._generateMap();
+
if (!this.columnPicker) {
this.columnPicker =
new openils.widget.GridColumnPicker(
this.inherited(arguments);
},
+ "canSort": function(idx, skip_structure /* API abuse */) {
+ var initial = this.inherited(arguments);
+
+ /* idx is one-based instead of zero-based for a reason. */
+ var view_idx = Math.abs(idx) - 1;
+ return initial && (
+ skip_structure ||
+ this.views.views[0].structure.cells[0][view_idx].fsort
+ );
+ },
+
/* Maps ColumnPicker sort fields to the correct format.
If no sort fields specified, falls back to defaultSort */
"_mapCPSortFields": function(sortFields) {
"_finishStartup": function(sortFields) {
- this.setStore(
+ this._setStore( /* Seriously, let's leave this as _setStore. */
new openils.FlattenerStore({
"fmClass": this.fmClass,
"fmIdentifier": this.fmIdentifier,
- "mapClause": (this.mapClause ||
- this._cleanMapForStore(this._generateMap())),
+ "mapClause": this._cleanMapForStore(this.mapClause),
"baseSort": this.baseSort,
- "defaultSort": this._mapCPSortFields(sortFields)
+ "defaultSort": this._mapCPSortFields(sortFields),
+ "sortFieldReMap": this.sortFieldReMap
+
}), this.query
);
// pick up any column label changes
this.columnPicker.reloadStructure();
+ if (!this.fetchLock)
+ this._refresh(true);
+
this._showing_create_pane = false;
this.overrideEditWidgets = {};
}
},
+ "refresh": function() {
+ this.fetchLock = false;
+ this._refresh(/* isRender */ true);
+ },
+
+ "_fetch": function() {
+ if (this.fetchLock)
+ return;
+ else
+ return this.inherited(arguments);
+ },
+
/* ******** below are methods mostly copied but
* slightly changed from AutoGrid ******** */
);
}
);
+ },
+
+ /* 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. */
+ "print": function() {
+ var coal = this._columnOrderingAndLabels();
+ var req = {
+ "query": this.query,
+ "queryOptions": {
+ "all": true,
+ "columns": coal.columns,
+ "labels": coal.labels
+ },
+ "onComplete": function(text) {
+ openils.Util.printHtmlString(text);
+ }
+ };
+
+ this.store.fetchToPrint(req);
}
}
);
cellDef[a] = value;
}
);
+
+ /* fsort and _visible are different. Assume true unless defined. */
+ dojo.forEach(
+ ["fsort", "_visible"], function(a) {
+ var val = dojo.attr(node, a);
+ cellDef[a] = (typeof val == "undefined" || val === null) ?
+ true : dojo.fromJson(val);
+ }
+ );
};
})();
};
},
- /** Loads the current grid structure and passes the
- * structure back to the grid to force a UI refresh.
- * This is necessary if external forces alter the structure.
+ /** Loads any grid column label changes, clears any
+ * non-visible fields from the structure, and passes
+ * the structure back to the grid to force a UI refresh.
*/
reloadStructure : function() {
- this.structure = this.grid.structure;
- this.cells = this.structure[0].cells[0].slice();
+
+ // update our copy of the column labels
+ var _this = this;
+ dojo.forEach(
+ this.grid.structure[0].cells[0],
+ function(gcell) {
+ var cell = _this.cells.filter(
+ function(c) { return c.field == gcell.field }
+ )[0];
+ cell.name = gcell.name;
+ }
+ );
+
+ this.pruneInvisibleFields();
this.grid.setStructure(this.structure);
},
+
// determine the visible sorting from the
// view and update our list of cells to match
refreshCells : function() {
"<th width='23%'>Auto Width</th><th width='23%'>Sort Priority</th></tr></thead>" +
"<tbody />"});
- var tDiv = dojo.create('div', {style : 'height:400px; overflow-y:auto;'});
+ var tDiv = dojo.create('div');
tDiv.appendChild(table);
var bDiv = dojo.create('div', {style : 'text-align:right; width:100%;',
else
this.dialogTable.appendChild(tr);
- if ( this.grid.canSort(i+1) ) { // column index is 1-based
-
- // must be added after its parent node is inserted into the DOM.
- var ns = new dijit.form.NumberSpinner(
- { constraints : {places : 0},
- value : cell._sort || 0,
- style : 'width:4em',
- name : 'sort',
- }, ipt3
- );
+ if (this.grid.canSort(
+ i + 1, /* column index is 1-based */
+ true /* skip structure test (API abuse) */
+ )) {
+
+ /* Ugly kludge. When using with FlattenerGrid the
+ * conditional is needed. Shouldn't hurt usage with
+ * AutoGrid. */
+ if (typeof cell.fsort == "undefined" || cell.fsort) {
+ // must be added after its parent node is inserted into the DOM.
+ var ns = new dijit.form.NumberSpinner(
+ { constraints : {places : 0},
+ value : cell._sort || 0,
+ style : 'width:4em',
+ name : 'sort',
+ }, ipt3
+ );
+ }
}
}
},
this.grid.update();
},
+ // *only* call this when no usr setting tells us what columns
+ // are visible or not.
+ pruneInvisibleFields : function() {
+ this.structure[0].cells[0] = dojo.filter(
+ this.structure[0].cells[0],
+ dojo.hitch(this, function(c) {
+ // keep true or undef, lose false
+ return typeof c._visible == "undefined" || c._visible;
+ })
+ );
+ },
+
load : function() {
var _this = this;
<!ENTITY staff.patron.holds_overlay.print_full_pull_list.accesskey "u">
<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.label "Print Full Pull List (Alternate strategy)">
<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.accesskey "y">
+<!ENTITY staff.patron.holds_overlay.simplified_pull_list.label "Simplified Pull List Interface">
+<!ENTITY staff.patron.holds_overlay.simplified_pull_list.accesskey "i">
<!ENTITY staff.patron.holds_overlay.place_hold.label "Place Hold">
<!ENTITY staff.patron.holds_overlay.place_hold.accesskey "H">
<!ENTITY staff.patron.holds_overlay.show_cancelled_holds.label "Show Cancelled Holds">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
+ <style type="text/css">
+ /* This CSS controls whether data printed from an interface
+ based on FlattenerGrid has visible table cell borders. */
+
+ table { border-collapse: collapse; }
+ td, th { border: 1px solid black; }
+ </style>
</head>
<body>
<table>
}
}
],
+ 'cmd_simplified_pull_list' : [
+ ['command'],
+ function() {
+ try {
+ var content_params = {
+ "session": ses(),
+ "authtime": ses("authtime"),
+ "no_xulG": false,
+ "show_nav_buttons": true,
+ "show_print_button": true
+ };
+ ["url_prefix", "new_tab", "set_tab",
+ "close_tab", "new_patron_tab",
+ "set_patron_tab", "volume_item_creator",
+ "get_new_session",
+ "holdings_maintenance_tab", "set_tab_name",
+ "open_chrome_window", "url_prefix",
+ "network_meter", "page_meter",
+ "set_statusbar", "set_help_context"
+ ].forEach(function(k) {
+ content_params[k] = xulG[k];
+ });
+
+ var loc = urls.XUL_BROWSER + "?url=" + window.escape(
+ xulG.url_prefix("/eg/circ/hold_pull_list").replace("http:","https:")
+ );
+ xulG.new_tab(
+ loc, {
+ "tab_name": "Simplified Pull List", /* XXX i18n */
+ "browser": false,
+ "show_print_button": false
+ }, content_params
+ );
+ } catch (E) {
+ g.error.sdump("D_ERROR", E);
+ }
+ }
+ ],
'cmd_holds_print' : [
['command'],
function() {
var x_expired_checkbox = document.getElementById('expired_checkbox');
var x_print_full_pull_list = document.getElementById('print_full_btn');
var x_print_full_pull_list_alt = document.getElementById('print_alt_btn');
+ var x_simplified_pull_list = document.getElementById('simplified_pull_list_btn');
switch(obj.hold_interface_type) {
case 'shelf':
obj.render_lib_menus({'pickup_lib':true});
if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false;
if (x_clear_shelf_widgets) x_clear_shelf_widgets.hidden = false;
if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
+ if (x_simplified_pull_list) x_simplified_pull_list.hidden = true;
break;
case 'pull' :
if (x_fetch_more) x_fetch_more.hidden = false;
if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = false;
if (x_lib_type_menu) x_lib_type_menu.hidden = true;
if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true;
+ if (x_simplified_pull_list) x_simplified_pull_list.hidden = false;
break;
case 'record' :
obj.render_lib_menus({'pickup_lib':true,'request_lib':true});
if (x_lib_type_menu) x_lib_type_menu.hidden = false;
if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false;
+ if (x_simplified_pull_list) x_simplified_pull_list.hidden = true;
break;
default:
if (obj.controller.view.cmd_search_opac) obj.controller.view.cmd_search_opac.setAttribute('hidden', false);
if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true;
if (x_show_cancelled_deck) x_show_cancelled_deck.hidden = false;
if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
+ if (x_simplified_pull_list) x_simplified_pull_list.hidden = true;
break;
}
setTimeout( // We do this because render_lib_menus above creates and appends a DOM node, but until this thread exits, it doesn't really happen
<command id="cmd_holds_print" />
<command id="cmd_holds_print_full" />
<command id="cmd_holds_print_alt" />
+ <command id="cmd_simplified_pull_list" />
<command id="cmd_show_catalog" />
<command id="cmd_retrieve_patron" />
<command id="cmd_holds_edit_desire_mint_condition" />
<button id="holds_print" label="&staff.patron.holds_overlay.print.label;" command="cmd_holds_print" accesskey="&staff.patron.holds_overlay.print.accesskey;" />
<button id="print_full_btn" hidden="true" label="&staff.patron.holds_overlay.print_full_pull_list.label;" command="cmd_holds_print_full" accesskey="&staff.patron.holds_overlay.print_full_pull_list.accesskey;" />
<button id="print_alt_btn" hidden="true" label="&staff.patron.holds_overlay.print_alt_pull_list.label;" command="cmd_holds_print_alt" accesskey="&staff.patron.holds_overlay.print_alt_pull_list.accesskey;" />
+ <button id="simplified_pull_list_btn" hidden="true" label="&staff.patron.holds_overlay.simplified_pull_list.label;" command="cmd_simplified_pull_list" accesskey="&staff.patron.holds_overlay.simplified_pull_list.accesskey;" />
<spacer flex="1"/>
</hbox>
--- /dev/null
+Simplified Hold Pull List
+-------------------------
+
+There is a new hold pull list interface based on the Flattener service that's
+designed to perform faster than existing pull list interfaces, both in staff
+client display and printing.
+
+Sorting
+~~~~~~~
+
+You can sort on any one column by clicking on it. Click again to reverse
+direction. This is typical of similar interfaces.
+
+Now you can also sort by multiple columns. Right click the column headers of
+the grid in the pull list interface to get a dialog that allows you to sort
+by multiple columns, in any order.
+
+Column Picking
+~~~~~~~~~~~~~~
+
+The same dialog that allows you to choose multiple sort columns (accessed by
+right clicking any column header) also allows you to toggle the display of any
+column available to the pull list on or off.
+
+Persistence
+~~~~~~~~~~~
+
+Once saved, your changes in this dialog persist for your user account. Column
+display, display order, and `sorting choices affect printing as well as
+displayed output.