From b5d32378c7d7e8e1cc573f0a402301081aa07679 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 4 Aug 2014 14:06:02 -0400 Subject: [PATCH] LP#1350042 Browser client templates/scripts (phase 1) * Templates in Open-ILS/src/templates/staff/ * JS files in Open-ILS/web/js/ui/default/staff/ * Also includes a few TPAC modifications for embedded mode * Resurrects the pre-XUL user permission editor Signed-off-by: Bill Erickson Signed-off-by: Ben Shum --- .gitignore | 3 + .../templates/opac/parts/bookbag_actions.tt2 | 2 +- .../opac/parts/record/copy_table.tt2 | 14 + Open-ILS/src/templates/staff/README | 6 + .../staff/admin/t_user_perms_lookup.tt2 | 24 + .../src/templates/staff/admin/user_perms.tt2 | 18 + .../staff/admin/workstation/index.tt2 | 24 + .../admin/workstation/t_print_config.tt2 | 176 ++ .../admin/workstation/t_print_templates.tt2 | 59 + .../staff/admin/workstation/t_splash.tt2 | 120 ++ .../admin/workstation/t_stored_prefs.tt2 | 65 + Open-ILS/src/templates/staff/base.tt2 | 48 + Open-ILS/src/templates/staff/base_js.tt2 | 47 + .../staff/cat/bucket/record/index.tt2 | 65 + .../cat/bucket/record/t_bucket_create.tt2 | 35 + .../cat/bucket/record/t_bucket_delete.tt2 | 16 + .../staff/cat/bucket/record/t_bucket_edit.tt2 | 34 + .../cat/bucket/record/t_bucket_export.tt2 | 41 + .../staff/cat/bucket/record/t_bucket_info.tt2 | 16 + .../cat/bucket/record/t_bucket_selector.tt2 | 27 + .../staff/cat/bucket/record/t_grid_menu.tt2 | 20 + .../staff/cat/bucket/record/t_load_shared.tt2 | 25 + .../staff/cat/bucket/record/t_pending.tt2 | 20 + .../staff/cat/bucket/record/t_search.tt2 | 46 + .../staff/cat/bucket/record/t_view.tt2 | 28 + .../src/templates/staff/cat/catalog/index.tt2 | 21 + .../templates/staff/cat/catalog/t_catalog.tt2 | 48 + .../templates/staff/cat/catalog/t_holds.tt2 | 108 ++ .../src/templates/staff/cat/item/index.tt2 | 83 + .../staff/cat/item/missing_pieces.tt2 | 69 + .../staff/cat/item/replace_barcode/index.tt2 | 49 + .../templates/staff/cat/item/t_cat_pane.tt2 | 3 + .../staff/cat/item/t_circ_list_pane.tt2 | 51 + .../templates/staff/cat/item/t_circs_pane.tt2 | 189 ++ .../templates/staff/cat/item/t_holds_pane.tt2 | 125 ++ .../src/templates/staff/cat/item/t_list.tt2 | 21 + .../staff/cat/item/t_summary_pane.tt2 | 178 ++ .../cat/item/t_triggered_events_pane.tt2 | 2 + .../src/templates/staff/cat/item/t_view.tt2 | 34 + .../staff/cat/share/t_record_summary.tt2 | 53 + .../staff/cat/t_triggered_events_pane.tt2 | 2 + .../templates/staff/circ/checkin/index.tt2 | 19 + .../staff/circ/checkin/t_checkin.tt2 | 225 +++ .../staff/circ/checkin/t_checkin_table.tt2 | 89 + .../src/templates/staff/circ/holds/index.tt2 | 30 + .../src/templates/staff/circ/holds/t_pull.tt2 | 26 + .../staff/circ/holds/t_pull_list.tt2 | 88 + .../templates/staff/circ/holds/t_shelf.tt2 | 40 + .../staff/circ/holds/t_shelf_list.tt2 | 85 + .../staff/circ/in_house_use/index.tt2 | 81 + .../src/templates/staff/circ/patron/index.tt2 | 175 ++ .../templates/staff/circ/patron/pending.tt2 | 17 + .../templates/staff/circ/patron/register.tt2 | 15 + .../templates/staff/circ/patron/t_alerts.tt2 | 75 + .../staff/circ/patron/t_bcsearch.tt2 | 22 + .../staff/circ/patron/t_bill_history.tt2 | 34 + .../circ/patron/t_bill_history_payments.tt2 | 79 + .../circ/patron/t_bill_history_xacts.tt2 | 49 + .../templates/staff/circ/patron/t_bills.tt2 | 109 ++ .../staff/circ/patron/t_bills_list.tt2 | 87 + .../staff/circ/patron/t_checkout.tt2 | 143 ++ .../staff/circ/patron/t_credentials.tt2 | 65 + .../templates/staff/circ/patron/t_edit.tt2 | 2 + .../circ/patron/t_edit_due_date_dialog.tt2 | 24 + .../staff/circ/patron/t_edit_perms.tt2 | 1 + .../templates/staff/circ/patron/t_group.tt2 | 55 + .../templates/staff/circ/patron/t_holds.tt2 | 38 + .../staff/circ/patron/t_holds_create.tt2 | 4 + .../staff/circ/patron/t_holds_list.tt2 | 82 + .../staff/circ/patron/t_items_out.tt2 | 73 + .../staff/circ/patron/t_last_patron.tt2 | 9 + .../staff/circ/patron/t_messages.tt2 | 52 + .../circ/patron/t_move_to_group_dialog.tt2 | 28 + .../staff/circ/patron/t_new_note_dialog.tt2 | 42 + .../templates/staff/circ/patron/t_notes.tt2 | 44 + .../staff/circ/patron/t_pending_list.tt2 | 34 + .../circ/patron/t_renew_with_date_dialog.tt2 | 24 + .../templates/staff/circ/patron/t_search.tt2 | 158 ++ .../staff/circ/patron/t_search_results.tt2 | 30 + .../staff/circ/patron/t_stat_cats.tt2 | 19 + .../templates/staff/circ/patron/t_summary.tt2 | 143 ++ .../staff/circ/patron/t_triggered_events.tt2 | 3 + .../staff/circ/patron/t_xact_details.tt2 | 144 ++ .../src/templates/staff/circ/renew/index.tt2 | 20 + .../templates/staff/circ/renew/t_renew.tt2 | 141 ++ .../staff/circ/share/circ_strings.tt2 | 39 + .../staff/circ/share/hold_strings.tt2 | 30 + .../staff/circ/share/t_backdate_dialog.tt2 | 24 + .../staff/circ/share/t_bad_barcode_dialog.tt2 | 23 + .../staff/circ/share/t_bill_patron_dialog.tt2 | 93 + .../staff/circ/share/t_cancel_hold_dialog.tt2 | 38 + .../staff/circ/share/t_circ_exists_dialog.tt2 | 31 + .../circ/share/t_copy_in_transit_dialog.tt2 | 32 + .../circ/share/t_copy_not_avail_dialog.tt2 | 20 + .../circ/share/t_event_override_dialog.tt2 | 27 + .../circ/share/t_hold_copy_quality_dialog.tt2 | 24 + .../staff/circ/share/t_hold_dates.tt2 | 75 + .../staff/circ/share/t_hold_details.tt2 | 149 ++ .../circ/share/t_hold_edit_pickup_lib.tt2 | 23 + .../staff/circ/share/t_hold_note_dialog.tt2 | 51 + .../circ/share/t_hold_notification_dialog.tt2 | 33 + .../circ/share/t_hold_notification_prefs.tt2 | 78 + .../staff/circ/share/t_hold_shelf_dialog.tt2 | 63 + .../share/t_mark_claims_returned_dialog.tt2 | 25 + .../staff/circ/share/t_new_message_dialog.tt2 | 45 + .../staff/circ/share/t_noncat_dialog.tt2 | 25 + .../staff/circ/share/t_precat_dialog.tt2 | 44 + .../staff/circ/share/t_transit_dialog.tt2 | 65 + Open-ILS/src/templates/staff/config.tt2 | 13 + Open-ILS/src/templates/staff/css/circ.css.tt2 | 60 + .../src/templates/staff/css/print.css.tt2 | 13 + .../src/templates/staff/css/style.css.tt2 | 412 +++++ Open-ILS/src/templates/staff/index.tt2 | 17 + Open-ILS/src/templates/staff/navbar.tt2 | 244 +++ Open-ILS/src/templates/staff/share/README | 5 + .../staff/share/print_templates/index.tt2 | 2 + .../share/print_templates/t_bill_payment.tt2 | 69 + .../share/print_templates/t_bills_current.tt2 | 49 + .../print_templates/t_bills_historical.tt2 | 49 + .../staff/share/print_templates/t_checkin.tt2 | 17 + .../share/print_templates/t_checkout.tt2 | 17 + .../print_templates/t_hold_pull_list.tt2 | 29 + .../print_templates/t_hold_shelf_slip.tt2 | 37 + .../print_templates/t_hold_transit_slip.tt2 | 34 + .../share/print_templates/t_holds_for_bib.tt2 | 29 + .../print_templates/t_holds_for_patron.tt2 | 14 + .../share/print_templates/t_items_out.tt2 | 17 + .../print_templates/t_patron_address.tt2 | 12 + .../share/print_templates/t_patron_note.tt2 | 11 + .../staff/share/print_templates/t_renew.tt2 | 17 + .../share/print_templates/t_transit_slip.tt2 | 21 + .../templates/staff/share/t_alert_dialog.tt2 | 16 + .../src/templates/staff/share/t_autogrid.tt2 | 297 ++++ .../staff/share/t_confirm_dialog.tt2 | 18 + .../src/templates/staff/share/t_eframe.tt2 | 9 + .../templates/staff/share/t_prompt_dialog.tt2 | 21 + Open-ILS/src/templates/staff/statusbar.tt2 | 33 + Open-ILS/src/templates/staff/t_login.tt2 | 57 + Open-ILS/src/templates/staff/t_splash.tt2 | 69 + Open-ILS/web/js/ui/default/opac/staff.js | 225 ++- Open-ILS/web/js/ui/default/staff/Gruntfile.js | 164 ++ .../web/js/ui/default/staff/README.install | 93 + .../js/ui/default/staff/admin/user_perms.js | 100 ++ .../ui/default/staff/admin/workstation/app.js | 557 ++++++ Open-ILS/web/js/ui/default/staff/app.js | 125 ++ Open-ILS/web/js/ui/default/staff/bower.json | 30 + .../ui/default/staff/cat/bucket/record/app.js | 535 ++++++ .../js/ui/default/staff/cat/catalog/app.js | 209 +++ .../web/js/ui/default/staff/cat/item/app.js | 540 ++++++ .../default/staff/cat/item/missing_pieces.js | 123 ++ .../staff/cat/item/replace_barcode/app.js | 39 + .../ui/default/staff/cat/services/record.js | 106 ++ .../js/ui/default/staff/circ/checkin/app.js | 316 ++++ .../web/js/ui/default/staff/circ/holds/app.js | 315 ++++ .../ui/default/staff/circ/in_house_use/app.js | 123 ++ .../js/ui/default/staff/circ/patron/app.js | 1494 ++++++++++++++++ .../js/ui/default/staff/circ/patron/bills.js | 731 ++++++++ .../ui/default/staff/circ/patron/checkout.js | 201 +++ .../js/ui/default/staff/circ/patron/holds.js | 155 ++ .../ui/default/staff/circ/patron/items_out.js | 406 +++++ .../ui/default/staff/circ/patron/pending.js | 84 + .../ui/default/staff/circ/patron/register.js | 72 + .../web/js/ui/default/staff/circ/renew/app.js | 213 +++ .../ui/default/staff/circ/services/billing.js | 175 ++ .../js/ui/default/staff/circ/services/circ.js | 1485 ++++++++++++++++ .../ui/default/staff/circ/services/holds.js | 605 +++++++ Open-ILS/web/js/ui/default/staff/package.json | 28 + .../web/js/ui/default/staff/services/auth.js | 262 +++ .../web/js/ui/default/staff/services/core.js | 6 + .../js/ui/default/staff/services/coresvc.js | 33 + .../js/ui/default/staff/services/eframe.js | 210 +++ .../web/js/ui/default/staff/services/env.js | 165 ++ .../web/js/ui/default/staff/services/event.js | 56 + .../web/js/ui/default/staff/services/file.js | 28 + .../web/js/ui/default/staff/services/grid.js | 1522 +++++++++++++++++ .../web/js/ui/default/staff/services/hatch.js | 428 +++++ .../web/js/ui/default/staff/services/idl.js | 141 ++ .../js/ui/default/staff/services/navbar.js | 79 + .../web/js/ui/default/staff/services/net.js | 100 ++ .../web/js/ui/default/staff/services/org.js | 115 ++ .../web/js/ui/default/staff/services/pcrud.js | 298 ++++ .../web/js/ui/default/staff/services/print.js | 199 +++ .../js/ui/default/staff/services/startup.js | 84 + .../js/ui/default/staff/services/statusbar.js | 60 + .../js/ui/default/staff/services/strings.js | 22 + .../web/js/ui/default/staff/services/ui.js | 297 ++++ .../web/js/ui/default/staff/services/user.js | 56 + .../js/ui/default/staff/test/data/eg_mock.js | 50 + .../js/ui/default/staff/test/data/idl2js.pl | 22 + .../js/ui/default/staff/test/karma.conf.js | 88 + .../js/ui/default/staff/test/unit/egCore.js | 18 + .../js/ui/default/staff/test/unit/egEvent.js | 44 + .../ui/default/staff/test/unit/egHomeApp.js | 21 + .../js/ui/default/staff/test/unit/egIDL.js | 25 + .../js/ui/default/staff/test/unit/egOrg.js | 37 + .../ui/default/staff/test/unit/egPatronApp.js | 31 + .../ui/default/staff/test/unit/egStrings.js | 14 + Open-ILS/web/opac/locale/en-US/lang.dtd | 3 + .../server/patron/user_edit.xhtml | 176 ++ .../server/patron/user_edit_xhtml.js | 499 ++++++ 200 files changed, 21847 insertions(+), 94 deletions(-) create mode 100644 Open-ILS/src/templates/staff/README create mode 100644 Open-ILS/src/templates/staff/admin/t_user_perms_lookup.tt2 create mode 100644 Open-ILS/src/templates/staff/admin/user_perms.tt2 create mode 100644 Open-ILS/src/templates/staff/admin/workstation/index.tt2 create mode 100644 Open-ILS/src/templates/staff/admin/workstation/t_print_config.tt2 create mode 100644 Open-ILS/src/templates/staff/admin/workstation/t_print_templates.tt2 create mode 100644 Open-ILS/src/templates/staff/admin/workstation/t_splash.tt2 create mode 100644 Open-ILS/src/templates/staff/admin/workstation/t_stored_prefs.tt2 create mode 100644 Open-ILS/src/templates/staff/base.tt2 create mode 100644 Open-ILS/src/templates/staff/base_js.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/index.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_create.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_delete.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_edit.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_export.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_info.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_selector.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_grid_menu.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_load_shared.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_pending.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_search.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/bucket/record/t_view.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/catalog/index.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/index.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/missing_pieces.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/replace_barcode/index.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/t_cat_pane.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/t_circ_list_pane.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/t_circs_pane.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/t_holds_pane.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/t_list.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/t_summary_pane.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/t_triggered_events_pane.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/item/t_view.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/share/t_record_summary.tt2 create mode 100644 Open-ILS/src/templates/staff/cat/t_triggered_events_pane.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/checkin/index.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/checkin/t_checkin.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/holds/index.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/holds/t_pull.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/holds/t_pull_list.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/holds/t_shelf.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/in_house_use/index.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/index.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/pending.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/register.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_alerts.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_bcsearch.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_bill_history.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_bill_history_payments.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_bill_history_xacts.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_bills.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_credentials.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_edit_due_date_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_edit_perms.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_group.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_holds.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_holds_create.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_holds_list.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_items_out.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_last_patron.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_messages.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_move_to_group_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_new_note_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_notes.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_pending_list.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_renew_with_date_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_search.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_stat_cats.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_summary.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_triggered_events.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/patron/t_xact_details.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/renew/index.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/renew/t_renew.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/circ_strings.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/hold_strings.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_backdate_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_bad_barcode_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_bill_patron_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_cancel_hold_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_circ_exists_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_copy_in_transit_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_copy_not_avail_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_event_override_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_hold_copy_quality_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_hold_dates.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_hold_details.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_hold_edit_pickup_lib.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_hold_note_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_hold_notification_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_hold_notification_prefs.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_hold_shelf_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_mark_claims_returned_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_new_message_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_noncat_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_precat_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/circ/share/t_transit_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/config.tt2 create mode 100644 Open-ILS/src/templates/staff/css/circ.css.tt2 create mode 100644 Open-ILS/src/templates/staff/css/print.css.tt2 create mode 100644 Open-ILS/src/templates/staff/css/style.css.tt2 create mode 100644 Open-ILS/src/templates/staff/index.tt2 create mode 100644 Open-ILS/src/templates/staff/navbar.tt2 create mode 100644 Open-ILS/src/templates/staff/share/README create mode 100644 Open-ILS/src/templates/staff/share/print_templates/index.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_bill_payment.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_bills_current.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_bills_historical.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_checkin.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_checkout.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_hold_pull_list.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_hold_shelf_slip.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_hold_transit_slip.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_holds_for_bib.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_holds_for_patron.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_items_out.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_patron_address.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_patron_note.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_renew.tt2 create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_transit_slip.tt2 create mode 100644 Open-ILS/src/templates/staff/share/t_alert_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/share/t_autogrid.tt2 create mode 100644 Open-ILS/src/templates/staff/share/t_confirm_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/share/t_eframe.tt2 create mode 100644 Open-ILS/src/templates/staff/share/t_prompt_dialog.tt2 create mode 100644 Open-ILS/src/templates/staff/statusbar.tt2 create mode 100644 Open-ILS/src/templates/staff/t_login.tt2 create mode 100644 Open-ILS/src/templates/staff/t_splash.tt2 create mode 100644 Open-ILS/web/js/ui/default/staff/Gruntfile.js create mode 100644 Open-ILS/web/js/ui/default/staff/README.install create mode 100644 Open-ILS/web/js/ui/default/staff/admin/user_perms.js create mode 100644 Open-ILS/web/js/ui/default/staff/admin/workstation/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/bower.json create mode 100644 Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/cat/catalog/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/cat/item/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/cat/item/missing_pieces.js create mode 100644 Open-ILS/web/js/ui/default/staff/cat/item/replace_barcode/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/cat/services/record.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/checkin/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/holds/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/in_house_use/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/patron/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/patron/bills.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/patron/checkout.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/patron/holds.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/patron/items_out.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/patron/pending.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/patron/register.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/renew/app.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/services/billing.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/services/circ.js create mode 100644 Open-ILS/web/js/ui/default/staff/circ/services/holds.js create mode 100644 Open-ILS/web/js/ui/default/staff/package.json create mode 100644 Open-ILS/web/js/ui/default/staff/services/auth.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/core.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/coresvc.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/eframe.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/env.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/event.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/file.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/grid.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/hatch.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/idl.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/navbar.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/net.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/org.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/pcrud.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/print.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/startup.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/statusbar.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/strings.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/ui.js create mode 100644 Open-ILS/web/js/ui/default/staff/services/user.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/data/eg_mock.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/data/idl2js.pl create mode 100644 Open-ILS/web/js/ui/default/staff/test/karma.conf.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/unit/egCore.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/unit/egEvent.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/unit/egHomeApp.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/unit/egIDL.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/unit/egOrg.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/unit/egPatronApp.js create mode 100644 Open-ILS/web/js/ui/default/staff/test/unit/egStrings.js create mode 100644 Open-ILS/xul/staff_client/server/patron/user_edit.xhtml create mode 100644 Open-ILS/xul/staff_client/server/patron/user_edit_xhtml.js diff --git a/.gitignore b/.gitignore index 76675d67a1..073c02dc1f 100644 --- a/.gitignore +++ b/.gitignore @@ -353,3 +353,6 @@ Open-ILS/xul/staff_client/xulrunner-stub.exe Thumbs.db /js* /JavaScript* +Open-ILS/web/js/ui/default/staff/build/ +Open-ILS/web/js/ui/default/staff/node_modules/ +Open-ILS/web/js/ui/default/staff/bower_components/ diff --git a/Open-ILS/src/templates/opac/parts/bookbag_actions.tt2 b/Open-ILS/src/templates/opac/parts/bookbag_actions.tt2 index de33841e28..611d1fc960 100644 --- a/Open-ILS/src/templates/opac/parts/bookbag_actions.tt2 +++ b/Open-ILS/src/templates/opac/parts/bookbag_actions.tt2 @@ -4,7 +4,7 @@ # Wrap a url to open in a new tab in staff client. MACRO opac_wrap(url) BLOCK; - IF ctx.is_staff; + IF ctx.is_staff AND NOT ctx.is_browser_staff; # void(0) to return false and not go to new page in current tab. "javascript:xulG.new_tab(xulG.urls.XUL_OPAC_WRAPPER, {}, {'opac_url' : 'oils://remote" _ url _ "'});void(0);"; ELSE; diff --git a/Open-ILS/src/templates/opac/parts/record/copy_table.tt2 b/Open-ILS/src/templates/opac/parts/record/copy_table.tt2 index 814c4f926b..6ecaa10848 100644 --- a/Open-ILS/src/templates/opac/parts/record/copy_table.tt2 +++ b/Open-ILS/src/templates/opac/parts/record/copy_table.tt2 @@ -105,6 +105,19 @@ END; # FOREACH bib [% copy_info.barcode | html -%] [% IF ctx.is_staff %] + [%- IF ctx.is_browser_staff %] + [% l('view') %] + [% IF ctx.has_perm('UPDATE_COPY', copy_info.circ_lib) + OR ctx.has_perm('UPDATE_COPY', copy_info.call_number_owning_lib) %] + | + + + [% END %] + [% ELSE %] [% l('view') %] [%# if the user can edit copies, show the copy edit link %] @@ -116,6 +129,7 @@ END; # FOREACH bib [% l(' edit') %] [% END %] + [% END %] [% END %] [%- IF attrs.gtin13; ''; diff --git a/Open-ILS/src/templates/staff/README b/Open-ILS/src/templates/staff/README new file mode 100644 index 0000000000..920630183f --- /dev/null +++ b/Open-ILS/src/templates/staff/README @@ -0,0 +1,6 @@ +AnguarJS/Web Staff Client +========================= + + * TT templates loaded via JS routes must be preceded with t_* (or similar), + otherwise apache will serve the template at that path instead of the + index file since the path maps to a real template. diff --git a/Open-ILS/src/templates/staff/admin/t_user_perms_lookup.tt2 b/Open-ILS/src/templates/staff/admin/t_user_perms_lookup.tt2 new file mode 100644 index 0000000000..3a23cc50f1 --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/t_user_perms_lookup.tt2 @@ -0,0 +1,24 @@ +
+
+ + + + + + +
+
+ +
+
+ [% l('Barcode Not Found: [_1]', '{{bcNotFound}}') %] +
+ + diff --git a/Open-ILS/src/templates/staff/admin/user_perms.tt2 b/Open-ILS/src/templates/staff/admin/user_perms.tt2 new file mode 100644 index 0000000000..23a1257957 --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/user_perms.tt2 @@ -0,0 +1,18 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("User Permission Editor"); + ctx.page_app = "egUserPermsEditor"; +%] + +[% BLOCK APP_JS %] + + +[% END %] + + + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/admin/workstation/index.tt2 b/Open-ILS/src/templates/staff/admin/workstation/index.tt2 new file mode 100644 index 0000000000..3f927a4445 --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/workstation/index.tt2 @@ -0,0 +1,24 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Workstation Administration"); + ctx.page_app = "egWorkstationAdmin"; +%] + +[% BLOCK APP_JS %] + + + + + +[% END %] + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/admin/workstation/t_print_config.tt2 b/Open-ILS/src/templates/staff/admin/workstation/t_print_config.tt2 new file mode 100644 index 0000000000..06067accf3 --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/workstation/t_print_config.tt2 @@ -0,0 +1,176 @@ +
+ + + +
+
+

[% l('Printer Settings for Remote Printing') %]

+
+
+ +
+
+ +
+
+ + +
+
+
+
+ + +
+ + + +
+
+
+
+
+ + +
+
+
+
+
+
+

[% l('Compiled Printer Settings') %]

+
{{printerConfString()}}
+
+
+ + +
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ diff --git a/Open-ILS/src/templates/staff/admin/workstation/t_print_templates.tt2 b/Open-ILS/src/templates/staff/admin/workstation/t_print_templates.tt2 new file mode 100644 index 0000000000..cbc79f8d80 --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/workstation/t_print_templates.tt2 @@ -0,0 +1,59 @@ + + +

[% l('Print Templates') %]

+ +
+
[% l('Template Name') %]
+
+ +
+
+
+ +
+
+ +
+ +
+ +
+
+

[% l('Preview') %]

+
+
+
+

[% l('Template') %]

+
+ [% l( + "Unable to load template '[_1]'. The web server returned an error.", + '{{print.template_name}}') + %] +
+
+ +
+
+
+ diff --git a/Open-ILS/src/templates/staff/admin/workstation/t_splash.tt2 b/Open-ILS/src/templates/staff/admin/workstation/t_splash.tt2 new file mode 100644 index 0000000000..3a14bbeb73 --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/workstation/t_splash.tt2 @@ -0,0 +1,120 @@ +
+ + +
+ +
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+ [% l('Workstations Registered With This Computer') %] +
+
+
+
+ +
+
+ +
+
+ + + +
+
+ +
+
+ [% l('Register a New Workstation For This Computer') %] +
+
+
+
+
+
+ + +
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
diff --git a/Open-ILS/src/templates/staff/admin/workstation/t_stored_prefs.tt2 b/Open-ILS/src/templates/staff/admin/workstation/t_stored_prefs.tt2 new file mode 100644 index 0000000000..dc031b44bb --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/workstation/t_stored_prefs.tt2 @@ -0,0 +1,65 @@ + +
+
+
+

[% l('Stored User Preferences') %]

