Bugzilla – Attachment 26071 Details for
Bug 11559
Professional cataloger's interface
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 11559 - Rancor: professional cataloging interface
Bug-11559---Rancor-professional-cataloging-interfa.patch (text/plain), 125.81 KB, created by
Jesse Weaver
on 2014-03-11 10:54:43 UTC
(
hide
)
Description:
Bug 11559 - Rancor: professional cataloging interface
Filename:
MIME Type:
Creator:
Jesse Weaver
Created:
2014-03-11 10:54:43 UTC
Size:
125.81 KB
patch
obsolete
>From 4d02c0027fcdad7766415486252ac9e0ff1630a1 Mon Sep 17 00:00:00 2001 >From: Jesse Weaver <pianohacker@gmail.com> >Date: Tue, 11 Mar 2014 11:28:40 +0100 >Subject: [PATCH] Bug 11559 - Rancor: professional cataloging interface > >Test plan: > > 1. Apply patches for bug 10486 and this bug. > 2. Reinstall patches for pazpar2. > 3. Switch back and forth between basic and advanced editor, and verify > that preference is saved (by workstation, not user. > 4. Enter in a record (with all required fields). > 5. Save to ISO2709, MARCXML and catalog, verify that all three match. > 6. Import a MARCXML and ISO2709 record, and verify that the record is > correct after import. > 7. Run keyword and advanced searches with a variety of Z39.50 servers > to check that searching is correct, and try to both import records > as new records and substitute them for the contents of existing > records. > 8. Create, edit and delete macros, verifying that they are autosaved. > 9. Try running macros (example follows). > 10. Try enabling and disabling fixed-field widgets and customizing > fonts, and test the editor afterwards. > >Example macro: > >goto field 003 >copy field data >insert new field 040 data= >goto subfield end >insert new subfield a data= >goto subfield a >paste >--- > cataloguing/addbiblio.pl | 6 + > cataloguing/editor.pl | 70 ++ > .../lib/koha/cateditor/koha-backend.js | 201 ++++++ > .../intranet-tmpl/lib/koha/cateditor/macros.js | 196 +++++ > .../lib/koha/cateditor/marc-editor.js | 358 +++++++++ > .../intranet-tmpl/lib/koha/cateditor/marc-mode.js | 129 ++++ > .../lib/koha/cateditor/marc-record.js | 351 +++++++++ > .../lib/koha/cateditor/preferences.js | 28 + > .../intranet-tmpl/lib/koha/cateditor/search.js | 87 +++ > .../intranet-tmpl/lib/koha/cateditor/text-marc.js | 78 ++ > .../intranet-tmpl/lib/koha/cateditor/widget.js | 169 +++++ > koha-tmpl/intranet-tmpl/lib/koha/cateditor/xslt.js | 68 ++ > koha-tmpl/intranet-tmpl/prog/en/css/cateditor.css | 332 +++++++++ > .../prog/en/includes/cateditor-ui.inc | 804 +++++++++++++++++++++ > .../prog/en/includes/cateditor-widgets-marc21.inc | 155 ++++ > .../prog/en/modules/cataloguing/addbiblio.tt | 15 + > .../prog/en/modules/cataloguing/editor.tt | 201 ++++++ > svc/cataloguing/framework | 73 ++ > 18 files changed, 3321 insertions(+) > create mode 100755 cataloguing/editor.pl > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/macros.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-editor.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-mode.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-record.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/search.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/text-marc.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/widget.js > create mode 100644 koha-tmpl/intranet-tmpl/lib/koha/cateditor/xslt.js > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/css/cateditor.css > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-widgets-marc21.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt > create mode 100755 svc/cataloguing/framework > >diff --git a/cataloguing/addbiblio.pl b/cataloguing/addbiblio.pl >index 118bfc2..0168597 100755 >--- a/cataloguing/addbiblio.pl >+++ b/cataloguing/addbiblio.pl >@@ -767,8 +767,14 @@ if ($frameworkcode eq 'FA'){ > 'stickyduedate' => $fa_stickyduedate, > 'duedatespec' => $fa_duedatespec, > ); >+} elsif ( $input->cookie( 'catalogue_editor_' . $loggedinuser ) eq 'advanced' ) { >+ # Only use the advanced editor for non-fast-cataloging. >+ # breedingid is not handled because those would only come off a Z39.50 >+ # search initiated by the basic editor. >+ print $input->redirect( '/cgi-bin/koha/cataloguing/editor.pl' . ( $biblionumber && ( '#catalog:' . $biblionumber ) ) ); > } > >+ > # Getting the list of all frameworks > # get framework list > my $frameworks = getframeworks; >diff --git a/cataloguing/editor.pl b/cataloguing/editor.pl >new file mode 100755 >index 0000000..1dc4b4e >--- /dev/null >+++ b/cataloguing/editor.pl >@@ -0,0 +1,70 @@ >+#!/usr/bin/perl >+# >+# Copyright 2013 ByWater >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+# >+ >+use Modern::Perl '2009'; >+ >+use CGI; >+use MARC::Record; >+ >+use C4::Auth; >+use C4::Biblio; >+use C4::Context; >+use C4::Output; >+use C4::XSLT qw( XSLTGetFilename ); >+ >+my $input = CGI->new; >+ >+my ( $template, $loggedinuser, $cookie ) = get_template_and_user( >+ { >+ template_name => 'cataloguing/editor.tt', >+ query => $input, >+ type => 'intranet', >+ authnotrequired => 0, >+ flagsrequired => { editcatalogue => 'edit_catalogue' }, >+ } >+); >+ >+# Needed information for cataloging plugins >+$template->{VARS}->{DefaultLanguageField008} = pack( 'A3', C4::Context->preference('DefaultLanguageField008') || 'eng' ); >+ >+# Z39.50 servers >+my $dbh = C4::Context->dbh; >+$template->{VARS}->{z3950_targets} = $dbh->selectall_arrayref( q{ >+ SELECT * FROM z3950servers >+ ORDER BY name >+}, { Slice => {} } ); >+ >+my @xsltResultStylesheets; >+my @xsltDetailStylesheets; >+ >+foreach my $syntax ( qw( MARC21 UNIMARC NORMARC ) ) { >+ if ( XSLTGetFilename( $syntax, 'XSLTResultsDisplay' ) =~ m,/intranet-tmpl/.*|^https:?.*, ) { >+ push @xsltResultStylesheets, { syntax => $syntax, url => $& }; >+ } >+ >+ if ( XSLTGetFilename( $syntax, 'XSLTDetailsDisplay' ) =~ m,/intranet-tmpl/.*|^https:?.*, ) { >+ push @xsltDetailStylesheets, { syntax => $syntax, url => $& }; >+ } >+} >+ >+$template->{VARS}->{xslt_result_stylesheets} = \@xsltResultStylesheets; >+$template->{VARS}->{xslt_detail_stylesheets} = \@xsltDetailStylesheets; >+ >+output_html_with_http_headers $input, $cookie, $template->output; >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js >new file mode 100644 >index 0000000..c70c3bf >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js >@@ -0,0 +1,201 @@ >+define( [ '/cgi-bin/koha/svc/cataloguing/framework?frameworkcode=&callback=define', 'marc-record' ], function( defaultFramework, MARC ) { >+ var _authorised_values = defaultFramework.authorised_values; >+ var _frameworks = {}; >+ var _framework_mappings = {}; >+ >+ function _fromXMLStruct( data ) { >+ result = {}; >+ >+ $(data).children().eq(0).children().each( function() { >+ var $contents = $(this).contents(); >+ if ( $contents.length == 1 && $contents[0].nodeType == Node.TEXT_NODE ) { >+ result[ this.localName ] = $contents[0].data; >+ } else { >+ result[ this.localName ] = $contents.filter( function() { return this.nodeType != Node.TEXT_NODE || !this.data.match( /^\s+$/ ) } ).toArray(); >+ } >+ } ); >+ >+ return result; >+ } >+ >+ function _importFramework( frameworkcode, frameworkinfo ) { >+ _frameworks[frameworkcode] = frameworkinfo; >+ _framework_mappings[frameworkcode] = {}; >+ >+ $.each( frameworkinfo, function( i, tag ) { >+ var tagnum = tag[0], taginfo = tag[1]; >+ >+ var subfields = {}; >+ >+ $.each( taginfo.subfields, function( i, subfield ) { >+ subfields[ subfield[0] ] = subfield[1]; >+ } ); >+ >+ _framework_mappings[frameworkcode][tagnum] = $.extend( {}, taginfo, { subfields: subfields } ); >+ } ); >+ } >+ >+ _importFramework( '', defaultFramework.framework ); >+ >+ var KohaBackend = { >+ NOT_EMPTY: {}, // Sentinel value >+ >+ GetAllTagsInfo: function( frameworkcode, tagnumber ) { >+ return _framework_mappings[frameworkcode]; >+ }, >+ >+ GetAuthorisedValues: function( category ) { >+ return _authorised_values[category]; >+ }, >+ >+ GetTagInfo: function( frameworkcode, tagnumber ) { >+ if ( !_framework_mappings[frameworkcode] ) return undefined; >+ return _framework_mappings[frameworkcode][tagnumber]; >+ }, >+ >+ GetRecord: function( id, callback ) { >+ $.get( >+ '/cgi-bin/koha/svc/bib/' + id >+ ).done( function( data ) { >+ var record = new MARC.Record(); >+ record.loadMARCXML(data); >+ callback(record); >+ } ).fail( function( data ) { >+ callback( { data: { error: data } } ); >+ } ); >+ }, >+ >+ CreateRecord: function( record, callback ) { >+ $.ajax( { >+ type: 'POST', >+ url: '/cgi-bin/koha/svc/new_bib', >+ data: record.toXML(), >+ contentType: 'text/xml' >+ } ).done( function( data ) { >+ callback( _fromXMLStruct( data ) ); >+ } ).fail( function( data ) { >+ callback( { error: data } ); >+ } ); >+ }, >+ >+ SaveRecord: function( id, record, callback ) { >+ $.ajax( { >+ type: 'POST', >+ url: '/cgi-bin/koha/svc/bib/' + id, >+ data: record.toXML(), >+ contentType: 'text/xml' >+ } ).done( function( data ) { >+ callback( _fromXMLStruct( data ) ); >+ } ).fail( function( data ) { >+ callback( { data: { error: data } } ); >+ } ); >+ }, >+ >+ GetTagsBy: function( frameworkcode, field, value ) { >+ var result = {}; >+ >+ $.each( _frameworks[frameworkcode], function( undef, tag ) { >+ var tagnum = tag[0], taginfo = tag[1]; >+ >+ if ( taginfo[field] == value ) result[tagnum] = true; >+ } ); >+ >+ return result; >+ }, >+ >+ GetSubfieldsBy: function( frameworkcode, field, value ) { >+ var result = {}; >+ >+ $.each( _frameworks[frameworkcode], function( undef, tag ) { >+ var tagnum = tag[0], taginfo = tag[1]; >+ >+ $.each( taginfo.subfields, function( undef, subfield ) { >+ var subfieldcode = subfield[0], subfieldinfo = subfield[1]; >+ >+ if ( subfieldinfo[field] == value ) { >+ if ( !result[tagnum] ) result[tagnum] = {}; >+ >+ result[tagnum][subfieldcode] = true; >+ } >+ } ); >+ } ); >+ >+ return result; >+ }, >+ >+ FillRecord: function( frameworkcode, record, allTags ) { >+ $.each( _frameworks[frameworkcode], function( undef, tag ) { >+ var tagnum = tag[0], taginfo = tag[1]; >+ >+ if ( taginfo.mandatory != "1" && !allTags ) return; >+ >+ var fields = record.fields(tagnum); >+ >+ if ( fields.length == 0 ) { >+ var newField = new MARC.Field( tagnum, ' ', ' ', [] ); >+ fields.push( newField ); >+ record.addFieldGrouped( newField ); >+ >+ if ( tagnum < '010' ) { >+ newField.addSubfield( [ '@', '' ] ); >+ return; >+ } >+ } >+ >+ $.each( taginfo.subfields, function( undef, subfield ) { >+ var subfieldcode = subfield[0], subfieldinfo = subfield[1]; >+ >+ if ( subfieldinfo.mandatory != "1" && !allTags ) return; >+ >+ $.each( fields, function( undef, field ) { >+ if ( !field.hasSubfield(subfieldcode) ) field.addSubfieldGrouped( [ subfieldcode, '' ] ); >+ } ); >+ } ); >+ } ); >+ }, >+ >+ ValidateRecord: function( frameworkcode, record ) { >+ var errors = []; >+ >+ var mandatoryTags = KohaBackend.GetTagsBy( '', 'mandatory', '1' ); >+ var mandatorySubfields = KohaBackend.GetSubfieldsBy( '', 'mandatory', '1' ); >+ var nonRepeatableTags = KohaBackend.GetTagsBy( '', 'repeatable', '0' ); >+ var nonRepeatableSubfields = KohaBackend.GetSubfieldsBy( '', 'repeatable', '0' ); >+ >+ $.each( mandatoryTags, function( tag ) { >+ if ( !record.hasField( tag ) ) errors.push( { type: 'missingTag', tag: tag } ); >+ } ); >+ >+ var seenTags = {}; >+ >+ $.each( record.fields(), function( undef, field ) { >+ if ( seenTags[ field.tagnumber() ] && nonRepeatableTags[ field.tagnumber() ] ) { >+ errors.push( { type: 'unrepeatableTag', line: field.sourceLine, tag: field.tagnumber() } ); >+ return; >+ } >+ >+ seenTags[ field.tagnumber() ] = true; >+ >+ var seenSubfields = {}; >+ >+ $.each( field.subfields(), function( undef, subfield ) { >+ if ( seenSubfields[ subfield[0] ] != null && nonRepeatableSubfields[ field.tagnumber() ][ subfield[0] ] ) { >+ errors.push( { type: 'unrepeatableSubfield', subfield: subfield[0], line: field.sourceLine } ); >+ } else { >+ seenSubfields[ subfield[0] ] = subfield[1]; >+ } >+ } ); >+ >+ $.each( mandatorySubfields[ field.tagnumber() ] || {}, function( subfield ) { >+ if ( !seenSubfields[ subfield ] ) { >+ errors.push( { type: 'missingSubfield', subfield: subfield[0], line: field.sourceLine } ); >+ } >+ } ); >+ } ); >+ >+ return errors; >+ }, >+ }; >+ >+ return KohaBackend; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/macros.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/macros.js >new file mode 100644 >index 0000000..8aaa2f3 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/macros.js >@@ -0,0 +1,196 @@ >+define( [ 'widget' ], function( Widget ) { >+ function _setIndicators( editor, ind1, ind2 ) { >+ var info = editor.getLineInfo( editor.cm.getCursor() ); >+ if (!info || !info.subfields) return false; >+ >+ var cur = editor.cm.getCursor(); >+ >+ var indicators = [ ind1 || info.contents.substring(4, 5) || '_', ind2 || info.contents.substring(6, 7) || '_' ]; >+ >+ editor.cm.replaceRange( >+ info.tagNumber + ' ' + indicators.join(' ') + ' ' + info.contents.substring(8), >+ { line: cur.line, ch: 0 }, >+ { line: cur.line }, >+ 'marcAware' >+ ); >+ >+ return true; >+ } >+ >+ var _commandGenerators = [ >+ [ /^copy field data$/i, function() { >+ return function( editor, state ) { >+ var info = editor.getLineInfo( editor.cm.getCursor() ); >+ if (!info) return false; >+ >+ if (info.subfields) { >+ state.clipboard = info.contents.substring(4); >+ } else { >+ state.clipboard = info.contents.substring(8); >+ } >+ }; >+ } ], >+ [ /^copy subfield data$/i, function() { >+ return function( editor, state ) { >+ var info = editor.getLineInfo( editor.cm.getCursor() ); >+ if (!info) return false; >+ >+ var cur = editor.cm.getCursor(); >+ >+ if (info.subfields) { >+ for (var i = 0; i < info.subfields.length; i++) { >+ var end = i == info.subfields.length - 1 ? info.contents.length : info.subfields[i+1].ch; >+ if (cur.ch > end) continue; >+ >+ state.clipboard = info.contents.substring(info.subfields[i].ch + 3, end); >+ return; >+ } >+ } >+ >+ return false; >+ } >+ } ], >+ [ /^delete field$/i, function() { >+ return function( editor, state ) { >+ var cur = editor.cm.getCursor(); >+ >+ editor.cm.replaceRange( "", { line: cur.line, ch: 0 }, { line: cur.line + 1, ch: 0 }, 'marcAware' ); >+ } >+ } ], >+ [ /^goto field end$/i, function() { >+ return function( editor, state ) { >+ editor.cm.setCursor( { line: editor.cm.lastLine() } ); >+ } >+ } ], >+ [ /^goto field (\w{3})$/i, function(field) { >+ var matcher = new RegExp('^' + field + ' '); >+ return function( editor, state ) { >+ for ( var line = 0, contents; (contents = editor.cm.getLine(line)); line++ ) { >+ if ( matcher.exec( contents ) ) { >+ editor.cm.setCursor( { line: line, ch: 0 } ); >+ return; >+ } >+ } >+ >+ return false; >+ } >+ } ], >+ [ /^goto subfield end$/i, function() { >+ return function( editor, state ) { >+ var cur = editor.cm.getCursor(); >+ >+ editor.cm.setCursor( { line: cur.line } ); >+ } >+ } ], >+ [ /^goto subfield (\w)$/i, function( code ) { >+ return function( editor, state ) { >+ var info = editor.getLineInfo( editor.cm.getCursor() ); >+ if (!info || !info.subfields) return false; >+ >+ var cur = editor.cm.getCursor(); >+ >+ for (var i = 0; i < info.subfields.length; i++) { >+ if ( info.subfields[i].code != code ) continue; >+ >+ var end = i == info.subfields.length - 1 ? info.contents.length : info.subfields[i+1].ch; >+ editor.cm.setCursor( { line: cur.line, ch: end } ); >+ return; >+ } >+ >+ return false; >+ } >+ } ], >+ [ /^insert (new )?field (\w{3}) data=(.*)/i, function(undef, field, data) { >+ var new_contents = field + ( field < '100' ? ' ' : ' _ _ ' ) + data.replace(/\\([0-9a-z])/g, '$$$1 '); >+ return function( editor, state ) { >+ var line, contents; >+ >+ for ( line = 0; (contents = editor.cm.getLine(line)); line++ ) { >+ if ( contents && contents[0] > field[0] ) break; >+ } >+ >+ if ( line > editor.cm.lastLine() ) { >+ new_contents = '\n' + new_contents; >+ } else { >+ new_contents = new_contents + '\n'; >+ } >+ >+ editor.cm.replaceRange( new_contents, { line: line, ch: 0 }, null, 'marcAware' ); >+ editor.cm.setCursor( { line: line, ch: 0 } ); >+ } >+ } ], >+ [ /^insert (new )?subfield (\w) data=(.*)/i, function(undef, subfield, data) { >+ return function( editor, state ) { >+ editor.cm.replaceRange( '$' + subfield + ' ' + data, { line: editor.cm.getCursor().line }, null, 'marcAware' ); >+ } >+ } ], >+ [ /^paste$/i, function() { >+ return function( editor, state ) { >+ var cur = editor.cm.getCursor(); >+ >+ editor.cm.replaceRange( state.clipboard, cur, null, 'marcAware' ); >+ } >+ } ], >+ [ /^set indicator([12])=([ _0-9])$/i, function( ind, value ) { >+ return function( editor, state ) { >+ return ind == '1' ? _setIndicators( editor, ind1, null ) : _setIndicators( editor, null, ind2 ); >+ } >+ } ], >+ [ /^set indicators=([ _0-9])([ _0-9])$/i, function( ind1, ind2 ) { >+ return function( editor, state ) { >+ return _setIndicators( editor, ind1, ind2 ); >+ } >+ } ], >+ ]; >+ >+ var Macros = { >+ Compile: function( macro ) { >+ var result = { commands: [], errors: [] }; >+ >+ $.each( macro.split(/\r\n|\n/), function( line, contents ) { >+ var command; >+ >+ if ( contents.match(/^\s*$/) ) return; >+ >+ $.each( _commandGenerators, function( undef, gen ) { >+ var match; >+ >+ if ( !( match = gen[0].exec( contents ) ) ) return; >+ >+ command = gen[1].apply(null, match.slice(1)); >+ return false; >+ } ); >+ >+ if ( !command ) { >+ result.errors.push( { line: line, error: 'unrecognized' } ); >+ } >+ >+ result.commands.push( { func: command, orig: contents, line: line } ); >+ } ); >+ >+ return result; >+ }, >+ Run: function( editor, macro ) { >+ var compiled = Macros.Compile(macro); >+ if ( compiled.errors.length ) return { errors: compiled.errors }; >+ var state = { >+ clipboard: '', >+ }; >+ >+ var result = { errors: [] }; >+ >+ editor.cm.operation( function() { >+ $.each( compiled.commands, function( undef, command ) { >+ if ( command.func( editor, state ) === false ) { >+ result.errors.push( { line: command.line, error: 'failed' } ); >+ return false; >+ } >+ } ); >+ } ); >+ >+ return result; >+ }, >+ }; >+ >+ return Macros; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-editor.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-editor.js >new file mode 100644 >index 0000000..3c6992b >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-editor.js >@@ -0,0 +1,358 @@ >+define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ], function( MARC, KohaBackend, Preferences, TextMARC, Widget ) { >+ var NOTIFY_TIMEOUT = 250; >+ >+ function editorCursorActivity( cm ) { >+ var editor = cm.marceditor; >+ if ( editor.textMode ) return; >+ >+ $('#status-tag-info').empty(); >+ $('#status-subfield-info').empty(); >+ >+ var info = editor.getLineInfo( cm.getCursor() ); >+ >+ if ( !info ) return; // No tag at all on this line >+ >+ var taginfo = KohaBackend.GetTagInfo( '', info.tagNumber ); >+ $('#status-tag-info').html( '<strong>' + info.tagNumber + ':</strong> ' ); >+ >+ if ( taginfo ) { >+ $('#status-tag-info').append( taginfo.lib ); >+ >+ if ( !info.currentSubfield ) return; // No current subfield >+ >+ var subfieldinfo = taginfo.subfields[info.currentSubfield]; >+ $('#status-subfield-info').html( '<strong>$' + info.currentSubfield + ':</strong> ' ); >+ >+ if ( subfieldinfo ) { >+ $('#status-subfield-info').append( subfieldinfo.lib ); >+ } else { >+ $('#status-subfield-info').append( '<em>' + _("Unknown subfield") + '</em>' ); >+ } >+ } else { >+ $('#status-tag-info').append( '<em>' + _("Unknown tag") + '</em>' ); >+ } >+ } >+ >+ function editorBeforeChange( cm, change ) { >+ var editor = cm.marceditor; >+ if ( editor.textMode || change.origin == 'marcAware' ) return; >+ >+ // FIXME: Should only cancel changes if this is a control field/subfield widget >+ if ( change.from.line !== change.to.line || Math.abs( change.from.ch - change.to.ch ) > 1 || change.text.length != 1 || change.text[0].length != 0 ) return; // Not single-char change >+ >+ if ( change.from.ch == change.to.ch - 1 && cm.findMarksAt( { line: change.from.line, ch: change.from.ch + 1 } ).length ) { >+ change.cancel(); >+ } else if ( change.from.ch == change.to.ch && cm.findMarksAt(change.from).length && !change.text[0].match(/^[$|Çâ¡]$/) ) { >+ change.cancel(); >+ } >+ } >+ >+ function editorChange( cm, change ) { >+ var editor = cm.marceditor; >+ if ( editor.textMode ) return; >+ >+ var updatedLines = {}; >+ do { >+ var from = change.from; >+ var to = change.to; >+ if ( to.line < from.line || to.ch < from.ch ) { >+ var temp = from; >+ from = to; >+ to = temp; >+ } >+ >+ var startLine, endLine; >+ if ( change.text.length == 2 && from.line == to.line && from.ch == to.ch) { >+ if ( from.ch == 0 ) { >+ startLine = endLine = from.line; >+ } else if ( from.ch == cm.getLine(from.line).length ){ >+ startLine = endLine = from.line + 1; >+ } >+ } else { >+ startLine = (from.ch == cm.getLine(from.line).length && from.line < to.line) ? Math.min(cm.lastLine(), from.line + 1) : from.line; >+ endLine = ((to.ch == 0 && from.line < to.line) ? Math.max(to.line - 1, 0) : to.line) + change.text.length - 1; >+ } >+ >+ for ( var line = startLine; line <= endLine; line++ ) { >+ if ( updatedLines[line] ) continue; >+ >+ if ( Preferences.user.fieldWidgets ) Widget.UpdateLine( cm.marceditor, line ); >+ if ( change.origin != 'setValue' && change.origin != 'marcWidgetPrefill' ) cm.addLineClass( line, 'wrapper', 'modified-line' ); >+ updatedLines[line] = true; >+ } >+ } while ( change = change.next ) >+ >+ Widget.ActivateAt( cm, cm.getCursor() ); >+ cm.marceditor.startNotify(); >+ } >+ >+ // Editor helper functions >+ function activateTabPosition( cm, cur, idx ) { >+ cm.setCursor( cur ); >+ Widget.ActivateAt( cm, cur, idx ); >+ } >+ >+ function getTabPositions( editor, cur ) { >+ var info = editor.getLineInfo( cur || editor.cm.getCursor() ); >+ >+ if ( info ) { >+ if ( info.subfields ) { >+ var positions = [ 0, 4, 6 ]; >+ >+ $.each( info.subfields, function( undef, subfield ) { >+ positions.push( subfield.ch + 3 ); >+ } ); >+ >+ return positions; >+ } else { >+ return [ 0, 4 ]; >+ } >+ } else { >+ return []; >+ } >+ } >+ >+ var _editorKeys = { >+ Enter: function( cm ) { >+ var cursor = cm.getCursor(); >+ cm.replaceRange( '\n', { line: cursor.line }, null, 'marcAware' ); >+ cm.setCursor( { line: cursor.line + 1, ch: 0 } ); >+ }, >+ >+ 'Shift-Enter': function( cm ) { >+ var cursor = cm.getCursor(); >+ cm.replaceRange( '\n', { line: cursor.line, ch: 0 }, null, 'marcAware' ); >+ cm.setCursor( { line: cursor.line, ch: 0 } ); >+ }, >+ >+ 'Ctrl-X': function( cm ) { >+ // Delete subfield (or cut) >+ if ( cm.somethingSelected() ) return true; >+ >+ var cur = cm.getCursor(); >+ var info = cm.marceditor.getLineInfo( cur ); >+ if ( !info || !info.subfields ) return true; >+ >+ for (var i = 0; i < info.subfields.length; i++) { >+ var end = i == info.subfields.length - 1 ? info.contents.length : info.subfields[i+1].ch; >+ if (cur.ch > end) continue; >+ >+ cm.replaceRange( "", { line: cur.line, ch: info.subfields[i].ch }, { line: cur.line, ch: end }, 'marcAware' ); >+ return; >+ } >+ }, >+ >+ 'Shift-Ctrl-X': function( cm ) { >+ // Delete line >+ var cur = cm.getCursor(); >+ >+ cm.replaceRange( "", { line: cur.line, ch: 0 }, { line: cur.line + 1, ch: 0 }, 'marcAware' ); >+ }, >+ >+ Tab: function( cm ) { >+ // Move through parts of tag/fixed fields >+ var positions = getTabPositions( cm.marceditor ); >+ var cur = cm.getCursor(); >+ >+ for ( var i = 0; i < positions.length; i++ ) { >+ if ( positions[i] > cur.ch ) { >+ activateTabPosition( cm, { line: cur.line, ch: positions[i] } ); >+ return false; >+ } >+ } >+ >+ cm.setCursor( { line: cur.line + 1, ch: 0 } ); >+ }, >+ >+ 'Shift-Tab': function( cm ) { >+ // Move backwards through parts of tag/fixed fields >+ var positions = getTabPositions( cm.marceditor ); >+ var cur = cm.getCursor(); >+ >+ for ( var i = positions.length - 1; i >= 0; i-- ) { >+ if ( positions[i] < cur.ch ) { >+ activateTabPosition( cm, { line: cur.line, ch: positions[i] } ); >+ return false; >+ } >+ } >+ >+ if ( cur.line == 0 ) return; >+ >+ var prevPositions = getTabPositions( cm, { line: cur.line - 1, ch: cm.getLine( cur.line - 1 ).length } ); >+ >+ if ( prevPositions.length ) { >+ activateTabPosition( cm, { line: cur.line - 1, ch: prevPositions[ prevPositions.length - 1 ] }, -1 ); >+ } else { >+ cm.setCursor( { line: cur.line - 1, ch: 0 } ); >+ } >+ }, >+ }; >+ >+ function MARCEditor( position ) { >+ this.cm = CodeMirror( >+ position, >+ { >+ extraKeys: _editorKeys, >+ gutters: [ >+ 'modified-line-gutter', >+ ], >+ lineWrapping: true, >+ mode: { >+ name: 'marc', >+ nonRepeatableTags: KohaBackend.GetTagsBy( '', 'repeatable', '0' ), >+ nonRepeatableSubfields: KohaBackend.GetSubfieldsBy( '', 'repeatable', '0' ) >+ } >+ } >+ ); >+ this.cm.marceditor = this; >+ >+ this.cm.on( 'beforeChange', editorBeforeChange ); >+ this.cm.on( 'change', editorChange ); >+ this.cm.on( 'cursorActivity', editorCursorActivity ); >+ >+ this.subscribers = []; >+ this.subscribe( function( marceditor ) { >+ Widget.Notify( marceditor ); >+ } ); >+ } >+ >+ MARCEditor.prototype.setUseWidgets = function( val ) { >+ if ( val ) { >+ for ( var line = 0; line <= this.cm.lastLine(); line++ ) { >+ Widget.UpdateLine( this, line ); >+ } >+ } else { >+ $.each( this.cm.getAllMarks(), function( undef, mark ) { >+ if ( mark.widget ) mark.widget.clearToText(); >+ } ); >+ } >+ }; >+ >+ MARCEditor.prototype.focus = function() { >+ this.cm.focus(); >+ }; >+ >+ MARCEditor.prototype.refresh = function() { >+ this.cm.refresh(); >+ }; >+ >+ MARCEditor.prototype.displayRecord = function( record ) { >+ this.cm.setValue( TextMARC.RecordToText(record) ); >+ }; >+ >+ MARCEditor.prototype.getRecord = function() { >+ this.textMode = true; >+ >+ $.each( this.cm.getAllMarks(), function( undef, mark ) { >+ if ( mark.widget ) mark.widget.clearToText(); >+ } ); >+ var record = TextMARC.TextToRecord( this.cm.getValue() ); >+ for ( var line = 0; line <= this.cm.lastLine(); line++ ) { >+ if ( Preferences.user.fieldWidgets ) Widget.UpdateLine( this, line ); >+ } >+ >+ this.textMode = false; >+ >+ return record; >+ }; >+ >+ MARCEditor.prototype.getLineInfo = function( pos ) { >+ var contents = this.cm.getLine( pos.line ); >+ if ( contents == null ) return {}; >+ >+ var tagNumber = contents.match( /^([A-Za-z0-9]{3}) / ); >+ >+ if ( !tagNumber ) return null; // No tag at all on this line >+ tagNumber = tagNumber[1]; >+ >+ if ( tagNumber < '010' ) return { tagNumber: tagNumber, contents: contents }; // No current subfield >+ >+ var matcher = /[$|Çâ¡]([a-z0-9%]) /g; >+ var match; >+ >+ var subfields = []; >+ var currentSubfield; >+ >+ while ( ( match = matcher.exec(contents) ) ) { >+ subfields.push( { code: match[1], ch: match.index } ); >+ if ( match.index < pos.ch ) currentSubfield = match[1]; >+ } >+ >+ return { tagNumber: tagNumber, subfields: subfields, currentSubfield: currentSubfield, contents: contents }; >+ }; >+ >+ MARCEditor.prototype.addError = function( line, error ) { >+ var found = false; >+ var options = {}; >+ >+ if ( line == null ) { >+ line = 0; >+ options.above = true; >+ } >+ >+ $.each( this.cm.getLineHandle(line).widgets || [], function( undef, widget ) { >+ if ( !widget.isErrorMarker ) return; >+ >+ found = true; >+ >+ $( widget.node ).append( '; ' + error ); >+ widget.changed(); >+ >+ return false; >+ } ); >+ >+ if ( found ) return; >+ >+ var node = $( '<div class="structure-error"><i class="icon-remove"></i> ' + error + '</div>' )[0]; >+ var widget = this.cm.addLineWidget( line, node, options ); >+ >+ widget.node = node; >+ widget.isErrorMarker = true; >+ }; >+ >+ MARCEditor.prototype.removeErrors = function() { >+ for ( var line = 0; line < this.cm.lineCount(); line++ ) { >+ $.each( this.cm.getLineHandle( line ).widgets || [], function( undef, lineWidget ) { >+ if ( lineWidget.isErrorMarker ) lineWidget.clear(); >+ } ); >+ } >+ }; >+ >+ MARCEditor.prototype.getFixedField = function(field) { >+ field += ' '; >+ for ( var line = 0; line < this.cm.lineCount(); line++ ) { >+ var contents = this.cm.getLine(line); >+ if ( contents.substr( 0, 4 ) != field ) continue; >+ >+ var marks = this.cm.findMarksAt( { line: line, ch: 4 } ); >+ if ( marks[0] && marks[0].widget ) { >+ return marks[0].widget.text; >+ } else { >+ return contents.substr(4); >+ } >+ } >+ >+ return null; >+ }; >+ >+ MARCEditor.prototype.startNotify = function() { >+ if ( this.notifyTimeout ) clearTimeout( this.notifyTimeout ); >+ this.notifyTimeout = setTimeout( $.proxy( function() { >+ this.notifyAll(); >+ >+ this.notifyTimeout = null; >+ }, this ), NOTIFY_TIMEOUT ); >+ }; >+ >+ MARCEditor.prototype.notifyAll = function() { >+ $.each( this.subscribers, $.proxy( function( undef, subscriber ) { >+ subscriber(this); >+ }, this ) ); >+ }; >+ >+ MARCEditor.prototype.subscribe = function( subscriber ) { >+ this.subscribers.push( subscriber ); >+ }; >+ >+ return MARCEditor; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-mode.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-mode.js >new file mode 100644 >index 0000000..bfa0174 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-mode.js >@@ -0,0 +1,129 @@ >+/** >+ * Textual MARC mode for CodeMirror. >+ * Copyright (c) 2013 ByWater >+ */ >+ >+// Expected format: 245 _ 1 $a Pizza |c 34ars >+ >+CodeMirror.defineMode( 'marc', function( config, modeConfig ) { >+ modeConfig.nonRepeatableTags = modeConfig.nonRepeatableTags || {}; >+ modeConfig.nonRepeatableSubfields = modeConfig.nonRepeatableSubfields || {}; >+ >+ return { >+ startState: function( prevState ) { >+ var state = prevState || {}; >+ >+ if ( !prevState ) { >+ state.seenTags = {}; >+ } >+ >+ state.indicatorNeeded = false; >+ state.subAllowed = true; >+ state.subfieldCode = undefined; >+ state.tagNumber = undefined; >+ state.seenSubfields = {}; >+ >+ return state; >+ }, >+ copyState: function( prevState ) { >+ var result = $.extend( {}, prevState ); >+ result.seenTags = $.extend( {}, prevState.seenTags ); >+ result.seenSubfields = $.extend( {}, prevState.seenSubfields ); >+ >+ return result; >+ }, >+ token: function( stream, state ) { >+ var match; >+ if ( stream.sol() ) { >+ this.startState( state ); >+ if ( match = stream.match( /[0-9A-Za-z]+/ ) ) { >+ match = match[0]; >+ if ( match.length != 3 ) { >+ if ( stream.eol() && match.length < 3 ) { >+ // Don't show error for incomplete number >+ return 'tagnumber'; >+ } else { >+ stream.skipToEnd(); >+ return 'error'; >+ } >+ } >+ >+ state.tagNumber = match; >+ if ( state.tagNumber < '010' ) { >+ // Control field >+ state.subAllowed = false; >+ } >+ >+ if ( state.seenTags[state.tagNumber] && modeConfig.nonRepeatableTags[state.tagNumber] ) { >+ return 'bad-tagnumber'; >+ } else { >+ state.seenTags[state.tagNumber] = true; >+ return 'tagnumber'; >+ } >+ } else { >+ stream.skipToEnd(); >+ return 'error'; >+ } >+ } >+ >+ if ( stream.eol() ) { >+ return; >+ } >+ >+ if ( !state.subAllowed && stream.pos == 3 ) { >+ if ( stream.next() == ' ' ) { >+ return 'reqspace'; >+ } else { >+ stream.skipToEnd(); >+ return 'error'; >+ } >+ } >+ >+ if ( stream.pos < 8 && state.subAllowed ) { >+ switch ( stream.pos ) { >+ case 3: >+ case 5: >+ case 7: >+ if ( stream.next() == ' ' ) { >+ return 'reqspace'; >+ } else { >+ stream.skipToEnd(); >+ return 'error'; >+ } >+ case 4: >+ case 6: >+ if ( /[0-9A-Za-z_]/.test( stream.next() ) ) { >+ return 'indicator'; >+ } else { >+ stream.skipToEnd(); >+ return 'error'; >+ } >+ } >+ } >+ >+ if ( state.subAllowed ) { >+ if ( stream.pos != 8 && stream.match( /[^$|Çâ¡]+/ ) ) return; >+ >+ if ( stream.eat( /[$|Çâ¡]/ ) ) { >+ var subfieldCode; >+ if ( ( subfieldCode = stream.eat( /[a-z0-9%]/ ) ) && stream.eat( ' ' ) ) { >+ state.subfieldCode = subfieldCode; >+ if ( state.seenSubfields[state.subfieldCode] && ( modeConfig.nonRepeatableSubfields[state.tagNumber] || {} )[state.subfieldCode] ) { >+ return 'bad-subfieldcode'; >+ } else { >+ state.seenSubfields[state.subfieldCode] = true; >+ return 'subfieldcode'; >+ } >+ } >+ } >+ >+ if ( stream.pos < 11 && ( !stream.eol() || stream.pos == 8 ) ) { >+ stream.skipToEnd(); >+ return 'error'; >+ } >+ } else { >+ stream.skipToEnd(); >+ } >+ } >+ }; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-record.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-record.js >new file mode 100644 >index 0000000..95adf85 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-record.js >@@ -0,0 +1,351 @@ >+/** >+ * Adapted and cleaned up from biblios.net, which is purportedly under the GPL. >+ * Source: http://git.librarypolice.com/?p=biblios.git;a=blob_plain;f=plugins/marc21editor/marcrecord.js;hb=master >+ * >+ * ISO2709 import/export is cribbed from marcjs, which is under the MIT license. >+ * Source: https://github.com/fredericd/marcjs/blob/master/lib/marcjs.js >+ */ >+ >+define( function() { >+ var MARC = {}; >+ >+ var _escape_map = { >+ "<": "<", >+ "&": "&", >+ "\"": """ >+ }; >+ >+ function _escape(str) { >+ return str.replace( /[<&"]/, function (c) { return _escape_map[c] } ); >+ } >+ >+ function _intpadded(i, digits) { >+ i = i + ''; >+ while (i.length < digits) { >+ i = '0' + i; >+ } >+ return i; >+ } >+ >+ MARC.Record = function (fieldlist) { >+ this._fieldlist = fieldlist || []; >+ } >+ >+ $.extend( MARC.Record.prototype, { >+ leader: function(val) { >+ var field = this.field('000'); >+ >+ if (val) { >+ if (field) { >+ field.subfield( '@', val ); >+ } else { >+ field = new MARC.Field( '000', '', '', [ [ '@', val ] ] ); >+ this.addFieldGrouped(field); >+ } >+ } else { >+ return ( field && field.subfield('@') ) || ' nam a22 7a 4500'; >+ } >+ }, >+ >+ /** >+ * If a tagnumber is given, returns all fields with that tagnumber. >+ * Otherwise, returns all fields. >+ */ >+ fields: function(fieldno) { >+ if (!fieldno) return this._fieldlist; >+ >+ var results = []; >+ for(var i=0; i<this._fieldlist.length; i++){ >+ if( this._fieldlist[i].tagnumber() == fieldno ) { >+ results.push(this._fieldlist[i]); >+ } >+ } >+ >+ return results; >+ }, >+ >+ /** >+ * Returns the first field with the given tagnumber, or false. >+ */ >+ field: function(fieldno) { >+ for(var i=0; i<this._fieldlist.length; i++){ >+ if( this._fieldlist[i].tagnumber() == fieldno ) { >+ return this._fieldlist[i]; >+ } >+ } >+ return false; >+ }, >+ >+ /** >+ * Adds the given MARC.Field to the record, at the end. >+ */ >+ addField: function(field) { >+ this._fieldlist.push(field); >+ return true; >+ }, >+ >+ /** >+ * Adds the given MARC.Field to the record, at the end of the matching >+ * x00 group. If a record has a 100, 245 and 300 field, for instance, a >+ * 260 field would be added after the 245 field. >+ */ >+ addFieldGrouped: function(field) { >+ for ( var i = this._fieldlist.length - 1; i >= 0; i-- ) { >+ if ( this._fieldlist[i].tagnumber()[0] <= field.tagnumber()[0] ) { >+ this._fieldlist.splice(i+1, 0, field); >+ return true; >+ } >+ } >+ this._fieldlist.push(field); >+ return true; >+ }, >+ >+ /** >+ * Removes the first field with the given tagnumber. Returns false if no >+ * such field was found. >+ */ >+ removeField: function(fieldno) { >+ for(var i=0; i<this._fieldlist.length; i++){ >+ if( this._fieldlist[i].tagnumber() == fieldno ) { >+ this._fieldlist.splice(i, 1); >+ return true; >+ } >+ } >+ return false; >+ }, >+ >+ /** >+ * Check to see if this record contains a field with the given >+ * tagnumber. >+ */ >+ hasField: function(fieldno) { >+ for(var i=0; i<this._fieldlist.length; i++){ >+ if( this._fieldlist[i].tagnumber() == fieldno ) { >+ return true; >+ } >+ } >+ return false; >+ }, >+ >+ toXML: function() { >+ var xml = '<record xmlns="http://www.loc.gov/MARC21/slim">'; >+ for(var i=0; i<this._fieldlist.length; i++){ >+ xml += this._fieldlist[i].toXML(); >+ } >+ xml += '</record>'; >+ return xml; >+ }, >+ >+ /** >+ * Truncates this record, and loads in the data from the given MARCXML >+ * document. >+ */ >+ loadMARCXML: function(xmldoc) { >+ var record = this; >+ record.xmlSource = xmldoc; >+ this._fieldlist.length = 0; >+ this.leader( $('leader', xmldoc).text() ); >+ $('controlfield', xmldoc).each( function(i) { >+ val = $(this).text(); >+ tagnum = $(this).attr('tag'); >+ record._fieldlist.push( new MARC.Field(tagnum, '', '', [ [ '@', val ] ]) ); >+ }); >+ $('datafield', xmldoc).each(function(i) { >+ var value = $(this).text(); >+ var tagnum = $(this).attr('tag'); >+ var ind1 = $(this).attr('ind1') || ' '; >+ var ind2 = $(this).attr('ind2') || ' '; >+ var subfields = new Array(); >+ $('subfield', this).each(function(j) { >+ var sfval = $(this).text(); >+ var sfcode = $(this).attr('code'); >+ subfields.push( [ sfcode, sfval ] ); >+ }); >+ record._fieldlist.push( new MARC.Field(tagnum, ind1, ind2, subfields) ); >+ }); >+ }, >+ >+ toISO2709: function() { >+ var FT = '\x1e', RT = '\x1d', DE = '\x1f'; >+ var directory = '', >+ from = 0, >+ chunks = ['', '']; >+ >+ $.each( this._fieldlist, function( undef, element ) { >+ var chunk = ''; >+ var tag = element.tagnumber(); >+ if (tag == '000') { >+ return; >+ } else if (tag < '010') { >+ chunk = element.subfields()[0][1]; >+ } else { >+ chunk = element.indicators().join(''); >+ $.each( element.subfields(), function( undef, subfield ) { >+ chunk += DE + subfield[0] + subfield[1]; >+ } ); >+ } >+ chunk += FT; >+ chunks.push(chunk); >+ directory += _intpadded(tag,3) + _intpadded(chunk.length,4) + _intpadded(from,5); >+ from += chunk.length; >+ }); >+ >+ chunks.push(RT); >+ directory += FT; >+ var offset = 24 + 12 * (this._fieldlist.length - 1) + 1; >+ var length = offset + from + 1; >+ var leader = this.leader(); >+ leader = _intpadded(length,5) + leader.substr(5,7) + _intpadded(offset,5) + >+ leader.substr(17); >+ chunks[0] = leader; >+ chunks[1] = directory; >+ return chunks.join(''); >+ }, >+ >+ loadISO2709: function(data) { >+ this._fieldlist.length = 0; >+ this.leader(data.substr(0, 24)); >+ var directory_len = parseInt(data.substring(12, 17), 0) - 25, >+ number_of_tag = directory_len / 12; >+ for (var i = 0; i < number_of_tag; i++) { >+ var off = 24 + i * 12, >+ tag = data.substring(off, off+3), >+ len = parseInt(data.substring(off+3, off+7), 0) - 1, >+ pos = parseInt(data.substring(off+7, off+12), 0) + 25 + directory_len, >+ value = data.substring(pos, pos+len); >+ if ( parseInt(tag) < 10 ) { >+ this.addField( new MARC.Field( tag, '', '', [ [ '@', value ] ] ) ); >+ } else { >+ if ( value.indexOf('\x1F') ) { // There are some letters >+ var ind1 = value.substr(0, 1), ind2 = value.substr(1, 1); >+ var subfields = []; >+ >+ $.each( value.substr(3).split('\x1f'), function( undef, v ) { >+ if (v.length < 2) return; >+ subfields.push([v.substr(0, 1), v.substr(1)]); >+ } ); >+ >+ this.addField( new MARC.Field( tag, ind1, ind2, subfields ) ); >+ } >+ } >+ } >+ } >+ } ); >+ >+ MARC.Field = function(tagnumber, indicator1, indicator2, subfields) { >+ this._tagnumber = tagnumber; >+ this._indicators = [ indicator1, indicator2 ]; >+ this._subfields = subfields; >+ }; >+ >+ $.extend( MARC.Field.prototype, { >+ tagnumber: function() { >+ return this._tagnumber; >+ }, >+ >+ isControlField: function() { >+ return this._tagnumber < '010'; >+ }, >+ >+ indicator: function(num, val) { >+ if( val != null ) { >+ this._indicators[num] = val; >+ } >+ return this._indicators[num]; >+ }, >+ >+ indicators: function() { >+ return this._indicators; >+ }, >+ >+ hasSubfield: function(code) { >+ for(var i = 0; i<this._subfields.length; i++) { >+ if( this._subfields[i][0] == code ) { >+ return true; >+ } >+ } >+ return false; >+ }, >+ >+ removeSubfield: function(code) { >+ for(var i = 0; i<this._subfields.length; i++) { >+ if( this._subfields[i][0] == code ) { >+ this._subfields.splice(i,1); >+ return true; >+ } >+ } >+ return false; >+ }, >+ >+ subfields: function() { >+ return this._subfields; >+ }, >+ >+ addSubfield: function(sf) { >+ this._subfields.push(sf); >+ return true; >+ }, >+ >+ addSubfieldGrouped: function(sf) { >+ function _kind( sc ) { >+ if ( /[a-z]/.test( sc ) ) { >+ return 0; >+ } else if ( /[0-9]/.test( sc ) ) { >+ return 1; >+ } else { >+ return 2; >+ } >+ } >+ >+ for ( var i = this._subfields.length - 1; i >= 0; i-- ) { >+ if ( i == 0 && _kind( sf[0] ) < _kind( this._subfields[i][0] ) ) { >+ this._subfields.splice( 0, 0, sf ); >+ return true; >+ } else if ( _kind( this._subfields[i][0] ) <= _kind( sf[0] ) ) { >+ this._subfields.splice( i + 1, 0, sf ); >+ return true; >+ } >+ } >+ >+ this._subfields.push(sf); >+ return true; >+ }, >+ >+ subfield: function(code, val) { >+ var sf = ''; >+ for(var i = 0; i<this._subfields.length; i++) { >+ if( this._subfields[i][0] == code ) { >+ sf = this._subfields[i]; >+ if( val != null ) { >+ sf[1] = val; >+ } >+ return sf[1]; >+ } >+ } >+ return false; >+ }, >+ >+ toXML: function() { >+ // decide if it's controlfield of datafield >+ if( this._tagnumber == '000') { >+ return '<leader>' + _escape( this._subfields[0][1] ) + '</leader>'; >+ } else if ( this._tagnumber < '010' ) { >+ return '<controlfield tag="' + this._tagnumber + '">' + _escape( this._subfields[0][1] ) + '</controlfield>'; >+ } else { >+ var result = '<datafield tag="' + this._tagnumber + '"'; >+ result += ' ind1="' + this._indicators[0] + '"'; >+ result += ' ind2="' + this._indicators[1] + '">'; >+ for( var i = 0; i< this._subfields.length; i++) { >+ result += '<subfield code="' + this._subfields[i][0] + '">'; >+ result += _escape( this._subfields[i][1] ); >+ result += '</subfield>'; >+ } >+ result += '</datafield>'; >+ >+ return result; >+ } >+ } >+ } ); >+ >+ return MARC; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js >new file mode 100644 >index 0000000..1372dbf >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js >@@ -0,0 +1,28 @@ >+define( function() { >+ var Preferences = { >+ Load: function( borrowernumber ) { >+ if ( !borrowernumber ) return; >+ var saved_prefs; >+ try { >+ saved_prefs = JSON.parse( localStorage[ 'cateditor_preferences_' + borrowernumber ] ); >+ } catch (e) {} >+ >+ Preferences.user = $.extend( { >+ // Preference defaults >+ fieldWidgets: true, >+ font: 'monospace', >+ fontSize: '1em', >+ macros: {}, >+ }, saved_prefs ); >+ }, >+ >+ Save: function( borrowernumber ) { >+ if ( !borrowernumber ) return; >+ if ( !Preferences.user ) Preferences.Load(borrowernumber); >+ >+ localStorage[ 'cateditor_preferences_' + borrowernumber ] = JSON.stringify(Preferences.user); >+ }, >+ }; >+ >+ return Preferences; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/search.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/search.js >new file mode 100644 >index 0000000..06dd8fc >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/search.js >@@ -0,0 +1,87 @@ >+define( [ 'marc-record', 'pz2' ], function( MARC, Pazpar2 ) { >+ //var _pz; >+ var _onresults; >+ var _recordCache = {}; >+ var _options; >+ >+ var Search = { >+ Init: function( targets, options ) { >+ var initOpts = {}; >+ >+ _onresults = options.onresults; >+ >+ $.each( targets, function ( url, info ) { >+ initOpts[ 'pz:name[' + url + ']' ] = info.name; >+ initOpts[ 'pz:queryencoding[' + url + ']' ] = info.encoding; >+ initOpts[ 'pz:xslt[' + url + ']' ] = info.kohasyntax.toLowerCase() + '-work-groups.xsl'; >+ initOpts[ 'pz:requestsyntax[' + url + ']' ] = info.syntax; >+ >+ // Load in default CCL mappings >+ // Pazpar2 seems to have a bug where wildcard cclmaps are ignored. >+ // What an incredible surprise. >+ initOpts[ 'pz:cclmap:term[' + url + ']' ] = 'u=1016 t=l,r s=al'; >+ initOpts[ 'pz:cclmap:Author-name[' + url + ']' ] = 'u=1004 s=al'; >+ initOpts[ 'pz:cclmap:Classification-Dewey[' + url + ']' ] = 'u=13'; >+ initOpts[ 'pz:cclmap:Classification-LC[' + url + ']' ] = 'u=16'; >+ initOpts[ 'pz:cclmap:Date[' + url + ']' ] = 'u=30 r=r'; >+ initOpts[ 'pz:cclmap:Identifier-ISBN[' + url + ']' ] = 'u=7'; >+ initOpts[ 'pz:cclmap:Identifier-ISSN[' + url + ']' ] = 'u=8'; >+ initOpts[ 'pz:cclmap:Identifier-publisher-for-music[' + url + ']' ] = 'u=51'; >+ initOpts[ 'pz:cclmap:Identifier-standard[' + url + ']' ] = 'u=1007'; >+ initOpts[ 'pz:cclmap:LC-card-number[' + url + ']' ] = 'u=9'; >+ initOpts[ 'pz:cclmap:Local-number[' + url + ']' ] = 'u=12'; >+ initOpts[ 'pz:cclmap:Subject[' + url + ']' ] = 'u=21 s=al'; >+ initOpts[ 'pz:cclmap:Title[' + url + ']' ] = 'u=4 s=al'; >+ >+ if ( info.authentication ) initOpts[ 'pz:authentication[' + url + ']' ] = info.authentication; >+ } ); >+ >+ _options = $.extend( { >+ initopts: initOpts, >+ onshow: Search._onshow, >+ errorhandler: options.onerror, >+ }, options ); >+ >+ _pz = new Pazpar2( _options ); >+ }, >+ Reconnect: function() { >+ _pz.reset(); >+ _pz = new Pazpar2( _options ); >+ }, >+ Start: function( targets, q, limit ) { >+ Search.includedTargets = []; >+ recordcache = {}; >+ >+ $.each( targets, function ( url, info ) { >+ if ( !info.disabled ) Search.includedTargets.push( url ); >+ } ); >+ >+ _pz.search( q, limit, 'relevance:0', 'pz:id=' + Search.includedTargets.join( '|' ) ); >+ }, >+ Fetch: function( offset ) { >+ _pz.show( offset ); >+ }, >+ GetDetailedRecord: function( recid, callback ) { >+ if ( _recordCache[recid] ) { >+ callback( _recordCache[recid] ); >+ return; >+ } >+ >+ _pz.record( recid, 0, undefined, { callback: function(data) { >+ var record = _recordCache[recid] = new MARC.Record(); >+ record.loadMARCXML(data.xmlDoc); >+ >+ callback(record); >+ } } ); >+ }, >+ _onshow: function( data ) { >+ $.each( data.hits, function( undef, hit ) { >+ hit.id = 'search:' + encodeURIComponent( hit.recid[0] ); >+ } ); >+ >+ _onresults( data ); >+ }, >+ }; >+ >+ return Search; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/text-marc.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/text-marc.js >new file mode 100644 >index 0000000..5c76c62 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/text-marc.js >@@ -0,0 +1,78 @@ >+define( [ 'marc-record' ], function( MARC ) { >+ return { >+ RecordToText: function( record ) { >+ var lines = []; >+ var fields = record.fields(); >+ >+ for ( var i = 0; i < fields.length; i++ ) { >+ var field = fields[i]; >+ >+ if ( field.isControlField() ) { >+ lines.push( field.tagnumber() + ' ' + field.subfield( '@' ) ); >+ } else { >+ var result = [ field.tagnumber() + ' ' ]; >+ >+ result.push( field.indicator(0) == ' ' ? '_' : field.indicator(0), ' ' ); >+ result.push( field.indicator(1) == ' ' ? '_' : field.indicator(1), ' ' ); >+ >+ $.each( field.subfields(), function( i, subfield ) { >+ result.push( '$' + subfield[0] + ' ' + subfield[1] ); >+ } ); >+ >+ lines.push( result.join('') ); >+ } >+ } >+ >+ return lines.join('\n'); >+ }, >+ >+ TextToRecord: function( text ) { >+ var record = new MARC.Record(); >+ var errors = []; >+ >+ $.each( text.split('\n'), function( i, line ) { >+ var tagNumber = line.match( /^([A-Za-z0-9]{3}) / ); >+ >+ if ( !tagNumber ) { >+ errors.push( { type: 'noTag', line: i } ); >+ return; >+ } >+ tagNumber = tagNumber[1]; >+ >+ if ( tagNumber < '010' ) { >+ var field = new MARC.Field( tagNumber, ' ', ' ', [ [ '@', line.substring( 4 ) ] ] ); >+ field.sourceLine = i; >+ record.addField( field ); >+ } else { >+ var indicators = line.match( /^... ([0-9A-Za-z_]) ([0-9A-Za-z_])/ ); >+ if ( !indicators ) { >+ errors.push( { type: 'noIndicators', line: i } ); >+ return; >+ } >+ >+ var field = new MARC.Field( tagNumber, ( indicators[1] == '_' ? ' ' : indicators[1] ), ( indicators[2] == '_' ? ' ' : indicators[2] ), [] ); >+ >+ var matcher = /[$|Çâ¡]([a-z0-9%]) /g; >+ var match; >+ >+ var subfields = []; >+ >+ while ( ( match = matcher.exec(line) ) ) { >+ subfields.push( { code: match[1], ch: match.index } ); >+ } >+ >+ $.each( subfields, function( i, subfield ) { >+ var next = subfields[ i + 1 ]; >+ >+ field.addSubfield( [ subfield.code, line.substring( subfield.ch + 3, next ? next.ch : line.length ) ] ); >+ } ); >+ >+ field.sourceLine = i; >+ record.addField( field ); >+ } >+ } ); >+ >+ return errors.length ? { errors: errors } : record; >+ } >+ }; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/widget.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/widget.js >new file mode 100644 >index 0000000..08122ef >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/widget.js >@@ -0,0 +1,169 @@ >+define( function() { >+ var Widget = { >+ Base: { >+ // Marker utils >+ clearToText: function() { >+ var range = this.mark.find(); >+ this.mark.doc.replaceRange( this.text, range.from, range.to, 'marcAware' ); >+ }, >+ >+ reCreate: function() { >+ this.postCreate( this.node, this.mark ); >+ }, >+ >+ // Fixed field utils >+ bindFixed: function( sel, start, end ) { >+ var $node = $( this.node ).find( sel ); >+ $node.val( this.getFixed( start, end ) ); >+ >+ $node.change( $.proxy( function() { >+ this.setFixed( start, end, $node.val(), '+input' ); >+ }, this ) ); >+ }, >+ >+ getFixed: function( start, end ) { >+ return this.text.substring( start, end ); >+ }, >+ >+ setFixed: function( start, end, value, source ) { >+ this.setText( this.text.substring( 0, start ) + this.padString( value.toString().substr( 0, end - start ), end - start ) + this.text.substring( end ), source ); >+ }, >+ >+ setText: function( text, source ) { >+ if ( source == '+input' ) this.mark.doc.cm.addLineClass( this.mark.find().from.line, 'wrapper', 'modified-line' ); >+ this.text = text; >+ this.editor.startNotify(); >+ }, >+ >+ // Template utils >+ insertTemplate: function( sel ) { >+ var wsOnly = /^\s*$/; >+ $( sel ).contents().clone().each( function() { >+ if ( this.nodeType == Node.TEXT_NODE ) { >+ this.data = this.data.replace( /^\s+|\s+$/g, '' ); >+ } >+ } ).appendTo( this.node ); >+ }, >+ >+ padNum: function( number, length ) { >+ var result = number.toString(); >+ >+ while ( result.length < length ) result = '0' + result; >+ >+ return result; >+ }, >+ >+ padString: function( result, length ) { >+ while ( result.length < length ) result = ' ' + result; >+ >+ return result; >+ } >+ }, >+ >+ ActivateAt: function( editor, cur, idx ) { >+ var marks = editor.findMarksAt( cur ); >+ if ( !marks.length ) return false; >+ >+ var $input = $(marks[0].widget.node).find('input, select').eq(idx || 0); >+ if ( !$input.length ) return false; >+ >+ $input.focus(); >+ return true; >+ }, >+ >+ Notify: function( editor ) { >+ $.each( editor.cm.getAllMarks(), function( undef, mark ) { >+ if ( mark.widget && mark.widget.notify ) mark.widget.notify(); >+ } ); >+ }, >+ >+ UpdateLine: function( editor, line ) { >+ var info = editor.getLineInfo( { line: line, ch: 0 } ); >+ var lineh = editor.cm.getLineHandle( line ); >+ if ( !lineh ) return; >+ >+ if ( !info ) { >+ if ( lineh.markedSpans ) { >+ $.each( lineh.markedSpans, function ( undef, span ) { >+ var mark = span.marker; >+ if ( !mark.widget ) return; >+ >+ mark.widget.clearToText(); >+ } ); >+ } >+ return; >+ } >+ >+ var subfields = []; >+ >+ var end = editor.cm.getLine( line ).length; >+ if ( info.tagNumber < '010' ) { >+ if ( end >= 4 ) subfields.push( { code: '@', from: 4, to: end } ); >+ } else { >+ for ( var i = 0; i < info.subfields.length; i++ ) { >+ var next = ( i < info.subfields.length - 1 ) ? info.subfields[i + 1].ch : end; >+ subfields.push( { code: info.subfields[i].code, from: info.subfields[i].ch + 3, to: next } ); >+ } >+ } >+ >+ $.each( subfields, function ( undef, subfield ) { >+ var id = info.tagNumber + subfield.code; >+ var marks = editor.cm.findMarksAt( { line: line, ch: subfield.from } ); >+ >+ if ( marks.length ) { >+ if ( marks[0].id == id ) { >+ return; >+ } else { >+ marks[0].widget.clearToText(); >+ } >+ } >+ >+ if ( !editorWidgets[id] ) return; >+ var fullBase = $.extend( Object.create( Widget.Base ), editorWidgets[id] ); >+ var widget = Object.create( fullBase ); >+ >+ if ( subfield.from == subfield.to ) { >+ editor.cm.replaceRange( widget.makeTemplate ? widget.makeTemplate() : '<empty>', { line: line, ch: subfield.from }, null, 'marcWidgetPrefill' ); >+ return; // We'll do the actual work when the change event is triggered again >+ } >+ >+ var text = editor.cm.getRange( { line: line, ch: subfield.from }, { line: line, ch: subfield.to } ); >+ >+ widget.text = text; >+ var node = widget.init(); >+ >+ var mark = editor.cm.markText( { line: line, ch: subfield.from }, { line: line, ch: subfield.to }, { >+ inclusiveLeft: false, >+ inclusiveRight: false, >+ replacedWith: node, >+ } ); >+ >+ mark.id = id; >+ mark.widget = widget; >+ >+ widget.node = node; >+ widget.mark = mark; >+ widget.editor = editor; >+ >+ if ( widget.postCreate ) { >+ widget.postCreate(); >+ mark.changed(); >+ } >+ >+ var $lastInput = $(widget.node).find('input, select').eq(-1); >+ if ( $lastInput.length ) { >+ $lastInput.bind( 'keypress', 'tab', function() { >+ var cur = editor.cm.getCursor(); >+ editor.cm.setCursor( { line: cur.line } ); >+ // FIXME: ugly hack >+ editor.cm.options.extraKeys.Tab( editor.cm ); >+ editor.focus(); >+ return false; >+ } ); >+ } >+ } ); >+ }, >+ }; >+ >+ return Widget; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/xslt.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/xslt.js >new file mode 100644 >index 0000000..fc9b9a7 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/xslt.js >@@ -0,0 +1,68 @@ >+define( function() { >+ var XSLT = { >+ TransformToFragment: function( xmlDoc, xslDoc ) { >+ if ( window.XSLTProcessor ) { >+ var proc = new XSLTProcessor(); >+ proc.importStylesheet( xslDoc ); >+ proc.setParameter( null, 'showAvailability', false ); >+ return (new XMLSerializer).serializeToString( proc.transformToFragment( xmlDoc, document ) ); >+ } else if ( window.ActiveXObject ) { >+ var xslt = new ActiveXObject( "Msxml2.XSLTemplate" ); >+ xslt.stylesheet = xslDoc; >+ var xslProc = xslt.createProcessor(); >+ xslProc.input = xmlDoc; >+ xslProc.addParameter( 'showAvailability', false ); >+ xslProc.transform(); >+ return xslProc.output; >+ } else { >+ return null; >+ } >+ }, >+ >+ Get: ( function() { >+ // Horrible browser hack, but required due to the following hard-to-detect and long-unfixed bug: >+ // https://bugs.webkit.org/show_bug.cgi?id=60276 >+ var isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor); >+ var isSafari = /Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor); >+ >+ if ( !isChrome && !isSafari ) return $.get; >+ >+ return function( url ) { >+ var result = new jQuery.Deferred(); >+ var basepath = url.match( /(.*\/)*/ )[0]; >+ >+ $.get( url ).done( function( xslDoc ) { >+ var xslImports = xslDoc.getElementsByTagNameNS( 'http://www.w3.org/1999/XSL/Transform', 'import' ); >+ var importsRemaining = xslImports.length; >+ >+ if ( importsRemaining == 0 ) { >+ result.resolve( xslDoc ); >+ return; >+ } >+ >+ $.each( xslImports, function( i, importElem ) { >+ var path = $( importElem ).attr( 'href' ); >+ if ( !/^(\/|https?:)/.test( path ) ) path = basepath + path; >+ >+ XSLT.Get( path ).done( function( subDoc ) { >+ importsRemaining--; >+ $( importElem ).replaceWith( subDoc.documentElement.childNodes ); >+ >+ if ( importsRemaining == 0 ) result.resolve( xslDoc ); >+ } ).fail( function() { >+ importsRemaining = -1; >+ >+ result.reject(); >+ } ); >+ } ); >+ } ).fail( function() { >+ result.reject(); >+ } ); >+ >+ return result; >+ }; >+ } )(), >+ }; >+ >+ return XSLT; >+} ); >diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/cateditor.css b/koha-tmpl/intranet-tmpl/prog/en/css/cateditor.css >new file mode 100644 >index 0000000..33cfb58 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/css/cateditor.css >@@ -0,0 +1,332 @@ >+/*> Infrastructure */ >+body { >+ padding: 0; >+} >+ >+#loading { >+ background-color: #FFF; >+ cursor: wait; >+ height: 100%; >+ left: 0; >+ opacity: .7; >+ position: fixed; >+ top: 0; >+ width: 100%; >+ z-index: 1000; >+} >+ >+#loading div { >+ background : transparent url(../../img/loading.gif) top left no-repeat; >+ font-size : 175%; >+ font-weight: bold; >+ height: 2em; >+ left: 50%; >+ margin: -1em 0 0 -2.5em; >+ padding-left : 50px; >+ position: absolute; >+ top: 50%; >+ width: 15em; >+} >+ >+/*> MARC editor */ >+#editor .CodeMirror { >+ line-height: 1.2; >+} >+ >+.cm-tagnumber { >+ color: #080; >+ font-weight: bold; >+} >+ >+.cm-bad-tagnumber { >+ color: #A20; >+ font-weight: bold; >+} >+ >+.cm-indicator { >+ color: #884; >+} >+ >+.cm-subfieldcode { >+ background-color: #F4F4F4; >+ color: #187848; >+ border-radius: 3px 8px 8px 3px; >+ border-right: 2px solid white; >+ font-weight: bold; >+ margin-right: -2px; >+} >+ >+.cm-bad-subfieldcode { >+ background-color: #FFD9D9; >+ color: #482828; >+ border-radius: 3px 8px 8px 3px; >+ font-weight: bold; >+} >+ >+#editor .modified-line-gutter { >+ width: 10px; >+} >+ >+#editor .modified-line { >+ background: #F8F8F8; >+ border-left: 5px solid black; >+ margin-left: -10px; >+ padding-left: 5px; >+} >+ >+#editor .CodeMirror-gutters { >+ background: transparent; >+ border-right: none; >+} >+ >+/*> MARC editor widgets */ >+ >+#editor .subfield-widget { >+ color: #538200; >+ border: solid 2px #538200; >+ border-radius: 6px; >+ font-family: inherit; >+ line-height: 2.75; >+ margin: 3px 0; >+ padding: 4px; >+} >+ >+#editor .subfield-widget select { >+ height: 1.5em; >+ vertical-align: middle; >+} >+ >+#editor .subfield-widget select:focus { >+ outline: 1px #83A230 solid; >+} >+ >+#editor .fixed-widget select { >+ width: 3em; >+} >+ >+#editor .hidden-widget { >+ color: #999999; >+ border: solid 2px #AAAAAA; >+ line-height: 2; >+ padding: 2px; >+} >+ >+.structure-error { >+ background: #FFEEEE; >+ font-size: 0.9em; >+ line-height: 1.5; >+ margin: .5em; >+ padding: 0 .5em; >+} >+ >+.structure-error i { >+ vertical-align: text-bottom; >+} >+ >+#statusbar { >+ background-color: #F4F8F9; >+ border: solid 2px #b9d8d9; >+ border-bottom-style: none; >+ border-radius: 6px 6px 0 0; >+ height: 18px; >+ margin-bottom: -32px; >+ overflow: auto; >+ padding: 4px; >+ padding-bottom: 0; >+} >+ >+#statusbar #status-tag-info, #statusbar #status-subfield-info { >+ float: left; >+ overflow: hidden; >+ padding-right: 2%; >+ width: 48%; >+} >+ >+#record-info .label { >+ float: none; >+} >+ >+#record-info .label + span { >+ display: block; >+ padding-left: 1em; >+} >+ >+/*> Search */ >+ >+#advanced-search-ui, #search-results-ui, #macro-ui { >+ background-color: white; >+ border: solid 2px #444; >+ border-radius: 6px 6px 6px 6px; >+ height: 80%; >+ overflow: auto; >+ padding: 10px; >+ width: 90%; >+} >+ >+#quicksearch input, #quicksearch a { >+ font-size: 1.2em; >+ padding: 3px 0; >+ width: 96%; // I have no idea why this is necessary >+} >+ >+#show-advanced-search { >+ display: block; >+ margin-top: .3em; >+} >+ >+#advanced-search-fields { >+ margin: 0; >+ padding: 0; >+} >+ >+#advanced-search-fields li { >+ display: inline-block; >+ list-style-type: none; >+} >+ >+#advanced-search-fields label { >+ display: inline-block; >+ font-weight: bold; >+ padding: 1em 1em 1em 0; >+ width: 10em; >+ text-align: right; >+} >+ >+#advanced-search-fields input { >+ display: inline-block; >+ margin: 0px auto; >+ width: 14em; >+} >+ >+.icon-loading { >+ display: inline-block; >+ height: 16px; >+ width: 16px; >+ background: transparent url("../../img/spinner-small.gif") top left no-repeat; >+ padding: -1px; >+ vertical-align: text-top; >+} >+ >+/*> Search results */ >+ >+#searchresults table { >+ width: 100%; >+} >+ >+.sourcecol { >+ width: 50px; >+} >+ >+.results-info { >+ height: 100px; >+ overflow: auto; >+} >+ >+.toolscol { >+ padding: 0; >+ width: 100px; >+} >+ >+.toolscol ul { >+ margin: 0; >+ padding: 0; >+} >+ >+#searchresults .toolscol li { >+ list-style-type: none; >+ list-style-image: none; >+} >+ >+.toolscol a { >+ border-bottom: 1px solid #BCBCBC; >+ display: block; >+ padding: 0 1em; >+ line-height: 24px; >+} >+ >+.results-marc { >+ font-family: monospace; >+ height: auto; >+ white-space: pre-wrap; >+} >+ >+#searchresults { >+ position: relative; >+} >+ >+#search-overlay { >+ background: white; >+ bottom: 0; >+ font-size: 2em; >+ left: 0; >+ opacity: .7; >+ padding: 2em; >+ position: absolute; >+ right: 0; >+ text-align: center; >+ top: 0; >+ z-index: 9001; >+} >+ >+/*> Macros */ >+ >+#macro-ui .CodeMirror { >+ height: 400px; >+ width: 100%; >+} >+ >+#macro-save-message { >+ color: #666; >+ font-size: 13px; >+ float: right; >+ line-height: 26px; >+} >+ >+#macro-list > li { >+ border: 2px solid #F0F0F0; >+ border-radius: 6px; >+ display: block; >+ font-size: 115%; >+} >+ >+#macro-list > li + li { >+ margin-top: -2px; >+} >+ >+#macro-list .active { >+ background: #EDF4F6; >+ border-color: none; >+} >+ >+#macro-list a { >+ display: block; >+ padding: 6px; >+} >+ >+#macro-list a:focus { >+ outline: none; >+} >+ >+.macro-info { >+ background-color: #F4F4F4; >+ display: none; >+ margin: 0; >+ padding: 10px; >+ text-align: right; >+} >+ >+.macro-info li { >+ color: #666; >+ font-size: 75%; >+ list-style-type: none; >+} >+ >+.macro-info .label { >+ clear: left; >+ font-weight: bold; >+ float: left; >+} >+ >+#macro-list .active .macro-info { >+ display: block; >+} >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc >new file mode 100644 >index 0000000..a831c3b >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc >@@ -0,0 +1,804 @@ >+<script src="/intranet-tmpl/lib/codemirror/codemirror.js"></script> >+<script src="/intranet-tmpl/lib/codemirror/lib/runmode.js"></script> >+<script src="/intranet-tmpl/lib/filesaver.js"></script> >+<script src="/intranet-tmpl/lib/koha/cateditor/marc-mode.js"></script> >+<script src="/intranet-tmpl/lib/require.js"></script> >+<script src="/intranet-tmpl/lib/jquery/plugins/jquery.lightbox_me.js"></script> >+<script> >+require.config( { >+ baseUrl: '/intranet-tmpl/lib/koha/cateditor/', >+ paths: { >+ pz2: '../../pz2', >+ }, >+ shim: { >+ pz2: { exports: 'pz2' }, >+ }, >+} ); >+</script> >+ >+[% IF marcflavour == 'MARC21' %] >+[% PROCESS 'cateditor-widgets-marc21.inc' %] >+[% ELSE %] >+<script>var editorWidgets = {};</script> >+[% END %] >+ >+<script> >+require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'preferences', 'text-marc', 'widget', 'xslt' ], function( KohaBackend, Search, Macros, MARCEditor, MARC, Preferences, TextMARC, Widget, XSLT ) { >+ var xsltResultStylesheets = { >+ [% FOREACH stylesheet IN xslt_result_stylesheets %] >+ '[% stylesheet.syntax %]': XSLT.Get( '[% stylesheet.url %]' ), >+ [% END %] >+ }; >+ >+ var xsltDetailStylesheets = { >+ [% FOREACH stylesheet IN xslt_detail_stylesheets %] >+ '[% stylesheet.syntax %]': XSLT.Get( '[% stylesheet.url %]' ), >+ [% END %] >+ }; >+ >+ var z3950Targets = { >+ [% FOREACH target = z3950_targets %] >+ '[% target.host %]:[% target.port %]/[% target.db %]': { >+ 'name': '[% target.name %]', >+ 'authentication': '[% target.userid %]:[% target.password %]', >+ 'syntax': '[% target.syntax %]', >+ 'kohasyntax': '[% target.syntax == 'USMARC' ? 'MARC21' : target.syntax %]', >+ 'encoding': '[% target.encoding %]', >+ 'checked': [% target.checked ? 'true' : 'false' %], >+ }, >+ [% END %] >+ }; >+ >+ // The columns that should show up in a search, in order, and keyed by the corresponding <metadata> tag in the XSL and Pazpar2 config >+ var z3950Labels = [ >+ [ "md-work-title", "Title" ], >+ [ "md-series-title", "Series-title" ], >+ [ "md-work-author", "Author" ], >+ [ "md-lccn", "LCCN" ], >+ [ "md-isbn", "ISBN" ], >+ [ "md-issn", "ISSN" ], >+ [ "md-medium", "Medium" ], >+ [ "md-edition", "Edition" ], >+ [ "md-description", "Description" ], >+ ]; >+ >+ var state = { >+ backend: '', >+ saveBackend: 'catalog', >+ recordID: undefined >+ }; >+ >+ var editor; >+ var macroEditor; >+ >+ function makeAuthorisedValueWidgets( frameworkCode ) { >+ $.each( KohaBackend.GetAllTagsInfo( frameworkCode ), function( tag, tagInfo ) { >+ $.each( tagInfo.subfields, function( subfield, subfieldInfo ) { >+ if ( !subfieldInfo.authorised_value ) return; >+ >+ var authorisedWidget = { >+ init: function() { >+ var $result = $( '<span class="subfield-widget"></span>' ); >+ >+ return $result[0]; >+ }, >+ postCreate: function() { >+ this.setText( subfieldInfo.defaultvalue ); >+ >+ $( '<select></select>' ).appendTo( this.node ); >+ var $node = $( this.node ).find( 'select' ); >+ $.each( KohaBackend.GetAuthorisedValues( subfieldInfo.authorised_value ), function( undef, authval ) { >+ $node.append( '<option value="' + authval.value + '">' + authval.lib + '</option>' ); >+ } ); >+ $node.val( this.text ); >+ >+ $node.change( $.proxy( function() { >+ this.setText( $node.val() ); >+ }, this ) ); >+ } >+ }; >+ >+ editorWidgets[ tag + subfield ] = authorisedWidget; >+ } ); >+ } ); >+ } >+ >+ function bindGlobalKeys() { >+ function bindKey( key, handler ) { >+ $( document ).bind( 'keydown', key, handler ); >+ $( document ).bind( 'keypress', key, handler ); >+ $( '#sidebar input' ).bind( 'keydown', key, handler ); >+ $( '#editor textarea' ).bind( 'keydown', key, handler ); >+ } >+ >+ shortcut.add( 'ctrl+s', function(event) { >+ $( '#save-record' ).click(); >+ >+ event.preventDefault(); >+ } ); >+ >+ shortcut.add( 'alt+ctrl+k', function(event) { >+ $( '#search-by-keywords' ).focus(); >+ >+ return false; >+ } ); >+ >+ shortcut.add( 'alt+ctrl+a', function(event) { >+ $( '#search-by-author' ).focus(); >+ >+ return false; >+ } ); >+ >+ shortcut.add( 'alt+ctrl+i', function(event) { >+ $( '#search-by-isbn' ).focus(); >+ >+ return false; >+ } ); >+ >+ shortcut.add( 'alt+ctrl+t', function(event) { >+ $( '#search-by-title' ).focus(); >+ >+ return false; >+ } ); >+ >+ $('#quicksearch .search-box').each( function() { >+ shortcut.add( 'enter', $.proxy( function() { >+ var q = this.value; >+ if (!q) return false; >+ >+ showResultsBox(); >+ Search.Start( z3950Targets, $(this).data('qualifier') + q, 20 ); >+ >+ return false; >+ }, this), { target: this, type: 'keypress' } ); >+ } ); >+ } >+ >+ // Record loading >+ var backends = { >+ 'new': { >+ recordLabel: _("new record"), >+ get: function( id, callback ) { >+ record = new MARC.Record(); >+ KohaBackend.FillRecord( '', record ); >+ >+ callback( record ); >+ }, >+ }, >+ 'new-full': { >+ recordLabel: _("new full record"), >+ get: function( id, callback ) { >+ record = new MARC.Record(); >+ KohaBackend.FillRecord( '', record, true ); >+ >+ callback( record ); >+ }, >+ }, >+ 'catalog': { >+ recordLabel: _("catalog record #{ID}"), >+ saveLabel: _("to catalog"), >+ get: function( id, callback ) { >+ if ( !id ) return false; >+ >+ KohaBackend.GetRecord( id, callback ); >+ }, >+ save: function( id, record, done ) { >+ function finishCb( data ) { >+ done( { error: data.error, newRecord: data.marcxml && data.marcxml[0], newId: data.biblionumber && [ 'catalog', data.biblionumber ] } ); >+ } >+ >+ if ( id ) { >+ KohaBackend.SaveRecord( id, record, finishCb ); >+ } else { >+ KohaBackend.CreateRecord( record, finishCb ); >+ } >+ } >+ }, >+ 'iso2709': { >+ saveLabel: _("to ISO2709 (.mrc) file"), >+ save: function( id, record, done ) { >+ saveAs( new Blob( [record.toISO2709()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.mrc' ); >+ >+ done( {} ); >+ } >+ }, >+ 'marcxml': { >+ saveLabel: _("to MARCXML (.xml) file"), >+ save: function( id, record, done ) { >+ saveAs( new Blob( [record.toXML()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.xml' ); >+ >+ done( {} ); >+ } >+ }, >+ 'search': { >+ recordLabel: _("search result"), >+ get: function( id, callback ) { >+ if ( !id ) return false; >+ >+ Search.GetDetailedRecord( decodeURIComponent(id), callback ); >+ }, >+ }, >+ }; >+ >+ function setSource(parts) { >+ state.backend = parts[0]; >+ state.recordID = parts[1]; >+ state.canSave = backends[ state.backend ].save != null; >+ state.saveBackend = state.canSave ? state.backend : 'catalog'; >+ >+ document.location.hash = '#' + parts[0] + ':' + parts[1]; >+ $( '#title' ).text( _("Editing ") + backends[ state.backend ].recordLabel.replace( '{ID}', parts[1] ) ); >+ $( 'title', document.head ).html( _("Koha › Cataloging › Editing ") + backends[ state.backend ].recordLabel.replace( '{ID}', parts[1] ) ); >+ $( '#save-record span' ).text( _("Save ") + backends[ state.saveBackend ].saveLabel ); >+ } >+ >+ function saveRecord( recid, editor, callback ) { >+ var parts = recid.split(':'); >+ if ( parts.length != 2 ) return false; >+ >+ if ( !backends[ parts[0] ] || !backends[ parts[0] ].save ) return false; >+ >+ editor.removeErrors(); >+ var record = editor.getRecord(); >+ >+ if ( record.errors ) { >+ state.saving = false; >+ callback( { error: 'syntax', errors: record.errors } ); >+ return; >+ } >+ >+ var errors = KohaBackend.ValidateRecord( '', record ); >+ if ( errors.length ) { >+ state.saving = false; >+ callback( { error: 'invalid', errors: errors } ); >+ return; >+ } >+ >+ backends[ parts[0] ].save( parts[1], record, function(data) { >+ state.saving = false; >+ >+ if (data.newRecord) { >+ var record = new MARC.Record(); >+ record.loadMARCXML(data.newRecord); >+ editor.displayRecord( record ); >+ } >+ >+ if (data.newId) { >+ setSource(data.newId); >+ } else { >+ setSource( [ state.backend, state.recordID ] ); >+ } >+ >+ if (callback) callback( data ); >+ } ); >+ } >+ >+ function loadRecord( recid, editor, callback ) { >+ var parts = recid.split(':'); >+ if ( parts.length != 2 ) return false; >+ >+ if ( !backends[ parts[0] ] || !backends[ parts[0] ].get ) return false; >+ >+ backends[ parts[0] ].get( parts[1], function( record ) { >+ editor.displayRecord( record ); >+ editor.focus(); >+ >+ if (callback) callback(record); >+ } ); >+ >+ return true; >+ } >+ >+ function openRecord( recid, editor, callback ) { >+ return loadRecord( recid, editor, function ( record ) { >+ setSource( recid.split(':') ); >+ >+ if (callback) callback( record ); >+ } ); >+ } >+ >+ // Search functions >+ function showAdvancedSearch() { >+ $('#advanced-search-ui').lightbox_me(); >+ } >+ >+ function startAdvancedSearch() { >+ var search = ''; >+ >+ $('#advanced-search-ui input').each( function() { >+ if (!this.value) return; >+ if (search) search += ' and '; >+ search += $(this).data('qualifier') + this.value; >+ } ); >+ >+ if (!search) return; >+ >+ $('#advanced-search-ui').trigger('close'); >+ showResultsBox(); >+ Search.Start( z3950Targets, search, 20 ); >+ } >+ >+ function showResultsBox(data) { >+ $('#searchresults thead tr').empty(); >+ $('#searchresults tbody').empty(); >+ $('#search-targetsinfo').empty().append('<li>' + _("Loading...") + '</li>'); >+ $('#search-results-ui').lightbox_me(); >+ } >+ >+ function showDetailedResult( hit, $tr, fetchOnly ) { >+ Search.GetDetailedRecord( hit.recid, function( record ) { >+ if ( fetchOnly ) return; >+ >+ xsltResultStylesheets[ z3950Targets[ hit.location[0]['@id'] ].kohasyntax ].done( function( xslDoc ) { >+ $tr.find( '.results-info' ).html( XSLT.TransformToFragment( record.xmlSource, xslDoc ) ); >+ } ); >+ } ); >+ } >+ >+ function showSearchResults( editor, data ) { >+ $('#searchresults thead tr').empty(); >+ $('#searchresults tbody').empty(); >+ >+ var seenColumns = {}; >+ >+ $.each( data.hits, function( undef, hit ) { >+ for ( key in hit ) { >+ if ( /^md-/.test(key) ) seenColumns[key] = true; >+ } >+ >+ $.each( hit.location, function( undef, location ) { >+ for ( key in location ) { >+ if ( /^md-/.test(key) ) seenColumns[key] = true; >+ } >+ } ); >+ } ); >+ >+ $('#searchresults thead tr').append('<th>' + _("Source") + '</th>'); >+ >+ $.each( z3950Labels, function( undef, label ) { >+ if ( seenColumns[ label[0] ] ) { >+ $('#searchresults thead tr').append( '<th>' + label[1] + '</th>' ); >+ } >+ } ); >+ >+ $('#searchresults thead tr').append('<th>' + _("Tools") + '</th>'); >+ >+ $.each( data.hits, function( undef, hit ) { >+ var result = '<tr>'; >+ result += '<td class="sourcecol">' + hit.location[0]['@name'] + '</td>'; >+ >+ $.each( z3950Labels, function( undef, label ) { >+ if ( !seenColumns[ label[0] ] ) return; >+ >+ if ( hit[ label[0] ] ) { >+ result += '<td class="infocol">' + hit[ label[0] ].join('<br/>') + '</td>'; >+ } else if ( hit.location[0][ label[0] ] ) { >+ result += '<td class="infocol">' + hit.location[0][ label[0] ].join('<br/>') + '</td>'; >+ } else { >+ result += '<td class="infocol"> </td>'; >+ } >+ } ); >+ >+ result += '<td class="toolscol"><ul><li><a href="#" class="marc-link">' + _("View MARC") + '</a></li>'; >+ result += '<li><a href="#" class="open-link">' + _("Import") + '</a></li>'; >+ if ( state.canSave ) result += '<li><a href="#" class="substitute-link" title="' + _("Replace the current record's contents") + '">' + _("Substitute") + '</a></li>'; >+ // REMOVE: (vim syntax highlighting bug) "</a></a>" >+ result += '</ul></td></tr>'; >+ >+ var $tr = $( result ); >+ $tr.find( '.marc-link' ).click( function() { >+ Search.GetDetailedRecord( hit.recid, function( record ) { >+ var $columns = $tr.find( '.infocol' ); >+ CodeMirror.runMode( TextMARC.RecordToText( record ), 'marc', $( '<td class="infocol results-marc" colspan="' + $columns.length + '"></td>' ).replaceAll( $columns.slice(1).remove().end()[0] )[0] ); >+ } ); >+ >+ return false; >+ } ); >+ $tr.find( '.open-link' ).click( function() { >+ $( '#search-results-ui' ).trigger( 'close' ); >+ openRecord( hit.id, editor ); >+ >+ return false; >+ } ); >+ $tr.find( '.substitute-link' ).click( function() { >+ $( '#search-results-ui' ).trigger( 'close' ); >+ loadRecord( hit.id, editor ); >+ >+ return false; >+ } ); >+ $('#searchresults tbody').append( $tr ); >+ >+ //showDetailedResult( hit, $tr, !!data.activeclients ); >+ } ); >+ >+ var $overlay = $('#search-overlay'); >+ $overlay.find('span').text(_("Loading")); >+ $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedTargets.length ) + '%' } ); >+ >+ if ( data.activeclients ) { >+ $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedTargets.length ) + '%' } ); >+ $overlay.show(); >+ } else { >+ $overlay.find('.bar').css( { display: 'block', width: '100%' } ); >+ $overlay.fadeOut(); >+ } >+ } >+ >+ function invalidateSearchResults() { >+ var $overlay = $('#search-overlay'); >+ $overlay.find('span').text(_("Search expired, please try again")); >+ $overlay.find('.bar').css( { display: 'none' } ); >+ $overlay.show(); >+ } >+ >+ function showSearchTargets(data) { >+ $('#search-targetsinfo').empty(); >+ >+ $.each( data, function( undef, target ) { >+ $('#search-targetsinfo').append( '<li>' + target.name + ' (' + target.hits + ')' + '</li>' ); >+ } ); >+ } >+ >+ function handleSearchError(error) { >+ if (error.code == 1) { >+ invalidateSearchResults(); >+ Search.Reconnect(); >+ } else { >+ humanMsg.displayMsg( _("<h3>Internal search error</h3>") + '<p>' + error + '</p>' + _("<p>Please <b>refresh</b> the page and try again."), { className: 'humanError' } ); >+ } >+ } >+ >+ // Preference functions >+ function showPreference( pref ) { >+ var value = Preferences.user[pref]; >+ >+ switch (pref) { >+ case 'fieldWidgets': >+ $( '#set-field-widgets' ).text( value ? _("Show fields verbatim") : _("Show helpers for fixed and coded fields") ); >+ break; >+ case 'font': >+ $( '#editor .CodeMirror' ).css( { fontFamily: value } ); >+ editor.refresh(); >+ break; >+ case 'fontSize': >+ $( '#editor .CodeMirror' ).css( { fontSize: value } ); >+ editor.refresh(); >+ break; >+ case 'macros': >+ showSavedMacros(); >+ break; >+ } >+ } >+ >+ function bindPreference( editor, pref ) { >+ function _addHandler( sel, event, handler ) { >+ $( sel ).on( event, function (e) { >+ e.preventDefault(); >+ handler( e, Preferences.user[pref] ); >+ Preferences.Save( [% USER_INFO.0.borrowernumber %] ); >+ showPreference(pref); >+ } ); >+ } >+ >+ switch (pref) { >+ case 'fieldWidgets': >+ _addHandler( '#set-field-widgets', 'click', function( e, oldValue ) { >+ editor.setUseWidgets( Preferences.user.fieldWidgets = !Preferences.user.fieldWidgets ); >+ } ); >+ break; >+ case 'font': >+ _addHandler( '#prefs-menu .set-font', 'click', function( e, oldValue ) { >+ Preferences.user.font = $( e.target ).css( 'font-family' ); >+ } ); >+ break; >+ case 'fontSize': >+ _addHandler( '#prefs-menu .set-fontSize', 'click', function( e, oldValue ) { >+ Preferences.user.fontSize = $( e.target ).css( 'font-size' ); >+ } ); >+ break; >+ } >+ } >+ >+ function displayPreferences( editor ) { >+ $.each( Preferences.user, function( pref, value ) { >+ showPreference( pref ); >+ bindPreference( editor, pref ); >+ } ); >+ } >+ >+ //> Macro functions >+ function loadMacro( name ) { >+ $( '#macro-list li' ).removeClass( 'active' ); >+ macroEditor.activeMacro = name; >+ >+ if ( !name ) { >+ macroEditor.setValue( '' ); >+ return; >+ } >+ >+ $( '#macro-list li[data-name="' + name + '"]' ).addClass( 'active' ); >+ var macro = Preferences.user.macros[name]; >+ macroEditor.setValue( macro.contents ); >+ if ( macro.history ) macroEditor.setHistory( macro.history ); >+ } >+ >+ function storeMacro( name, macro ) { >+ if ( macro ) { >+ Preferences.user.macros[name] = macro; >+ } else { >+ delete Preferences.user.macros[name]; >+ } >+ >+ Preferences.Save( [% USER_INFO.0.borrowernumber %] ); >+ } >+ >+ function showSavedMacros( macros ) { >+ var scrollTop = $('#macro-list').scrollTop(); >+ $( '#macro-list' ).empty(); >+ var macro_list = $.map( Preferences.user.macros, function( macro, name ) { >+ return $.extend( { name: name }, macro ); >+ } ); >+ macro_list.sort( function( a, b ) { >+ return a.name.localeCompare(b.name); >+ } ); >+ $.each( macro_list, function( undef, macro ) { >+ var $li = $( '<li data-name="' + macro.name + '"><a href="#">' + macro.name + '</a><ol class="macro-info"></ol></li>' ); >+ $li.click( function() { >+ loadMacro(macro.name); >+ return false; >+ } ); >+ if ( macro.name == macroEditor.activeMacro ) $li.addClass( 'active' ); >+ var modified = macro.modified && new Date(macro.modified); >+ $li.find( '.macro-info' ).append( >+ '<li><span class="label">' + _("Last changed:") + '</span>' + >+ ( modified ? modified.toLocaleFormat() : _("never") ) + '</li>' >+ ); >+ $('#macro-list').append($li); >+ } ); >+ var $new_li = $( '<li class="new-macro"><a href="#">' + _("New macro...") + '</a></li>' ); >+ $new_li.click( function() { >+ // TODO: make this a bit less retro >+ var name = prompt(_("Please enter the name for the new macro:")); >+ if (!name) return; >+ >+ if ( !Preferences.user.macros[name] ) storeMacro( name, { contents: "" } ); >+ showSavedMacros(); >+ loadMacro( name ); >+ } ); >+ $('#macro-list').append($new_li); >+ $('#macro-list').scrollTop(scrollTop); >+ } >+ >+ function saveMacro() { >+ var name = macroEditor.activeMacro; >+ >+ if ( !name || macroEditor.savedGeneration == macroEditor.changeGeneration() ) return; >+ >+ macroEditor.savedGeneration = macroEditor.changeGeneration(); >+ storeMacro( name, { contents: macroEditor.getValue(), modified: (new Date()).valueOf(), history: macroEditor.getHistory() } ); >+ $('#macro-save-message').text(_("Saved")); >+ showSavedMacros(); >+ } >+ >+ $(document).ready( function() { >+ // Editor setup >+ editor = new MARCEditor( function (elt) { $(elt).insertAfter('#toolbar') } ); >+ >+ macroEditor = CodeMirror( >+ $('#macro-editor')[0], >+ { >+ mode: 'null', >+ lineNumbers: true, >+ } >+ ); >+ >+ var resizeTimer = null; >+ $( window ).resize( function() { >+ if ( resizeTimer == null ) resizeTimer = setTimeout( function() { >+ resizeTimer = null; >+ >+ var pos = $('#editor .CodeMirror').position(); >+ $('#editor .CodeMirror').height( $(window).height() - pos.top - 24 ); >+ }, 100); >+ } ).resize(); >+ >+ var saveableBackends = []; >+ $.each( backends, function( id, backend ) { >+ if ( backend.save ) saveableBackends.push( [ backend.saveLabel, id ] ); >+ } ); >+ saveableBackends.sort(); >+ $.each( saveableBackends, function( undef, backend ) { >+ $( '#save-dropdown' ).append( '<li><a href="#" data-backend="' + backend[1] + '">' + _("Save ") + backend[0] + '</a></li>' ); >+ } ); >+ >+ // Click bindings >+ $( '#save-record, #save-dropdown a' ).click( function() { >+ $( '#save-record' ).find('i').attr( 'class', 'icon-loading' ).siblings( 'span' ).text( _("Saving...") ); >+ >+ function finishCb(result) { >+ if ( result.error == 'syntax' ) { >+ humanMsg.displayAlert( _("Incorrect syntax, cannot save"), { className: 'humanError' } ); >+ } else if ( result.error == 'invalid' ) { >+ humanMsg.displayAlert( _("Record structure invalid, cannot save"), { className: 'humanError' } ); >+ } else if ( !result.error ) { >+ humanMsg.displayAlert( _("Record saved "), { className: 'humanSuccess' } ); >+ } >+ >+ $.each( result.errors || [], function( undef, error ) { >+ switch ( error.type ) { >+ case 'noTag': >+ editor.addError( error.line, _("Invalid tag number") ); >+ break; >+ case 'noIndicators': >+ editor.addError( error.line, _("Invalid indicators") ); >+ break; >+ case 'missingTag': >+ editor.addError( null, _("Missing mandatory tag: ") + error.tag ); >+ break; >+ case 'missingSubfield': >+ if ( error.subfield == '@' ) { >+ editor.addError( error.line, _("Missing control field contents") ); >+ } else { >+ editor.addError( error.line, _("Missing mandatory subfield: $") + error.subfield ); >+ } >+ break; >+ case 'unrepeatableTag': >+ editor.addError( error.line, _("Tag ") + error.tag + _(" cannot be repeated") ); >+ break; >+ case 'unrepeatableSubfield': >+ editor.addError( error.line, _("Subfield $") + error.subfield + _(" cannot be repeated") ); >+ break; >+ } >+ } ); >+ >+ $( '#save-record' ).find('i').attr( 'class', 'icon-hdd' ); >+ >+ if ( result.error ) { >+ // Reset backend info >+ setSource( [ state.backend, state.recordID ] ); >+ } >+ } >+ >+ var backend = $( this ).data( 'backend' ) || ( state.saveBackend ); >+ if ( state.backend == backend ) { >+ saveRecord( backend + ':' + state.recordID, editor, finishCb ); >+ } else { >+ saveRecord( backend + ':', editor, finishCb ); >+ } >+ >+ return false; >+ } ); >+ >+ $('#import-records').click( function() { >+ $('#import-records-input') >+ .off('change') >+ .change( function() { >+ if ( !this.files || !this.files.length ) return; >+ >+ var file = this.files[0]; >+ var reader = new FileReader(); >+ >+ reader.onload = function() { >+ var record = new MARC.Record(); >+ >+ if ( /\.mrc$/.test( file.name ) ) { >+ record.loadISO2709( reader.result ); >+ } else if ( /\.xml$/.test( file.name ) ) { >+ record.loadMARCXML( reader.result ); >+ } else { >+ humanMsg.displayAlert( _("Unknown record type, cannot import"), { className: 'humanError' } ); >+ return; >+ } >+ >+ editor.displayRecord( record ); >+ }; >+ >+ reader.readAsText( file ); >+ } ) >+ .click(); >+ >+ return false; >+ } ); >+ >+ $('#open-macros').click( function() { >+ $('#macro-ui').lightbox_me(); >+ >+ return false; >+ } ); >+ >+ $('#run-macro').click( function() { >+ var result = Macros.Run( editor, macroEditor.getValue() ); >+ >+ if ( !result.errors.length ) return false; >+ >+ var errors = []; >+ $.each( result.errors, function() { >+ var error = '<b>' + _("Line ") + (this.line + 1) + ':</b> '; >+ >+ switch ( this.error ) { >+ case 'failed': error += _("failed to run"); break; >+ case 'unrecognized': error += _("unrecognized command"); break; >+ } >+ >+ errors.push(error); >+ } ); >+ >+ humanMsg.displayMsg( _("<h3>Failed to run macro:</h3>") + '<ul><li>' + errors.join('</li><li>') + '</li></ul>', { className: 'humanError' } ); >+ >+ return false; >+ } ); >+ >+ $('#delete-macro').click( function() { >+ if ( !macroEditor.activeMacro || !confirm( _("Are you sure you want to delete this macro?") ) ) return; >+ >+ storeMacro( macroEditor.activeMacro, undefined ); >+ showSavedMacros(); >+ loadMacro( undefined ); >+ >+ return false; >+ } ); >+ >+ var saveTimeout; >+ macroEditor.on( 'change', function( cm, change ) { >+ $('#macro-save-message').empty(); >+ if ( change.origin == 'setValue' ) return; >+ >+ if ( saveTimeout ) clearTimeout( saveTimeout ); >+ saveTimeout = setTimeout( function() { >+ saveMacro(); >+ >+ saveTimeout = null; >+ }, 500 ); >+ } ); >+ >+ $( '#switch-editor' ).click( function() { >+ if ( !confirm( _("Any changes will not be saved. Continue?") ) ) return; >+ >+ $.cookie( 'catalogue_editor_[% USER_INFO.0.borrowernumber %]', 'basic', { expires: 365, path: '/' } ); >+ >+ if ( state.backend == 'catalog' ) { >+ window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl?biblionumber=' + state.recordID; >+ } else if ( state.backend == 'new' ) { >+ window.location = '/cgi-bin/koha/cataloguing/addbiblio.pl'; >+ } else { >+ humanMsg.displayAlert( _("Cannot open this record in the basic editor"), { className: 'humanError' } ); >+ } >+ } ); >+ >+ $( '#show-advanced-search' ).click( function() { >+ showAdvancedSearch(); >+ >+ return false; >+ } ); >+ >+ $('#advanced-search').submit( function() { >+ startAdvancedSearch(); >+ >+ return false; >+ } ); >+ >+ // Key bindings >+ bindGlobalKeys(); >+ >+ // Start editor >+ Preferences.Load( [% USER_INFO.0.borrowernumber %] ); >+ displayPreferences(editor); >+ makeAuthorisedValueWidgets( '' ); >+ Search.Init( z3950Targets, { onresults: function(data) { showSearchResults( editor, data ) }, onbytarget: showSearchTargets, onerror: handleSearchError } ); >+ >+ function finishCb() { >+ $("#loading").hide(); >+ editor.focus(); >+ } >+ >+ if ( "[% auth_forwarded_hash %]" ) { >+ document.location.hash = "[% auth_forwarded_hash %]"; >+ } >+ >+ if ( !document.location.hash || !openRecord( document.location.hash.slice(1), editor, finishCb ) ) { >+ openRecord( 'new:', editor, finishCb ); >+ } >+ } ); >+} )(); >+ >+</script> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-widgets-marc21.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-widgets-marc21.inc >new file mode 100644 >index 0000000..ef6035d >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-widgets-marc21.inc >@@ -0,0 +1,155 @@ >+<div id="editor-widget-templates" style="display:none"> >+ <div id="widget-leader"> >+ Leader: <span title="Record length (autogenerated)">#####</span> >+ <select name="f5" title="Record status"> >+ <option value="a">a - Increase in encoding level</option> >+ <option value="c">c - Corrected or revised</option> >+ <option value="d">d - Deleted</option> >+ <option value="n">n - New</option> >+ <option value="p">p - Increase in encoding level from prepublication</option> >+ </select> >+ <select name="f6" title="Type of record"> >+ <option value="a">a - Language material</option> >+ <option value="c">c - Notated music</option> >+ <option value="d">d - Manuscript notated music</option> >+ <option value="e">e - Cartographic material</option> >+ <option value="f">f - Manuscript cartographic material</option> >+ <option value="g">g - Projected medium</option> >+ <option value="i">i - Nonmusical sound recording</option> >+ <option value="j">j - Musical sound recording</option> >+ <option value="k">k - Two-dimensional nonprojectable graphic</option> >+ <option value="m">m - Computer file</option> >+ <option value="o">o - Kit</option> >+ <option value="p">p - Mixed materials</option> >+ <option value="r">r - Three-dimensional artifact or naturally occurring object</option> >+ <option value="t">t - Manuscript language material</option> >+ </select> >+ <select name="f7" title="Bibliographic level"> >+ <option value="a">a - Monographic component part</option> >+ <option value="b">b - Serial component part</option> >+ <option value="c">c - Collection</option> >+ <option value="d">d - Subunit</option> >+ <option value="i">i - Integrating resource</option> >+ <option value="m">m - Monograph/item</option> >+ <option value="s">s - Serial</option> >+ </select> >+ <select name="f8" title="Type of control"> >+ <option value=" ">_ - No specific type</option> >+ <option value="a">a - Archival</option> >+ </select> >+ <span title="Encoding (forced Unicode)">a</span> >+ <span title="Indicator/subfield lengths">22</span> >+ <span title="Data base address (autogenerated)">#####</span> >+ <select name="f17" title="Encoding level"> >+ <option value=" ">_ - Full level</option> >+ <option value="1">1 - Full level, material not examined</option> >+ <option value="2">2 - Less-than-full level, material not examined</option> >+ <option value="3">3 - Abbreviated level</option> >+ <option value="4">4 - Core level</option> >+ <option value="5">5 - Partial (preliminary) level</option> >+ <option value="7">7 - Minimal level</option> >+ <option value="8">8 - Prepublication level</option> >+ <option value="u">u - Unknown</option> >+ <option value="z">z - Not applicable</option> >+ </select> >+ <select name="f18" title="Descriptive cataloging form"> >+ <option value=" ">_ - Non-ISBD</option> >+ <option value="a">a - AACR 2</option> >+ <option value="c">c - ISBD punctuation omitted</option> >+ <option value="i">i - ISBD punctuation included</option> >+ <option value="u">u - Unknown</option> >+ </select> >+ <select name="f19" title="Multipart record resource level"> >+ <option value=" ">_ - Not specified or not applicable</option> >+ <option value="a">a - Set</option> >+ <option value="b">b - Part with independent title</option> >+ <option value="c">c - Part with dependent title</option> >+ </select> >+ <span title="Length of directory elements">4500</span> >+ </div> >+</div> >+ >+<script> >+ >+/** >+ * Each widget should provide one to three methods: >+ * init( text ): Returns the DOM node for this widget. >+ * postCreate( node, mark ): Optional, called once the mark has been created >+ * and the node shown. Bind event handlers here. >+ * makeTemplate(): Optional, should return some sane default contents for a >+ * newly created field/subfield. '<empty>' will be used if this >+ * method is unset. >+ * >+ * Following the Koha convention, control fields are defined as tags with a >+ * single subfield, '@'. >+ */ >+ >+var editorWidgets = { >+ '000@': { >+ makeTemplate: function() { >+ return ' nam a22 7a 4500'; >+ }, >+ init: function() { >+ var $result = $( '<span class="subfield-widget fixed-widget"></span>' ); >+ >+ return $result[0]; >+ }, >+ postCreate: function() { >+ // Clear the length and directory start fields; these are unnecessary for MARCXML and will be filled in upon USMARC export >+ this.setFixed( 0, 5, ' ' ); >+ this.setFixed( 9, 17, 'a22 ' ); >+ this.setFixed( 20, 24, '4500' ); >+ >+ this.insertTemplate( '#widget-leader' ); >+ >+ this.bindFixed( '[name=f5]', 5, 6 ); >+ this.bindFixed( '[name=f6]', 6, 7 ); >+ this.bindFixed( '[name=f7]', 7, 8 ); >+ this.bindFixed( '[name=f8]', 8, 9 ); >+ this.bindFixed( '[name=f17]', 17, 18 ); >+ this.bindFixed( '[name=f18]', 18, 19 ); >+ this.bindFixed( '[name=f19]', 19, 20 ); >+ }, >+ }, >+ '005@': { >+ init: function() { >+ var $result = $( '<span class="subfield-widget fixed-widget">Updated: </span>' ); >+ >+ return $result[0]; >+ }, >+ postCreate: function( node, mark ) { >+ var parts = this.text.match( /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\.(\d)/ ); >+ >+ if ( parts ) { >+ var dateVal = new Date( >+ parseInt( parts[1] ), // Year >+ parseInt( parts[2] ) - 1, // Month (0-11) >+ parseInt( parts[3] ), // Day >+ parseInt( parts[4] ), // Hour >+ parseInt( parts[5] ), // Minute >+ parseInt( parts[6] ), // Second >+ parseInt( parts[7] ) * 100 // Millisecond >+ ); >+ >+ $( this.node ).append( dateVal.toLocaleString() ); >+ } else { >+ $( this.node ).append( '<span class="hint">unset</span>' ); >+ } >+ } >+ }, >+ '008@': { >+ makeTemplate: function() { >+ var now = new Date(); >+ return this.padNum( now.getYear() % 100, 2 ) + this.padNum( now.getMonth() + 1, 2 ) + this.padNum( now.getDate(), 2 ) + "b xxu||||| |||| 00| 0 [% DefaultLanguageField008 %] d"; >+ }, >+ init: function() { >+ var $result = $( '<span class="subfield-widget fixed-widget">Fixed data: <span class="hint">under construction</span></span>' ); >+ >+ return $result[0]; >+ }, >+ postCreate: function( node, mark ) { >+ } >+ } >+}; >+ >+</script> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt >index cbe0a39..0e7393f 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt >@@ -74,6 +74,20 @@ > return false; > }); > >+ $( '#switcheditor' ).click( function() { >+ if ( !confirm( _("Any changes will not be saved. Continue?") ) ) return; >+ >+ $.cookie( 'catalogue_editor_[% USER_INFO.0.borrowernumber %]', 'advanced', { expires: 365, path: '/' } ); >+ >+ var biblionumber = [% biblionumber || "null" %]; >+ >+ if ( biblionumber ) { >+ window.location = '/cgi-bin/koha/cataloguing/editor.pl#catalog:' + biblionumber; >+ } else { >+ window.location = '/cgi-bin/koha/cataloguing/editor.pl'; >+ } >+ } ); >+ > }); > > function redirect(dest){ >@@ -402,6 +416,7 @@ function Changefwk(FwkList) { > > [% UNLESS (circborrowernumber) %][%# Hide in fast cataloging %] > <div class="btn-group"><a class="btn btn-small" href="#" id="z3950search"><i class="icon-search"></i> Z39.50 search</a></div> >+ <div class="btn-group"><a href="#" id="switcheditor" class="btn btn-small">Switch to advanced editor</a></div> > [% IF (biblionumber) %] > [% IF ( BiblioDefaultViewmarc ) %] > <div class="btn-group"> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt >new file mode 100644 >index 0000000..6cd5e56 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt >@@ -0,0 +1,201 @@ >+[% INCLUDE 'doc-head-open.inc' %] >+<title>Koha › Cataloging › Editor</title> >+[% INCLUDE 'doc-head-close.inc' %] >+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.fixFloat.js"></script> >+<link type="text/css" rel="stylesheet" href="[% themelang %]/css/cateditor.css" /> >+<link type="text/css" rel="stylesheet" href="/intranet-tmpl/lib/codemirror/codemirror.css" /> >+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/humanmsg.css" /> >+<script src="[% themelang %]/lib/jquery/plugins/humanmsg.js" type="text/javascript"></script> >+[% IF ( bidi ) %] >+ <link rel="stylesheet" type="text/css" href="[% themelang %]/css/right-to-left.css" /> >+[% END %] >+</head> >+<body id="cat_addbiblio" class="cat"> >+ >+ <div id="loading"> >+ <div>Loading, please wait...</div> >+ </div> >+ >+[% INCLUDE 'header.inc' %] >+ >+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/cataloguing/addbooks.pl">Cataloging</a> › Editor</div> >+ >+<div id="doc3" class="yui-t2"> >+<div id="bd"> >+ >+<h1 id="title">Cataloging editor</h1> >+ >+<div id="yui-main"><div class="yui-b"> >+ >+<div id="editor"> >+ <input id="import-records-input" type="file" style="display: none"> >+ <div id="toolbar" class="btn-toolbar"> >+ <div class="btn-group"> >+ <button class="btn btn-small" id="save-record" title="Save current record (Ctrl-S)"><i class="icon-hdd"></i> <span>Save</span></button> >+ <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"> >+ <span class="caret"></span> >+ </button> >+ <ul class="dropdown-menu" id="save-dropdown"> >+ </ul> >+ </div> >+ <button class="btn btn-small" id="import-records" title="Import an ISO2709 or MARCXML record"><i class="icon-upload"></i> <span>Import record...</span></button> >+ <button class="btn btn-small" id="open-macros" title="Run and edit macros"><i class="icon-play"></i> <span>Macros...</span></button> >+ <div class="btn-group"> >+ <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"><i class="icon-cog"></i> Settings <span class="caret"></span></button> >+ <ul id="prefs-menu" class="dropdown-menu"> >+ <li><a id="switch-editor" href="#">Switch to basic editor</a></li> >+ <li><a id="set-field-widgets" href="#"></a></li> >+ <li class="divider"></li> >+ <li><a class="set-fontSize" style="font-size: .92em" href="#">Small text</a></li> >+ <li><a class="set-fontSize" style="font-size: 1em" href="#">Normal text</a></li> >+ <li><a class="set-fontSize" style="font-size: 1.08em" href="#">Large text</a></li> >+ <li><a class="set-fontSize" style="font-size: 1.18em" href="#">Huge text</a></li> >+ <li class="divider"></li> >+ <li><a class="set-font" style="font-family: monospace" href="#">Default font</a></li> >+ <li><a class="set-font" style="font-family: 'Courier New'" href="#">Courier New</a></li> >+ <li><a class="set-font" style="font-family: peep" href="#">peep</a></li> >+ </ul> >+ </div> >+ </div> >+ [%# CodeMirror instance will be inserted here %] >+ <div id="statusbar"> >+ <div id="status-tag-info"> >+ </div> >+ <div id="status-subfield-info"> >+ </div> >+ </div> >+</div> >+ >+</div></div> >+ >+<div class="yui-b" id="sidebar"> >+ >+<h3>Search</h3> >+<form id="quicksearch"> >+ <fieldset class="brief"> >+ <ol> >+ <li><label for="search-by-keywords">Keywords:</label></li> >+ <li><input class="search-box" data-qualifier="term=" id="search-by-keywords" placeholder="(Ctrl-Alt-K)" /></li> >+ <li><label for="search-by-author">Author:</label></li> >+ <li><input class="search-box" data-qualifier="Author-name=" id="search-by-author" placeholder="(Ctrl-Alt-A)" /></li> >+ <li><label for="search-by-isbn">ISBN:</label></li> >+ <li><input class="search-box" data-qualifier="Identifier-ISBN=" id="search-by-isbn" placeholder="(Ctrl-Alt-I)" /></li> >+ <li><label for="search-by-title">Title:</label></li> >+ <li><input class="search-box" data-qualifier="Title=" id="search-by-title" placeholder="(Ctrl-Alt-T)" /></li> >+ <li><a href="#" id="show-advanced-search" title="Show advanced search (Ctrl-Alt-S)">Advanced »</a></li> >+ </fieldset> >+</form> >+ >+</div> >+ >+</div> >+</div> >+ >+<div id="advanced-search-ui" style="display: none"> >+ >+<h1>Advanced search</h1> >+ >+<form id="advanced-search"> >+ <div id="toolbar" class="btn-toolbar"> >+ <button class="btn btn-small" type="submit"><i class="icon-search"></i> <span>Search</span></button> >+ <button class="btn btn-small" type="reset"><i class="icon-remove"></i> <span>Clear</span></button> >+ </div> >+ <ul id="advanced-search-fields"> >+ <li> >+ <label for="advanced-search-by-author">Author:</label> >+ <input class="search-box" data-qualifier="Author-name=" id="advanced-search-by-author" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-control-number">Control number:</label> >+ <input class="search-box" data-qualifier="Local-number=" id="advanced-search-by-control-number" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-dewey">Dewey number:</label> >+ <input class="search-box" data-qualifier="Classification-Dewey=" id="advanced-search-by-dewey" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-isbn">ISBN:</label> >+ <input class="search-box" data-qualifier="Identifier-ISBN=" id="advanced-search-by-isbn" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-issn">ISSN:</label> >+ <input class="search-box" data-qualifier="Identifier-ISSN=" id="advanced-search-by-issn" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-lccn">LCCN:</label> >+ <input class="search-box" data-qualifier="LC-card-number=" id="advanced-search-by-lccn" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-lc-number">LC call number:</label> >+ <input class="search-box" data-qualifier="Classification-LC=" id="advanced-search-by-lc-number" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-publisher-number">Publisher number:</label> >+ <input class="search-box" data-qualifier="Identifier-publisher-for-music=" id="advanced-search-by-publisher-number" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-standard-number">Standard number:</label> >+ <input class="search-box" data-qualifier="Identifier-standard=" id="advanced-search-by-standard-number" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-subject">Subject:</label> >+ <input class="search-box" data-qualifier="Subject=" id="advanced-search-by-subject" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-publication-date">Publication date:</label> >+ <input class="search-box" data-qualifier="Date=" id="advanced-search-by-publication-date" /> >+ </li> >+ <li> >+ <label for="advanced-search-by-title">Title:</label> >+ <input class="search-box" data-qualifier="Title=" id="advanced-search-by-title" /> >+ </li> >+ </ul> >+</form> >+ >+</div> >+ >+<div id="search-results-ui" style="display: none"> >+ >+<h1>Results</h1> >+<div class="yui-gf"> >+ <div class="yui-u first"> >+ <div id="search-facets"> >+ <ul> >+ <li>Targets:<ul id="search-targetsinfo"></ul></li> >+ </ul> >+ </div> >+ </div> >+ <div class="yui-u"> >+ <div id="searchresults"> >+ <table> >+ <thead> >+ <tr></tr> >+ </thead> >+ <tbody></tbody> >+ </table> >+ <div id="search-overlay"><span>Loading...</span><div class="progress progress-striped active"><div class="bar" style="width: 0"></div></div></div> >+ </div> >+ </div> >+</div> >+ >+</div> >+ >+<div id="macro-ui" style="display: none"> >+ >+<h1>Macros</h1> >+<div class="yui-gf"> >+ <div class="yui-u first"><ul id="macro-list"></ul></div> >+ <div class="yui-u" id="macro-editor"> >+ <div id="macro-toolbar" class="btn-toolbar"> >+ <button class="btn btn-small" id="run-macro"itle="Run and edit macros"><i class="icon-play"></i> Run macro</button> >+ <button class="btn btn-small" id="delete-macro" title="Delete macro"><i class="icon-remove"></i> Delete macro</button> >+ <div id="macro-save-message"></div> >+ </div> >+ </div> >+</div> >+ >+</div> >+ >+[% PROCESS 'cateditor-ui.inc' %] >+ >+[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/svc/cataloguing/framework b/svc/cataloguing/framework >new file mode 100755 >index 0000000..8213de6 >--- /dev/null >+++ b/svc/cataloguing/framework >@@ -0,0 +1,73 @@ >+#!/usr/bin/perl >+ >+use Modern::Perl '2009'; >+ >+use CGI; >+use C4::Branch; >+use C4::ClassSource; >+use C4::Context; >+use C4::Biblio; >+use C4::Service; >+ >+my ( $query, $response ) = C4::Service->init( editcatalogue => 'edit_catalogue' ); >+ >+my ( $frameworkcode ) = C4::Service->require_params( 'frameworkcode' ); >+ >+my $tagslib = GetMarcStructure( 1, $frameworkcode ); >+ >+my @tags; >+ >+foreach my $tag ( sort keys %$tagslib ) { >+ my $taglib = $tagslib->{$tag}; >+ my $taginfo = { map { $_, $taglib->{$_} } grep { length $_ > 1 } keys %$taglib }; >+ $taginfo->{subfields} = [ map { [ $_, $taglib->{$_} ] } grep { length $_ == 1 } sort keys %$taglib ]; >+ >+ push @tags, [ $tag, $taginfo ]; >+} >+ >+my $dbh = C4::Context->dbh; >+my $authorised_values = {}; >+ >+$authorised_values->{branches} = []; >+my $onlymine=C4::Context->preference('IndependentBranches') && >+ C4::Context->userenv && >+ C4::Context->userenv->{flags} % 2 == 0 && >+ C4::Context->userenv->{branch}; >+my $branches = GetBranches($onlymine); >+foreach my $thisbranch ( sort keys %$branches ) { >+ push @{ $authorised_values->{branches} }, { value => $thisbranch, lib => $branches->{$thisbranch}->{'branchname'} }; >+} >+ >+$authorised_values->{itemtypes} = $dbh->selectall_arrayref( q{ >+ SELECT itemtype AS value, description AS lib FROM itemtypes ORDER BY description >+}, { Slice => {} } ); >+ >+my $class_sources = GetClassSources(); >+ >+my $default_source = C4::Context->preference("DefaultClassificationSource"); >+ >+foreach my $class_source (sort keys %$class_sources) { >+ next unless $class_sources->{$class_source}->{'used'} or >+ ($class_source eq $default_source); >+ push @{ $authorised_values->{cn_source} }, { value => $class_source, lib => $class_sources->{$class_source}->{'description'} }; >+} >+ >+my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : ""; >+my $auth_query = "SELECT category, authorised_value, lib >+ FROM authorised_values"; >+$auth_query .= qq{ LEFT JOIN authorised_values_branches ON ( id = av_id )} if $branch_limit; >+$auth_query .= " AND ( branchcode = ? OR branchcode IS NULL )" if $branch_limit; >+$auth_query .= " GROUP BY lib ORDER BY lib, lib_opac"; >+my $authorised_values_sth = $dbh->prepare( $auth_query ); >+$authorised_values_sth->execute( >+ $branch_limit ? $branch_limit : (), >+); >+ >+while ( my ( $category, $value, $lib ) = $authorised_values_sth->fetchrow_array ) { >+ $authorised_values->{$category} ||= []; >+ push @{ $authorised_values->{$category} }, { value => $lib, lib => $lib }; >+} >+ >+$response->param( framework => \@tags, authorised_values => $authorised_values ); >+ >+C4::Service->return_success( $response ); >-- >1.9.0
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 11559
:
26069
|
26070
|
26071
|
26104
|
26653
|
26654
|
27140
|
27141
|
27142
|
27757
|
27758
|
27759
|
27760
|
27761
|
27900
|
27901
|
27902
|
27993
|
27995
|
34458
|
34459
|
34460
|
34461
|
34462
|
34463
|
36243
|
36244
|
36245
|
36246
|
36247
|
36248
|
36455
|
38533
|
38534
|
38535
|
38536
|
38537
|
38538
|
38539
|
40153
|
40154
|
40155
|
40543
|
40555
|
40556
|
40557
|
40558
|
40811
|
40812
|
40813
|
40814
|
40815
|
41425
|
41453
|
41476
|
42373
|
43261
|
43547
|
43614
|
43615
|
43616
|
43617
|
43618
|
43619
|
43620
|
43621
|
43622
|
43623
|
43703
|
43704
|
43705
|
43706
|
43707
|
43708
|
43709
|
43710
|
43711
|
43712
|
43713
|
43772
|
43790
|
43883
|
43969
|
44016
|
44018
|
44022
|
44023
|
44024
|
44025
|
44026
|
44027
|
44028
|
44029
|
44030
|
44031
|
44032
|
44033
|
44034
|
44035
|
44036