+
+[% |l %] +Preference values are stored as JSON strings. +Click on a preference to view the stored value. +Click on the delete (X) button to remove a preference's value. +[% END %] +
+
+
+ +
+
+ + +
+
+ +
+
{{$index + 1}}.
+
+ {{key}} +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
{{getCurrentKeyContent()}}
+
+ +
+
diff --git a/Open-ILS/src/templates/staff/base.tt2 b/Open-ILS/src/templates/staff/base.tt2 new file mode 100644 index 0000000000..ce9cc5ebb7 --- /dev/null +++ b/Open-ILS/src/templates/staff/base.tt2 @@ -0,0 +1,48 @@ + +[%- PROCESS 'staff/config.tt2' %] + + + [% l('Evergreen Staff [_1]', ctx.page_title) %] + + + + [% IF EXPAND_WEB_IMPORTS %] + + + [% ELSE %] + + [% END %] + + + + + + + + + + + + +
[% content %]
+ + [% + # status bar along bottom of page + INCLUDE "staff/statusbar.tt2"; + + # script imports + INCLUDE "staff/base_js.tt2"; + + # App-specific JS load commands go into an APP_JS block. + PROCESS APP_JS; + %] + + + + + diff --git a/Open-ILS/src/templates/staff/base_js.tt2 b/Open-ILS/src/templates/staff/base_js.tt2 new file mode 100644 index 0000000000..76bc5a32a8 --- /dev/null +++ b/Open-ILS/src/templates/staff/base_js.tt2 @@ -0,0 +1,47 @@ + + +[% IF EXPAND_WEB_IMPORTS %] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[% ELSE %] + + + + +[% END %] + + + diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/index.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/index.tt2 new file mode 100644 index 0000000000..2f21e7258c --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/index.tt2 @@ -0,0 +1,65 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Record Buckets"); + ctx.page_app = "egCatRecordBuckets"; + ctx.page_ctrl = "RecordBucketCtrl"; +%] + +[% BLOCK APP_JS %] + + + +[% END %] + + + + +
+
+ + +
+
+ [% INCLUDE 'staff/cat/bucket/record/t_bucket_info.tt2' %] +
+
+ + +
+
+ [% l('The selected bucket "{{bucketId}}" is not visible to this login.') %] +
+
+ +
+
+
+ +[% END %] + + diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_create.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_create.tt2 new file mode 100644 index 0000000000..e6bb3fe2ed --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_create.tt2 @@ -0,0 +1,35 @@ + + + +
+
+ + + +
+
diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_delete.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_delete.tt2 new file mode 100644 index 0000000000..0ca9887f9a --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_delete.tt2 @@ -0,0 +1,16 @@ + diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_edit.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_edit.tt2 new file mode 100644 index 0000000000..288c577c95 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_edit.tt2 @@ -0,0 +1,34 @@ + +
+
+ + + +
+
diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_export.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_export.tt2 new file mode 100644 index 0000000000..ffc26d06a1 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_export.tt2 @@ -0,0 +1,41 @@ + +
+
+ + + +
+
diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_info.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_info.tt2 new file mode 100644 index 0000000000..877fcf6aaf --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_info.tt2 @@ -0,0 +1,16 @@ + +
+ [% l('Bucket: {{bucket().name()}}') %] + + + + + / [% l('Created {{bucket().create_time() | date}}') %] + / {{bucket().description()}} +
+ +
+ [% l('No Bucket Selected') %] +
+ diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_selector.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_selector.tt2 new file mode 100644 index 0000000000..37eef805e4 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_bucket_selector.tt2 @@ -0,0 +1,27 @@ + + diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_grid_menu.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_grid_menu.tt2 new file mode 100644 index 0000000000..a2e2bde533 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_grid_menu.tt2 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_load_shared.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_load_shared.tt2 new file mode 100644 index 0000000000..9aab308bda --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_load_shared.tt2 @@ -0,0 +1,25 @@ + +
+
+ + + +
+
+ diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_pending.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_pending.tt2 new file mode 100644 index 0000000000..eefc60aca6 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_pending.tt2 @@ -0,0 +1,20 @@ + + + [% INCLUDE 'staff/cat/bucket/record/t_grid_menu.tt2' %] + + + + + + + diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_search.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_search.tt2 new file mode 100644 index 0000000000..684b13999f --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_search.tt2 @@ -0,0 +1,46 @@ +
+ + +
+
+
+
+ [% l('Record Query') %] + +
+
+
+
+
+
+
+
+
+ [% l('Searching...') %] +
+
+
+
+ + + + + [% INCLUDE 'staff/cat/bucket/record/t_grid_menu.tt2' %] + + + + + + + diff --git a/Open-ILS/src/templates/staff/cat/bucket/record/t_view.tt2 b/Open-ILS/src/templates/staff/cat/bucket/record/t_view.tt2 new file mode 100644 index 0000000000..39c866fe0d --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/bucket/record/t_view.tt2 @@ -0,0 +1,28 @@ + + + [% INCLUDE 'staff/cat/bucket/record/t_grid_menu.tt2' %] + + + + + + + + + + + {{item.title}} + + + + + diff --git a/Open-ILS/src/templates/staff/cat/catalog/index.tt2 b/Open-ILS/src/templates/staff/cat/catalog/index.tt2 new file mode 100644 index 0000000000..9e799d4f69 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/catalog/index.tt2 @@ -0,0 +1,21 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Catalog"); + ctx.page_app = "egCatalogApp"; +%] + +[% BLOCK APP_JS %] + + + + +[% INCLUDE 'staff/circ/share/circ_strings.tt2' %] + +[% INCLUDE 'staff/circ/share/hold_strings.tt2' %] + +[% END %] + +
+ +[% END %] + diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2 new file mode 100644 index 0000000000..47f1c6f183 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2 @@ -0,0 +1,48 @@ + +
+
+
+ [% l('Catalog') %] + [% l('MARC HTML') %] + [% l('Holds for Record') %] +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+ [% INCLUDE 'staff/cat/catalog/t_holds.tt2' %] +
+
+ diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2 new file mode 100644 index 0000000000..62af9180d0 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2 @@ -0,0 +1,108 @@ + +
+
+
+
+ [% l('Pickup Library') %] + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + {{item.hold.current_copy().barcode()}} + + + + + + + + + + + + {{item.mvr.title()}} + + + + + + + + + + + + + + +
+
+
+ +
+
+
+ + +
+
+
+ +
+
+
+ + +
diff --git a/Open-ILS/src/templates/staff/cat/item/index.tt2 b/Open-ILS/src/templates/staff/cat/item/index.tt2 new file mode 100644 index 0000000000..2232a7d584 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/index.tt2 @@ -0,0 +1,83 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Item Status"); + ctx.page_app = "egItemStatus"; + ctx.page_ctrl = "SearchCtrl"; +%] + +[% BLOCK APP_JS %] + + + + + + + +[% END %] + + + +

[% l('Item Status Display') %]

+ +

[% l('Scan Item') %]

+ +
+ +
+
+ + +
+ +
+
+
[% l('OR') %]
+
+ +
+
+
+
+
+ +
+ +
+
+ + +
+
+
+ [% l('Item Not Found') %] +
+
+
+ +
+ +[% END %] + + diff --git a/Open-ILS/src/templates/staff/cat/item/missing_pieces.tt2 b/Open-ILS/src/templates/staff/cat/item/missing_pieces.tt2 new file mode 100644 index 0000000000..2581ba7274 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/missing_pieces.tt2 @@ -0,0 +1,69 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Scan Item as Missing Pieces"); + ctx.page_app = "egItemMissingPieces"; + ctx.page_ctrl = "MissingPiecesCtrl"; +%] + +[% BLOCK APP_JS %] + + + + +[% END %] + +
+
+ [% l('Scan Item as Missing Pieces') %] +
+
+ +
+
+ + + + + + +
+
+ +
+
+ [% l('Barcode Not Found: [_1]', '{{bcNotFound}}') %] +
+ +
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +[% END %] + + diff --git a/Open-ILS/src/templates/staff/cat/item/replace_barcode/index.tt2 b/Open-ILS/src/templates/staff/cat/item/replace_barcode/index.tt2 new file mode 100644 index 0000000000..6472a4890e --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/replace_barcode/index.tt2 @@ -0,0 +1,49 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Replace Item Barcode"); + ctx.page_app = "egItemReplaceBarcode"; + ctx.page_ctrl = "ReplaceItemBarcodeCtrl"; +%] + +[% BLOCK APP_JS %] + + +[% END %] + +

[% l('Replace Item Barcode') %]

+ +
+
+
+
+ + +
+
+ + +
+ +
+
+
+ +
+
+
+ [% l('Copy Not Found') %] +
+
+ [% l('Copy Updated') %] + + + [% l('View Item Details') %] + +
+
+
+[% END %] diff --git a/Open-ILS/src/templates/staff/cat/item/t_cat_pane.tt2 b/Open-ILS/src/templates/staff/cat/item/t_cat_pane.tt2 new file mode 100644 index 0000000000..43436aff52 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/t_cat_pane.tt2 @@ -0,0 +1,3 @@ +

[% l('MARC Record') %]

+ + diff --git a/Open-ILS/src/templates/staff/cat/item/t_circ_list_pane.tt2 b/Open-ILS/src/templates/staff/cat/item/t_circ_list_pane.tt2 new file mode 100644 index 0000000000..9f7bbb5934 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/t_circ_list_pane.tt2 @@ -0,0 +1,51 @@ +
+
+ [% l('Item has not circulated.') %] +
+
+ +
+ +
+
[% l('Check Out Date') %]
+
{{circ.xact_start() | date:'short'}}
+
[% l('Due Date') %]
+
{{circ.due_date() | date:'short'}}
+
[% l('Stop Fines Time') %]
+
{{circ.stop_fines_time() | date:'short'}}
+
[% l('Checkin Time') %]
+
{{circ.checkin_time() | date:'short'}}
+
+
+
[% l('Check Out Library') %]
+
{{circ.circ_lib().shortname()}}
+
[% l('Renewal?') %]
+
{{ + circ.phone_renewal() == 't' || + circ.desk_renewal() == 't' || + circ.opac_renewal() == 't' + }}
+
[% l('Stop Fines Reason') %]
+
{{circ.stop_fines()}}
+
[% l('Check In Library') %]
+
{{circ.checkin_lib().shortname()}}
+
+
+
+ diff --git a/Open-ILS/src/templates/staff/cat/item/t_circs_pane.tt2 b/Open-ILS/src/templates/staff/cat/item/t_circs_pane.tt2 new file mode 100644 index 0000000000..24a3241537 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/t_circs_pane.tt2 @@ -0,0 +1,189 @@ +
+
+ [% l('No Previous Circ Group') %] +
+
+
+
+
+ [% l('Previous Circ Group') %] +
+
+ + + +
+
[% l('Total Circs') %]
+
+ {{prev_circ_summary.num_circs()}} +
+
+ +
+
[% l('Checkout Date') %]
+
+ {{prev_circ_summary.start_time() | date:'short'}} +
+
+ +
+
[% l('Checkout Workstation') %]
+
+ {{prev_circ_summary.checkout_workstation()}} +
+
+ +
+
[% l('Last Renewed On') %]
+
+ {{prev_circ_summary.last_renewal_time() | date:'short'}} +
+
+ +
+
[% l('Renewal Workstation') %]
+
+ {{prev_circ_summary.last_renewal_workstation()}} +
+
+ +
+
[% l('Stop Fines Reason') %]
+
+ {{prev_circ_summary.last_stop_fines()}} +
+
+
+
[% l('Stop Fines Time') %]
+
+ {{prev_circ_summary.last_stop_fines_time() | date:'short'}} +
+
+
+
[% l('Checkin Time') %]
+
+ {{prev_circ_summary.last_checkin_time() | date:'short'}} +
+
+
+
[% l('Checkin Scan Time') %]
+
+ {{prev_circ_summary.last_checkin_scan_time() | date:'short'}} +
+
+
+
[% l('Checkin Workstation') %]
+
+ {{prev_circ_summary.last_checkin_workstation()}} +
+
+
+ +
+
+ [% l('No Recent Circ Group') %] +
+
+
+
+
+ [% l('Most Recent Circ Group') %] +
+
+ + +
+
[% l('Total Circs') %]
+
+ {{circ_summary.num_circs()}} +
+
+ +
+
[% l('Checkout Date') %]
+
+ {{circ.xact_start() | date:'short'}} +
+
+ +
+
[% l('Checkout Workstation') %]
+
+ {{circ.workstation().name()}} +
+
+ +
+
[% l('Last Renewed On') %]
+
+ {{circ_summary.last_renewal_time() | date:'short'}} +
+
+ +
+
[% l('Renewal Workstation') %]
+
+ {{circ_summary.last_renewal_workstation()}} +
+
+ +
+
[% l('Stop Fines Reason') %]
+
+ {{circ.stop_fines()}} +
+
+ +
+
[% l('Stop Fines Time') %]
+
+ {{circ.stop_fines_time() | date:'short'}} +
+
+ +
+
[% l('Checkin Time') %]
+
+ {{circ.checkin_time() | date:'short'}} +
+
+ +
+
[% l('Checkin Scan Time') %]
+
+ {{circ.checkin_scan_time() | date:'short'}} +
+
+ +
+
[% l('Checkin Workstation') %]
+
+ {{circ.checkin_workstation.name()}} +
+
+
+ diff --git a/Open-ILS/src/templates/staff/cat/item/t_holds_pane.tt2 b/Open-ILS/src/templates/staff/cat/item/t_holds_pane.tt2 new file mode 100644 index 0000000000..601c1287bc --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/t_holds_pane.tt2 @@ -0,0 +1,125 @@ +
+
+ [% l('Item is not captured for a hold') %] +
+
+
+
+
+ [% l('Captured Hold Info') %] +
+
+ + +
+
[% l('Pickup Lib') %]
+
+ {{hold.pickup_lib().shortname()}} +
+
+
+
[% l('Current Shelf Lib') %]
+
+ {{hold.current_shelf_lib().shortname()}} +
+
+
+
[% l('Request Date') %]
+
+ {{hold.request_time() | date:'short'}} +
+
+
+
[% l('Capture Date') %]
+
+ {{hold.capture_time() | date:'short'}} +
+
+
+
[% l('Shelf Time') %]
+
+ {{hold.shelf_time() | date:'short'}} +
+
+
+
[% l('Shelf Expire Time') %]
+
+ {{hold.shelf_expire_time() | date:'short'}} +
+
+
+
[% l('Hold Expire Time') %]
+
+ {{hold.expire_time() | date:'short'}} +
+
+
+
[% l('Behind Desk') %]
+
+ {{hold.behind_desk()}} +
+
+
+ +
+
+ [% l('Item has not transited') %] +
+
+ +
+
+
+ [% l('Most Recent Transit') %] +
+
+
+
[% l('Transit Source') %]
+
+ {{transit.source().shortname()}} +
+
+
+
[% l('Transit Destination') %]
+
+ {{transit.dest().shortname()}} +
+
+
+
[% l('Transit Send Time') %]
+
+ {{transit.source_send_time() | date:'short'}} +
+
+
+
[% l('Transit Receive Time') %]
+
+ {{transit.source_recv_time() | date:'short'}} +
+
+
+ diff --git a/Open-ILS/src/templates/staff/cat/item/t_list.tt2 b/Open-ILS/src/templates/staff/cat/item/t_list.tt2 new file mode 100644 index 0000000000..cbd2e76256 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/t_list.tt2 @@ -0,0 +1,21 @@ + + + + + + + + + {{item['call_number.record.simple_record.title']}} + + + + diff --git a/Open-ILS/src/templates/staff/cat/item/t_summary_pane.tt2 b/Open-ILS/src/templates/staff/cat/item/t_summary_pane.tt2 new file mode 100644 index 0000000000..69181249ee --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/t_summary_pane.tt2 @@ -0,0 +1,178 @@ + + +
+ +
+
[% l('Barcode') %]
+
{{copy.barcode()}}
+ +
[% l('Circ Library') %]
+
{{copy.circ_lib().shortname()}}
+ +
[% l('Call # Prefix') %]
+
+ {{copy.call_number().prefix().label()}} +
+ +
[% l('Status') %]
+
{{copy.status().name()}}
+
+ +
+
[% l('Price') %]
+
{{copy.price()}}
+ +
[% l('Owning Library') %]
+
{{copy.circ_lib().shortname()}}
+ +
[% l('Call #') %]
+
{{copy.call_number().label()}}
+ +
[% l('Due Date') %]
+
{{circ.due_date() | date:'short'}}
+
+ +
+
[% l('ISBN') %]
+
+ {{copy.call_number().record().simple_record().isbn()}} +
+ +
[% l('Copy Location') %]
+
{{copy.location().name()}}
+ +
[% l('Call # Suffix') %]
+
+ {{copy.call_number().suffix().label()}} +
+ +
[% l('Checkout Date') %]
+
{{circ.xact_start() | date:'short'}}
+
+ +
+
[% l('Date Created') %]
+
{{copy.create_date() | date:'short'}}
+ +
[% l('Loan Duration') %]
+
{{circ.duration()}}
+ +
[% l('Renewal Type') %]
+
+
[% l('OPAC') %]
+
[% l('Desk') %]
+
[% l('Phone') %]
+
+ +
[% l('Checkout Workstation') %]
+
{{circ.workstation().name()}}
+
+ +
+
[% l('Date Active') %]
+
{{copy.active_date() | date:'short'}}
+ +
[% l('Fine Level') %]
+
{{circ.duration_rule().name()}}
+ +
[% l('Total Circs') %]
+
{{total_circs}}
+ +
[% l('Duration Rule') %]
+
{{circ.duration_rule().name()}}
+
+ +
+
[% l('Status Changed') %]
+
{{copy.status_changed_time() | date:'short'}}
+ +
[% l('Reference') %]
+
{{copy.ref()}}
+ +
[% l('Total Circs - Current Year') %]
+
{{total_circs_this_year}}
+ +
[% l('Recurring Fine Rule') %]
+
{{circ.recurring_fine_rule().name()}}
+
+ +
+
[% l('Copy ID') %]
+
{{copy.id()}}
+ +
[% l('OPAC Visible') %]
+
{{copy.opac_visible()}}
+ +
[% l('Total Circs - Prev Year') %]
+
{{total_circs_prev_year}}
+ +
[% l('Max Fine Rule') %]
+
{{circ.max_fine_rule().name()}}
+
+ +
+
[% l('TCN') %]
+
{{copy.call_number().record().tcn_value()}}
+ +
[% l('Holdable') %]
+
{{copy.opac_visible()}}
+ +
[% l('Renewal Workstation') %]
+
{{circ_summary.last_renewal_workstation()}}
+ +
[% l('Checkin Time') %]
+
+ {{circ.checkin_time() || + circ_summary.last_checkin_time() | date:'short'}} +
+
+ +
+
[% l('Floating') %]
+
{{copy.floating()}}
+ +
[% l('Circulate') %]
+
{{copy.circulate()}}
+ +
[% l('Remaining Renewals') %]
+
{{circ.renewal_remaining()}}
+ +
[% l('Checkin Scan Time') %]
+
+ {{circ.checkin_scan_time() || + circ_summary.last_checkin_scan_time() | date:'short'}} +
+
+ +
+ +
+
+ +
[% l('Circ Modifier') %]
+
{{copy.circ_modifier().name()}}
+ + +
+
+ +
[% l('Checkin Workstation') %]
+
+ {{circ.checkin_workstation().name() || + circ_summary.last_checkin_workstation().name()}} +
+
+ +
+
[% l('Alert Message') %]
+
+ {{copy.alert_message()}} +
+
+ +
diff --git a/Open-ILS/src/templates/staff/cat/item/t_triggered_events_pane.tt2 b/Open-ILS/src/templates/staff/cat/item/t_triggered_events_pane.tt2 new file mode 100644 index 0000000000..1e320739f8 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/t_triggered_events_pane.tt2 @@ -0,0 +1,2 @@ + + diff --git a/Open-ILS/src/templates/staff/cat/item/t_view.tt2 b/Open-ILS/src/templates/staff/cat/item/t_view.tt2 new file mode 100644 index 0000000000..82920b4324 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/item/t_view.tt2 @@ -0,0 +1,34 @@ + + + + +
+ + +
+
+
+
+
+
+
+ diff --git a/Open-ILS/src/templates/staff/cat/share/t_record_summary.tt2 b/Open-ILS/src/templates/staff/cat/share/t_record_summary.tt2 new file mode 100644 index 0000000000..3de97e5daa --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/share/t_record_summary.tt2 @@ -0,0 +1,53 @@ +
[% l('Record Summary') %]
+ +
+
+
[% l('Title:') %]
+ + +
[% l('Edition:') %]
+
+ +
[% l('TCN:') %]
+
{{record.tcn_value()}}
+ +
[% l('Created By:') %]
+
{{record.creator().usrname()}}
+
+ +
+
[% l('Author:') %]
+
{{record.simple_record().author()}}
+ +
[% l('Pub Date:') %]
+
+ {{record.simple_record().pubdate()}} +
+ +
[% l('Databse ID:') %]
+
{{record.id()}}
+ +
[% l('Last Edited By:') %]
+
{{record.editor().usrname()}}
+
+ +
+
[% l('Bib Call #:') %]
+
+ +
+
+ +
[% l('Record Owner:') %]
+
{{record.owner().shortname()}}
+ +
[% l('Last Edited On:') %]
+
{{record.edit_date() | date:'short'}}
+
+
+ diff --git a/Open-ILS/src/templates/staff/cat/t_triggered_events_pane.tt2 b/Open-ILS/src/templates/staff/cat/t_triggered_events_pane.tt2 new file mode 100644 index 0000000000..1e320739f8 --- /dev/null +++ b/Open-ILS/src/templates/staff/cat/t_triggered_events_pane.tt2 @@ -0,0 +1,2 @@ + + diff --git a/Open-ILS/src/templates/staff/circ/checkin/index.tt2 b/Open-ILS/src/templates/staff/circ/checkin/index.tt2 new file mode 100644 index 0000000000..dd2c0cf4d2 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/checkin/index.tt2 @@ -0,0 +1,19 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Check In"); + ctx.page_app = "egCheckinApp"; +%] + +[% BLOCK APP_JS %] + + + + +[% INCLUDE 'staff/circ/share/circ_strings.tt2' %] + + +[% END %] + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/circ/checkin/t_checkin.tt2 b/Open-ILS/src/templates/staff/circ/checkin/t_checkin.tt2 new file mode 100644 index 0000000000..83b88f50b4 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/checkin/t_checkin.tt2 @@ -0,0 +1,225 @@ +
+
+ [% l('Checkin Items') %] + [% l('Capture Holds') %] +
+
+ +
+
+
+
+ [% l('Backdated Check In [_1]', + '{{checkinArgs.backdate | date:"shortDate"}}') %] +
+
+ [% l('Ignore Pre-Cataloged Items') %] +
+
+ [% l('Suppress Holds and Transits') %] +
+
+ [% l('Amnesty Mode') %] +
+
+ [% l('Auto-Print Hold and Transit Slips') %] +
+
+ [% l('Clear Holds Shelf') %] +
+
+
+ [% l('Always Retarget Local Holds') %] +
+
+ [% l('Retarget Local Holds') %] +
+
+
+ [% l('Capture Local Holds As Transits') %] +
+
+
+
+ + +
+
+
+
+ + + + + + +
+
+
+ +
+
+ + [% l('[_1] was already checked in.', '{{alert.already_checked_in}}') %] + + + [% l('Item [_1] has never circulated.', '{{alert.item_never_circed}}') %] + +
+
+ +
+
+
+
[% l('Effective Date') %]
+ +
+
+
+
+
+ +
+
+ [% l('Fine Tally:') %] + {{fine_total | currency}} + + [% l('Transaction for [_1] billed:', '{{billable_barcode}}') %] + {{billable_amount | currency}} + +
+
+ +
+ +[% INCLUDE 'staff/circ/checkin/t_checkin_table.tt2' %] + + + diff --git a/Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2 b/Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2 new file mode 100644 index 0000000000..1ee0c09f78 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2 @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + {{item.copy_barcode}} + + + + + + + + + + + + + + + + + + + + + {{item.title}} + + + + + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/circ/holds/index.tt2 b/Open-ILS/src/templates/staff/circ/holds/index.tt2 new file mode 100644 index 0000000000..b32773806b --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/holds/index.tt2 @@ -0,0 +1,30 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Holds Shelf"); + ctx.page_app = "egHoldsApp"; +%] + +[% BLOCK APP_JS %] + + + + +[% INCLUDE 'staff/circ/share/circ_strings.tt2' %] + +[% INCLUDE 'staff/circ/share/hold_strings.tt2' %] + + + + +[% END %] + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/circ/holds/t_pull.tt2 b/Open-ILS/src/templates/staff/circ/holds/t_pull.tt2 new file mode 100644 index 0000000000..de2988b571 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/holds/t_pull.tt2 @@ -0,0 +1,26 @@ +
+
+ [% l('Holds Pull List') %] +
+
+ +
+ +
+[% INCLUDE 'staff/circ/holds/t_pull_list.tt2' %] +
+ + +
+
+
+ +
+
+
+ + +
diff --git a/Open-ILS/src/templates/staff/circ/holds/t_pull_list.tt2 b/Open-ILS/src/templates/staff/circ/holds/t_pull_list.tt2 new file mode 100644 index 0000000000..8cea1d1e72 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/holds/t_pull_list.tt2 @@ -0,0 +1,88 @@ +
+ [% l('Loading... [_1]', '{{print_list_progress}}') %] +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{item.hold.current_copy().barcode()}} + + + + + + + + + + + + + {{item.mvr.title()}} + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/circ/holds/t_shelf.tt2 b/Open-ILS/src/templates/staff/circ/holds/t_shelf.tt2 new file mode 100644 index 0000000000..b0a4aafbca --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/holds/t_shelf.tt2 @@ -0,0 +1,40 @@ +
+
+ [% l('Holds Shelf') %] +
+
+ +
+
+
+ [% l('Pickup Library') %] + +
+
+
+ + {{clear_progress.value}} / {{clear_progress.max}} + +
+
+ +
+ +
+[% INCLUDE 'staff/circ/holds/t_shelf_list.tt2' %] +
+ + +
+
+
+ +
+
+
+ + +
diff --git a/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2 b/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2 new file mode 100644 index 0000000000..3700df9dcf --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2 @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{item.hold.current_copy().barcode()}} + + + + + + + + + + + + + {{item.mvr.title()}} + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/circ/in_house_use/index.tt2 b/Open-ILS/src/templates/staff/circ/in_house_use/index.tt2 new file mode 100644 index 0000000000..2299603dac --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/in_house_use/index.tt2 @@ -0,0 +1,81 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("In-House Use"); + ctx.page_app = "egInHouseUseApp"; + ctx.page_ctrl = "InHouseUseCtrl"; +%] + +[% BLOCK APP_JS %] + + + +[% END %] + + + +
+
+ +
+
+ + +
+
+ +
+
+
+ + +
+ + + +
+
+
+
+ +
+
[% l('Copy Not Found') %]
+
+ + + + + + + + + + +[% END %] diff --git a/Open-ILS/src/templates/staff/circ/patron/index.tt2 b/Open-ILS/src/templates/staff/circ/patron/index.tt2 new file mode 100644 index 0000000000..9048faa308 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/index.tt2 @@ -0,0 +1,175 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Patron"); + ctx.page_app = "egPatronApp"; + ctx.page_ctrl = "PatronCtrl"; +%] + +[% BLOCK APP_JS %] + + + + + + +[% INCLUDE 'staff/circ/share/circ_strings.tt2' %] + +[% INCLUDE 'staff/circ/share/hold_strings.tt2' %] + + + + + + + + + + + + + + + + + + +[% END %] + + + +
+
+ [% INCLUDE 'staff/circ/patron/t_summary.tt2' %] +
+
+
+
+
+
+
+
+
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/circ/patron/pending.tt2 b/Open-ILS/src/templates/staff/circ/patron/pending.tt2 new file mode 100644 index 0000000000..21c21cb32a --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/pending.tt2 @@ -0,0 +1,17 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Pending Patrons"); + ctx.page_app = "egPendingPatronsApp"; +%] + +[% BLOCK APP_JS %] + + + + + +[% END %] + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/circ/patron/register.tt2 b/Open-ILS/src/templates/staff/circ/patron/register.tt2 new file mode 100644 index 0000000000..4a3c9ce3df --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/register.tt2 @@ -0,0 +1,15 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Patron Registration"); + ctx.page_app = "egPatronRegApp"; +%] + +[% BLOCK APP_JS %] + + + +[% END %] + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/circ/patron/t_alerts.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_alerts.tt2 new file mode 100644 index 0000000000..822240c2d6 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_alerts.tt2 @@ -0,0 +1,75 @@ +
+ + + + +
+ [% l('Holds available: [_1]', '{{patron_stats().holds.ready}}') %] +
+ +
+ [% l('Patron account is EXPIRED.') %] +
+ +
+ [% l('Patron account will expire soon. Please renew.') %] +
+ +
+ [% l('Patron account is BARRED') %] +
+ +
+ [% l('Patron account is INACTIVE') %] +
+ +
+ [% l('Patron account retrieved with an INACTIVE card.') %] +
+ + +
+
+
+
+
[% l('Alert Message') %]
+
+
+ {{patron().alert_message()}} +
+
+
+
+ + +
+
+
+
+
[% l('Penalties') %]
+
+
+
+
+ {{penalty.org_unit().shortname()}} +
+
+ {{penalty.standing_penalty().label()}} +
{{penalty.note()}}
+
+
+ {{penalty.set_date() | date:'shortDate'}} +
+
+
+
+
+
+ +
+
+[% l('Press a navigation button above (for example, Check Out) to clear this alert.') %] +
+
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bcsearch.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bcsearch.tt2 new file mode 100644 index 0000000000..216b1862f0 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_bcsearch.tt2 @@ -0,0 +1,22 @@ + +
+
+ + + + + + +
+
+ +
+
+ [% l('Barcode Not Found: [_1]', '{{bcNotFound}}') %] +
+ + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bill_history.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bill_history.tt2 new file mode 100644 index 0000000000..6739d7e6bf --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_bill_history.tt2 @@ -0,0 +1,34 @@ +

[% l('Bill History') %]

+ + +
+
+ +
+
[% l('Selected Billed:') %]
+
{{totals.selected_billed() | currency}}
+
[% l('Selected Paid:') %]
+
{{totals.selected_paid() | currency}}
+
+
[% l('Start Date:') %]
+
+
[% l('End Date:') %]
+
+
+
+ [% INCLUDE 'staff/circ/patron/t_bill_history_xacts.tt2' %] + [% INCLUDE 'staff/circ/patron/t_bill_history_payments.tt2' %] +
+
+ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bill_history_payments.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bill_history_payments.tt2 new file mode 100644 index 0000000000..c4fa9d3f93 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_bill_history_payments.tt2 @@ -0,0 +1,79 @@ + +
+ + + + + + + + + + + + + + + {{item.title}} + + + + + + + {{item.copy_barcode}} + + + + + + + + + + + + + + +
+ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bill_history_xacts.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bill_history_xacts.tt2 new file mode 100644 index 0000000000..70f48b6ca0 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_bill_history_xacts.tt2 @@ -0,0 +1,49 @@ + +
+ + + + + + + + + + + + + + + + {{item.title}} + + + + + + + {{item.copy_barcode}} + + + + + + + + + + + + +
+ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bills.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bills.tt2 new file mode 100644 index 0000000000..b8ab9177ac --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_bills.tt2 @@ -0,0 +1,109 @@ + +
+
+ +
+
[% l('Total Owed:') %]
+
{{summary.balance_owed() | currency}}
+
[% l('Refunds Available:') %]
+
{{refunds_available() | currency}}
+
+
+
[% l('Total Billed:') %]
+
{{summary.total_owed() | currency}}
+
[% l('Credit Available:') %]
+
{{patron().credit_forward_balance() | currency}}
+
+
+
[% l('Total Paid:') %]
+
{{summary.total_paid() | currency}}
+
[% l('Session Voided:') %]
+
{{session_voided | currency}}
+
+

+
+
[% l('Owed for Selected:') %]
+
{{owed_selected() | currency}}
+
[% l('Pending Payment:') %]
+
{{pending_payment() | currency}}
+
+
+
[% l('Billed for Selected:') %]
+
{{billed_selected() | currency}}
+
[% l('Pending Change:') %]
+
{{pending_change() | currency}}
+
+
+
[% l('Paid for Selected:') %]
+
{{paid_selected() | currency}}
+
+
+ +
+
+
+ [% l('Pay Bill') %] + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+ +
+[% INCLUDE 'staff/circ/patron/t_bills_list.tt2' %] +
+ + +
+
+
+
+ +
+
+ + +
+
+
+ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 new file mode 100644 index 0000000000..c98d0411b5 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{item.title}} + + + + + + + {{item.copy_barcode}} + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2 new file mode 100644 index 0000000000..945f2dba4a --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2 @@ -0,0 +1,143 @@ + + +
+
+
+
+ +
+ + +
+ + + + + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ + + + + + + + + + + {{item.copy_barcode}} + + + + + + + + + + + + + + + + + + {{item.title}} + + + + + + + + + + + + + +
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_credentials.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_credentials.tt2 new file mode 100644 index 0000000000..524cb7fa74 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_credentials.tt2 @@ -0,0 +1,65 @@ +
+
+
+
+ [% l('Verify Credentials') %] +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ [% l('Succes testing credentials') %] +
+
+ [% l('Failure testing credentials') %] +
+
+ [% l('No user found with the requested username / barcode') %] +
+
+
+ +
+
+
+
+
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 new file mode 100644 index 0000000000..3f85366f4e --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 @@ -0,0 +1,2 @@ + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_edit_due_date_dialog.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_edit_due_date_dialog.tt2 new file mode 100644 index 0000000000..dcca6d3311 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_edit_due_date_dialog.tt2 @@ -0,0 +1,24 @@ +
+ + + +
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_edit_perms.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_edit_perms.tt2 new file mode 100644 index 0000000000..76d10f6e8a --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_edit_perms.tt2 @@ -0,0 +1 @@ + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_group.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_group.tt2 new file mode 100644 index 0000000000..21bb4c22e5 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_group.tt2 @@ -0,0 +1,55 @@ + +
[% l('Group Member Details') %]
+
+
[% l('Total Owed: ') %]
+
{{totals.owed | currency}}
+
[% l('Total Out: ') %]
+
{{totals.total_out}}
+
[% l('Total Overdue: ') %]
+
{{totals.overdue}}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_holds.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_holds.tt2 new file mode 100644 index 0000000000..5ccd762a76 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_holds.tt2 @@ -0,0 +1,38 @@ + + + + +
+ +
+[% INCLUDE 'staff/circ/patron/t_holds_list.tt2' %] +
+ + +
+
+
+ +
+
+
+ + +
+ + + +
+ +
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_holds_create.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_holds_create.tt2 new file mode 100644 index 0000000000..e8e82e4e64 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_holds_create.tt2 @@ -0,0 +1,4 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_holds_list.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_holds_list.tt2 new file mode 100644 index 0000000000..df026fa945 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_holds_list.tt2 @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + {{item.hold.current_copy().barcode()}} + + + + + + + + + + + + {{item.mvr.title()}} + + + + + + + + + + + + + + +
+
+
+ +
+
+ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_items_out.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_items_out.tt2 new file mode 100644 index 0000000000..71fc1a0c4a --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_items_out.tt2 @@ -0,0 +1,73 @@ + + + +
+ [% l('Items Checked Out') %] +
+ + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_last_patron.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_last_patron.tt2 new file mode 100644 index 0000000000..9a917d859e --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_last_patron.tt2 @@ -0,0 +1,9 @@ +
+
+ [% l('No patrons recently accessed.') %] + + [% l('Try Patron Search') %] + +
+
+
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_messages.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_messages.tt2 new file mode 100644 index 0000000000..e23c258fa4 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_messages.tt2 @@ -0,0 +1,52 @@ + +
[% l('Staff-Generated Penalties / Messages') %]
+
+ + + + + + + + + + + + + + + + + + +

+ +
+
[% l('Archived Penalties / Messages') %]
+
+
[% l('Set Date Start:') %]
+
+
[% l('Set Date End:') %]
+
+
+ + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_move_to_group_dialog.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_move_to_group_dialog.tt2 new file mode 100644 index 0000000000..96e97332e7 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_move_to_group_dialog.tt2 @@ -0,0 +1,28 @@ +
+ + + +
+ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_new_note_dialog.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_new_note_dialog.tt2 new file mode 100644 index 0000000000..cfc5b4081d --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_new_note_dialog.tt2 @@ -0,0 +1,42 @@ +
+ + + +
+ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_notes.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_notes.tt2 new file mode 100644 index 0000000000..b83f317131 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_notes.tt2 @@ -0,0 +1,44 @@ +
+
+ +
+
+ +
+
+
+
{{note.title()}}
+
+
+ [% l('Patron Visible') %] + [% l('Staff Only') %] + {{note.create_date() | date:'short'}} + [% l('Created by [_1]', '{{note.creator().usrname()}}') %] +
+
+
+
+ +
+
+
+
{{note.value()}}
+
+
+
+ + +
+
+
+
+
+
+
+
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_pending_list.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_pending_list.tt2 new file mode 100644 index 0000000000..3809d1230b --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_pending_list.tt2 @@ -0,0 +1,34 @@ +
+
+ [% l('Pending Patrons') %] +
+
+ +[% l('Home Library: ' ) %] + +
+ + + + + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_renew_with_date_dialog.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_renew_with_date_dialog.tt2 new file mode 100644 index 0000000000..d8112d00db --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_renew_with_date_dialog.tt2 @@ -0,0 +1,24 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_search.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_search.tt2 new file mode 100644 index 0000000000..29abb4092a --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_search.tt2 @@ -0,0 +1,158 @@ + + + +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ + +
+ + +
+
+ +
+ + +
+ +
+
+ +
+
+
+
+
+
+ + +
+
+
+ [% INCLUDE 'staff/circ/patron/t_search_results.tt2' %] +
+
+ + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2 new file mode 100644 index 0000000000..ac83e6ddc9 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2 @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_stat_cats.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_stat_cats.tt2 new file mode 100644 index 0000000000..21eb217609 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_stat_cats.tt2 @@ -0,0 +1,19 @@ +
+
+
+
+ {{map.stat_cat().name()}} + {{map.stat_cat_entry()}} +
+
+
+ [% l('Patron Visible') %] + [% l('Staff Only') %] + [% l('@ [_1]', '{{map.stat_cat().owner().shortname()}}') %] +
+
+
+
+
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_summary.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_summary.tt2 new file mode 100644 index 0000000000..1f09c7704a --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_summary.tt2 @@ -0,0 +1,143 @@ + +
+
+
+
+ {{penalty.note() || penalty.standing_penalty().label()}} +
+
+ {{penalty.set_date() | date:'shortDate'}} +
+
+
+
[% l('Profile') %]
+
{{patron().profile().name()}}
+
+
+
[% l('Home Library') %]
+
{{patron().home_ou().shortname()}}
+
+
+
[% l('Net Access') %]
+
{{patron().net_access_level().name()}}
+
+
+
[% l('Last Activity') %]
+
{{patron().usr_activity()[0].event_time() | date:'shortDate'}}
+
+
+
[% l('Last Updated') %]
+
{{patron().last_update_time() | date:'shortDate'}}
+
+
+
[% l('Create Date') %]
+
{{patron().create_date() | date:'shortDate'}}
+
+
+
[% l('Expire Date') %]
+
{{patron().expire_date() | date:'shortDate'}}
+
+
+
[% l('Fines Owed') %]
+
+ {{patron_stats().fines.balance_owed | currency}} +
+
+
+
[% l('Group Fines') %]
+
+ {{patron_stats().fines.group_balance_owed | currency}} +
+
+
+
[% l('Items Out') %]
+
{{patron_stats().checkouts.out}}
+
+
+
[% l('Overdue') %]
+
{{patron_stats().checkouts.overdue}}
+
+
+
[% l('Long Overdue') %]
+
{{patron_stats().checkouts.long_overdue}}
+
+
+
[% l('Claimed Returned') %]
+
{{patron_stats().checkouts.claims_returned}}
+
+
+
[% l('Lost') %]
+
{{patron_stats().checkouts.lost}}
+
+
+
[% l('Holds') %]
+
+ {{patron_stats().holds.total}} / {{patron_stats().holds.ready}} +
+
+
+
[% l('Card') %]
+
{{patron().card().barcode()}}
+
+
+
[% l('Username') %]
+
{{patron().usrname()}}
+
+
+
[% l('Day Phone') %]
+
{{patron().day_phone()}}
+
+
+
[% l('Evening Phone') %]
+
{{patron().evening_phone()}}
+
+
+
[% l('Other Phone') %]
+
{{patron().other_phone()}}
+
+
+
[% l('ID1') %]
+
{{patron().ident_type().name()}}
+
+
+
[% l('ID2') %]
+
{{patron().ident_type2().name()}}
+
+
+
[% l('Email') %]
+
{{patron().email()}}
+
+
+
{{map.stat_cat().name()}}
+
{{map.stat_cat_entry()}}
+
+
+ +
+
+
+
+ + {{addr.address_type()}} + [% l('(print)') %] + +
{{addr.street1()}} {{addr.street2()}}
+
{{addr.city()}}, {{addr.state()}} {{addr.post_code()}}
+
+
+
+
+
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_triggered_events.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_triggered_events.tt2 new file mode 100644 index 0000000000..fe5091fde6 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_triggered_events.tt2 @@ -0,0 +1,3 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_xact_details.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_xact_details.tt2 new file mode 100644 index 0000000000..7bc04e7682 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/patron/t_xact_details.tt2 @@ -0,0 +1,144 @@ +

[% l('Transaction #[_1]', '{{xact.id()}}') %]

+ +
+
[% l('Billing Location') %]
+
{{xact.billing_location().shortname()}}
+
[% l('Total Billed') %]
+
{{xact.summary().total_owed() | currency}}
+
[% l('Title') %]
+
+ {{title}} + {{title}} +
+
+
+
[% l('Type') %]
+
{{xact.summary().xact_type()}}
+
[% l('Total Paid') %]
+
{{xact.summary().total_paid() | currency}}
+
[% l('Checked Out') %]
+
{{xact.circulation().xact_start() | date:'short'}}
+
+
+
[% l('Start') %]
+
{{xact.xact_start() | date:'short'}}
+
[% l('Total Billed') %]
+
{{xact.summary().balance_owed() | currency}}
+
[% l('Due Date') %]
+
{{xact.circulation().due_date() | date:'short'}}
+
+
+
[% l('Finish') %]
+
{{xact.xact_finish() | date:'short'}}
+
[% l('Renewal?') %]
+
+ [% l('Desk') %] + [% l('Phone') %] + [% l('OPAC') %] +
+
[% l('Checked In') %]
+
{{xact.circulation().checkin_time() | date:'short'}}
+
+ +
+
+

[% l('Item Summary') %]

+
+
[% l('Barcode') %]
+ +
[% l('Location') %]
+
+ {{xact.circulation().target_copy().location().name()}} +
+
[% l('Call Number') %]
+
+ {{xact.circulation().target_copy().call_number().label()}} +
+
+
+
[% l('Status') %]
+
+ {{xact.circulation().target_copy().status().name()}} +
+
[% l('Circulate') %]
+
+ {{xact.circulation().target_copy().circulate() == 't'}} +
+
[% l('Reference') %]
+
+ {{xact.circulation().target_copy().ref() == 't'}} +
+
+
+
[% l('Holdable') %]
+
+ {{xact.circulation().target_copy().holdable() == 't'}} +
+
[% l('OPAC Visible') %]
+
+ {{xact.circulation().target_copy().opac_visible() == 't'}} +
+ +
[% l('Created') %]
+
+ {{xact.circulation().target_copy().create_date() | date:'short'}} +
+
+
+
[% l('Edited') %]
+
+ {{xact.circulation().target_copy().edit_date() | date:'short'}} +
+
[% l('Age Protect') %]
+
+ {{xact.circulation().target_copy().age_protect().name()}} +
+
[% l('Total Circulations') %]
+
+ TODO +
+
+
+ + + +
+ + + + + + + + + + +
+ + + + + + diff --git a/Open-ILS/src/templates/staff/circ/renew/index.tt2 b/Open-ILS/src/templates/staff/circ/renew/index.tt2 new file mode 100644 index 0000000000..415556b242 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/renew/index.tt2 @@ -0,0 +1,20 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Renew"); + ctx.page_app = "egRenewApp"; +%] + +[% BLOCK APP_JS %] + + + + +[% INCLUDE 'staff/circ/share/circ_strings.tt2' %] + + + +[% END %] + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/circ/renew/t_renew.tt2 b/Open-ILS/src/templates/staff/circ/renew/t_renew.tt2 new file mode 100644 index 0000000000..c2880c1b2e --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/renew/t_renew.tt2 @@ -0,0 +1,141 @@ + + +
+
+ [% l('Renew Items') %] +
+
+ +
+
+
+
+ + + + + + +
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + {{item.copy_barcode}} + + + + + + + + + + + + + + + + + + {{item.title}} + + + + + + + + + + + + + +
+
+
+ +
+
+ +
+
+
+ +
+
+ diff --git a/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2 b/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2 new file mode 100644 index 0000000000..316e64a8f3 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2 @@ -0,0 +1,39 @@ +[%# Strings for circ/services/circ.js %] + + + + diff --git a/Open-ILS/src/templates/staff/circ/share/hold_strings.tt2 b/Open-ILS/src/templates/staff/circ/share/hold_strings.tt2 new file mode 100644 index 0000000000..9e67e19de7 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/hold_strings.tt2 @@ -0,0 +1,30 @@ +[%# Strings for circ/services/circ.js %] + + + + diff --git a/Open-ILS/src/templates/staff/circ/share/t_backdate_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_backdate_dialog.tt2 new file mode 100644 index 0000000000..034e9091bc --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_backdate_dialog.tt2 @@ -0,0 +1,24 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/share/t_bad_barcode_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_bad_barcode_dialog.tt2 new file mode 100644 index 0000000000..c3044166e1 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_bad_barcode_dialog.tt2 @@ -0,0 +1,23 @@ +
+ + + +
+ + diff --git a/Open-ILS/src/templates/staff/circ/share/t_bill_patron_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_bill_patron_dialog.tt2 new file mode 100644 index 0000000000..b946b8cbe7 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_bill_patron_dialog.tt2 @@ -0,0 +1,93 @@ + +
+ +
+ + diff --git a/Open-ILS/src/templates/staff/circ/share/t_cancel_hold_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_cancel_hold_dialog.tt2 new file mode 100644 index 0000000000..81417f2029 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_cancel_hold_dialog.tt2 @@ -0,0 +1,38 @@ +
+ +
+ diff --git a/Open-ILS/src/templates/staff/circ/share/t_circ_exists_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_circ_exists_dialog.tt2 new file mode 100644 index 0000000000..3a8b5d194e --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_circ_exists_dialog.tt2 @@ -0,0 +1,31 @@ + +
+ +
+ diff --git a/Open-ILS/src/templates/staff/circ/share/t_copy_in_transit_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_copy_in_transit_dialog.tt2 new file mode 100644 index 0000000000..4d38922123 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_copy_in_transit_dialog.tt2 @@ -0,0 +1,32 @@ +
+ + + +
diff --git a/Open-ILS/src/templates/staff/circ/share/t_copy_not_avail_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_copy_not_avail_dialog.tt2 new file mode 100644 index 0000000000..711e777d84 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_copy_not_avail_dialog.tt2 @@ -0,0 +1,20 @@ +
+ + + +
diff --git a/Open-ILS/src/templates/staff/circ/share/t_event_override_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_event_override_dialog.tt2 new file mode 100644 index 0000000000..5850ac5d08 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_event_override_dialog.tt2 @@ -0,0 +1,27 @@ +
+ + + +
diff --git a/Open-ILS/src/templates/staff/circ/share/t_hold_copy_quality_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_hold_copy_quality_dialog.tt2 new file mode 100644 index 0000000000..19aa885ddd --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_hold_copy_quality_dialog.tt2 @@ -0,0 +1,24 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/share/t_hold_dates.tt2 b/Open-ILS/src/templates/staff/circ/share/t_hold_dates.tt2 new file mode 100644 index 0000000000..b145d0e601 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_hold_dates.tt2 @@ -0,0 +1,75 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/share/t_hold_details.tt2 b/Open-ILS/src/templates/staff/circ/share/t_hold_details.tt2 new file mode 100644 index 0000000000..1117ae1c67 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_hold_details.tt2 @@ -0,0 +1,149 @@ + +

[% l('Hold Details') %]

+
+
[% l('Request Date') %]
+
{{hold.request_time() | date:'short'}}
+
[% l('Capture Date') %]
+
{{hold.capture_time() | date:'short'}}
+
[% l('Available On') %]
+
{{hold.shelf_time() | date:'short'}}
+
+
+
[% l('Hold Type') %]
+
{{hold.hold_type()}}
+
[% l('Current Copy') %]
+ +
[% l('Call Number') %]
+
{{volume.label()}}
+
+
+
[% l('Pickup Lib') %]
+
{{hold.pickup_lib().shortname()}}
+
[% l('Status') %]
+
{{status_string}}
+
[% l('Behind Desk') %]
+
{{hold.behind_desk() == 't'}}
+
+
+
[% l('Current Shelf Lib') %]
+
{{hold.current_shelf_lib().shortname()}}
+
[% l('Current Copy Location') %]
+
{{copy.location().name()}}
+
[% l('Force Copy Quality') %]
+
{{hold.mint_condition() == 't'}}
+
+
+
[% l('Email Notify') %]
+
{{hold.email_notify() == 't'}}
+
[% l('Phone Notify') %]
+
{{hold.phone_notify()}}
+
[% l('SMS Notify') %]
+
{{hold.sms_notify()}}
+
+
+
[% l('Cancel Cause') %]
+
{{hold.cancel_cause().label()}}
+
[% l('Cancel Time') %]
+
{{hold.cancel_time() | date:'short'}}
+
[% l('Cancel Note') %]
+
{{hold.cancel_note()}}
+
+ + +
+
+ +
+ + + +
+
+
+
{{note.title()}}
+
+
+ [% l('Print on Slip') %] + [% l('Patron Visible') %] + [% l('Staff Only') %] + [% l('Staff Created') %] + [% l('Patron Created') %] +
+
+
+
+ +
+
+
+
{{note.body()}}
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+ + + +
+
+
+
{{notify.method()}}
+
+
+ {{notify.notify_time() | date:'short'}} + [% l('Created by [_1]', '{{notify.notify_staff().usrname()}}') %] +
+
+
+
+ +
+
+
+
{{notify.note()}}
+
+
+
+
+
+
+
+ +
+
+ diff --git a/Open-ILS/src/templates/staff/circ/share/t_hold_edit_pickup_lib.tt2 b/Open-ILS/src/templates/staff/circ/share/t_hold_edit_pickup_lib.tt2 new file mode 100644 index 0000000000..895cda0a57 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_hold_edit_pickup_lib.tt2 @@ -0,0 +1,23 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/share/t_hold_note_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_hold_note_dialog.tt2 new file mode 100644 index 0000000000..92113da7cc --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_hold_note_dialog.tt2 @@ -0,0 +1,51 @@ +
+ + + + +
diff --git a/Open-ILS/src/templates/staff/circ/share/t_hold_notification_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_hold_notification_dialog.tt2 new file mode 100644 index 0000000000..01b5b48115 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_hold_notification_dialog.tt2 @@ -0,0 +1,33 @@ +
+ + + + +
diff --git a/Open-ILS/src/templates/staff/circ/share/t_hold_notification_prefs.tt2 b/Open-ILS/src/templates/staff/circ/share/t_hold_notification_prefs.tt2 new file mode 100644 index 0000000000..7f2659d21e --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_hold_notification_prefs.tt2 @@ -0,0 +1,78 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/share/t_hold_shelf_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_hold_shelf_dialog.tt2 new file mode 100644 index 0000000000..22e74e173a --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_hold_shelf_dialog.tt2 @@ -0,0 +1,63 @@ +
+ + diff --git a/Open-ILS/src/templates/staff/circ/share/t_mark_claims_returned_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_mark_claims_returned_dialog.tt2 new file mode 100644 index 0000000000..858e242f94 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_mark_claims_returned_dialog.tt2 @@ -0,0 +1,25 @@ + + + diff --git a/Open-ILS/src/templates/staff/circ/share/t_new_message_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_new_message_dialog.tt2 new file mode 100644 index 0000000000..1cd94f0963 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_new_message_dialog.tt2 @@ -0,0 +1,45 @@ +
+ + + +
diff --git a/Open-ILS/src/templates/staff/circ/share/t_noncat_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_noncat_dialog.tt2 new file mode 100644 index 0000000000..345848eed7 --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_noncat_dialog.tt2 @@ -0,0 +1,25 @@ + +
+ + + +
diff --git a/Open-ILS/src/templates/staff/circ/share/t_precat_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_precat_dialog.tt2 new file mode 100644 index 0000000000..fc14ec3cff --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_precat_dialog.tt2 @@ -0,0 +1,44 @@ + +
+
+ +
+
diff --git a/Open-ILS/src/templates/staff/circ/share/t_transit_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_transit_dialog.tt2 new file mode 100644 index 0000000000..1491fb299a --- /dev/null +++ b/Open-ILS/src/templates/staff/circ/share/t_transit_dialog.tt2 @@ -0,0 +1,65 @@ +
+
+ + + +
+
diff --git a/Open-ILS/src/templates/staff/config.tt2 b/Open-ILS/src/templates/staff/config.tt2 new file mode 100644 index 0000000000..c563d7ab97 --- /dev/null +++ b/Open-ILS/src/templates/staff/config.tt2 @@ -0,0 +1,13 @@ +[%- + +# FIXME: update via build process +EVERGREEN_VERSION='0.0.1' + +# create script / css refs to individual files instead of using +# compressed build files. Use this for development and debugging. +EXPAND_WEB_IMPORTS = 1; + +# path to build files (js, css, fonts) +WEB_BUILD_PATH = ctx.media_prefix _ '/js/ui/default/staff/build/'; + +%] diff --git a/Open-ILS/src/templates/staff/css/circ.css.tt2 b/Open-ILS/src/templates/staff/css/circ.css.tt2 new file mode 100644 index 0000000000..8d6c139d88 --- /dev/null +++ b/Open-ILS/src/templates/staff/css/circ.css.tt2 @@ -0,0 +1,60 @@ +/** style to make a grid look like a striped table */ +#patron-summary-grid div.row {padding: 3px; border-right: 2px solid rgb(248, 248, 248);} +#patron-summary-grid div.row:nth-child(odd) {background-color: rgb(248, 248, 248);} + +/* there are bootstrap tyles for error, warning, etc., +but the ones I'm finding aren't quite cutting it..*/ +.patron-summary-alert {color: red; font-weight:bold} +.patron-summary-alert-small {color: red} +.patron-summary-divider { border-top: 1px solid #CCC} +.patron-summary-act-link {font-size: .8em;} + +/* FIXME: use .barcode instead */ +#patron-checkout-barcode, +#patron-renewal-barcode, +#patron-checkin-barcode { width: 16em; } + +#patron-search-form div.form-group { + margin-bottom: 5px; +} + +/* let search form elements fill their containers w/ slight padding */ +#patron-search-form-row {margin-left: 0px;} +#patron-search-form div.col-md-2 { padding: 2px; } +#patron-search-form input:not([type="checkbox"]) { width: 100%; } +#patron-search-form .eg-org-selector, +#patron-search-form .eg-org-selector button, +#patron-search-form .patron-search-selector, + #patron-search-form .patron-search-selector button { + width: 100%; + text-align: left +} + + +#patron-payments-spreadsheet { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid #aaa; +} + +#patron-payments-spreadsheet .flex-cell { + margin: 2px; +} + +#patron-payments-spreadsheet .flex-cell.well { + min-height: 1.5em; + margin-bottom: 0px; /* bootstrap default is 20px */ +} + +#hold-notify-settings div.row { margin-top: 12px; } +#hold-notify-settings div.row:not(.header-row):nth-child(odd) { + background-color: rgb(248, 248, 248); +} +#hold-notify-settings div.row:not(.header-row) { + border-bottom: 1px solid #CCC; +} + + +[%# +vim: ft=css +%] diff --git a/Open-ILS/src/templates/staff/css/print.css.tt2 b/Open-ILS/src/templates/staff/css/print.css.tt2 new file mode 100644 index 0000000000..5410ba990b --- /dev/null +++ b/Open-ILS/src/templates/staff/css/print.css.tt2 @@ -0,0 +1,13 @@ + +/* hide everything but the print div */ +head { display: none; } /* just to be safe */ +body div:not([id="print-div"]) { display:none } + +div { display: none } +#print-div { display: block } +#print-div div { display: block } +#print-div pre { border: none } + +[%# +vim: ft=css +%] diff --git a/Open-ILS/src/templates/staff/css/style.css.tt2 b/Open-ILS/src/templates/staff/css/style.css.tt2 new file mode 100644 index 0000000000..300668c15f --- /dev/null +++ b/Open-ILS/src/templates/staff/css/style.css.tt2 @@ -0,0 +1,412 @@ +/* -------------------------------------------------------------------------- + * Simple default navbar style adjustements to apply the Evergreen color. + * TODO: style other components to match EG color scheme + */ +#top-navbar.navbar-default { + background: -webkit-linear-gradient(#00593d, #007a54); + background-color: #007a54; + color: #fff; +} +#top-navbar.navbar-default .navbar-nav>li>a { + color: #fff; +} +#top-navbar.navbar-default .navbar-nav>li>a:hover { + color: #ddd; +} +#top-navbar.navbar-default .navbar-nav>.dropdown>a .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +#top-navbar.navbar-default .navbar-nav>.dropdown>a:hover .caret { + border-top-color: #ddd; + border-bottom-color: #ddd; +} + +/* status bar along the bottom of the page ------------------------ */ +/* decrease padding to decrease overall height */ + +/** TODO:move status bar items into navbar config entry (top-right) + * to avoid body padding weirdness. Or if we want a permenently + * visible status bar, maybe put it just below the navbar.. */ + +/* bottom padding ensures no body content is hidden behind the status + * bar. When content reaches the status bar a scroll bar appears */ +/*body { padding-bottom: 26px; }*/ + +#status-bar { + min-height:1.8em !important; +} +#status-bar > ul { + margin-right:6px; +} +#status-bar li { + padding-left: 10px; +} +#status-bar > li > a { + padding-top:5px !important; + padding-bottom:5px !important; +} +.status-bar-connected { + color: rgb(92, 184, 92); /* success */ +} + +/* -------------------------------------------------------------------------- + * Structural modifications + */ + +#top-content-container { + /* allow the primary container to occupy most of the page, + * but leave some narrow gutters along the side, much + * narrower than the default Bootstrapp container gutters. + */ + width: 95%; +} + + +/* -------------------------------------------------------------------------- + * Temporaray local CSS required to make angular-ui-bootstrap + * version 0.6.0 look right with Bootstrap CSS 3.0 + */ +.nav, .pagination, .carousel a { cursor: pointer; } +/* +.modal { + display: block; + height: 0; + overflow: visible; +} +.modal-body:before, +.modal-body:after { + display: table; + content: " "; +} +.modal-header:before, +.modal-header:after { + display: table; + content: " "; +} +*/ + +/* -------------------------------------------------------------------------- +/* Form Validation CSS - http://docs.angularjs.org/guide/forms + * TODO: these colors are harsh and don't fit the EG color scheme + */ +.form-validated input.ng-invalid.ng-dirty { + background-color: #FA787E; +} +.form-validated input.ng-valid.ng-dirty { + background-color: #78FA89; +} + +/* -------------------------------------------------------------------------- + * Local style + */ + +#splash-nav .panel-body div { + padding-bottom: 10px; +} + +table.list tr.selected td { /* deprecated? */ + color: #2a6496; + background-color: #F5F5F5; +} + +.pad-horiz {padding : 0px 10px 0px 10px; } +.pad-vert {padding : 20px 0px 10px 0px;} +.pad-left {padding-left: 10px;} +.pad-right {padding-right: 10px;} +.pad-all-min {padding : 5px; } +.pad-all {padding : 10px; } + +#print-div { display: none; } + +/* by default, give all tab panes some top padding */ +.tab-pane { padding-top: 20px; } + +.nav-pills-like-tabs { + border-bottom:1px solid #CCC; +} + +.btn-pad { + /* sometimes you don't want buttons scrunched together -- add some margin */ + margin-left: 10px; +} + +.strong-text { + font-weight: bold; +} +.strong-text-1 { + font-size: 110%; + font-weight: bold; +} +.strong-text-2 { + font-size: 120%; + font-weight: bold; +} +.strong-text-3 { + font-size: 130%; + font-weight: bold; +} +.strong-text-4 { + font-size: 140%; + font-weight: bold; +} + +.currency-input { + width: 8em; +} + +/* barcode inputs are everywhere. Let's have a consistent style. */ +.barcode { width: 16em !important; } + +/* bootstrap alerts are heavily padded. use this to reduce */ +.alert-less-pad {padding: 5px;} + +/* text displayed inside a , typically the max/progress values */ +.progressbar-text { + color:black; + white-space:nowrap; +} + +/* embedded UI iframe */ +.eg-embed-frame { + width: 100%; +} +.eg-embed-frame iframe { + width: 100%; + border: none; + margin: 0px; + padding: 0px; +} + +/* ---------------------------------------------------------------------- + * Grid + * ---------------------------------------------------------------------- */ + +.eg-grid-primary-label { + font-weight: bold; + font-size: 120%; +} + +/* odd/even row styling */ +.eg-grid-content-body > div:nth-child(odd):not(.eg-grid-row-selected) { + background-color: rgb(248, 248, 248); +} + +.eg-grid-row { + width: 100%; + display: flex; + border: 1px solid #ccc; +} + +.eg-grid-row:not(.eg-grid-header-row):not(.eg-grid-conf-row) { + /* TODO: remove, pretty sure this is no longer needed w/ nowrap */ + /*height: 1.8em;*/ +} + +.eg-grid-action-row { + border: none; + /* margin should not have to be this large; something's up */ + margin-bottom: 12px; +} + +.eg-grid-header-row { + font-weight: bold; +} + +.eg-grid-header-row > .eg-grid-cell { + border-right: 1px solid #CCC; + text-align: center; + + /* vertically align header cell text by treating + each header cell as a vertical flex container */ + display:flex; + flex-direction:column; + justify-content:flex-end; +} + +.eg-grid-cell { + /* avoid text flowing into adjacent cells */ + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +/* in config display, make cells more obvious */ +.eg-grid-as-conf .eg-grid-row { + border: 1px solid #777; +} +.eg-grid-as-conf .eg-grid-cell { + border-right: 1px solid #777; +} + +/* stock columns need fixed-width controls */ +.eg-grid-cell-stock { + width: 2.2em; + text-align: center; +} + +/* the conf header must be twice the stock flex */ +.eg-grid-cell-conf-header { + width: 4.4em; + font-weight: bold; +} + +.eg-grid-row-selected { + color: rgb(51, 51, 51); + background-color: rgb(201, 221, 225); + border-bottom: 1px solid #888; +} + +/* Improve ::selection styling by only allowing selection on text + * content cells within the main body of the grid. Otherwise, the browser + * styles row background and text (all dark blue?) when shift-click or + * click-drag is used. + */ +.eg-grid-content-body .eg-grid-row { + user-select:none; + -moz-user-select: none; + -webkit-user-select: none; +} +.eg-grid-content-body .eg-grid-cell-content { + user-select:text; + -moz-user-select: text; + -webkit-user-select: text; +} +.eg-grid-cell-content::-moz-selection { + color: rgb(51, 51, 51); + background: rgb(201, 221, 225); + border-bottom: 1px solid #888; +} +.eg-grid-cell-content::selection { + color: rgb(51, 51, 51); + background: rgb(201, 221, 225); + border-bottom: 1px solid #888; +} + +.eg-grid-conf-cell-entry { + width:98%; + text-align:center; + padding: 3px; +} + +.eg-grid-conf-cell-entry:not(:first-child) { + border-top:1px solid #ccc; +} + +.eg-grid-conf-row { + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.eg-grid-conf-row:first-child { + /* alignment fix; account for one missing border */ + padding-right: 1px; +} + +.eg-grid-column-move-handle:hover { + cursor: move; +} + +.eg-grid-column-move-handle-active, +.eg-grid-column-move-handle-active:active { + /* similar to label-primary, sans padding */ + background-color: rgb(66, 139, 202); + color: #fff; +} + +.eg-grid-col-hover { + /* similar to label-success, sans padding */ + background-color: rgb(92, 184, 92); + color: #fff; +} + +.eg-grid-column-resize-handle { + height: 100%; +} +.eg-grid-column-resize-handle:hover { + cursor: col-resize; +} + +/* for these to be useful, they would have to be applied + * to the dragover targets. not yet done */ +.eg-grid-column-resize-handle-west { + cursor: w-resize; +} +.eg-grid-column-resize-handle-east { + cursor: e-resize; +} + +.eg-grid-menu-item { + margin-right: 10px; +} + + +/* hack to make the header columns line up with the content columns + when the scroll bar is visible along the right side of the content + columns. TODO: if this varies enough by browser, we'll need to + calculate the width instead. */ +/* +.eg-grid-scroll > .eg-grid-header-row, +.eg-grid-scroll > .eg-grid-conf-row { + padding-right: 15px; +} +.eg-grid-scroll > .eg-grid-content-body { + overflow-y:scroll; + height: 600px; +} +*/ +.eg-grid-column-picker { + height: auto; + max-height: 400px; + overflow: auto; + box-shadow: none; +} + + +/* ---------------------------------------------------------------------- + * /Grid + * ---------------------------------------------------------------------- */ + + +/* simple flex container for consistent-width cell-based structures */ +.flex-container-striped > .flex-row:nth-child(odd) { + background-color: #f5f5f5; +} +.flex-container-bordered .flex-cell { + border-bottom: 1px solid #ddd; +} +.flex-row { + display: flex; +} +.flex-row.padded div { + padding: 5px; +} +.flex-row.left-anchored > div { + margin-right: 10px; +} +.flex-cell { + flex: 1; + padding: 4px; /* bootstrap default is much bigger */ +} +.flex-cell.well { + min-height: 2.5em; /* don't let empty wells scrunch down */ + margin-bottom: 5px; /* bootstrap default is 20px */ +} +.flex-2 { /* meh, convience */ + flex: 2; +} + +/* TODO: match media size to Bootstrap "md" col resizing */ +@media all and (max-width: 800px) { + .flex-row { + flex-direction: column; + } + .eg-grid-row { + flex-direction: column; + } +} + + +[%# +vim: ft=css +%] diff --git a/Open-ILS/src/templates/staff/index.tt2 b/Open-ILS/src/templates/staff/index.tt2 new file mode 100644 index 0000000000..b2e25f8782 --- /dev/null +++ b/Open-ILS/src/templates/staff/index.tt2 @@ -0,0 +1,17 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Home"); + ctx.page_app = "egHome"; +%] + +[% BLOCK APP_JS %] + + + + +[% END %] + +
+ +[% END %] + diff --git a/Open-ILS/src/templates/staff/navbar.tt2 b/Open-ILS/src/templates/staff/navbar.tt2 new file mode 100644 index 0000000000..6d0e6d87b2 --- /dev/null +++ b/Open-ILS/src/templates/staff/navbar.tt2 @@ -0,0 +1,244 @@ + + + + + diff --git a/Open-ILS/src/templates/staff/share/README b/Open-ILS/src/templates/staff/share/README new file mode 100644 index 0000000000..bcbddf563f --- /dev/null +++ b/Open-ILS/src/templates/staff/share/README @@ -0,0 +1,5 @@ +Location for globally shared template files. These are generally used +by AngularJS directives. + +App-specific shared templates should live within the application's +directory. diff --git a/Open-ILS/src/templates/staff/share/print_templates/index.tt2 b/Open-ILS/src/templates/staff/share/print_templates/index.tt2 new file mode 100644 index 0000000000..0bcedf0290 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/index.tt2 @@ -0,0 +1,2 @@ +[% USE CGI %] +[% l('Print Template Not Found: [_1]', CGI.url("-path",1,"-relative",1)) %] diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_bill_payment.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_bill_payment.tt2 new file mode 100644 index 0000000000..fe6851ab98 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_bill_payment.tt2 @@ -0,0 +1,69 @@ +Welcome to {{current_location.name}}!
+A receipt of your transaction:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[% l('Original Balance:') %]{{previous_balance | currency}}
[% l('Payment Method:') %] +
+
[% l('Cash') %]
+
[% l('Check') %]
+
[% l('Credit Card') %]
+
[% l('Patron Credit') %]
+
[% l('Work') %]
+
[% l('Forgive') %]
+
[% l('Goods') %]
+
+
[% l('Payment Received:') %]{{payment_total | currency}}
[% l('Payment Applied:') %]{{payment_applied | currency}}
[% l('Billings Voided:') %]{{amount_voided | currency}}
[% l('Change Given:') %]{{change_given | currency}}
[% l('New Balance:') %]{{new_balance | currency}}
+ +

[% l('Note: [_1]', '{{payment_note}}') %]

+ +

+[% l('Specific Bills') %] +

+
+ + + + + + + + + +
[% l('Bill # [_1]', '{{payment.xact.id}}') %]{{payment.xact.summary.last_billing_type}}[% l('Received: [_1]', '{{payment.amount | currency}}') %]
+ {{payment.xact.copy_barcode}} {{payment.xact.title}} +
+
+
+
+

+
+

+{{current_location.shortname}} {{today | date:'short'}} diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_bills_current.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_bills_current.tt2 new file mode 100644 index 0000000000..c99cb4d1ee --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_bills_current.tt2 @@ -0,0 +1,49 @@ +Welcome to {{current_location.name}}!
+You have the following bills: +
+
+
+
Bill #{{xact.id}}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[% l('Date:') %]{{xact.xact_start | date:'short'}}
[% l('Type') %]:{{xact.summary.xact_type}}
[% l('Last Billing') %]:{{xact.summary.last_billing_type}}
+ {{xact.summary.last_billing_note}} +
[% l('Total Billed') %]:{{xact.summary.total_owed | currency}}
[% l('Last Payment') %]:{{xact.summary.last_payment_type}}
+ {{xact.summary.last_payment_note}} +
[% l('Total Paid') %]:{{xact.summary.total_paid | currency}}
[% l('Balance') %]:{{xact.summary.balance_owed | currency}}
+
+
+
+
+
+{{current_location.shortname}} {{today | date:'short'}} +

+ diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_bills_historical.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_bills_historical.tt2 new file mode 100644 index 0000000000..c99cb4d1ee --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_bills_historical.tt2 @@ -0,0 +1,49 @@ +Welcome to {{current_location.name}}!
+You have the following bills: +
+
+
+
Bill #{{xact.id}}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[% l('Date:') %]{{xact.xact_start | date:'short'}}
[% l('Type') %]:{{xact.summary.xact_type}}
[% l('Last Billing') %]:{{xact.summary.last_billing_type}}
+ {{xact.summary.last_billing_note}} +
[% l('Total Billed') %]:{{xact.summary.total_owed | currency}}
[% l('Last Payment') %]:{{xact.summary.last_payment_type}}
+ {{xact.summary.last_payment_note}} +
[% l('Total Paid') %]:{{xact.summary.total_paid | currency}}
[% l('Balance') %]:{{xact.summary.balance_owed | currency}}
+
+
+
+
+
+{{current_location.shortname}} {{today | date:'short'}} +

+ diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_checkin.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_checkin.tt2 new file mode 100644 index 0000000000..7bc56e4b32 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_checkin.tt2 @@ -0,0 +1,17 @@ +
+
[% l('Welcome to [_1]', '{{current_location.name}}') %]
+
[% l('You checked in the following items:') %]
+
+
    +
  1. +
    {{checkin.title}}
    + [% l('Barcode: ') %] + {{checkin.copy_barcode}} + [% l('Call Number: ') %] + {{checkin.call_number.label || "[% l("Not Cataloged") %]"}} +
  2. +
+
+
{{current_location.shortname}} {{today | date:'short'}}
+
+
diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_checkout.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_checkout.tt2 new file mode 100644 index 0000000000..1f1218e8d3 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_checkout.tt2 @@ -0,0 +1,17 @@ +
+
[% l('Welcome to [_1]', '{{current_location.name}}') %]
+
[% l('You checked out the following items:') %]
+
+
    +
  1. +
    {{checkout.title}}
    +
    [% l('Barcode: [_1] Due: [_2]', + '{{checkout.copy.barcode}}', + '{{checkout.circ.due_date | date:"short"}}') %]
    +
  2. +
+
+
{{current_location.shortname}} {{today | date:'short'}}
+
[% l('You were helped by [_1]', '{{staff.first_given_name}}') %]
+
+ diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_hold_pull_list.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_hold_pull_list.tt2 new file mode 100644 index 0000000000..ce23fe4993 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_hold_pull_list.tt2 @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + +
[% l('Type') %][% l('Title') %][% l('Author') %][% l('Shelf Location') %][% l('Call Number') %][% l('Barcode/Part') %]
{{hold_data.hold.hold_type}}{{hold_data.title}}{{hold_data.author}}{{hold_data.copy.location.name}}{{hold_data.volume.label}}{{hold_data.copy.barcode}} {{hold_data.part.label}}
diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_hold_shelf_slip.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_hold_shelf_slip.tt2 new file mode 100644 index 0000000000..f1980d7540 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_hold_shelf_slip.tt2 @@ -0,0 +1,37 @@ +
+
+
+ [% l('This item needs to be routed to the [_1]Private Holds Shelf[_2].', + '','') %] +
+
+ [% l('This item needs to be routed to the [_1]Public Holds Shelf[_2].', + '','') %] +
+
+
+ + [% l('Barcode: [_1]', '{{copy.barcode}}') %]
+ [% l('Title: [_1]', '{{title}}') %]
+ +
+
+ +
[% l('Hold for patron [_1], [_2] [_3]', + '{{patron.family_given_name}}', + '{{patron.first_given_name}}', + '{{patron.second_given_name}}') %]
+
[% l('Barcode: [_1]', '{{patron.card.barcode}}') %]
+
[% l('Notify by phone: [_1]', '{{hold.phone_notify}}') %]
+
[% l('Notify by text: [_1]', '{{hold.sms_notify}}') %]
+
[% l('Notify by email: [_1]', '{{patron.email}}') %]
+ +
+ +
[% l('Request Date: [_1]', + '{{hold.request_time | date:"short"}}') %]
+
[% l('Slip Date: [_1]', '{{today | date:"short"}}') %]
+
[% l('Printed by [_1] at [_2]', + '{{staff.first_given_name}}', '{{current_location.shortname}}') %]
+ +
diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_hold_transit_slip.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_hold_transit_slip.tt2 new file mode 100644 index 0000000000..6b36c25f87 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_hold_transit_slip.tt2 @@ -0,0 +1,34 @@ +
+
[% l('This item needs to be routed to [_1]', '{{dest_location.shortname}}') %]
+
{{dest_location.name}}
+
{{dest_address.street1}} +
{{dest_address.street2}}
+
{{dest_address.city}}, + {{dest_address.state}} + {{dest_address.post_code}}
+
+ + [% l('Barcode: [_1]', '{{copy.barcode}}') %]
+ [% l('Title: [_1]', '{{title}}') %]
+ [% l('Author: [_1]', '{{author}}') %] + +
+ +
[% l('Hold for patron [_1], [_2] [_3]', + '{{patron.family_given_name}}', + '{{patron.first_given_name}}', + '{{patron.second_given_name}}') %]
+
[% l('Barcode: [_1]', '{{patron.card.barcode}}') %]
+
[% l('Notify by phone: [_1]', '{{hold.phone_notify}}') %]
+
[% l('Notify by text: [_1]', '{{hold.sms_notify}}') %]
+
[% l('Notify by email: [_1]', '{{patron.email}}') %]
+ +
+ +
[% l('Request Date: [_1]', + '{{hold.request_time | date:"short"}}') %]
+
[% l('Slip Date: [_1]', '{{today | date:"short"}}') %]
+
[% l('Printed by [_1] at [_2]', + '{{staff.first_given_name}}', '{{current_location.shortname}}') %]
+ + diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_holds_for_bib.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_holds_for_bib.tt2 new file mode 100644 index 0000000000..8d3061cb31 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_holds_for_bib.tt2 @@ -0,0 +1,29 @@ +
+
[% l('Holds for record: [_1]', '{{holds[0].title}}') %]
+
+ + + + + + + + + + + + + + + + + + + + +
[% l('Request Date') %][% l('Patron Barcode') %][% l('Patron Last') %][% l('Patron Alias') %][% l('Current Copy') %]
{{hold.hold.request_time | date:'short'}}{{hold.patron_barcode}}{{hold.patron_last}}{{hold.patron_alias}}{{hold.copy.barcode}}
+
+
{{current_location.shortname}} {{today | date:'short'}}
+
[% l('Printed by [_1]', '{{staff.first_given_name}}') %]
+
+ diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_holds_for_patron.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_holds_for_patron.tt2 new file mode 100644 index 0000000000..492454c7c1 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_holds_for_patron.tt2 @@ -0,0 +1,14 @@ +
+
[% l('Welcome to [_1]', '{{current_location.name}}') %]
+
[% l('You have the following title on hold:') %]
+
+
    +
  1. +
    {{hold.title}}
    +
  2. +
+
+
{{current_location.shortname}} {{today | date:'short'}}
+
[% l('You were helped by [_1]', '{{staff.first_given_name}}') %]
+
+ diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_items_out.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_items_out.tt2 new file mode 100644 index 0000000000..fee903b86a --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_items_out.tt2 @@ -0,0 +1,17 @@ +
+
[% l('Welcome to [_1]', '{{current_location.name}}') %]
+
[% l('You have the following items:') %]
+
+
    +
  1. +
    {{checkout.title}}
    +
    [% l('Barcode: [_1] Due: [_2]', + '{{checkout.copy.barcode}}', + '{{checkout.circ.due_date | date:"short"}}') %]
    +
  2. +
+
+
{{current_location.shortname}} {{today | date:'short'}}
+
[% l('You were helped by [_1]', '{{staff.first_given_name}}') %]
+
+ diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_patron_address.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_patron_address.tt2 new file mode 100644 index 0000000000..c1a3e37951 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_patron_address.tt2 @@ -0,0 +1,12 @@ +
+
+ {{patron.first_given_name}} + {{patron.second_given_name}} + {{patron.family_name}} +
+
{{address.street1}}
+
{{address.street2}}
+
+ {{address.city}}, {{address.state}} {{address.post_code}} +
+
diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_patron_note.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_patron_note.tt2 new file mode 100644 index 0000000000..5b9868397b --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_patron_note.tt2 @@ -0,0 +1,11 @@ +

[% l( + 'Pertaining to [_1], [_2] [_3] : [_4]', + '{{note.usr.family_name}}', + '{{note.usr.first_given_name}}', + '{{note.usr.second_given_name}}', + '{{note.usr.card.barcode}}') %]

+ +

[% l('Created on [_1]', '{{note.create_date | date:"short"}}') %]

+{{note.title}} +
+

{{note.value}}

diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_renew.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_renew.tt2 new file mode 100644 index 0000000000..8e964454d5 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_renew.tt2 @@ -0,0 +1,17 @@ +
+
[% l('Welcome to [_1]', '{{current_location.name}}') %]
+
[% l('You renewed the following items:') %]
+
+
    +
  1. +
    {{renewal.title}}
    +
    [% l('Barcode: [_1] Due: [_2]', + '{{renewal.copy.barcode}}', + '{{renewal.circ.due_date | date:"short"}}') %]
    +
  2. +
+
+
{{current_location.shortname}} {{today | date:'short'}}
+
[% l('You were helped by [_1]', '{{staff.first_given_name}}') %]
+
+ diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_transit_slip.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_transit_slip.tt2 new file mode 100644 index 0000000000..324ad5e1eb --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_transit_slip.tt2 @@ -0,0 +1,21 @@ +
+
[% l('This item needs to be routed to [_1]', '{{dest_location.shortname}}') %]
+
{{dest_location.name}}
+
{{dest_address.street1}} +
{{dest_address.street2}}
+
{{dest_address.city}}, + {{dest_address.state}} + {{dest_address.post_code}}
+
+ + [% l('Barcode: [_1]', '{{copy.barcode}}') %]
+ [% l('Title: [_1]', '{{title}}') %]
+ [% l('Author: [_1]', '{{author}}') %]
+ +
+ +
[% l('Slip Date: [_1]', '{{today | date:"short"}}') %]
+
[% l('Printed by [_1] at [_2]', + '{{staff.first_given_name}}', '{{current_location.shortname}}') %]
+ +
diff --git a/Open-ILS/src/templates/staff/share/t_alert_dialog.tt2 b/Open-ILS/src/templates/staff/share/t_alert_dialog.tt2 new file mode 100644 index 0000000000..b71614a32a --- /dev/null +++ b/Open-ILS/src/templates/staff/share/t_alert_dialog.tt2 @@ -0,0 +1,16 @@ + +
+ + + +
diff --git a/Open-ILS/src/templates/staff/share/t_autogrid.tt2 b/Open-ILS/src/templates/staff/share/t_autogrid.tt2 new file mode 100644 index 0000000000..136252cf28 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/t_autogrid.tt2 @@ -0,0 +1,297 @@ + + + +
+ +
{{mainLabel}}
+ +
+ + +
+ + +
+ +
+ + +
+ + +
+ + + + + + + + + + + + +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + +
+ + +
+ +
+
+
[% l('#') %]
+
+
+
+ +
+
+
+ +
+
+ +
+
{{col.label}}
+
+
+
 
+
+
+
+ + +
+
+
[% l('Expand') %]
+
[% l('Shrink') %]
+
[% l('Sort') %]
+
+
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+
+ +
+
[% l('No Items To Display') %]
+ +
+
+ + {{$index + offset() + 1}} + +
{{$index + offset() + 1}}
+
+
+ +
+ +
+
+
+ + + + + + + + {{itemFieldValue(item, col) | egGridValueFilter:col}} + +
+
+
+ + +
+ diff --git a/Open-ILS/src/templates/staff/share/t_confirm_dialog.tt2 b/Open-ILS/src/templates/staff/share/t_confirm_dialog.tt2 new file mode 100644 index 0000000000..45e8ca1609 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/t_confirm_dialog.tt2 @@ -0,0 +1,18 @@ + +
+ + + +
diff --git a/Open-ILS/src/templates/staff/share/t_eframe.tt2 b/Open-ILS/src/templates/staff/share/t_eframe.tt2 new file mode 100644 index 0000000000..8fc95ca6c6 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/t_eframe.tt2 @@ -0,0 +1,9 @@ +
+ + +
+ diff --git a/Open-ILS/src/templates/staff/share/t_prompt_dialog.tt2 b/Open-ILS/src/templates/staff/share/t_prompt_dialog.tt2 new file mode 100644 index 0000000000..ce19832db1 --- /dev/null +++ b/Open-ILS/src/templates/staff/share/t_prompt_dialog.tt2 @@ -0,0 +1,21 @@ + +
+ + + +
diff --git a/Open-ILS/src/templates/staff/statusbar.tt2 b/Open-ILS/src/templates/staff/statusbar.tt2 new file mode 100644 index 0000000000..7eef88b5d8 --- /dev/null +++ b/Open-ILS/src/templates/staff/statusbar.tt2 @@ -0,0 +1,33 @@ + + + diff --git a/Open-ILS/src/templates/staff/t_login.tt2 b/Open-ILS/src/templates/staff/t_login.tt2 new file mode 100644 index 0000000000..135e9daacd --- /dev/null +++ b/Open-ILS/src/templates/staff/t_login.tt2 @@ -0,0 +1,57 @@ +
+
+
+
+
+ [% l('Sign In') %] + +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ [% l('Login Failed') %] +
+
+ +
+
+
+
+
+
diff --git a/Open-ILS/src/templates/staff/t_splash.tt2 b/Open-ILS/src/templates/staff/t_splash.tt2 new file mode 100644 index 0000000000..d259698db3 --- /dev/null +++ b/Open-ILS/src/templates/staff/t_splash.tt2 @@ -0,0 +1,69 @@ +
+
+
+ +
+
+
+
+ +
+
+
+
[% l('Circulation and Patrons') %]
+
+ +
+
+ +
+
+
+
[% l('Item Search and Cataloging') %]
+
+ +
+
+ +
+ +
+ +
+
diff --git a/Open-ILS/web/js/ui/default/opac/staff.js b/Open-ILS/web/js/ui/default/opac/staff.js index ebed5e50f4..d281ca2c48 100644 --- a/Open-ILS/web/js/ui/default/opac/staff.js +++ b/Open-ILS/web/js/ui/default/opac/staff.js @@ -1,4 +1,11 @@ /* staff client integration functions */ + +// Browser staff client runs the TPAC within an iframe, whose onload +// is not called until after the page onload is called. window.onload +// actions are wrapped in timeouts (below) to ensure the wrapping page +// has a chance to insert the necessary xulG, etc. functions into the +// window. + function debug(msg){dump(msg+'\n')} var eventCache={}; function attachEvt(scope, name, action) { @@ -34,107 +41,137 @@ function staff_hold_usr_barcode_changed(isload) { return; } - if(typeof xulG != 'undefined' && xulG.get_barcode_and_settings) { - var cur_hold_barcode = undefined; - var barcode = isload; - if(!barcode || barcode === true) barcode = document.getElementById('staff_barcode').value; - var only_settings = true; - if(!document.getElementById('hold_usr_is_requestor').checked) { - if(!isload) { - barcode = document.getElementById('hold_usr_input').value; - only_settings = false; - } - if(barcode && barcode != '' && !document.getElementById('hold_usr_is_requestor_not').checked) - document.getElementById('hold_usr_is_requestor_not').checked = 'checked'; - } - if(barcode == undefined || barcode == '') { - document.getElementById('patron_name').innerHTML = ''; - // No submitting on empty barcode, but empty barcode doesn't really count as "not found" either - document.getElementById('place_hold_submit').disabled = true; - document.getElementById("patron_usr_barcode_not_found").style.display = 'none'; - cur_hold_barcode = null; - return; + if (!window.xulG) return; + + var cur_hold_barcode = undefined; + var barcode = isload; + if(!barcode || barcode === true) barcode = document.getElementById('staff_barcode').value; + var only_settings = true; + if(!document.getElementById('hold_usr_is_requestor').checked) { + if(!isload) { + barcode = document.getElementById('hold_usr_input').value; + only_settings = false; } - if(barcode == cur_hold_barcode) - return; - // No submitting until we think the barcode is valid + if(barcode && barcode != '' && !document.getElementById('hold_usr_is_requestor_not').checked) + document.getElementById('hold_usr_is_requestor_not').checked = 'checked'; + } + if(barcode == undefined || barcode == '') { + document.getElementById('patron_name').innerHTML = ''; + // No submitting on empty barcode, but empty barcode doesn't really count as "not found" either document.getElementById('place_hold_submit').disabled = true; + document.getElementById("patron_usr_barcode_not_found").style.display = 'none'; + cur_hold_barcode = null; + return; + } + if(barcode == cur_hold_barcode) + return; + // No submitting until we think the barcode is valid + document.getElementById('place_hold_submit').disabled = true; + + if (window.IAMBROWSER) { + // Browser client operates asynchronously + if (!xulG.get_barcode_and_settings_async) return; + xulG.get_barcode_and_settings_async(barcode, only_settings) + .then( + function(load_info) { // load succeeded + staff_hold_usr_barcode_changed2( + isload, only_settings, barcode, cur_hold_barcode, load_info); + }, + function() { + // load failed (rejected). Call staff_hold_usr_barcode_changed2 + // anyway, since it handles clearing the form + staff_hold_usr_barcode_changed2( + isload, only_settings, barcode, cur_hold_barcode, false); + } + ) + } else { + // XUL version is synchronous + if (!xulG.get_barcode_and_settings) return; var load_info = xulG.get_barcode_and_settings(window, barcode, only_settings); - if(load_info == false || load_info == undefined) { - document.getElementById('patron_name').innerHTML = ''; - document.getElementById("patron_usr_barcode_not_found").style.display = ''; - cur_hold_barcode = null; - return; - } - cur_hold_barcode = load_info.barcode; - if(!only_settings || (isload && isload !== true)) document.getElementById('hold_usr_input').value = load_info.barcode; // Safe at this point as we already set cur_hold_barcode - if(load_info.settings['opac.default_pickup_location']) - document.getElementById('pickup_lib').value = load_info.settings['opac.default_pickup_location']; - if(!load_info.settings['opac.default_phone']) load_info.settings['opac.default_phone'] = ''; - if(!load_info.settings['opac.default_sms_notify']) load_info.settings['opac.default_sms_notify'] = ''; - if(!load_info.settings['opac.default_sms_carrier']) load_info.settings['opac.default_sms_carrier'] = ''; - if(load_info.settings['opac.hold_notify'] || load_info.settings['opac.hold_notify'] === '') { - var email = load_info.settings['opac.hold_notify'].indexOf('email') > -1; - var phone = load_info.settings['opac.hold_notify'].indexOf('phone') > -1; - var sms = load_info.settings['opac.hold_notify'].indexOf('sms') > -1; - var update_elements = document.getElementsByName('email_notify'); - for(var i in update_elements) update_elements[i].checked = (email ? 'checked' : ''); - update_elements = document.getElementsByName('phone_notify_checkbox'); - for(var i in update_elements) update_elements[i].checked = (phone ? 'checked' : ''); - update_elements = document.getElementsByName('sms_notify_checkbox'); - for(var i in update_elements) update_elements[i].checked = (sms ? 'checked' : ''); - } - update_elements = document.getElementsByName('phone_notify'); - for(var i in update_elements) update_elements[i].value = load_info.settings['opac.default_phone']; - update_elements = document.getElementsByName('sms_notify'); - for(var i in update_elements) update_elements[i].value = load_info.settings['opac.default_sms_notify']; - update_elements = document.getElementsByName('sms_carrier'); - for(var i in update_elements) update_elements[i].value = load_info.settings['opac.default_sms_carrier']; - update_elements = document.getElementsByName('email_notify'); - for(var i in update_elements) { - update_elements[i].disabled = (load_info.user_email ? false : true); - if(update_elements[i].disabled) update_elements[i].checked = false; - } - update_elements = document.getElementsByName('email_address'); - for(var i in update_elements) update_elements[i].textContent = load_info.user_email; - if(!document.getElementById('hold_usr_is_requestor').checked && document.getElementById('hold_usr_input').value) { - document.getElementById('patron_name').innerHTML = load_info.patron_name; - document.getElementById("patron_usr_barcode_not_found").style.display = 'none'; - } - // Ok, now we can allow submitting again, unless this is a "true" load, in which case we likely have a blank barcode box active - - // update the advanced hold options link to propagate the patron - // barcode if clicked. This is needed when the patron barcode - // is manually entered (i.e. the staff client does not provide one). - var adv_link = document.getElementById('advanced_hold_link'); - if (adv_link) { // not present on MR hold pages - var href = adv_link.getAttribute('href').replace( - /;usr_barcode=[^;\&]+|$/, - ';usr_barcode=' + encodeURIComponent(cur_hold_barcode)); - adv_link.setAttribute('href', href); - } + staff_hold_usr_barcode_changed2(isload, only_settings, barcode, cur_hold_barcode, load_info); + } +} + +function staff_hold_usr_barcode_changed2( + isload, only_settings, barcode, cur_hold_barcode, load_info) { + + if(load_info == false || load_info == undefined) { + document.getElementById('patron_name').innerHTML = ''; + document.getElementById("patron_usr_barcode_not_found").style.display = ''; + cur_hold_barcode = null; + return; + } + cur_hold_barcode = load_info.barcode; + if(!only_settings || (isload && isload !== true)) document.getElementById('hold_usr_input').value = load_info.barcode; // Safe at this point as we already set cur_hold_barcode + if(load_info.settings['opac.default_pickup_location']) + document.getElementById('pickup_lib').value = load_info.settings['opac.default_pickup_location']; + if(!load_info.settings['opac.default_phone']) load_info.settings['opac.default_phone'] = ''; + if(!load_info.settings['opac.default_sms_notify']) load_info.settings['opac.default_sms_notify'] = ''; + if(!load_info.settings['opac.default_sms_carrier']) load_info.settings['opac.default_sms_carrier'] = ''; + if(load_info.settings['opac.hold_notify'] || load_info.settings['opac.hold_notify'] === '') { + var email = load_info.settings['opac.hold_notify'].indexOf('email') > -1; + var phone = load_info.settings['opac.hold_notify'].indexOf('phone') > -1; + var sms = load_info.settings['opac.hold_notify'].indexOf('sms') > -1; + var update_elements = document.getElementsByName('email_notify'); + for(var i in update_elements) update_elements[i].checked = (email ? 'checked' : ''); + update_elements = document.getElementsByName('phone_notify_checkbox'); + for(var i in update_elements) update_elements[i].checked = (phone ? 'checked' : ''); + update_elements = document.getElementsByName('sms_notify_checkbox'); + for(var i in update_elements) update_elements[i].checked = (sms ? 'checked' : ''); + } + update_elements = document.getElementsByName('phone_notify'); + for(var i in update_elements) update_elements[i].value = load_info.settings['opac.default_phone']; + update_elements = document.getElementsByName('sms_notify'); + for(var i in update_elements) update_elements[i].value = load_info.settings['opac.default_sms_notify']; + update_elements = document.getElementsByName('sms_carrier'); + for(var i in update_elements) update_elements[i].value = load_info.settings['opac.default_sms_carrier']; + update_elements = document.getElementsByName('email_notify'); + for(var i in update_elements) { + update_elements[i].disabled = (load_info.user_email ? false : true); + if(update_elements[i].disabled) update_elements[i].checked = false; + } + update_elements = document.getElementsByName('email_address'); + for(var i in update_elements) update_elements[i].textContent = load_info.user_email; + if(!document.getElementById('hold_usr_is_requestor').checked && document.getElementById('hold_usr_input').value) { + document.getElementById('patron_name').innerHTML = load_info.patron_name; + document.getElementById("patron_usr_barcode_not_found").style.display = 'none'; + } + // Ok, now we can allow submitting again, unless this is a "true" load, in which case we likely have a blank barcode box active - if (isload !== true) - document.getElementById('place_hold_submit').disabled = false; + // update the advanced hold options link to propagate the patron + // barcode if clicked. This is needed when the patron barcode + // is manually entered (i.e. the staff client does not provide one). + var adv_link = document.getElementById('advanced_hold_link'); + if (adv_link) { // not present on MR hold pages + var href = adv_link.getAttribute('href').replace( + /;usr_barcode=[^;\&]+|$/, + ';usr_barcode=' + encodeURIComponent(cur_hold_barcode)); + adv_link.setAttribute('href', href); } + + if (isload !== true) + document.getElementById('place_hold_submit').disabled = false; } window.onload = function() { // record details page events - var rec = location.href.match(/\/opac\/record\/(\d+)/); - if(rec && rec[1]) { - runEvt('rdetail', 'recordRetrieved', rec[1]); - runEvt('rdetail', 'MFHDDrawn'); - } - if(location.href.match(/place_hold/)) { - // patron barcode may come from XUL or a CGI param - var patron_barcode = xulG.patron_barcode || - document.getElementById('hold_usr_input').value; - if(patron_barcode) { - staff_hold_usr_barcode_changed(patron_barcode); - } else { - staff_hold_usr_barcode_changed(true); + + setTimeout(function() { + var rec = location.href.match(/\/opac\/record\/(\d+)/); + if(rec && rec[1]) { + runEvt('rdetail', 'recordRetrieved', rec[1]); + runEvt('rdetail', 'MFHDDrawn'); } - } + if(location.href.match(/place_hold/)) { + // patron barcode may come from XUL or a CGI param + var patron_barcode = xulG.patron_barcode || + document.getElementById('hold_usr_input').value; + if(patron_barcode) { + staff_hold_usr_barcode_changed(patron_barcode); + } else { + staff_hold_usr_barcode_changed(true); + } + } + }); } function rdetail_next_prev_actions(index, count, prev, next, start, end, results) { @@ -167,6 +204,8 @@ function rdetail_next_prev_actions(index, count, prev, next, start, end, results ol = window.onload; window.onload = function() { if(ol) ol(); - runEvt('rdetail', 'nextPrevDrawn', Number(index), Number(count)); + setTimeout(function() { + runEvt('rdetail', 'nextPrevDrawn', Number(index), Number(count)); + }); }; } diff --git a/Open-ILS/web/js/ui/default/staff/Gruntfile.js b/Open-ILS/web/js/ui/default/staff/Gruntfile.js new file mode 100644 index 0000000000..d55d71f9b3 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/Gruntfile.js @@ -0,0 +1,164 @@ +module.exports = function(grunt) { + + // Project configuration. + var config = { + pkg: grunt.file.readJSON('package.json'), + + // copy the files we care about from bower-fetched dependencies + // into our build directory + copy: { + + js : { + files: [{ + dest: 'build/js/', + flatten: true, + filter: 'isFile', + expand : true, + src: [ + 'bower_components/angular/angular.min.js', + 'bower_components/angular/angular.min.js.map', + 'bower_components/angular-route/angular-route.min.js', + 'bower_components/angular-route/angular-route.min.js.map', + 'bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js', + 'bower_components/angular-hotkeys/build/hotkeys.min.js', + ] + }] + }, + + css : { + files : [{ + dest : 'build/css/', + flatten : true, + filter : 'isFile', + expand : true, + src : [ + 'bower_components/angular-hotkeys/build/hotkeys.min.css', + 'bower_components/bootstrap/dist/css/bootstrap.min.css' + ] + }] + }, + + fonts : { + files : [{ + dest : 'build/fonts/', + flatten : true, + filter : 'isFile', + expand : true, + src : [ + 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot', + 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg', + 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf', + 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff' + ] + }] + } + }, + + // combine our CSS deps + // note: minification also supported, but not required (yet). + cssmin: { + combine: { + files: { + 'build/css/evergreen-staff-client-deps.<%= pkg.version %>.min.css' : [ + 'build/css/hotkeys.min.css', + 'build/css/bootstrap.min.css' + ] + } + } + }, + + // concatenation + minification + uglify: { + options: { + banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' + }, + build: { + src: [ + // These are concatenated in order in the final build file. + // The order is important. + 'build/js/angular.min.js', + 'build/js/angular-route.min.js', + 'build/js/ui-bootstrap-tpls.min.js', + 'build/js/hotkeys.min.js', + // NOTE: OpenSRF must be installed + '/openils/lib/javascript/JSON_v1.js', + '/openils/lib/javascript/opensrf.js', + '/openils/lib/javascript/opensrf_ws.js', + 'services/core.js', + 'services/strings.js', + 'services/idl.js', + 'services/event.js', + 'services/net.js', + 'services/auth.js', + 'services/pcrud.js', + 'services/env.js', + 'services/org.js', + 'services/startup.js', + 'services/hatch.js', + 'services/print.js', + 'services/coresvc.js', + 'services/navbar.js', + 'services/statusbar.js', + 'services/ui.js', + ], + dest: 'build/js/<%= pkg.name %>.<%= pkg.version %>.min.js' + } + }, + + // bare concat operation; useful for testing concat w/o minification + // to more easily detect if concat order is incorrect + concat: { + options: { + separator: ';', + } + }, + + exec : { + + // Generate test/data/IDL2js.js for unit tests. + // note: the output of this script is *not* part of the final build. + idl2js : { + command : 'cd test/data && perl idl2js.pl', + }, + + // Remove the unit test IDL2js.js file. We don't need it after testing + rmidl2js : { + command : 'rm test/data/IDL2js.js', + } + }, + + // unit tests configuration + karma : { + unit: { + configFile: 'test/karma.conf.js', + //background: true // for now, visually babysit unit tests + } + } + }; + + // tell concat about our uglify build options (instead of repeating them) + config.concat.build = config.uglify.build; + + // apply our configuration + grunt.initConfig(config); + + // Load our modules + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-cssmin'); + grunt.loadNpmTasks('grunt-karma'); + grunt.loadNpmTasks('grunt-exec'); + + // note: "grunt concat" is not requried + grunt.registerTask('build', ['copy', 'cssmin', 'uglify']); + + // test only, no minification + grunt.registerTask('test', ['copy', 'exec:idl2js', 'karma:unit', 'exec:rmidl2js']); + + // note: "grunt concat" is not requried + grunt.registerTask('all', ['test', 'cssmin', 'uglify']); + +}; + +// vim: ts=2:sw=2:softtabstop=2 diff --git a/Open-ILS/web/js/ui/default/staff/README.install b/Open-ILS/web/js/ui/default/staff/README.install new file mode 100644 index 0000000000..62668c1089 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/README.install @@ -0,0 +1,93 @@ += Building, Testing, Packaging the Browser Client = +:Author: Bill Erickson +:Email: berick@esilibrary.com +:Date: 2014-05-07 + +== Prerequisites == + + * http://bower.io/[Bower] + ** Dependency retrieval + * http://jasmine.github.io/[Jasmine] + ** Headless unit tests runner + * http://gruntjs.com/[Grunt] + ** Coordinating the build + ** Concatenation + minification of JS and CSS + +These are all Node.js plugins, so start by installing Node.js + +=== Install Node.js === + +Node.js does not have have Debian Wheezy build target. For now, I've opted +to install from source. For more, see also +https://github.com/joyent/node/wiki/installation[Node.js Installation] + +[source,sh] +------------------------------------------------------------------------------ +% git clone https://github.com/joyent/node.git +% cd node +% git checkout -b v0.10.28 v0.10.28 + +# set -j to number of CPU cores + 1 +% ./configure && make -j5 && sudo make install + +# update packages +% sudo npm update +------------------------------------------------------------------------------ + +=== Install Grunt CLI === + +[source,sh] +------------------------------------------------------------------------------ +% sudo npm install -g grunt-cli +------------------------------------------------------------------------------ + +=== Install Bower === + +[source,sh] +------------------------------------------------------------------------------ +% sudo npm install -g bower +------------------------------------------------------------------------------ + +== Building, Testing, Minification == + +The remaining steps all take place within the staff JS web root: + +[source,sh] +------------------------------------------------------------------------------ +% cd $EVERGREEN_ROOT/Open-ILS/web/js/ui/default/staff/ +------------------------------------------------------------------------------ + +=== Install Project-local Dependencies === + +npm inspects the 'package.json' file for dependencies and fetches them +from the Node package network. + +[source,sh] +------------------------------------------------------------------------------ +% npm install # fetch Grunt dependencies +% bower install # fetch JS dependencies +------------------------------------------------------------------------------ + +=== Running the Build Scripts === + +[source,sh] +------------------------------------------------------------------------------ + +# build, run tests +% grunt test + +# build, concat+minify +% grunt uglify + +# build, run tests, concat+minify +% grunt all +------------------------------------------------------------------------------ + +== TODO == + + * Minification of app-specific JS files + * Integrate this into the Evergreen Makefile test and install targets + ** Avoid installing test, node_modules, etc. into the web dir. + * Support fetching JS deps (angularjs, etc.) via direct retrieval for + installation without test + concat + minify (i.e. w/o requiring Node.js)? + diff --git a/Open-ILS/web/js/ui/default/staff/admin/user_perms.js b/Open-ILS/web/js/ui/default/staff/admin/user_perms.js new file mode 100644 index 0000000000..460e8d1ad2 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/admin/user_perms.js @@ -0,0 +1,100 @@ +/** + * App to drive the base page. + * Login Form + * Splash Page + */ + +angular.module('egUserPermsEditor', + ['ngRoute', 'ui.bootstrap', 'egCoreMod','egUiMod']) + +.config(['$routeProvider','$locationProvider','$compileProvider', + function($routeProvider , $locationProvider , $compileProvider) { + + $locationProvider.html5Mode(true); + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); + var resolver = {delay : function(egStartup) {return egStartup.go()}}; + + $routeProvider.when('/admin/user_perms', { + templateUrl: './admin/t_user_perms_lookup', + controller: 'UserPermsLookupCtrl', + resolve : resolver + }); + + $routeProvider.when('/admin/user_perms/:user_id', { + templateUrl: 'user-perms-template', + controller: 'UserPermsCtrl', + resolve : resolver + }); + + // default page + $routeProvider.otherwise({ + templateUrl : 'user-perms-template', + controller: 'UserPermsCtrl', + resolve : resolver + }); +}]) + +.controller('UserPermsLookupCtrl', + ['$scope','$window','$location','egCore', +function($scope , $window , $location , egCore) { + + $scope.selectMe = true; // focus text input + $scope.args = {}; + + // find the user by barcode, the jump to the editor + $scope.submitBarcode = function(args) { + + $scope.bcNotFound = null; + if (!args.barcode) return; + + $scope.selectMe = false; + + // lookup barcode + egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.get_barcodes', + egCore.auth.token(), egCore.auth.user().ws_ou(), + 'actor', args.barcode) + + .then(function(resp) { // get_barcodes + + if (evt = egCore.evt.parse(resp)) { + console.error(evt.toString()); + return; + } + + if (!resp || !resp[0]) { + $scope.bcNotFound = args.barcode; + $scope.selectMe = true; + return; + } + + // see if an opt-in request is needed + user_id = resp[0].id; + $location.path($location.path() + '/' + user_id); + }); + } + +}]) + +.controller('UserPermsCtrl', + ['$scope','$routeParams','$window','$location','egCore', +function($scope , $routeParams , $window , $location , egCore) { + var user_id = $routeParams.user_id; + + var url = $location.absUrl().replace( + /\/eg\/staff.*/, '/xul/server/patron/user_edit.xhtml'); + + url += '?usr=' + encodeURIComponent(user_id); + + // user_edit does not load the session via cookie. It uses URL + // params or xulG instead. Pass via xulG. + $scope.funcs = { + ses : egCore.auth.token(), + on_patron_save : function() { + $scope.funcs.reload(); + } + } + + $scope.user_perms_url = url; +}]) diff --git a/Open-ILS/web/js/ui/default/staff/admin/workstation/app.js b/Open-ILS/web/js/ui/default/staff/admin/workstation/app.js new file mode 100644 index 0000000000..51a4240e34 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/admin/workstation/app.js @@ -0,0 +1,557 @@ +/** + * App to drive the base page. + * Login Form + * Splash Page + */ + +angular.module('egWorkstationAdmin', + ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod']) + +.config(['$routeProvider','$locationProvider','$compileProvider', + function($routeProvider , $locationProvider , $compileProvider) { + + $locationProvider.html5Mode(true); + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); + var resolver = {delay : function(egStartup) {return egStartup.go()}}; + + $routeProvider.when('/admin/workstation/print/config', { + templateUrl: './admin/workstation/t_print_config', + controller: 'PrintConfigCtrl', + resolve : resolver + }); + + $routeProvider.when('/admin/workstation/print/templates', { + templateUrl: './admin/workstation/t_print_templates', + controller: 'PrintTemplatesCtrl', + resolve : resolver + }); + + $routeProvider.when('/admin/workstation/stored_prefs', { + templateUrl: './admin/workstation/t_stored_prefs', + controller: 'StoredPrefsCtrl', + resolve : resolver + }); + + + // default page + $routeProvider.otherwise({ + templateUrl : './admin/workstation/t_splash', + controller : 'SplashCtrl', + resolve : resolver + }); +}]) + +.controller('SplashCtrl', + ['$scope','$window','$location','egCore','egConfirmDialog', +function($scope , $window , $location , egCore , egConfirmDialog) { + + var allWorkstations = []; + var permMap = {}; + $scope.contextOrg = egCore.org.get(egCore.auth.user().ws_ou()); + + egCore.perm.hasPermAt('REGISTER_WORKSTATION', true) + .then(function(orgList) { + + // hide orgs in the context org selector where this login + // does not have the reg_ws perm + $scope.wsOrgHidden = function(id) { + return orgList.indexOf(id) == -1; + } + $scope.userHasRegPerm = + orgList.indexOf($scope.contextOrg.id()) > -1; + }); + + // fetch the stored WS info + egCore.hatch.getItem('eg.workstation.all') + .then(function(all) { + allWorkstations = all || []; + $scope.workstations = + allWorkstations.map(function(w) { return w.name }); + return egCore.hatch.getItem('eg.workstation.default'); + }) + .then(function(def) { + $scope.defaultWS = def; + $scope.activeWS = $scope.selectedWS = egCore.auth.workstation() || def; + }); + + $scope.getWSLabel = function(ws) { + return ws == $scope.defaultWS ? + egCore.strings.$replace(egCore.strings.DEFAULT_WS_LABEL, {ws:ws}) : ws; + } + + $scope.setDefaultWS = function() { + egCore.hatch.setItem( + 'eg.workstation.default', $scope.selectedWS) + .then(function() { $scope.defaultWS = $scope.selectedWS }); + } + + // redirect the user to the login page using the current + // workstation as the workstation URL param + $scope.useWS = function() { + $window.location.href = $location + .path('/login') + .search({ws : $scope.selectedWS}) + .absUrl(); + } + + $scope.registerWS = function() { + register_workstation( + $scope.newWSName, + $scope.contextOrg.shortname() + '-' + $scope.newWSName, + $scope.contextOrg.id() + ); + } + + function register_workstation(base_name, name, org_id, override) { + + var method = 'open-ils.actor.workstation.register'; + if (override) method += '.override'; + + egCore.net.request( + 'open-ils.actor', method, egCore.auth.token(), name, org_id) + + .then(function(resp) { + if (evt = egCore.evt.parse(resp)) { + console.log('register returned ' + evt.toString()); + + if (evt.textcode == 'WORKSTATION_NAME_EXISTS' && !override) { + egConfirmDialog.open( + egCore.strings.WS_EXISTS, base_name, { + ok : function() { + register_workstation(base_name, name, org_id, true); + }, + cancel : function() {} + } + ); + + } else { + // TODO: provide permission error display + alert(evt.toString()); + } + } else if (resp) { + $scope.workstations.push(name); + + allWorkstations.push({ + id : resp, + name : name, + owning_lib : org_id + }); + + egCore.hatch.setItem( + 'eg.workstation.all', allWorkstations) + .then(function() { + if (allWorkstations.length == 1) { + // first one registerd, also mark it as the default + $scope.selectedWS = name; + $scope.setDefaultWS(); + } + }); + } + }); + } + + $scope.wsOrgChanged = function(org) { + $scope.contextOrg = org; + } + + // --------------------- + // Hatch Configs + $scope.hatchURL = egCore.hatch.hatchURL(); + $scope.hatchRequired = + egCore.hatch.getLocalItem('eg.hatch.required'); + + $scope.updateHatchRequired = function() { + egCore.hatch.setLocalItem( + 'eg.hatch.required', $scope.hatchRequired); + } + + $scope.updateHatchURL = function() { + egCore.hatch.setLocalItem( + 'eg.hatch.url', $scope.hatchURL); + } +}]) + +.controller('PrintConfigCtrl', + ['$scope','egCore', +function($scope , egCore) { + console.log('PrintConfigCtrl'); + + $scope.actionPending = false; + $scope.isTestView = false; + + $scope.setContext = function(ctx) { + $scope.context = ctx; + $scope.isTestView = false; + $scope.actionPending = false; + } + $scope.setContext('default'); + + $scope.getPrinterByAttr = function(attr, value) { + var printer; + angular.forEach($scope.printers, function(p) { + if (p[attr] == value) printer = p; + }); + return printer; + } + + $scope.currentPrinter = function() { + if ($scope.printConfig && $scope.printConfig[$scope.context]) { + return $scope.getPrinterByAttr( + 'name', $scope.printConfig[$scope.context].printer + ); + } + } + + // fetch info on all remote printers + egCore.hatch.getPrinters() + .then(function(printers) { + $scope.printers = printers; + $scope.defaultPrinter = + $scope.getPrinterByAttr('is-default', true); + }) + .then(function() { return egCore.hatch.getPrintConfig() }) + .then(function(config) { + $scope.printConfig = config; + + var pname = ''; + if ($scope.defaultPrinter) { + pname = $scope.defaultPrinter.name; + + } else if ($scope.printers.length == 1) { + // if the OS does not report a default printer, but only + // one printer is available, treat it as the default. + pname = $scope.printers[0].name; + } + + // apply the default printer to every context which has + // no printer configured. + angular.forEach( + ['default','receipt','label','mail','offline'], + function(ctx) { + if (!$scope.printConfig[ctx]) { + $scope.printConfig[ctx] = { + context : ctx, + printer : pname + } + } + } + ); + }); + + $scope.printerConfString = function() { + if ($scope.printConfigError) return $scope.printConfigError; + if (!$scope.printConfig) return; + if (!$scope.printConfig[$scope.context]) return; + return JSON.stringify( + $scope.printConfig[$scope.context], undefined, 2); + } + + $scope.resetConfig = function() { + $scope.actionPending = true; + $scope.printConfigError = null; + $scope.printConfig[$scope.context] = { + context : $scope.context + } + + if ($scope.defaultPrinter) { + $scope.printConfig[$scope.context].printer = + $scope.defaultPrinter.name; + } + + egCore.hatch.setPrintConfig($scope.printConfig) + .finally(function() {$scope.actionPending = false}); + } + + $scope.configurePrinter = function() { + $scope.printConfigError = null; + $scope.actionPending = true; + egCore.hatch.configurePrinter( + $scope.context, + $scope.printConfig[$scope.context].printer + ) + .then( + function(config) {$scope.printConfig = config}, + function(error) {$scope.printConfigError = error} + ) + .finally(function() {$scope.actionPending = false}); + } + + $scope.setPrinter = function(name) { + $scope.printConfig[$scope.context].printer = name; + } + + // for testing + $scope.setContentType = function(type) { $scope.contentType = type } + + $scope.testPrint = function(withDialog) { + if ($scope.contentType == 'text/plain') { + egCore.print.print({ + context : $scope.context, + content_type : $scope.contentType, + content : $scope.textPrintContent, + show_dialog : withDialog + }); + } else { + egCore.print.print({ + context : $scope.context, + content_type : $scope.contentType, + content : $scope.htmlPrintContent, + scope : { + value1 : 'Value One', + value2 : 'Value Two', + date_value : '2015-02-04T14:04:34-0400' + }, + show_dialog : withDialog + }); + } + } + + $scope.setContentType('text/plain'); + +}]) + +.controller('PrintTemplatesCtrl', + ['$scope','$q','egCore', +function($scope , $q , egCore) { + + $scope.print = { + template_name : 'bills_current', + template_output : '' + }; + + // print preview scope data + // TODO: consider moving the template-specific bits directly + // into the templates or storing template- specific script files + // alongside the templates. + // NOTE: A lot of this data can be shared across templates. + var seed_user = { + first_given_name : 'Slow', + second_given_name : 'Joe', + family_name : 'Jones', + card : { + barcode : '30393830393' + } + } + var seed_addr = { + street1 : '123 Apple Rd', + street2 : 'Suite B', + city : 'Anywhere', + state : 'XX', + country : 'US', + post_code : '12345' + } + + var seed_record = { + title : 'Traveling Pants!!', + author : 'Jane Jones', + isbn : '1231312123' + }; + + var seed_copy = { + barcode : '33434322323' + } + + var one_hold = { + behind_desk : 'f', + phone_notify : '111-222-3333', + sms_notify : '111-222-3333', + email_notify : 'user@example.org', + request_time : new Date().toISOString() + } + + + $scope.preview_scope = { + //bills + transactions : [ + { + id : 1, + xact_start : new Date().toISOString(), + summary : { + xact_type : 'circulation', + last_billing_type : 'Overdue materials', + total_owed : 1.50, + last_payment_note : 'Test Note 1', + total_paid : 0.50, + balance_owed : 1.00 + } + }, { + id : 2, + xact_start : new Date().toISOString(), + summary : { + xact_type : 'circulation', + last_billing_type : 'Overdue materials', + total_owed : 2.50, + last_payment_note : 'Test Note 2', + total_paid : 0.50, + balance_owed : 2.00 + } + } + ], + + circulations : [ + { + due_date : new Date().toISOString(), + target_copy : seed_copy, + title : seed_record.title + }, + ], + + previous_balance : 8.45, + payment_total : 2.00, + payment_applied : 2.00, + new_balance : 6.45, + amount_voided : 0, + change_given : 0, + payment_type : 'cash_payment', + payment_note : 'Here is a payment note', + note : { + create_date : new Date().toISOString(), + title : 'Test Note Title', + usr : seed_user, + value : 'This patron is super nice!' + }, + + transit : { + dest : { + name : 'Library X', + shortname : 'LX', + holds_address : seed_addr + }, + target_copy : seed_copy + }, + title : seed_record.title, + author : seed_record.author, + patron : egCore.idl.toHash(egCore.auth.user()), + address : seed_addr, + hold : one_hold, + holds : [ + {hold : one_hold, title : 'Some Title 1', author : 'Some Author 1'}, + {hold : one_hold, title : 'Some Title 2', author : 'Some Author 2'}, + {hold : one_hold, title : 'Some Title 3', author : 'Some Author 3'} + ] + } + + $scope.preview_scope.payments = [ + {amount : 1.00, xact : $scope.preview_scope.transactions[0]}, + {amount : 1.00, xact : $scope.preview_scope.transactions[1]} + ] + $scope.preview_scope.payments[0].xact.title = 'Hali Bote Azikaban de tao fan'; + $scope.preview_scope.payments[0].xact.copy_barcode = '334343434'; + $scope.preview_scope.payments[1].xact.title = seed_record.title; + $scope.preview_scope.payments[1].xact.copy_barcode = seed_copy.barcode; + + // today, staff, current_location, etc. + egCore.print.fleshPrintScope($scope.preview_scope); + + $scope.template_changed = function() { + $scope.print.load_failed = false; + egCore.print.getPrintTemplate($scope.print.template_name) + .then( + function(html) { + $scope.print.template_content = html; + console.log('set template content'); + }, + function() { + $scope.print.template_content = ''; + $scope.print.load_failed = true; + } + ); + } + + $scope.save_locally = function() { + egCore.hatch.storePrintTemplate( + $scope.print.template_name, + $scope.print.template_content + ); + } + + $scope.template_changed(); // load the default +}]) + +// +.directive('egPrintTemplateOutput', ['$compile',function($compile) { + return function(scope, element, attrs) { + scope.$watch( + function(scope) { + return scope.$eval(attrs.content); + }, + function(value) { + // create an isolate scope and copy the print context + // data into the new scope. + // TODO: see also print security concerns in egHatch + var result = element.html(value); + var context = scope.$eval(attrs.context); + var print_scope = scope.$new(true); + angular.forEach(context, function(val, key) { + print_scope[key] = val; + }) + $compile(element.contents())(print_scope); + } + ); + }; +}]) + +.controller('StoredPrefsCtrl', + ['$scope','$q','egCore','egConfirmDialog', +function($scope , $q , egCore , egConfirmDialog) { + console.log('StoredPrefsCtrl'); + + $scope.setContext = function(ctx) { + $scope.context = ctx; + } + $scope.setContext('local'); + + // grab the edit perm + $scope.userHasDeletePerm = false; + egCore.perm.hasPermHere('ADMIN_WORKSTATION') + .then(function(bool) { $scope.userHasDeletePerm = bool }); + + // fetch the keys + + function refreshKeys() { + $scope.keys = {local : [], remote : []}; + + egCore.hatch.getRemoteKeys().then( + function(keys) { $scope.keys.remote = keys.sort() }) + + // local calls are non-async + $scope.keys.local = egCore.hatch.getLocalKeys(); + } + refreshKeys(); + + $scope.selectKey = function(key) { + $scope.currentKey = key; + $scope.currentKeyContent = null; + + if ($scope.context == 'local') { + $scope.currentKeyContent = egCore.hatch.getLocalItem(key); + } else { + egCore.hatch.getRemoteItem(key) + .then(function(content) { + $scope.currentKeyContent = content + }); + } + } + + $scope.getCurrentKeyContent = function() { + return JSON.stringify($scope.currentKeyContent, null, 2); + } + + $scope.removeKey = function(key) { + egConfirmDialog.open( + egCore.strings.PREFS_REMOVE_KEY_CONFIRM, '', + { deleteKey : key, + ok : function() { + if ($scope.context == 'local') { + egCore.hatch.removeLocalItem(key); + refreshKeys(); + } else { + egCore.hatch.removeItem(key) + .then(function() { refreshKeys() }); + } + }, + cancel : function() {} // user canceled, nothing to do + } + ); + } +}]) diff --git a/Open-ILS/web/js/ui/default/staff/app.js b/Open-ILS/web/js/ui/default/staff/app.js new file mode 100644 index 0000000000..5c155a90b2 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/app.js @@ -0,0 +1,125 @@ +/** + * App to drive the base page. + * Login Form + * Splash Page + */ + +angular.module('egHome', ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod']) + +.config( + ['$routeProvider','$locationProvider', +function($routeProvider , $locationProvider) { + $locationProvider.html5Mode(true); + + /** + * Route resolvers allow us to run async commands + * before the page controller is instantiated. + */ + var resolver = {delay : ['egCore', + function(egCore) {return egCore.startup.go()}]}; + + $routeProvider.when('/login', { + templateUrl: './t_login', + controller: 'LoginCtrl', + resolve : resolver + }); + + // default page + $routeProvider.otherwise({ + templateUrl : './t_splash', + controller : 'SplashCtrl', + resolve : resolver + }); +}]) + +/** + * Login controller. + * Reads the login form and submits the login request + */ +.controller('LoginCtrl', + /* inject services into our controller. Spelling them + * out like this allows the auto-magic injector to work + * even if the code has been minified */ + ['$scope','$location','$window','egCore', + function($scope , $location , $window , egCore) { + $scope.focusMe = true; + + // if the user is already logged in, jump to splash page + if (egCore.auth.user()) $location.path('/'); + + egCore.hatch.getItem('eg.workstation.all') + .then(function(all) { + if (all && all.length) { + $scope.workstations = all.map(function(a) { return a.name }); + + if (ws = $location.search().ws) { + // user requested a workstation via URL + var match = all.filter( + function(w) {return ws == w.name} )[0]; + + if (match) { + // requested WS registered on this client + $scope.args = {workstation : match.name}; + } else { + // the requested WS is not registered on this client + $scope.wsNotRegistered = true; + } + } else { + // no workstation requested; use the default + egCore.hatch.getItem('eg.workstation.default') + .then(function(ws) { + $scope.args = {workstation : ws} + }); + } + } + }) + + $scope.login = function(args) { + $scope.loginFailed = false; + + if (!args) args = {}; // see FF note below + + if (!args.username) { + /* + Issues with form autofill / auto-complete + https://github.com/angular/angular.js/issues/1460 + http://timothy.userapp.io/post/63412334209/form-autocomplete-and-remember-password-with-angularjs + For now, since FF will save the values, we should + honor them, even if it's hacky. */ + args.username = document.getElementById("login-username").value; + args.password = document.getElementById("login-password").value; + } + + if (! (args.username && args.password) ) return; + + args.type = 'staff'; + egCore.auth.login(args).then( + + function() { + // after login, send the user back to the originally + // requested page or, if none, the home page. + // TODO: this is a little hinky because it causes 2 + // redirects if no route_to is defined. Improve. + $window.location.href = + $location.search().route_to || + $location.path('/').absUrl() + }, + function() { + $scope.args.password = ''; + $scope.loginFailed = true; + $scope.focusMe = true; + } + ); + } + } +]) + +/** + * Splash page dynamic content. + */ +.controller('SplashCtrl', ['$scope', + function($scope) { + console.log('SplashCtrl'); + } +]); + diff --git a/Open-ILS/web/js/ui/default/staff/bower.json b/Open-ILS/web/js/ui/default/staff/bower.json new file mode 100644 index 0000000000..8c2ed1f4e6 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/bower.json @@ -0,0 +1,30 @@ +{ + "name": "evergreen-staff-client", + "version": "0.0.1", + "authors": [ + "Bill Erickson " + ], + "description": "Evergreen HTML Staff Client", + "keywords": [ + "evergreen" + ], + "license": "GPL", + "homepage": "http://evergreen-ils.org", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "devDependencies": { + "bootstrap": "~3.1.1", + "angular": "~1.2.16", + "angular-route": "~1.2.16", + "angular-mocks": "~1.2.16", + "angular-bootstrap": "~0.11.0" + }, + "dependencies": { + "angular-hotkeys": "chieffancypants/angular-hotkeys#~1.2.0" + } +} diff --git a/Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js b/Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js new file mode 100644 index 0000000000..de59a4578f --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js @@ -0,0 +1,535 @@ +/** + * Catalog Record Buckets + * + * Known Issues + * + * add-all actions only add visible/fetched items. + * remove all from bucket UI leaves busted pagination + * -- apply a refresh after item removal? + * problems with bucket view fetching by record ID instead of bucket item: + * -- dupe bibs always sort to the bottom + * -- dupe bibs result in more records displayed per page than requested + * -- item 'pos' ordering is not honored on initial load. + */ + +angular.module('egCatRecordBuckets', + ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod']) + +.config(function($routeProvider, $locationProvider, $compileProvider) { + $locationProvider.html5Mode(true); + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export + + var resolver = {delay : function(egStartup) {return egStartup.go()}}; + + $routeProvider.when('/cat/bucket/record/search/:id', { + templateUrl: './cat/bucket/record/t_search', + controller: 'SearchCtrl', + resolve : resolver + }); + + $routeProvider.when('/cat/bucket/record/search', { + templateUrl: './cat/bucket/record/t_search', + controller: 'SearchCtrl', + resolve : resolver + }); + + $routeProvider.when('/cat/bucket/record/pending/:id', { + templateUrl: './cat/bucket/record/t_pending', + controller: 'PendingCtrl', + resolve : resolver + }); + + $routeProvider.when('/cat/bucket/record/pending', { + templateUrl: './cat/bucket/record/t_pending', + controller: 'PendingCtrl', + resolve : resolver + }); + + $routeProvider.when('/cat/bucket/record/view/:id', { + templateUrl: './cat/bucket/record/t_view', + controller: 'ViewCtrl', + resolve : resolver + }); + + $routeProvider.when('/cat/bucket/record/view', { + templateUrl: './cat/bucket/record/t_view', + controller: 'ViewCtrl', + resolve : resolver + }); + + // default page / bucket view + $routeProvider.otherwise({redirectTo : '/cat/bucket/record/view'}); +}) + +/** + * bucketSvc allows us to communicate between the search, + * pending, and view controllers. It also allows us to cache + * data for each so that data reloads are not needed on every + * tab click (i.e. route persistence). + */ +.factory('bucketSvc', ['$q','egCore', function($q, egCore) { + + var service = { + allBuckets : [], // un-fleshed user buckets + queryString : '', // last run query + queryRecords : [], // last run query results + currentBucket : null, // currently viewed bucket + + // per-page list collections + searchList : [], + pendingList : [], + viewList : [], + + // fetches all staff/biblio buckets for the authenticated user + // this function may only be called after startup. + fetchUserBuckets : function(force) { + if (this.allBuckets.length && !force) return; + var self = this; + return egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.container.retrieve_by_class.authoritative', + egCore.auth.token(), egCore.auth.user().id(), + 'biblio', 'staff_client' + ).then(function(buckets) { self.allBuckets = buckets }); + }, + + createBucket : function(name, desc) { + var deferred = $q.defer(); + var bucket = new egCore.idl.cbreb(); + bucket.owner(egCore.auth.user().id()); + bucket.name(name); + bucket.description(desc || ''); + bucket.btype('staff_client'); + + egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.container.create', + egCore.auth.token(), 'biblio', bucket + ).then(function(resp) { + if (resp) { + if (typeof resp == 'object') { + console.error('bucket create error: ' + js2JSON(resp)); + deferred.reject(); + } else { + deferred.resolve(resp); + } + } + }); + + return deferred.promise; + }, + + // edit the current bucket. since we edit the + // local object, there's no need to re-fetch. + editBucket : function(args) { + var bucket = service.currentBucket; + bucket.name(args.name); + bucket.description(args.desc); + bucket.pub(args.pub); + return egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.container.update', + egCore.auth.token(), 'biblio', bucket + ); + } + } + + // returns 1 if full refresh is needed + // returns 2 if list refresh only is needed + service.bucketRefreshLevel = function(id) { + if (!service.currentBucket) return 1; + if (service.bucketNeedsRefresh) { + service.bucketNeedsRefresh = false; + service.currentBucket = null; + return 1; + } + if (service.currentBucket.id() != id) return 1; + return 2; + } + + // returns a promise, resolved with bucket, rejected if bucket is + // not fetch-able + service.fetchBucket = function(id) { + var refresh = service.bucketRefreshLevel(id); + if (refresh == 2) return $q.when(service.currentBucket); + + var deferred = $q.defer(); + + egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.container.flesh.authoritative', + egCore.auth.token(), 'biblio', id + ).then(function(bucket) { + var evt = egCore.evt.parse(bucket); + if (evt) { + console.debug(evt); + deferred.reject(evt); + return; + } + service.currentBucket = bucket; + deferred.resolve(bucket); + }); + + return deferred.promise; + } + + // deletes a single container item from a bucket by container item ID. + // promise is rejected on failure + service.detachRecord = function(itemId) { + var deferred = $q.defer(); + egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.container.item.delete', + egCore.auth.token(), 'biblio', itemId + ).then(function(resp) { + var evt = egCore.evt.parse(resp); + if (evt) { + console.error(evt); + deferred.reject(evt); + return; + } + console.log('detached bucket item ' + itemId); + deferred.resolve(resp); + }); + + return deferred.promise; + } + + // delete bucket by ID. + // resolved w/ response on successful delete, + // rejected otherwise. + service.deleteBucket = function(id) { + var deferred = $q.defer(); + egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.container.full_delete', + egCore.auth.token(), 'biblio', id + ).then(function(resp) { + var evt = egCore.evt.parse(resp); + if (evt) { + console.error(evt); + deferred.reject(evt); + return; + } + deferred.resolve(resp); + }); + return deferred.promise; + } + + return service; +}]) + +/** + * Top-level controller. + * Hosts functions needed by all controllers. + */ +.controller('RecordBucketCtrl', + ['$scope','$location','$q','$timeout','$modal', + '$window','egCore','bucketSvc', +function($scope, $location, $q, $timeout, $modal, + $window, egCore, bucketSvc) { + + $scope.bucketSvc = bucketSvc; + $scope.bucket = function() { return bucketSvc.currentBucket } + + // tabs: search, pending, view + $scope.setTab = function(tab) { + $scope.tab = tab; + + // for bucket selector; must be called after route resolve + bucketSvc.fetchUserBuckets(); + }; + + $scope.loadBucketFromMenu = function(item, bucket) { + if (bucket) return $scope.loadBucket(bucket.id()); + } + + $scope.loadBucket = function(id) { + $location.path( + '/cat/bucket/record/' + + $scope.tab + '/' + encodeURIComponent(id)); + } + + $scope.addToBucket = function(recs) { + if (recs.length == 0) return; + bucketSvc.bucketNeedsRefresh = true; + + angular.forEach(recs, + function(rec) { + var item = new egCore.idl.cbrebi(); + item.bucket(bucketSvc.currentBucket.id()); + item.target_biblio_record_entry(rec.id); + egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.container.item.create', + egCore.auth.token(), 'biblio', item + ).then(function(resp) { + + // HACK: add the IDs of the added items so that the size + // of the view list will grow (and update any UI looking at + // the list size). The data stored is inconsistent, but since + // we are forcing a bucket refresh on the next rendering of + // the view pane, the list will be repaired. + bucketSvc.currentBucket.items().push(resp); + }); + } + ); + } + + $scope.openCreateBucketDialog = function() { + $modal.open({ + templateUrl: './cat/bucket/record/t_bucket_create', + controller: + ['$scope', '$modalInstance', function($scope, $modalInstance) { + $scope.focusMe = true; + $scope.ok = function(args) { $modalInstance.close(args) } + $scope.cancel = function () { $modalInstance.dismiss() } + }] + }).result.then(function (args) { + if (!args || !args.name) return; + bucketSvc.createBucket(args.name, args.desc).then( + function(id) { + if (!id) return; + bucketSvc.viewList = []; + bucketSvc.allBuckets = []; // reset + bucketSvc.currentBucket = null; + $location.path( + '/cat/bucket/record/' + $scope.tab + '/' + id); + } + ); + }); + } + + $scope.openEditBucketDialog = function() { + $modal.open({ + templateUrl: './cat/bucket/record/t_bucket_edit', + controller: + ['$scope', '$modalInstance', function($scope, $modalInstance) { + $scope.focusMe = true; + $scope.args = { + name : bucketSvc.currentBucket.name(), + desc : bucketSvc.currentBucket.description(), + pub : bucketSvc.currentBucket.pub() == 't' + }; + $scope.ok = function(args) { + if (!args) return; + $scope.actionPending = true; + args.pub = args.pub ? 't' : 'f'; + // close the dialog after edit has completed + bucketSvc.editBucket(args).then( + function() { $modalInstance.close() }); + } + $scope.cancel = function () { $modalInstance.dismiss() } + }] + }) + } + + + // opens the delete confirmation and deletes the current + // bucket if the user confirms. + $scope.openDeleteBucketDialog = function() { + $modal.open({ + templateUrl: './cat/bucket/record/t_bucket_delete', + controller : + ['$scope', '$modalInstance', function($scope, $modalInstance) { + $scope.bucket = function() { return bucketSvc.currentBucket } + $scope.ok = function() { $modalInstance.close() } + $scope.cancel = function() { $modalInstance.dismiss() } + }] + }).result.then(function () { + bucketSvc.deleteBucket(bucketSvc.currentBucket.id()) + .then(function() { + bucketSvc.allBuckets = []; + $location.path('/cat/bucket/record/view'); + }); + }); + } + + // retrieves the requested bucket by ID + $scope.openSharedBucketDialog = function() { + $modal.open({ + templateUrl: './cat/bucket/record/t_load_shared', + controller : + ['$scope', '$modalInstance', function($scope, $modalInstance) { + $scope.focusMe = true; + $scope.ok = function(args) { + if (args && args.id) { + $modalInstance.close(args.id) + } + } + $scope.cancel = function() { $modalInstance.dismiss() } + }] + }).result.then(function(id) { + // RecordBucketCtrl $scope is not inherited by the + // modal, so we need to call loadBucket from the + // promise resolver. + $scope.loadBucket(id); + }); + } + + // opens the record export dialog + $scope.openExportBucketDialog = function() { + $modal.open({ + templateUrl: './cat/bucket/record/t_bucket_export', + controller : + ['$scope', '$modalInstance', function($scope, $modalInstance) { + $scope.args = {format : 'XML', encoding : 'UTF-8'}; // defaults + $scope.ok = function(args) { $modalInstance.close(args) } + $scope.cancel = function() { $modalInstance.dismiss() } + }] + }).result.then(function (args) { + if (!args) return; + args.containerid = bucketSvc.currentBucket.id(); + + var url = '/exporter?containerid=' + args.containerid + + '&format=' + args.format + '&encoding=' + args.encoding; + + if (args.holdings) url += '&holdings=1'; + + // TODO: improve auth cookie handling so this isn't necessary. + // today the cookie path is too specific (/eg/staff) for non-staff + // UIs to access it. See services/auth.js + url += '&ses=' + egCore.auth.token(); + + $timeout(function() { $window.open(url) }); + }); + } +}]) + +.controller('SearchCtrl', + ['$scope','$routeParams','egCore','bucketSvc', +function($scope, $routeParams, egCore , bucketSvc) { + + $scope.setTab('search'); + $scope.focusMe = true; + var idQueryHash = {}; + + function generateQuery() { + if (bucketSvc.queryRecords.length) + return {id : bucketSvc.queryRecords}; + else + return null; + } + + $scope.gridControls = { + setQuery : function() {return generateQuery()}, + setSort : function() {return ['id']} + } + + // add selected items directly to the pending list + $scope.addToPending = function(recs) { + angular.forEach(recs, function(rec) { + if (bucketSvc.pendingList.filter( // remove dupes + function(r) {return r.id == rec.id}).length) return; + bucketSvc.pendingList.push(rec); + }); + } + + $scope.search = function() { + $scope.searchList = []; + $scope.searchInProgress = true; + bucketSvc.queryRecords = []; + + egCore.net.request( + 'open-ils.search', + 'open-ils.search.biblio.multiclass.query', { + limit : 500 // meh + }, bucketSvc.queryString, true + ).then(function(resp) { + bucketSvc.queryRecords = + resp.ids.map(function(id){return id[0]}); + $scope.gridControls.setQuery(generateQuery()); + })['finally'](function() { + $scope.searchInProgress = false; + }); + } + + if ($routeParams.id && + (!bucketSvc.currentBucket || + bucketSvc.currentBucket.id() != $routeParams.id)) { + // user has accessed this page cold with a bucket ID. + // fetch the bucket for display, then set the totalCount + // (also for display), but avoid fully fetching the bucket, + // since it's premature, in this UI. + bucketSvc.fetchBucket($routeParams.id); + } +}]) + +.controller('PendingCtrl', + ['$scope','$routeParams','bucketSvc','egGridDataProvider', +function($scope, $routeParams, bucketSvc , egGridDataProvider) { + $scope.setTab('pending'); + + var provider = egGridDataProvider.instance({}); + provider.get = function(offset, count) { + return provider.arrayNotifier( + bucketSvc.pendingList, offset, count); + } + $scope.gridDataProvider = provider; + + $scope.resetPendingList = function() { + bucketSvc.pendingList = []; + } + + + if ($routeParams.id && + (!bucketSvc.currentBucket || + bucketSvc.currentBucket.id() != $routeParams.id)) { + // user has accessed this page cold with a bucket ID. + // fetch the bucket for display, then set the totalCount + // (also for display), but avoid fully fetching the bucket, + // since it's premature, in this UI. + bucketSvc.fetchBucket($routeParams.id); + } +}]) + +.controller('ViewCtrl', + ['$scope','$q','$routeParams','bucketSvc', +function($scope, $q , $routeParams, bucketSvc) { + + $scope.setTab('view'); + $scope.bucketId = $routeParams.id; + + var query; + $scope.gridControls = { + setQuery : function(q) { + if (q) query = q; + return query; + } + }; + + function drawBucket() { + return bucketSvc.fetchBucket($scope.bucketId).then( + function(bucket) { + var ids = bucket.items().map( + function(i){return i.target_biblio_record_entry()} + ); + if (ids.length) { + $scope.gridControls.setQuery({id : ids}); + } else { + $scope.gridControls.setQuery({}); + } + } + ); + } + + $scope.detachRecords = function(records) { + var promises = []; + angular.forEach(records, function(rec) { + var item = bucketSvc.currentBucket.items().filter( + function(i) { + return (i.target_biblio_record_entry() == rec.id) + } + ); + if (item.length) + promises.push(bucketSvc.detachRecord(item[0].id())); + }); + + bucketSvc.bucketNeedsRefresh = true; + return $q.all(promises).then(drawBucket); + } + + // fetch the bucket; on error show the not-allowed message + if ($scope.bucketId) + drawBucket()['catch'](function() { $scope.forbidden = true }); +}]) diff --git a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js new file mode 100644 index 0000000000..8c06ad5c1a --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js @@ -0,0 +1,209 @@ +/** + * TPAC Frame App + * + * currently, this app doesn't use routes for each sub-ui, because + * reloading the catalog each time is sloooow. better so far to + * swap out divs w/ ng-if / ng-show / ng-hide as needed. + * + */ + +angular.module('egCatalogApp', ['ui.bootstrap','ngRoute','egCoreMod','egGridMod']) + +.config(function($routeProvider, $locationProvider, $compileProvider) { + $locationProvider.html5Mode(true); + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export + + var resolver = {delay : + ['egStartup', function(egStartup) {return egStartup.go()}]} + + $routeProvider.when('/cat/catalog/index', { + templateUrl: './cat/catalog/t_catalog', + controller: 'CatalogCtrl', + resolve : resolver + }); + + // create some catalog page-specific mappings + $routeProvider.when('/cat/catalog/record/:record_id', { + templateUrl: './cat/catalog/t_catalog', + controller: 'CatalogCtrl', + resolve : resolver + }); + + // create some catalog page-specific mappings + $routeProvider.when('/cat/catalog/record/:record_id/:record_tab', { + templateUrl: './cat/catalog/t_catalog', + controller: 'CatalogCtrl', + resolve : resolver + }); + + $routeProvider.otherwise({redirectTo : '/cat/catalog/index'}); +}) + + +/** + * */ +.controller('CatalogCtrl', + ['$scope','$routeParams','$location','$q','egCore','egHolds', + 'egGridDataProvider','egHoldGridActions', +function($scope , $routeParams , $location , $q , egCore , egHolds, + egGridDataProvider , egHoldGridActions) { + + // set record ID on page load if available... + $scope.record_id = $routeParams.record_id; + + // also set it when the iframe changes to a new record + $scope.handle_page = function(url) { + + if (!url || url == 'about:blank') { + // nothing loaded. If we already have a record ID, leave it. + return; + } + + var match = url.match(/\/+opac\/+record\/+(\d+)/); + if (match) { + $scope.record_id = match[1]; + + // force the record_id to show up in the page. + // not sure why a $digest isn't occuring here. + try { $scope.$apply() } catch(E) {} + } else { + delete $scope.record_id; + } + } + + // xulG catalog handlers + $scope.handlers = { } + + // ------------------------------------------------------------------ + // Holds + var provider = egGridDataProvider.instance({}); + $scope.hold_grid_data_provider = provider; + $scope.grid_actions = egHoldGridActions; + $scope.hold_grid_controls = {}; + + var hold_ids = []; // current list of holds + function fetchHolds(offset, count) { + var ids = hold_ids.slice(offset, offset + count); + return egHolds.fetch_holds(ids).then(null, null, + function(hold_data) { + return hold_data; + } + ); + } + + provider.get = function(offset, count) { + if ($scope.record_tab != 'holds') return $q.when(); + var deferred = $q.defer(); + hold_ids = []; // no caching ATM + + // fetch the IDs + egCore.net.request( + 'open-ils.circ', + 'open-ils.circ.holds.retrieve_all_from_title', + egCore.auth.token(), $scope.record_id, + {pickup_lib : $scope.pickup_ou.id()} + ).then( + function(hold_data) { + angular.forEach(hold_data, function(list, type) { + hold_ids = hold_ids.concat(list); + }); + fetchHolds(offset, count).then( + deferred.resolve, null, deferred.notify); + } + ); + + return deferred.promise; + } + + $scope.detail_view = function(action, user_data, items) { + if (h = items[0]) { + $scope.detail_hold_id = h.hold.id(); + } + } + + $scope.list_view = function(items) { + $scope.detail_hold_id = null; + } + + // refresh the list of record holds when the pickup lib is changed. + $scope.pickup_ou = egCore.org.get(egCore.auth.user().ws_ou()); + $scope.pickup_ou_changed = function(org) { + $scope.pickup_ou = org; + provider.refresh(); + } + + $scope.print_holds = function() { + var holds = []; + angular.forEach($scope.hold_grid_controls.allItems(), function(item) { + holds.push({ + hold : egCore.idl.toHash(item.hold), + patron_last : item.patron_last, + patron_alias : item.patron_alias, + patron_barcode : item.patron_barcode, + copy : egCore.idl.toHash(item.copy), + volume : egCore.idl.toHash(item.volume), + title : item.mvr.title(), + author : item.mvr.author() + }); + }); + + egCore.print.print({ + context : 'receipt', + template : 'holds_for_bib', + scope : {holds : holds} + }); + } + + $scope.mark_hold_transfer_dest = function() { + egCore.hatch.setLocalItem( + 'eg.circ.hold.title_transfer_target', $scope.record_id); + } + + // UI presents this option as "all holds" + $scope.transfer_holds_to_marked = function() { + var hold_ids = $scope.hold_grid_controls.allItems().map( + function(hold_data) {return hold_data.hold.id()}); + egHolds.transfer_to_marked_title(hold_ids); + } + + // ------------------------------------------------------------------ + // Initialize the selected tab + + function init_cat_url() { + // Set the initial catalog URL. This only happens once. + // The URL is otherwise generated through user navigation. + if ($scope.catalog_url) return; + + var url = $location.absUrl().replace(/\/staff.*/, '/opac/advanced'); + + // A record ID in the path indicates a request for the record- + // specific page. + if ($routeParams.record_id) { + url = url.replace(/advanced/, '/record/' + $scope.record_id); + } + + $scope.catalog_url = url; + } + + $scope.set_record_tab = function(tab) { + $scope.record_tab = tab; + + switch(tab) { + + case 'catalog': + init_cat_url(); + break; + + case 'holds': + $scope.detail_hold_record_id = $scope.record_id; + // refresh the holds grid + provider.refresh(); + break; + } + } + + var tab = $routeParams.record_tab || 'catalog'; + $scope.set_record_tab(tab); + +}]) + diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/app.js b/Open-ILS/web/js/ui/default/staff/cat/item/app.js new file mode 100644 index 0000000000..82ed92380d --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/cat/item/app.js @@ -0,0 +1,540 @@ +/** + * Item Display + */ + +angular.module('egItemStatus', + ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod']) + +.config(function($routeProvider, $locationProvider, $compileProvider) { + $locationProvider.html5Mode(true); + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export + + var resolver = {delay : function(egStartup) {return egStartup.go()}}; + + // search page shows the list view by default + $routeProvider.when('/cat/item/search', { + templateUrl: './cat/item/t_list', + controller: 'ListCtrl', + resolve : resolver + }); + + $routeProvider.when('/cat/item/:id', { + templateUrl: './cat/item/t_view', + controller: 'ViewCtrl', + resolve : resolver + }); + + $routeProvider.when('/cat/item/:id/:tab', { + templateUrl: './cat/item/t_view', + controller: 'ViewCtrl', + resolve : resolver + }); + + // default page / bucket view + $routeProvider.otherwise({redirectTo : '/cat/item/search'}); +}) + +.factory('itemSvc', + ['egCore', +function(egCore) { + + var service = { + copies : [], // copy barcode search results + index : 0 // search grid index + }; + + service.flesh = { + flesh : 3, + flesh_fields : { + acp : ['call_number','location','status','location'], + acn : ['record','prefix','suffix'], + bre : ['simple_record','creator','editor'] + }, + select : { + // avoid fleshing MARC on the bre + // note: don't add simple_record.. not sure why + bre : ['id','tcn_value','creator','editor'], + } + } + + // resolved with the last received copy + service.fetch = function(barcode, id, noListDupes) { + var promise; + + if (barcode) { + promise = egCore.pcrud.search('acp', + {barcode : barcode, deleted : 'f'}, service.flesh); + } else { + promise = egCore.pcrud.retrieve('acp', id, service.flesh); + } + + var lastRes; + return promise.then( + function() {return lastRes}, + null, // error + + // notify reads the stream of copies, one at a time. + function(copy) { + + var flatCopy; + if (noListDupes) { + // use the existing copy if possible + flatCopy = service.copies.filter( + function(c) {return c.id == copy.id()})[0]; + } + + if (!flatCopy) { + flatCopy = egCore.idl.toHash(copy, true); + flatCopy.index = service.index++; + service.copies.unshift(flatCopy); + } + + return lastRes = { + copy : copy, + index : flatCopy.index + } + } + ); + } + + return service; +}]) + +/** + * Search bar along the top of the page. + * Parent scope for list and detail views + */ +.controller('SearchCtrl', + ['$scope','$location','egCore','egGridDataProvider','itemSvc', +function($scope , $location , egCore , egGridDataProvider , itemSvc) { + $scope.args = {}; // search args + + // sub-scopes (search / detail-view) apply their version + // of retrieval function to $scope.context.search + // and display toggling via $scope.context.toggleDisplay + $scope.context = { + selectBarcode : true + }; + + $scope.toggleView = function($event) { + $scope.context.toggleDisplay(); + $event.preventDefault(); // avoid form submission + } +}]) + +/** + * List view - grid stuff + */ +.controller('ListCtrl', + ['$scope','$q','$location','$timeout','egCore','egGridDataProvider','itemSvc', +function($scope , $q , $location , $timeout , egCore , egGridDataProvider , itemSvc) { + $scope.context.page = 'list'; + + /* + var provider = egGridDataProvider.instance(); + provider.get = function(offset, count) { + } + */ + + $scope.gridDataProvider = egGridDataProvider.instance({ + get : function(offset, count) { + //return provider.arrayNotifier(itemSvc.copies, offset, count); + return this.arrayNotifier(itemSvc.copies, offset, count); + } + }); + + // If a copy was just displayed in the detail view, ensure it's + // focused in the list view. + var selected = false; + var copyGrid = $scope.gridControls = { + itemRetrieved : function(item) { + if (selected || !itemSvc.copy) return; + if (itemSvc.copy.id() == item.id) { + copyGrid.selectItems([item.index]); + selected = true; + } + } + }; + + $scope.$watch('barcodesFromFile', function(newVal, oldVal) { + if (newVal && newVal != oldVal) { + $scope.args.barcode = ''; + var barcodes = []; + + angular.forEach(newVal.split(/\n/), function(line) { + if (!line) return; + // scrub any trailing spaces or commas from the barcode + line = line.replace(/(.*?)($|\s.*|,.*)/,'$1'); + barcodes.push(line); + }); + + itemSvc.fetch(barcodes).then( + function() { + copyGrid.refresh(); + copyGrid.selectItems([itemSvc.copies[0].index]); + } + ); + } + }); + + $scope.context.search = function(args) { + if (!args.barcode) return; + $scope.context.itemNotFound = false; + itemSvc.fetch(args.barcode).then(function(res) { + if (res) { + copyGrid.refresh(); + copyGrid.selectItems([res.index]); + $scope.args.barcode = ''; + } else { + $scope.context.itemNotFound = true; + } + $scope.context.selectBarcode = true; + }) + } + + $scope.context.toggleDisplay = function() { + var item = copyGrid.selectedItems()[0]; + if (item) + $location.path('/cat/item/' + item.id); + } + + $scope.context.show_triggered_events = function() { + var item = copyGrid.selectedItems()[0]; + if (item) + $location.path('/cat/item/' + item.id + '/triggered_events'); + } + +}]) + +/** + * Detail view -- shows one copy + */ +.controller('ViewCtrl', + ['$scope','$q','$location','$routeParams','egCore','itemSvc','egBilling', +function($scope , $q , $location , $routeParams , egCore , itemSvc , egBilling) { + var copyId = $routeParams.id; + $scope.tab = $routeParams.tab || 'summary'; + $scope.context.page = 'detail'; + + // use the cached record info + if (itemSvc.copy) + $scope.summaryRecord = itemSvc.copy.call_number().record(); + + function loadCopy(barcode) { + $scope.context.itemNotFound = false; + + // Avoid re-fetching the same copy while jumping tabs. + // In addition to being quicker, this helps to avoid flickering + // of the top panel which is always visible in the detail view. + // + // 'barcode' represents the loading of a new item - refetch it + // regardless of whether it matches the current item. + if (!barcode && itemSvc.copy && itemSvc.copy.id() == copyId) { + $scope.copy = itemSvc.copy; + $scope.recordId = itemSvc.copy.call_number().record().id(); + return $q.when(); + } + + delete $scope.copy; + delete itemSvc.copy; + + var deferred = $q.defer(); + itemSvc.fetch(barcode, copyId, true).then(function(res) { + $scope.context.selectBarcode = true; + + if (!res) { + copyId = null; + $scope.context.itemNotFound = true; + deferred.reject(); // avoid propagation of data fetch calls + return; + } + + var copy = res.copy; + itemSvc.copy = copy; + + + $scope.copy = copy; + $scope.recordId = copy.call_number().record().id(); + $scope.summaryRecord = itemSvc.copy.call_number().record(); + $scope.args.barcode = ''; + + // locally flesh org units + copy.circ_lib(egCore.org.get(copy.circ_lib())); + copy.call_number().owning_lib( + egCore.org.get(copy.call_number().owning_lib())); + + var r = copy.call_number().record(); + if (r.owner()) r.owner(egCore.org.get(r.owner())); + + // make boolean for auto-magic true/false display + angular.forEach( + ['ref','opac_visible','holdable','floating','circulate'], + function(field) { copy[field](Boolean(copy[field]() == 't')) } + ); + + // finally, if this is a different copy, redirect. + // Note that we flesh first since the copy we just + // fetched will be used after the redirect. + if (copyId && copyId != copy.id()) { + // if a new barcode is scanned in the detail view, + // update the url to match the ID of the new copy + $location.path('/cat/item/' + copy.id() + '/' + $scope.tab); + deferred.reject(); // avoid propagation of data fetch calls + return; + } + copyId = copy.id(); + + deferred.resolve(); + }); + + return deferred.promise; + } + + // if loadPrev load the two most recent circulations + function loadCurrentCirc(loadPrev) { + delete $scope.circ; + delete $scope.circ_summary; + delete $scope.prev_circ_summary; + if (!copyId) return; + + egCore.pcrud.search('circ', + {target_copy : copyId}, + { flesh : 2, + flesh_fields : { + circ : [ + 'usr', + 'workstation', + 'checkin_workstation', + 'duration_rule', + 'max_fine_rule', + 'recurring_fine_rule' + ], + au : ['card'] + }, + order_by : {circ : 'xact_start desc'}, + limit : 1 + } + + ).then(null, null, function(circ) { + $scope.circ = circ; + + // load the chain for this circ + egCore.net.request( + 'open-ils.circ', + 'open-ils.circ.renewal_chain.retrieve_by_circ.summary', + egCore.auth.token(), $scope.circ.id() + ).then(function(summary) { + $scope.circ_summary = summary.summary; + }); + + if (!loadPrev) return; + + // load the chain for the previous circ, plus the user + egCore.net.request( + 'open-ils.circ', + 'open-ils.circ.prev_renewal_chain.retrieve_by_circ.summary', + egCore.auth.token(), $scope.circ.id() + + ).then(null, null, function(summary) { + $scope.prev_circ_summary = summary.summary; + + egCore.pcrud.retrieve('au', summary.usr, + {flesh : 1, flesh_fields : {au : ['card']}}) + + .then(function(user) { + $scope.prev_circ_usr = user; + }); + }); + }); + } + + var maxHistory; + function fetchMaxCircHistory() { + if (maxHistory) return $q.when(maxHistory); + return egCore.org.settings( + 'circ.item_checkout_history.max') + .then(function(set) { + maxHistory = set['circ.item_checkout_history.max'] || 4; + return maxHistory; + }); + } + + $scope.addBilling = function(circ) { + egBilling.showBillDialog({ + xact_id : circ.id(), + patron : circ.usr() + }); + } + + function loadCircHistory() { + $scope.circ_list = []; + + var copy_org = + itemSvc.copy.call_number().id() == -1 ? + itemSvc.copy.circ_lib().id() : + itemSvc.copy.call_number().owning_lib().id() + + // there is an extra layer of permissibility over circ + // history views + egCore.perm.hasPermAt('VIEW_COPY_CHECKOUT_HISTORY', true) + .then(function(orgIds) { + + if (orgIds.indexOf(copy_org) == -1) { + console.log('User is not allowed to view circ history'); + return $q.when(0); + } + + return fetchMaxCircHistory(); + + }).then(function(count) { + + egCore.pcrud.search('circ', + {target_copy : copyId}, + { flesh : 2, + flesh_fields : { + circ : [ + 'usr', + 'workstation', + 'checkin_workstation', + 'recurring_fine_rule' + ], + au : ['card'] + }, + order_by : {circ : 'xact_start desc'}, + limit : count + } + + ).then(null, null, function(circ) { + + // flesh circ_lib locally + circ.circ_lib(egCore.org.get(circ.circ_lib())); + circ.checkin_lib(egCore.org.get(circ.checkin_lib())); + $scope.circ_list.push(circ); + }); + }); + } + + + function loadCircCounts() { + + delete $scope.circ_counts; + $scope.total_circs = 0; + $scope.total_circs_this_year = 0; + $scope.total_circs_prev_year = 0; + if (!copyId) return; + + egCore.pcrud.search('circbyyr', + {copy : copyId}, null, {atomic : true}) + + .then(function(counts) { + $scope.circ_counts = counts; + + angular.forEach(counts, function(count) { + $scope.total_circs += Number(count.count()); + }); + + var this_year = counts.filter(function(c) { + return c.year() == new Date().getFullYear(); + }); + + $scope.total_circs_this_year = + this_year.length ? this_year[0].count() : 0; + + var prev_year = counts.filter(function(c) { + return c.year() == new Date().getFullYear() - 1; + }); + + $scope.total_circs_prev_year = + prev_year.length ? prev_year[0].count() : 0; + + }); + } + + function loadHolds() { + delete $scope.hold; + if (!copyId) return; + + egCore.pcrud.search('ahr', + { current_copy : copyId, + cancel_time : null, + fulfillment_time : null, + capture_time : {'<>' : null} + }, { + flesh : 2, + flesh_fields : { + ahr : ['requestor', 'usr'], + au : ['card'] + } + } + ).then(null, null, function(hold) { + $scope.hold = hold; + hold.pickup_lib(egCore.org.get(hold.pickup_lib())); + if (hold.current_shelf_lib()) { + hold.current_shelf_lib( + egCore.org.get(hold.current_shelf_lib())); + } + hold.behind_desk(Boolean(hold.behind_desk() == 't')); + }); + } + + function loadTransits() { + delete $scope.transit; + delete $scope.hold_transit; + if (!copyId) return; + + egCore.pcrud.search('atc', + {target_copy : copyId}, + {order_by : {atc : 'source_send_time DESC'}} + + ).then(null, null, function(transit) { + $scope.transit = transit; + transit.source(egCore.org.get(transit.source())); + transit.dest(egCore.org.get(transit.dest())); + }) + } + + + // we don't need all data on all tabs, so fetch what's needed when needed. + function loadTabData() { + switch($scope.tab) { + case 'summary': + loadCurrentCirc(); + loadCircCounts(); + break; + + case 'circs': + loadCurrentCirc(true); + break; + + case 'circ_list': + loadCircHistory(); + break; + + case 'holds': + loadHolds() + loadTransits(); + break; + + case 'triggered_events': + var url = $location.absUrl().replace(/\/staff.*/, '/actor/user/event_log'); + url += '?copy_id=' + encodeURIComponent(copyId); + $scope.triggered_events_url = url; + $scope.funcs = {}; + } + } + + $scope.context.toggleDisplay = function() { + $location.path('/cat/item/search'); + } + + // handle the barcode scan box, which will replace our current copy + $scope.context.search = function(args) { + loadCopy(args.barcode).then(loadTabData); + } + + $scope.context.show_triggered_events = function() { + $location.path('/cat/item/' + copyId + '/triggered_events'); + } + + loadCopy().then(loadTabData); +}]) diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/missing_pieces.js b/Open-ILS/web/js/ui/default/staff/cat/item/missing_pieces.js new file mode 100644 index 0000000000..06fad21abd --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/cat/item/missing_pieces.js @@ -0,0 +1,123 @@ +angular.module('egItemMissingPieces', + ['ngRoute', 'ui.bootstrap', 'egCoreMod','egUiMod']) + +.controller('MissingPiecesCtrl', + ['$scope','$q','$window','$location','egCore','egConfirmDialog','egAlertDialog','egCirc', +function($scope , $q , $window , $location , egCore , egConfirmDialog , egAlertDialog , egCirc) { + + $scope.selectMe = true; // focus text input + $scope.args = {}; + + function get_copy(barcode) { + + return egCore.net.request( + 'open-ils.actor', + 'open-ils.actor.get_barcodes', + egCore.auth.token(), egCore.auth.user().ws_ou(), + 'asset', barcode) + + .then(function(resp) { // get_barcodes + + if (evt = egCore.evt.parse(resp)) { + console.error(evt.toString()); + return $q.reject(); + } + + if (!resp || !resp[0]) { + $scope.bcNotFound = barcode; + $scope.selectMe = true; + return $q.reject(); + } + + return egCore.pcrud.search('acp', {id : resp[0].id}, { + flesh : 3, + flesh_fields : { + acp : ['call_number'], + acn : ['record'], + bre : ['simple_record'] + }, + select : { + // avoid fleshing MARC on the bre + // note: don't add simple_record.. not sure why + bre : ['id'] + } + }) + }) + } + + function mark_missing_pieces(copy) { + + egConfirmDialog.open( + egCore.strings.CONFIRM_MARK_MISSING_TITLE, + egCore.strings.CONFIRM_MARK_MISSING_BODY, { + barcode : copy.barcode(), + title : copy.call_number().record().simple_record().title() + + }).result.then(function() { + + // kick off mark missing + return egCore.net.request( + 'open-ils.circ', + 'open-ils.circ.mark_item_missing_pieces', + egCore.auth.token(), copy.id() + ) + + }).then(function(resp) { + var evt = egCore.evt.parse(resp); // should always produce event + + if (evt.textcode == 'ACTION_CIRCULATION_NOT_FOUND') { + return egAlertDialog.open( + egCore.strings.CIRC_NOT_FOUND, {barcode : copy.barcode()}); + } + + var payload = evt.payload; + + // TODO: open copy editor inline? new tab? + + // print the missing pieces slip + var promise = $q.when(); + if (payload.slip) { + // wait for completion, since it may spawn a confirm dialog + promise = egCore.print.print({ + context : 'default', + content_type : 'text/html', + content : payload.slip.template_output().data() + }); + } + + if (payload.letter) { + $scope.letter = payload.letter.template_output().data(); + } + + // apply patron penalty + if (payload.circ) { + promise.then(function() { + egCirc.create_penalty(payload.circ.usr()) + }); + } + + }); + } + + $scope.print_letter = function() { + egCore.print.print({ + context : 'mail', + content_type : 'text/plain', + content : $scope.letter + }); + } + + // find the item by barcode, then proceed w/ missing pieces + $scope.submitBarcode = function(args) { + + $scope.bcNotFound = null; + if (!args.barcode) return; + + $scope.selectMe = false; + $scope.letter = null; + + get_copy(args.barcode).then(mark_missing_pieces); + } + +}]) + diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/replace_barcode/app.js b/Open-ILS/web/js/ui/default/staff/cat/item/replace_barcode/app.js new file mode 100644 index 0000000000..33d1cb6c61 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/cat/item/replace_barcode/app.js @@ -0,0 +1,39 @@ +/** + * Item Display + */ + +angular.module('egItemReplaceBarcode', + ['ngRoute', 'ui.bootstrap', 'egCoreMod','egUiMod']) + +.controller('ReplaceItemBarcodeCtrl', + ['$scope','egCore', +function($scope , egCore) { + egCore.startup.go(); + + $scope.focusBarcode = true; + + $scope.updateBarcode = function() { + $scope.copyNotFound = false; + $scope.updateOK = false; + + egCore.pcrud.search('acp', + {deleted : 'f', barcode : $scope.barcode1}) + .then(function(copy) { + + if (!copy) { + $scope.focusBarcode = true; + $scope.copyNotFound = true; + return; + } + + $scope.copyId = copy.id(); + copy.barcode($scope.barcode2); + + egCore.pcrud.update(copy).then(function(stat) { + $scope.updateOK = stat; + $scope.focusBarcode = true; + }); + }); + } +}]); + diff --git a/Open-ILS/web/js/ui/default/staff/cat/services/record.js b/Open-ILS/web/js/ui/default/staff/cat/services/record.js new file mode 100644 index 0000000000..6975b273b7 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/cat/services/record.js @@ -0,0 +1,106 @@ +/** + * Simple directive for rending the HTML view of a bib record. + * + * + * + * The value of myRecordIdScopeVariable is watched internally and the + * record is updated to match. + */ +angular.module('egCoreMod') + +.directive('egRecordHtml', function() { + return { + restrict : 'AE', + scope : {recordId : '='}, + link : function(scope, element, attrs) { + scope.element = angular.element(element); + + // kill refs to destroyed DOM elements + element.bind("$destroy", function() { + delete scope.element; + }); + }, + controller : + ['$scope','egCore', + function($scope , egCore) { + + function loadRecordHtml() { + egCore.net.request( + 'open-ils.search', + 'open-ils.search.biblio.record.html', + $scope.recordId + ).then(function(html) { + if (!html) return; + + // Remove those pesky non-i8n labels / actions. + // Note: for printing, use the browser print page + // option. The end result is the same. + html = html.replace( + /' + + '' + + '
', + + controller : ['$scope','$timeout','egOrg','egAuth', + function($scope , $timeout , egOrg , egAuth) { + + // avoid linking the full fleshed tree to the scope by + // tossing in a flattened list. + $scope.orgList = egOrg.list().map(function(org) { + return { + id : org.id(), + shortname : org.shortname(), + depth : org.ou_type().depth() + } + }); + + $scope.getSelectedName = function() { + if ($scope.selected) + return $scope.selected.shortname(); + return $scope.label; + } + + $scope.orgChanged = function(org) { + $scope.selected = egOrg.get(org.id); + if ($scope.onchange) $scope.onchange($scope.selected); + } + + if (!$scope.selected) + $scope.selected = egOrg.get(egAuth.user().ws_ou()); + }] + } +}) + + +/* +http://stackoverflow.com/questions/18061757/angular-js-and-html5-date-input-value-how-to-get-firefox-to-show-a-readable-d + +This directive allows us to use html5 input type="date" (for Chrome) and +gracefully fall back to a regular ISO text input for Firefox. +It also allows us to abstract away some browser finickiness. +*/ +.directive( + 'egDateInput', + function(dateFilter) { + return { + require: 'ngModel', + template: '', + replace: true, + link: function(scope, elm, attrs, ngModelCtrl) { + + // since this is a date-only selector, set the time + // portion to 00:00:00, which should better match the + // user's expectations. Note this allows us to retain + // the timezone. + function strip_time(date) { + if (!date) date = new Date(); + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + date.setMilliseconds(0); + return date; + } + + ngModelCtrl.$formatters.unshift(function (modelValue) { + // apply strip_time here in case the user never + // modifies the date value. + return dateFilter(strip_time(modelValue), 'yyyy-MM-dd'); + }); + + ngModelCtrl.$parsers.unshift(function(viewValue) { + return strip_time(new Date(viewValue)); + }); + }, + }; +}) diff --git a/Open-ILS/web/js/ui/default/staff/services/user.js b/Open-ILS/web/js/ui/default/staff/services/user.js new file mode 100644 index 0000000000..0ed5cac038 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/services/user.js @@ -0,0 +1,56 @@ +/** + * Service for fetching fleshed user objects. + */ + +angular.module('egUserMod', ['egCoreMod']) + +.factory('egUser', + ['$q','$timeout','egNet','egAuth','egOrg', +function($q, $timeout, egNet, egAuth, egOrg) { + + var service = { + defaultFleshFields : [ + 'card', + 'standing_penalties', + 'addresses', + 'billing_address', + 'mailing_address', + 'stat_cat_entries', + 'usr_activity' + ] + }; + + service.get = function(userId, args) { + var deferred = $q.defer(); + + var fields = service.defaultFleshFields; + if (args) { + if (args.useFields) { + // overridde flesh fields + fields = args.useFields; + } + if (args.addFields) { + // append flesh fields + fields = fields.concat(args.addFields); + } + } + + egNet.request( + 'open-ils.actor', + 'open-ils.actor.user.fleshed.retrieve', + egAuth.token(), userId, fields).then( + function(user) { + if (user && user.classname == 'au') { + deferred.resolve(user); + } else { + deferred.reject(user); + } + } + ); + + return deferred.promise; + }; + + return service; +}]); + diff --git a/Open-ILS/web/js/ui/default/staff/test/data/eg_mock.js b/Open-ILS/web/js/ui/default/staff/test/data/eg_mock.js new file mode 100644 index 0000000000..956d46ccb4 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/data/eg_mock.js @@ -0,0 +1,50 @@ +/** + * Mock data required by multiple unit tests. + */ + +window._eg_mock_data = { + + // builds a mock org unit tree fleshed with ou_types and + // absorbs the tree into egEnv + orgTree : function(egIDL, egEnv) { + var type1 = new egIDL.aout(); + type1.id(1); + type1.depth(0); + + var type2 = new egIDL.aout(); + type2.id(2); + type2.depth(1); + type2.parent(1); + + var type3 = new egIDL.aout(); + type3.id(3); + type3.depth(2); + type3.parent(2); + + var org1 = new egIDL.aou(); + org1.id(1); + org1.ou_type(type1); + + var org2 = new egIDL.aou(); + org2.id(2); + org2.parent_ou(1); + org2.ou_type(type2); + + var org3 = new egIDL.aou(); + org3.id(3); + org3.parent_ou(1); + org3.ou_type(type2); + + var org4 = new egIDL.aou(); + org4.id(4); + org4.parent_ou(2); + org4.ou_type(type3); + + org1.children([org2, org3]); + org2.children([org4]); + org3.children([]); + org4.children([]); + + egEnv.absorbTree(org1, 'aou'); + } +} diff --git a/Open-ILS/web/js/ui/default/staff/test/data/idl2js.pl b/Open-ILS/web/js/ui/default/staff/test/data/idl2js.pl new file mode 100644 index 0000000000..fbf998fdbb --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/data/idl2js.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl +use strict; use warnings; +use XML::LibXML; +use XML::LibXSLT; +my $out_file = 'IDL2js.js'; +my $idl_file = '../../../../../../../examples/fm_IDL.xml'; +my $xsl_file = '/openils/var/xsl/fm_IDL2js.xsl'; # FIXME: hard-coded path + +my $xslt = XML::LibXSLT->new(); +my $style_doc = XML::LibXML->load_xml(location => $xsl_file, no_cdata=>1); +my $stylesheet = $xslt->parse_stylesheet($style_doc); +my $idl_doc = XML::LibXML->load_xml(location => $idl_file); +my $results = $stylesheet->transform($idl_doc); +my $output = $stylesheet->output_as_bytes($results); + +open(IDL, ">$out_file") or die "Cannot open IDL2js file $out_file : $!\n"; + +print IDL $output; + +close(IDL); + + diff --git a/Open-ILS/web/js/ui/default/staff/test/karma.conf.js b/Open-ILS/web/js/ui/default/staff/test/karma.conf.js new file mode 100644 index 0000000000..a27ca66292 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/karma.conf.js @@ -0,0 +1,88 @@ +module.exports = function(config){ + config.set({ + basePath : '../', + + // config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + files : [ + 'build/js/angular.min.js', + 'build/js/angular-route.min.js', + 'bower_components/angular-mocks/angular-mocks.js', // testing only + 'build/js/ui-bootstrap.min.js', + 'build/js/hotkeys.min.js', + /* OpenSRF must be installed first */ + '/openils/lib/javascript/md5.js', + '/openils/lib/javascript/JSON_v1.js', + '/openils/lib/javascript/opensrf.js', + '/openils/lib/javascript/opensrf_ws.js', + + // mock data for testing only + 'test/data/IDL2js.js', + 'test/data/eg_mock.js', + + // service/*.js have to be loaded in order + 'services/core.js', + 'services/idl.js', + 'services/strings.js', + 'services/event.js', + 'services/net.js', + 'services/auth.js', + 'services/pcrud.js', + 'services/env.js', + 'services/org.js', + 'services/hatch.js', + 'services/print.js', + 'services/coresvc.js', + 'services/user.js', + 'services/startup.js', + 'services/ui.js', + 'services/statusbar.js', + 'services/grid.js', + 'services/navbar.js', + // load app scripts + 'app.js', + 'circ/**/*.js', + 'cat/**/*.js', + 'admin/**/*.js', + 'test/unit/egIDL.js', // order matters for some of these + 'test/unit/egOrg.js', + 'test/unit/**/*.js' + ], + + // test results reporter to use + // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' + reporters: ['spec'], // detailed report + //reporters: ['progress'], // summary report + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch : false, + + frameworks: ['jasmine'], + + browsers: ['PhantomJS'], + + // web server port + port: 9876, + + /* + coverageReporter: { + type : 'html', + dir : 'coverage/', + }, + + preprocessors: { + '../src/*.js': ['coverage'] + }, + */ + + // If browser does not capture in given timeout [ms], kill it + captureTimeout: 60000, + + // Continuous Integration mode + // if true, it capture browsers, run tests and exit + singleRun: true +})} diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egCore.js b/Open-ILS/web/js/ui/default/staff/test/unit/egCore.js new file mode 100644 index 0000000000..d19a371fdb --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/unit/egCore.js @@ -0,0 +1,18 @@ +'use strict'; + +describe('egCore', function(){ + beforeEach(module('egCoreMod')); + + it('should wrap services', inject(function(egCore, egIDL) { + expect(egCore.idl).toBe(egIDL); + })); + + it('should wrap services', inject(function(egCore, egIDL) { + expect(egCore.auth).not.toBe(egIDL); + })); + + it('should not wrap non-services', inject(function(egCore) { + expect(egCore.junk).not.toBeDefined(); + })); + +}); diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egEvent.js b/Open-ILS/web/js/ui/default/staff/test/unit/egEvent.js new file mode 100644 index 0000000000..7b17653680 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/unit/egEvent.js @@ -0,0 +1,44 @@ +'use strict'; + +describe('egEvent', function(){ + beforeEach(module('egCoreMod')); + + var evt = { + ilsevent: "12345", + pid: "12345", + desc: "Test Event Description", + payload: {test : 'xyz'}, + textcode: "TEST_EVENT", + servertime: "Wed Nov 6 16:05:50 2013" + }; + + it('should parse an event object', inject(function(egEvent) { + expect(egEvent.parse(evt)).not.toBe(null); + })); + + it('should not parse a non-event', inject(function(egEvent) { + expect(egEvent.parse({})).toBe(null); + })); + + it('should not parse a non-event', inject(function(egEvent) { + expect(egEvent.parse({abc : '123'})).toBe(null); + })); + + it('should not parse a non-event', inject(function(egEvent) { + expect(egEvent.parse([])).toBe(null); + })); + + it('should not parse a non-event', inject(function(egEvent) { + expect(egEvent.parse('STRING')).toBe(null); + })); + + it('should not parse a non-event', inject(function(egEvent) { + expect(egEvent.parse(true)).toBe(null); + })); + + it('should stringify an event', inject(function(egEvent) { + expect(egEvent.parse(evt).toString()).toBe( + 'Event: 12345:TEST_EVENT -> Test Event Description') + })); + +}); diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egHomeApp.js b/Open-ILS/web/js/ui/default/staff/test/unit/egHomeApp.js new file mode 100644 index 0000000000..eac4669bd2 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/unit/egHomeApp.js @@ -0,0 +1,21 @@ +'use strict'; + +describe('egHomeControllers', function(){ + beforeEach(module('egHome')); + + /* ---- LoginCtrl ---------------------------------- */ + + var loginCtrl, loginScope; + beforeEach(inject(function ($rootScope, $controller, $location) { + // pass the workstation name via (mock) URL param + $location.search({ws : 'TestWorkstation'}); + + loginScope = $rootScope.$new(); + loginCtrl = $controller('LoginCtrl', {$scope: loginScope}); + })); + + it('should focus the login controller', inject(function() { + expect(loginScope.focusMe).toBe(true); + })); + +}); diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egIDL.js b/Open-ILS/web/js/ui/default/staff/test/unit/egIDL.js new file mode 100644 index 0000000000..bbf4e35cc6 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/unit/egIDL.js @@ -0,0 +1,25 @@ +'use strict'; + +describe('egIDL', function(){ + beforeEach(module('egCoreMod')); + + it('should parse the IDL', inject(function(egIDL) { + egIDL.parseIDL(); + expect(egIDL.classes.aou.fields.length).toBeGreaterThan(0); + })); + + it('should create an aou object', inject(function(egIDL) { + egIDL.parseIDL(); + var org = new egIDL.aou(); + expect(typeof org.id).toBe('function'); + })); + + it('should create an aou object with accessor/mutators', inject(function(egIDL) { + egIDL.parseIDL(); + var org = new egIDL.aou(); + org.name('AN ORG'); + expect(org.name()).toBe('AN ORG'); + })); +}); + + diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egOrg.js b/Open-ILS/web/js/ui/default/staff/test/unit/egOrg.js new file mode 100644 index 0000000000..f919d667ec --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/unit/egOrg.js @@ -0,0 +1,37 @@ +'use strict'; + +describe('egOrg', function(){ + beforeEach(module('egCoreMod')); + + function mkTree(egIDL, egEnv) { // FIXME: external sample data + egIDL.parseIDL(); + window._eg_mock_data.orgTree(egIDL, egEnv); + } + + it('should provide get by ID', inject(function(egIDL, egEnv, egOrg) { + mkTree(egIDL, egEnv); + expect(egOrg.get(egEnv.aou.tree.id())).toBe(egEnv.aou.tree); + })); + + it('should provide get by node', inject(function(egIDL, egEnv, egOrg) { + mkTree(egIDL, egEnv); + expect(egOrg.get(egEnv.aou.tree).id()).toBe(egEnv.aou.tree.id()); + })); + + it('should provide ancestors', inject(function(egIDL, egEnv, egOrg) { + mkTree(egIDL, egEnv); + expect(egOrg.ancestors(2, true)).toEqual([2, 1]); + })); + + it('should provide descendants', inject(function(egIDL, egEnv, egOrg) { + mkTree(egIDL, egEnv); + expect(egOrg.descendants(2, true)).toEqual([2, 4]); + })); + + it('should provide full path', inject(function(egIDL, egEnv, egOrg) { + mkTree(egIDL, egEnv); + expect(egOrg.fullPath(4, true)).toEqual([4, 2, 1]); + })); +}); + + diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egPatronApp.js b/Open-ILS/web/js/ui/default/staff/test/unit/egPatronApp.js new file mode 100644 index 0000000000..60e9b15534 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/unit/egPatronApp.js @@ -0,0 +1,31 @@ +'use strict'; + +describe('egPatronAppTest', function(){ + beforeEach(module('egPatronApp')); + + // basic controller sanity checks + + var patronCtrl, patronScope; + beforeEach(inject(function ($rootScope, $controller, $location) { + patronScope = $rootScope.$new(); + patronCtrl = $controller('PatronCtrl', {$scope: patronScope}); + })); + + /** patronSvc tests **/ + describe('patronSvcTests', function() { + + it('patronSvc should start with empty lists', inject(function(patronSvc) { + expect(patronSvc.patrons.length).toEqual(0); + })); + + it('patronSvc reset should clear data', inject(function(patronSvc) { + patronSvc.checkout_overrides.a = 1; + expect(Object.keys(patronSvc.checkout_overrides).length).toBe(1); + patronSvc.resetPatronLists(); + expect(Object.keys(patronSvc.checkout_overrides).length).toBe(0); + expect(patronSvc.holds.length).toBe(0); + })); + + }); + +}); diff --git a/Open-ILS/web/js/ui/default/staff/test/unit/egStrings.js b/Open-ILS/web/js/ui/default/staff/test/unit/egStrings.js new file mode 100644 index 0000000000..fe81e084af --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/test/unit/egStrings.js @@ -0,0 +1,14 @@ +'use strict'; + +describe('egStrings', function(){ + beforeEach(module('egCoreMod')); + + it('should interpolate values', inject(function(egStrings) { + + egStrings.FOO = 'Hello, {{planet}}'; + + expect(egStrings.$replace(egStrings.FOO, {planet : 'Earth'})) + .toBe('Hello, Earth'); + })); + +}); diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index 54b63fe063..2da48ef065 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -3549,6 +3549,9 @@ + + + marked with color"> diff --git a/Open-ILS/xul/staff_client/server/patron/user_edit.xhtml b/Open-ILS/xul/staff_client/server/patron/user_edit.xhtml new file mode 100644 index 0000000000..9b7bbf05ea --- /dev/null +++ b/Open-ILS/xul/staff_client/server/patron/user_edit.xhtml @@ -0,0 +1,176 @@ + + + + + +]> + + + + + &staff.patron.user_edit.title; + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + +
&staff.patron.user_edit.user_name.label;&staff.patron.user_edit.barcode.label;
&staff.patron.user_edit.firstname.label;&staff.patron.user_edit.middlename.label;&staff.patron.user_edit.lastname.label;
+ + + + + + + + +
&staff.patron.user_edit.working_location.label;
+
+ + + + + + + + + + +
&staff.patron.user_edit.permission.label;&staff.patron.user_edit.applied.label;&staff.patron.user_edit.depth.label;&staff.patron.user_edit.grantable.label;
+
+ + +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ + +
+ + + + + +
+ + + + () +
+
+ +
+ + &staff.patron.user_edit.display_perm.select_one; + + + &staff.patron.user_edit.save_user.depth_required; + + + &staff.patron.user_edit.save_user.user_modified_successfully; + +
+ + + + diff --git a/Open-ILS/xul/staff_client/server/patron/user_edit_xhtml.js b/Open-ILS/xul/staff_client/server/patron/user_edit_xhtml.js new file mode 100644 index 0000000000..3b78ad5ccf --- /dev/null +++ b/Open-ILS/xul/staff_client/server/patron/user_edit_xhtml.js @@ -0,0 +1,499 @@ +var cgi; +var orgTree; +var user; +var ses_id; +var user_groups = []; +var adv_items = []; +var user_perms = []; +var perm_list = []; +var ou_type_list = []; +var user_work_ous = []; +var work_ou_list = []; + +function $(id) { return document.getElementById(id); } + +function set_work_ou(row) { + var wid = findNodeByName(row,'a.id').getAttribute('workou_id'); + var wapply = findNodeByName(row,'a.id').checked; + + var w; + for (var i in user_work_ous) { + if (!user_work_ous[i]) continue; + if (user_work_ous[i].work_ou() == wid) { + w = user_work_ous[i]; + if (wapply) { + w.isdeleted(0); + w.ischanged(1); + } else { + if (w.isnew()) { + user_work_ous[i] = null; + } else { + w.isdeleted(1); + } + } + break; + } + } + + if (!w) { + if (wapply) { + p = new puwoum(); + p.isnew(1); + p.work_ou(wid); + p.usr(user.id()); + + user_work_ous.push(p); + } + } +} + +function set_perm(row) { + var pid = findNodeByName(row,'p.code').getAttribute('permid'); + var papply = findNodeByName(row,'p.id').checked; + var pdepth = findNodeByName(row,'p.depth').options[findNodeByName(row,'p.depth').selectedIndex].value; + var pgrant = findNodeByName(row,'p.grantable').checked; + + var p; + for (var i in user_perms) { + if (user_perms[i].perm() == pid) { + p = user_perms[i]; + if (papply) { + p.isdeleted(0); + p.ischanged(1); + p.depth(pdepth); + p.grantable(pgrant ? 1 : 0); + } else { + if (p.isnew()) { + user_perms[i] = null; + } else { + p.isdeleted(1); + } + } + break; + } + } + + if (!p) { + if (papply) { + p = new pupm(); + p.isnew(1); + p.perm(pid); + p.usr(user.id()); + p.depth('' + pdepth); + p.grantable(pgrant ? 1 : 0); + + user_perms.push(p); + } + } + +} + +function save_user () { + + try { + + var save_perms = []; + for (var i in user_perms) { + // Group based perm? skip it. + if (user_perms[i].id() < 0) continue; + + if (user_perms[i].depth() == null) { + var p; + for (var j in perm_list) { + if (perm_list[j].id() == user_perms[i].perm()) { + p = perm_list[j]; + break; + } + } + + alert( + $('staff.patron.user_edit.save_user.depth_required').innerHTML + + '\n' + p.code() + ); + + throw new Error( + $('staff.patron.user_edit.save_user.depth_required').innerHTML + + '\n' + p.code() + ); + } + + save_perms.push( user_perms[i] ); + } + + var save_ous = []; + for (var i in user_work_ous) { + if (!user_work_ous[i]) continue; + save_ous.push( user_work_ous[i] ); + } + + var req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.user.work_ous.update', ses_id, save_ous ); + req.send(true); + var wok = req.getResultObject(); + + if (wok.ilsevent) throw wok; + + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.user.permissions.update', ses_id, save_perms ); + req.send(true); + var pok = req.getResultObject(); + + if (pok.ilsevent) throw pok; + + if (pok || wok) { + alert($('staff.patron.user_edit.save_user.user_modified_successfully').innerHTML); + // on_patron_save comes from the browser client + if (window.xulG && xulG.on_patron_save) xulG.on_patron_save(); + } + + init_editor(); + + } catch (e) { + dump( js2JSON( e )); + alert( js2JSON( e )); + }; + + + + return false; +} + +var adv_mode = true; +function apply_adv_mode (root) { + adv_items = findNodesByClass(root,'advanced'); + for (var i in adv_items) { + adv_mode ? + removeCSSClass(adv_items[i], 'hideme') : + addCSSClass(adv_items[i], 'hideme'); + } +} + +function init_editor (u) { + + var x = document.getElementById('editor').elements; + + cgi = new CGI(); + if (cgi.param('adv')) adv_mode = true; + try { + if (xulG) if (xulG.adv) adv_mode = true; + if (xulG) if (xulG.params) if (xulG.params.adv) adv_mode = true; + } catch (e) {} + + apply_adv_mode(document.getElementById('editor')); + + ses_id = cgi.param('ses'); + try { + if (xulG) if (xulG.ses) ses_id = xulG.ses; + if (xulG) if (xulG.params) if (xulG.params.ses) ses_id = xulG.params.ses; + } catch (e) {} + + var usr_id = cgi.param('usr'); + try { + if (xulG) if (xulG.usr_id) usr_id = xulG.usr_id; + if (xulG) if (xulG.params) if (xulG.params.usr_id) usr_id = xulG.params.usr_id; + } catch (e) {} + + var usr_barcode = cgi.param('barcode'); + try { + if (xulG) if (xulG.usr_barcode) usr_ibarcode = xulG.usr_barcode; + if (xulG) if (xulG.params) if (xulG.params.usr_barcode) usr_ibarcode = xulG.params.usr_barcode; + } catch (e) {} + + try { + var req; + if (usr_id) { + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.user.fleshed.retrieve', ses_id, usr_id ); + } else { + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.user.fleshed.retrieve_by_barcode', ses_id, usr_barcode ); + } + req.send(true); + user = req.getResultObject(); + } catch (E) { + alert(E); + } + + if (user.usrname()) x['user.usrname'].value = user.usrname(); + x['user.usrname'].setAttribute('onchange','user.usrname(this.value)'); + + if (user.card() && user.card().barcode()) x['user.card.barcode'].value = user.card().barcode(); + x['user.card.barcode'].setAttribute('onchange','user.card().barcode(this.value)'); + + if (user.first_given_name()) x['user.first_given_name'].value = user.first_given_name(); + x['user.first_given_name'].setAttribute('onchange','user.first_given_name(this.value)'); + + if (user.second_given_name()) x['user.second_given_name'].value = user.second_given_name(); + x['user.second_given_name'].setAttribute('onchange','user.second_given_name(this.value);'); + + if (user.family_name()) x['user.family_name'].value = user.family_name(); + x['user.family_name'].setAttribute('onchange','user.family_name(this.value)'); + + // grab the editing staff user object + req = new RemoteRequest( 'open-ils.auth', 'open-ils.auth.session.retrieve', ses_id ); + req.send(true); + var staff = req.getResultObject(); + + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.permissions.user_perms.retrieve', ses_id ); + req.send(true); + var staff_perms = req.getResultObject(); + + // Get the top of the staff perm org for ASSIGN_WORK_ORG_UNIT + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.user.perm.highest_org', ses_id, staff.id(), 'ASSIGN_WORK_ORG_UNIT' ); + req.send(true); + var top_work_ou = req.getResultObject(); + + // and now, the orgs where this staff member can apply the perms + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.org_tree.descendants.retrieve', top_work_ou); + req.send(true); + var work_ou_tree = req.getResultObject(); + + // and now, the orgs where this staff member can apply the perms + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.user.get_work_ous', ses_id, user.id()); + req.send(true); + user_work_ous = req.getResultObject(); + + // and finally, the ou types + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.org_types.retrieve' ); + req.send(true); + ou_type_list = req.getResultObject(); + + user_perms = []; + perm_list = []; + if (user.id() > 0) { + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.permissions.user_perms.retrieve', ses_id, user.id() ); + req.send(true); + user_perms = req.getResultObject(); + + req = new RemoteRequest( 'open-ils.actor', 'open-ils.actor.permissions.retrieve' ); + req.send(true); + perm_list = req.getResultObject(); + } + + f = document.getElementById('permissions'); + while (f.firstChild) f.removeChild(f.lastChild); + + var rcount = 0; + for (var i in perm_list.sort(function(a,b){ if (a.code() < b.code()) return -1;return 1; })) + display_perm(f,perm_list[i],staff_perms, rcount++); + + f = document.getElementById('work_ous'); + while (f.firstChild) f.removeChild(f.lastChild); + + //flatten the ou tree, keep only those with can_hav_users = true + work_ou_list = []; + trim_ou_tree( [work_ou_tree], work_ou_list ); + + rcount = 0; + for (var i in work_ou_list.sort( function(a,b){ if (a.name() < b.name()) return -1;return 1; }) ) + display_work_ou(f,work_ou_list[i], rcount++); + + return true; +} + +function grep ( code, list ) { + var ret = []; + for (var i in list) { + if (code(list[i])) ret.push(list[i]); + } + return ret; +} + +function trim_ou_tree (tree, list) { + for (var i in tree) { + if (!tree[i]) continue; + + var type = grep( function(x) {return x.id() == tree[i].ou_type()}, ou_type_list )[0]; + if ( type && type.can_have_users() == 't' ) + list.push(tree[i]); + + if (tree[i].children()) trim_ou_tree(tree[i].children(), list); + } +} + +function display_work_ou (root,ou_def,r) { + + var wrow = findNodeByName(document.getElementById('work_ou-tmpl'), 'wrow').cloneNode(true); + root.appendChild(wrow); + + var label_cell = findNodeByName(wrow,'label'); + findNodeByName(label_cell,'a.name').appendChild(text(ou_def.name())); + findNodeByName(label_cell,'a.shortname').appendChild(text(ou_def.shortname())); + if (r % 2) label_cell.className += ' odd'; + + var apply_cell = findNodeByName(wrow,'wapply'); + findNodeByName(apply_cell,'a.id').setAttribute('workou_id', ou_def.id()); + if (r % 2) apply_cell.className += ' odd'; + + var has_it = grep( + function(x){ return x.work_ou() == ou_def.id() }, + user_work_ous + ).length; + + findNodeByName(apply_cell,'a.id').checked = has_it > 0 ? true : false; +} + +function display_perm (root,perm_def,staff_perms, r) { + + var prow = findNodeByName(document.getElementById('permission-tmpl'), 'prow').cloneNode(true); + root.appendChild(prow); + + var all = false; + for (var i in staff_perms) { + if (staff_perms[i].perm() == -1) { + all = true; + break; + } + } + + + var sp,up; + if (!all) { + for (var i in staff_perms) { + if (perm_def.id() == staff_perms[i].perm() || staff_perms[i].perm() == -1) { + sp = staff_perms[i]; + break; + } + } + } + + for (var i in user_perms) { + if (perm_def.id() == user_perms[i].perm()) + up = user_perms[i]; + } + + + var dis = false; + if ((up && up.id() < 0) || !sp || !sp.grantable()) dis = true; + if (all) dis = false; + + var label_cell = findNodeByName(prow,'plabel'); + findNodeByName(label_cell,'p.code').appendChild(text(perm_def.code())); + findNodeByName(label_cell,'p.code').setAttribute('title', perm_def.description()); + findNodeByName(label_cell,'p.code').setAttribute('permid', perm_def.id()); + if (r % 2) label_cell.className += ' odd'; + + var apply_cell = findNodeByName(prow,'papply'); + findNodeByName(apply_cell,'p.id').disabled = dis; + findNodeByName(apply_cell,'p.id').checked = up ? true : false; + if (r % 2) apply_cell.className += ' odd'; + + var depth_cell = findNodeByName(prow,'pdepth'); + findNodeByName(depth_cell,'p.depth').disabled = dis; + findNodeByName(depth_cell,'p.depth').id = 'perm-depth-' + perm_def.id(); + if (r % 2) depth_cell.className += ' odd'; + selectBuilder( + 'perm-depth-' + perm_def.id(), + globalOrgTypes, + (up ? up.depth() : findOrgDepth(user.home_ou())), + { label_field : 'name', + value_field : 'depth', + empty_label : $('staff.patron.user_edit.display_perm.select_one').innerHTML, + empty_value : '', + clear : true } + ); + + var grant_cell = findNodeByName(prow,'pgrant'); + findNodeByName(grant_cell,'p.grantable').disabled = dis; + findNodeByName(grant_cell,'p.grantable').checked = up ? (up.grantable() ? true : false) : false; + if (r % 2) grant_cell.className += ' odd'; + +} + + +function selectBuilder (id, objects, def, args) { + var label_field = args['label_field']; + var value_field = args['value_field']; + var depth = args['depth']; + + if (!depth) depth = 0; + + args['depth'] = parseInt(depth) + 1; + + var child_field_name = args['child_field_name']; + + var sel = id; + if (typeof sel != 'object') + sel = document.getElementById(sel); + + if (args['clear']) { + for (var o in sel.options) { + sel.options[o] = null; + } + args['clear'] = false; + if (args['empty_label']) { + sel.options[0] = new Option( args['empty_label'], args['empty_value'] ); + sel.selectedIndex = 0; + } + } + + for (var i in objects) { + var l = objects[i][label_field]; + var v = objects[i][value_field]; + + if (typeof l == 'function') + l = objects[i][label_field](); + + if (typeof v == 'function') + v = objects[i][value_field](); + + var opt = new Option( l, v ); + + if (depth) { + var d = 10 * depth; + opt.style.paddingLeft = '' + d + 'px'; + } + + sel.options[sel.options.length] = opt; + + + if (typeof def == 'object') { + for (var j in def) { + if (v == def[j]) { + opt.selected = true; + sel.value = v; + } + } + } else { + if (v == def) { + opt.selected = true; + sel.value = v; + } + } + + if (child_field_name) { + var c = objects[i][child_field_name]; + if (typeof c == 'function') + c = objects[i][child_field_name](); + + selectBuilder( + id, + c, + def, + { label_field : args['label_field'], + value_field : args['value_field'], + depth : args['depth'], + child_field_name : args['child_field_name'] } + ); + } + + } +} + +function findNodesByClass(root, nodeClass, list) { + if(!list) list = []; + if( !root || !nodeClass) { + return null; + } + + if(root.nodeType != 1) { + return null; + } + + if(root.className.match(nodeClass)) list.push( root ); + + var children = root.childNodes; + + for( var i = 0; i != children.length; i++ ) { + findNodesByClass(children[i], nodeClass, list); + } + + return list; +} + -- 2.43.2