Page MenuHomec4science

No OneTemporary

File Metadata

Created
Thu, Jun 19, 13:38
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/config/invenio.conf b/config/invenio.conf
index 60f4e0fa5..9b989dc65 100644
--- a/config/invenio.conf
+++ b/config/invenio.conf
@@ -1,1342 +1,1347 @@
## This file is part of Invenio.
## Copyright (C) 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
###################################################
## About 'invenio.conf' and 'invenio-local.conf' ##
###################################################
## The 'invenio.conf' file contains the vanilla default configuration
## parameters of a Invenio installation, as coming out of the
## distribution. The file should be self-explanatory. Once installed
## in its usual location (usually /opt/invenio/etc), you could in
## principle go ahead and change the values according to your local
## needs, but this is not advised.
##
## If you would like to customize some of these parameters, you should
## rather create a file named 'invenio-local.conf' in the same
## directory where 'invenio.conf' lives and you should write there
## only the customizations that you want to be different from the
## vanilla defaults.
##
## Here is a realistic, minimalist, yet production-ready example of
## what you would typically put there:
##
## $ cat /opt/invenio/etc/invenio-local.conf
## [Invenio]
## CFG_SITE_NAME = John Doe's Document Server
## CFG_SITE_NAME_INTL_fr = Serveur des Documents de John Doe
## CFG_SITE_URL = http://your.site.com
## CFG_SITE_SECURE_URL = https://your.site.com
## CFG_SITE_ADMIN_EMAIL = john.doe@your.site.com
## CFG_SITE_SUPPORT_EMAIL = john.doe@your.site.com
## CFG_WEBALERT_ALERT_ENGINE_EMAIL = john.doe@your.site.com
## CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL = john.doe@your.site.com
## CFG_WEBCOMMENT_DEFAULT_MODERATOR = john.doe@your.site.com
## CFG_DATABASE_HOST = localhost
## CFG_DATABASE_NAME = invenio
## CFG_DATABASE_USER = invenio
## CFG_DATABASE_PASS = my123p$ss
##
## You should override at least the parameters mentioned above and the
## parameters mentioned in the `Part 1: Essential parameters' below in
## order to define some very essential runtime parameters such as the
## name of your document server (CFG_SITE_NAME and
## CFG_SITE_NAME_INTL_*), the visible URL of your document server
## (CFG_SITE_URL and CFG_SITE_SECURE_URL), the email address of the
## local Invenio administrator, comment moderator, and alert engine
## (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_ADMIN_EMAIL, etc), and last but
## not least your database credentials (CFG_DATABASE_*).
##
## The Invenio system will then read both the default invenio.conf
## file and your customized invenio-local.conf file and it will
## override any default options with the ones you have specified in
## your local file. This cascading of configuration parameters will
## ease your future upgrades.
[Invenio]
###################################
## Part 1: Essential parameters ##
###################################
## This part defines essential Invenio internal parameters that
## everybody should override, like the name of the server or the email
## address of the local Invenio administrator.
## CFG_DATABASE_* - specify which MySQL server to use, the name of the
## database to use, and the database access credentials.
CFG_DATABASE_HOST = localhost
CFG_DATABASE_PORT = 3306
CFG_DATABASE_NAME = invenio
CFG_DATABASE_USER = invenio
CFG_DATABASE_PASS = my123p$ss
## CFG_SITE_URL - specify URL under which your installation will be
## visible. For example, use "http://your.site.com". Do not leave
## trailing slash.
CFG_SITE_URL = http://localhost
## CFG_SITE_SECURE_URL - specify secure URL under which your
## installation secure pages such as login or registration will be
## visible. For example, use "https://your.site.com". Do not leave
## trailing slash. If you don't plan on using HTTPS, then you may
## leave this empty.
CFG_SITE_SECURE_URL = https://localhost
## CFG_SITE_NAME -- the visible name of your Invenio installation.
CFG_SITE_NAME = Atlantis Institute of Fictive Science
## CFG_SITE_NAME_INTL -- the international versions of CFG_SITE_NAME
## in various languages. (See also CFG_SITE_LANGS below.)
CFG_SITE_NAME_INTL_en = Atlantis Institute of Fictive Science
CFG_SITE_NAME_INTL_fr = Atlantis Institut des Sciences Fictives
CFG_SITE_NAME_INTL_de = Atlantis Institut der fiktiven Wissenschaft
CFG_SITE_NAME_INTL_es = Atlantis Instituto de la Ciencia Fictive
CFG_SITE_NAME_INTL_ca = Institut Atlantis de Ciència Fictícia
CFG_SITE_NAME_INTL_pt = Instituto Atlantis de Ciência Fictícia
CFG_SITE_NAME_INTL_it = Atlantis Istituto di Scienza Fittizia
CFG_SITE_NAME_INTL_ru = Институт Фиктивных Наук Атлантиды
CFG_SITE_NAME_INTL_sk = Atlantis Inštitút Fiktívnych Vied
CFG_SITE_NAME_INTL_cs = Atlantis Institut Fiktivních Věd
CFG_SITE_NAME_INTL_no = Atlantis Institutt for Fiktiv Vitenskap
CFG_SITE_NAME_INTL_sv = Atlantis Institut för Fiktiv Vetenskap
CFG_SITE_NAME_INTL_el = Ινστιτούτο Φανταστικών Επιστημών Ατλαντίδος
CFG_SITE_NAME_INTL_uk = Інститут вигаданих наук в Атлантісі
CFG_SITE_NAME_INTL_ja = Fictive 科学のAtlantis の協会
CFG_SITE_NAME_INTL_pl = Instytut Fikcyjnej Nauki Atlantis
CFG_SITE_NAME_INTL_bg = Институт за фиктивни науки Атлантис
CFG_SITE_NAME_INTL_hr = Institut Fiktivnih Znanosti Atlantis
CFG_SITE_NAME_INTL_zh_CN = 阿特兰提斯虚拟科学学院
CFG_SITE_NAME_INTL_zh_TW = 阿特蘭提斯虛擬科學學院
CFG_SITE_NAME_INTL_hu = Kitalált Tudományok Atlantiszi Intézete
CFG_SITE_NAME_INTL_af = Atlantis Instituut van Fiktiewe Wetenskap
CFG_SITE_NAME_INTL_gl = Instituto Atlantis de Ciencia Fictive
CFG_SITE_NAME_INTL_ro = Institutul Atlantis al Ştiinţelor Fictive
CFG_SITE_NAME_INTL_rw = Atlantis Ishuri Rikuru Ry'ubuhanga
CFG_SITE_NAME_INTL_ka = ატლანტიდის ფიქტიური მეცნიერების ინსტიტუტი
CFG_SITE_NAME_INTL_lt = Fiktyvių Mokslų Institutas Atlantis
## CFG_SITE_LANG -- the default language of the interface: '
CFG_SITE_LANG = en
## CFG_SITE_LANGS -- list of all languages the user interface should
## be available in, separated by commas. The order specified below
## will be respected on the interface pages. A good default would be
## to use the alphabetical order. Currently supported languages
## include Afrikaans, Bulgarian, Catalan, Czech, German, Georgian,
## Greek, English, Spanish, French, Croatian, Hungarian, Galician,
## Italian, Japanese, Kinyarwanda, Lithuanian, Norwegian, Polish,
## Portuguese, Romanian, Russian, Slovak, Swedish, Ukrainian, Chinese
## (China), Chinese (Taiwan), so that the eventual maximum you can
## currently select is
## "af,bg,ca,cs,de,el,en,es,fr,hr,gl,ka,it,rw,lt,hu,ja,no,pl,pt,ro,ru,sk,sv,uk,zh_CN,zh_TW".
CFG_SITE_LANGS = af,bg,ca,cs,de,el,en,es,fr,hr,gl,ka,it,rw,lt,hu,ja,no,pl,pt,ro,ru,sk,sv,uk,zh_CN,zh_TW
## CFG_SITE_SUPPORT_EMAIL -- the email address of the support team for
## this installation:
CFG_SITE_SUPPORT_EMAIL = info@invenio-software.org
## CFG_SITE_ADMIN_EMAIL -- the email address of the 'superuser' for
## this installation. Enter your email address below and login with
## this address when using Invenio inistration modules. You
## will then be automatically recognized as superuser of the system.
CFG_SITE_ADMIN_EMAIL = info@invenio-software.org
## CFG_SITE_EMERGENCY_EMAIL_ADDRESSES -- list of email addresses to
## which an email should be sent in case of emergency (e.g. bibsched
## queue has been stopped because of an error). Configuration
## dictionary allows for different recipients based on weekday and
## time-of-day. Example:
##
## CFG_SITE_EMERGENCY_EMAIL_ADDRESSES = {
## 'Sunday 22:00-06:00': '0041761111111@email2sms.foo.com',
## '06:00-18:00': 'team-in-europe@foo.com,0041762222222@email2sms.foo.com',
## '18:00-06:00': 'team-in-usa@foo.com',
## '*': 'john.doe.phone@foo.com'}
##
## If you want the emergency email notifications to always go to the
## same address, just use the wildcard line in the above example.
CFG_SITE_EMERGENCY_EMAIL_ADDRESSES = {}
## CFG_SITE_ADMIN_EMAIL_EXCEPTIONS -- set this to 0 if you do not want
## to receive any captured exception via email to CFG_SITE_ADMIN_EMAIL
## address. Captured exceptions will still be available in
## var/log/invenio.err file. Set this to 1 if you want to receive
## some of the captured exceptions (this depends on the actual place
## where the exception is captured). Set this to 2 if you want to
## receive all captured exceptions.
CFG_SITE_ADMIN_EMAIL_EXCEPTIONS = 1
+## CFG_SITE_RECORD -- what is the URI part representing detailed
+## record pages? We recomment to leave the default value `record'
+## unchanged.
+CFG_SITE_RECORD = record
+
## CFG_ERRORLIB_RESET_EXCEPTION_NOTIFICATION_COUNTER_AFTER -- set this to
## the number of seconds after which to reset the exception notification
## counter. A given repetitive exception is notified via email with a
## logarithmic strategy: the first time it is seen it is sent via email,
## then the second time, then the fourth, then the eighth and so forth.
## If the number of seconds elapsed since the last time it was notified
## is greater than CFG_ERRORLIB_RESET_EXCEPTION_NOTIFICATION_COUNTER_AFTER
## then the internal counter is reset in order not to have exception
## notification become more and more rare.
CFG_ERRORLIB_RESET_EXCEPTION_NOTIFICATION_COUNTER_AFTER = 14400
## CFG_CERN_SITE -- do we want to enable CERN-specific code?
## Put "1" for "yes" and "0" for "no".
CFG_CERN_SITE = 0
## CFG_INSPIRE_SITE -- do we want to enable INSPIRE-specific code?
## Put "1" for "yes" and "0" for "no".
CFG_INSPIRE_SITE = 0
## CFG_ADS_SITE -- do we want to enable ADS-specific code?
## Put "1" for "yes" and "0" for "no".
CFG_ADS_SITE = 0
## CFG_OPENAIRE_SITE -- do we want to enable OpenAIRE-specific code?
## Put "1" for "yes" and "0" for "no".
CFG_OPENAIRE_SITE = 0
## CFG_DEVEL_SITE -- is this a development site? If it is, you might
## prefer that it does not do certain things. For example, you might
## not want WebSubmit to send certain emails or trigger certain
## processes on a development site.
## Put "1" for "yes" (this is a development site) or "0" for "no"
## (this isn't a development site.)
CFG_DEVEL_SITE = 0
################################
## Part 2: Web page style ##
################################
## The variables affecting the page style. The most important one is
## the 'template skin' you would like to use and the obfuscation mode
## for your email addresses. Please refer to the WebStyle Admin Guide
## for more explanation. The other variables are listed here mostly
## for backwards compatibility purposes only.
## CFG_WEBSTYLE_TEMPLATE_SKIN -- what template skin do you want to
## use?
CFG_WEBSTYLE_TEMPLATE_SKIN = default
## CFG_WEBSTYLE_EMAIL_ADDRESSES_OBFUSCATION_MODE. How do we "protect"
## email addresses from undesired automated email harvesters? This
## setting will not affect 'support' and 'admin' emails.
## NOTE: there is no ultimate solution to protect against email
## harvesting. All have drawbacks and can more or less be
## circumvented. Choose you preferred mode ([t] means "transparent"
## for the user):
## -1: hide all emails.
## [t] 0 : no protection, email returned as is.
## foo@example.com => foo@example.com
## 1 : basic email munging: replaces @ by [at] and . by [dot]
## foo@example.com => foo [at] example [dot] com
## [t] 2 : transparent name mangling: characters are replaced by
## equivalent HTML entities.
## foo@example.com => foo@example.com
## [t] 3 : javascript insertion. Requires Javascript enabled on client
## side.
## 4 : replaces @ and . characters by gif equivalents.
## foo@example.com => foo<img src="at.gif" alt=" [at] ">example<img src="dot.gif" alt=" [dot] ">com
CFG_WEBSTYLE_EMAIL_ADDRESSES_OBFUSCATION_MODE = 2
## CFG_WEBSTYLE_INSPECT_TEMPLATES -- Do we want to debug all template
## functions so that they would return HTML results wrapped in
## comments indicating which part of HTML page was created by which
## template function? Useful only for debugging Pythonic HTML
## template. See WebStyle Admin Guide for more information.
CFG_WEBSTYLE_INSPECT_TEMPLATES = 0
## (deprecated) CFG_WEBSTYLE_CDSPAGEBOXLEFTTOP -- eventual global HTML
## left top box:
CFG_WEBSTYLE_CDSPAGEBOXLEFTTOP =
## (deprecated) CFG_WEBSTYLE_CDSPAGEBOXLEFTBOTTOM -- eventual global
## HTML left bottom box:
CFG_WEBSTYLE_CDSPAGEBOXLEFTBOTTOM =
## (deprecated) CFG_WEBSTYLE_CDSPAGEBOXRIGHTTOP -- eventual global
## HTML right top box:
CFG_WEBSTYLE_CDSPAGEBOXRIGHTTOP =
## (deprecated) CFG_WEBSTYLE_CDSPAGEBOXRIGHTBOTTOM -- eventual global
## HTML right bottom box:
CFG_WEBSTYLE_CDSPAGEBOXRIGHTBOTTOM =
## CFG_WEBSTYLE_HTTP_STATUS_ALERT_LIST -- when certain HTTP status
## codes are raised to the WSGI handler, the corresponding exceptions
## and error messages can be sent to the system administrator for
## inspecting. This is useful to detect and correct errors. The
## variable represents a comma-separated list of HTTP statuses that
## should alert admin. Wildcards are possible. If the status is
## followed by an "r", it means that a referer is required to exist
## (useful to distinguish broken known links from URL typos when 404
## errors are raised).
CFG_WEBSTYLE_HTTP_STATUS_ALERT_LIST = 404r,400,5*,41*
##################################
## Part 3: WebSearch parameters ##
##################################
## This section contains some configuration parameters for WebSearch
## module. Please note that WebSearch is mostly configured on
## run-time via its WebSearch Admin web interface. The parameters
## below are the ones that you do not probably want to modify very
## often during the runtime. (Note that you may modify them
## afterwards too, though.)
## CFG_WEBSEARCH_SEARCH_CACHE_SIZE -- how many queries we want to
## cache in memory per one Apache httpd process? This cache is used
## mainly for "next/previous page" functionality, but it caches also
## "popular" user queries if more than one user happen to search for
## the same thing. Note that large numbers may lead to great memory
## consumption. We recommend a value not greater than 100.
CFG_WEBSEARCH_SEARCH_CACHE_SIZE = 0
## CFG_WEBSEARCH_FIELDS_CONVERT -- if you migrate from an older
## system, you may want to map field codes of your old system (such as
## 'ti') to Invenio/MySQL ("title"). Use Python dictionary syntax
## for the translation table, e.g. {'wau':'author', 'wti':'title'}.
## Usually you don't want to do that, and you would use empty dict {}.
CFG_WEBSEARCH_FIELDS_CONVERT = {}
## CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH -- width of the
## search pattern window in the light search interface, in
## characters. CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH = 60
CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH = 60
## CFG_WEBSEARCH_SIMPLESEARCH_PATTERN_BOX_WIDTH -- width of the search
## pattern window in the simple search interface, in characters.
CFG_WEBSEARCH_SIMPLESEARCH_PATTERN_BOX_WIDTH = 40
## CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH -- width of the
## search pattern window in the advanced search interface, in
## characters.
CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH = 30
## CFG_WEBSEARCH_NB_RECORDS_TO_SORT -- how many records do we still
## want to sort? For higher numbers we print only a warning and won't
## perform any sorting other than default 'latest records first', as
## sorting would be very time consuming then. We recommend a value of
## not more than a couple of thousands.
CFG_WEBSEARCH_NB_RECORDS_TO_SORT = 1000
## CFG_WEBSEARCH_CALL_BIBFORMAT -- if a record is being displayed but
## it was not preformatted in the "HTML brief" format, do we want to
## call BibFormatting on the fly? Put "1" for "yes" and "0" for "no".
## Note that "1" will display the record exactly as if it were fully
## preformatted, but it may be slow due to on-the-fly processing; "0"
## will display a default format very fast, but it may not have all
## the fields as in the fully preformatted HTML brief format. Note
## also that this option is active only for old (PHP) formats; the new
## (Python) formats are called on the fly by default anyway, since
## they are much faster. When usure, please set "0" here.
CFG_WEBSEARCH_CALL_BIBFORMAT = 0
## CFG_WEBSEARCH_USE_ALEPH_SYSNOS -- do we want to make old SYSNOs
## visible rather than MySQL's record IDs? You may use this if you
## migrate from a different e-doc system, and you store your old
## system numbers into 970__a. Put "1" for "yes" and "0" for
## "no". Usually you don't want to do that, though.
CFG_WEBSEARCH_USE_ALEPH_SYSNOS = 0
## CFG_WEBSEARCH_I18N_LATEST_ADDITIONS -- Put "1" if you want the
## "Latest Additions" in the web collection pages to show
## internationalized records. Useful only if your brief BibFormat
## templates contains internationalized strings. Otherwise put "0" in
## order not to slow down the creation of latest additions by WebColl.
CFG_WEBSEARCH_I18N_LATEST_ADDITIONS = 0
## CFG_WEBSEARCH_INSTANT_BROWSE -- the number of records to display
## under 'Latest Additions' in the web collection pages.
CFG_WEBSEARCH_INSTANT_BROWSE = 10
## CFG_WEBSEARCH_INSTANT_BROWSE_RSS -- the number of records to
## display in the RSS feed.
CFG_WEBSEARCH_INSTANT_BROWSE_RSS = 25
## CFG_WEBSEARCH_RSS_I18N_COLLECTIONS -- comma-separated list of
## collections that feature an internationalized RSS feed on their
## main seach interface page created by webcoll. Other collections
## will have RSS feed using CFG_SITE_LANG.
CFG_WEBSEARCH_RSS_I18N_COLLECTIONS =
## CFG_WEBSEARCH_RSS_TTL -- number of minutes that indicates how long
## a feed cache is valid.
CFG_WEBSEARCH_RSS_TTL = 360
## CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS -- maximum number of request kept
## in cache. If the cache is filled, following request are not cached.
CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS = 1000
## CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD -- up to how many author names
## to print explicitely; for more print "et al". Note that this is
## used in default formatting that is seldomly used, as usually
## BibFormat defines all the format. The value below is only used
## when BibFormat fails, for example.
CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD = 3
## CFG_WEBSEARCH_NARROW_SEARCH_SHOW_GRANDSONS -- whether to show or
## not collection grandsons in Narrow Search boxes (sons are shown by
## default, grandsons are configurable here). Use 0 for no and 1 for
## yes.
CFG_WEBSEARCH_NARROW_SEARCH_SHOW_GRANDSONS = 1
## CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX -- shall we
## create help links for Ellis, Nick or Ellis, Nicholas and friends
## when Ellis, N was searched for? Useful if you have one author
## stored in the database under several name formats, namely surname
## comma firstname and surname comma initial cataloging policy. Use 0
## for no and 1 for yes.
CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX = 1
## CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS -- MathJax is a JavaScript
## library that renders (La)TeX mathematical formulas in the client
## browser. This parameter must contain a comma-separated list of
## output formats for which to apply the MathJax rendering, for example
## "hb,hd". If the list is empty, MathJax is disabled.
CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS =
## CFG_WEBSEARCH_EXTERNAL_COLLECTION_SEARCH_TIMEOUT -- when searching
## external collections (e.g. SPIRES, CiteSeer, etc), how many seconds
## do we wait for reply before abandonning?
CFG_WEBSEARCH_EXTERNAL_COLLECTION_SEARCH_TIMEOUT = 5
## CFG_WEBSEARCH_EXTERNAL_COLLECTION_SEARCH_MAXRESULTS -- how many
## results do we fetch?
CFG_WEBSEARCH_EXTERNAL_COLLECTION_SEARCH_MAXRESULTS = 10
## CFG_WEBSEARCH_SPLIT_BY_COLLECTION -- do we want to split the search
## results by collection or not? Use 0 for not, 1 for yes.
CFG_WEBSEARCH_SPLIT_BY_COLLECTION = 1
## CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS -- the default number of
## records to display per page in the search results pages.
CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS = 10
## CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS -- in order to limit denial of
## service attacks the total number of records per group displayed as a
## result of a search query will be limited to this number. Only the superuser
## queries will not be affected by this limit.
CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS = 200
## CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL -- logged in users
## might have rights to access some restricted collections. This variable
## tweaks the kind of support the system will automatically provide to the
## user with respect to searching into these restricted collections.
## Set this to 0 in order to have the user to explicitly activate restricted
## collections in order to search into them. Set this to 1 in order to
## propose to the user the list of restricted collections to which he/she has
## rights (note: this is not yet implemented). Set this to 2 in order to
## silently add all the restricted collections to which the user has rights to
## to any query.
## Note: the system will discover which restricted collections a user has
## rights to, at login time. The time complexity of this procedure is
## proportional to the number of restricted collections. E.g. for a system
## with ~50 restricted collections, you might expect ~1s of delay in the
## login time, when this variable is set to a value higher than 0.
CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL = 0
## CFG_WEBSEARCH_SHOW_COMMENT_COUNT -- do we want to show the 'N comments'
## links on the search engine pages? (useful only when you have allowed
## commenting)
CFG_WEBSEARCH_SHOW_COMMENT_COUNT = 1
## CFG_WEBSEARCH_SHOW_REVIEW_COUNT -- do we want to show the 'N reviews'
## links on the search engine pages? (useful only when you have allowed
## reviewing)
CFG_WEBSEARCH_SHOW_REVIEW_COUNT = 1
## CFG_WEBSEARCH_FULLTEXT_SNIPPETS -- how many full-text snippets to
## display for full-text searches?
CFG_WEBSEARCH_FULLTEXT_SNIPPETS = 4
## CFG_WEBSEARCH_FULLTEXT_SNIPPETS_WORDS -- how many context words
## to display around the pattern in the snippet?
CFG_WEBSEARCH_FULLTEXT_SNIPPETS_WORDS = 4
## CFG_WEBSEARCH_WILDCARD_LIMIT -- some of the queries, wildcard
## queries in particular (ex: cern*, a*), but also regular expressions
## (ex: [a-z]+), may take a long time to respond due to the high
## number of hits. You can limit the number of terms matched by a
## wildcard by setting this variable. A negative value or zero means
## that none of the queries will be limited (which may be wanted by
## also prone to denial-of-service kind of attacks).
CFG_WEBSEARCH_WILDCARD_LIMIT = 50000
## CFG_WEBSEARCH_SYNONYM_KBRS -- defines which knowledge bases are to
## be used for which index in order to provide runtime synonym lookup
## of user-supplied terms, and what massaging function should be used
## upon search pattern before performing the KB lookup. (Can be one
## of `exact', 'leading_to_comma', `leading_to_number'.)
CFG_WEBSEARCH_SYNONYM_KBRS = {
'journal': ['SEARCH-SYNONYM-JOURNAL', 'leading_to_number'],
}
## CFG_SOLR_URL -- optionally, you may use Solr to serve full-text
## queries. If so, please specify the URL of your Solr instance.
## (example: http://localhost:8080/sorl)
CFG_SOLR_URL =
#######################################
## Part 4: BibHarvest OAI parameters ##
#######################################
## This part defines parameters for the Invenio OAI gateway.
## Useful if you are running Invenio as OAI data provider.
## CFG_OAI_ID_FIELD -- OAI identifier MARC field:
CFG_OAI_ID_FIELD = 909COo
## CFG_OAI_SET_FIELD -- OAI set MARC field:
CFG_OAI_SET_FIELD = 909COp
## CFG_OAI_DELETED_POLICY -- OAI deletedrecordspolicy
## (no/transient/persistent).
CFG_OAI_DELETED_POLICY = no
## CFG_OAI_ID_PREFIX -- OAI identifier prefix:
CFG_OAI_ID_PREFIX = atlantis.cern.ch
## CFG_OAI_SAMPLE_IDENTIFIER -- OAI sample identifier:
CFG_OAI_SAMPLE_IDENTIFIER = oai:atlantis.cern.ch:CERN-TH-4036
## CFG_OAI_IDENTIFY_DESCRIPTION -- description for the OAI Identify verb:
CFG_OAI_IDENTIFY_DESCRIPTION = <description>
<oai-identifier xmlns="http://www.openarchives.org/OAI/2.0/oai-identifier"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai-identifier
http://www.openarchives.org/OAI/2.0/oai-identifier.xsd">
<scheme>oai</scheme>
<repositoryIdentifier>atlantis.cern.ch</repositoryIdentifier>
<delimiter>:</delimiter>
<sampleIdentifier>oai:atlantis.cern.ch:CERN-TH-4036</sampleIdentifier>
</oai-identifier>
</description>
<description>
<eprints xmlns="http://www.openarchives.org/OAI/1.1/eprints"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.openarchives.org/OAI/1.1/eprints
http://www.openarchives.org/OAI/1.1/eprints.xsd">
<content>
<URL>http://atlantis.cern.ch/</URL>
</content>
<metadataPolicy>
<text>Free and unlimited use by anybody with obligation to refer to original record</text>
</metadataPolicy>
<dataPolicy>
<text>Full content, i.e. preprints may not be harvested by robots</text>
</dataPolicy>
<submissionPolicy>
<text>Submission restricted. Submitted documents are subject of approval by OAI repository admins.</text>
</submissionPolicy>
</eprints>
</description>
## CFG_OAI_LOAD -- OAI number of records in a response:
CFG_OAI_LOAD = 1000
## CFG_OAI_EXPIRE -- OAI resumptionToken expiration time:
CFG_OAI_EXPIRE = 90000
## CFG_OAI_SLEEP -- service unavailable between two consecutive
## requests for CFG_OAI_SLEEP seconds:
CFG_OAI_SLEEP = 10
##################################
## Part 5: WebSubmit parameters ##
##################################
## This section contains some configuration parameters for WebSubmit
## module. Please note that WebSubmit is mostly configured on
## run-time via its WebSubmit Admin web interface. The parameters
## below are the ones that you do not probably want to modify during
## the runtime.
## CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT -- the fulltext
## documents are stored under "/opt/invenio/var/data/files/gX/Y"
## directories where X is 0,1,... and Y stands for bibdoc ID. Thusly
## documents Y are grouped into directories X and this variable
## indicates the maximum number of documents Y stored in each
## directory X. This limit is imposed solely for filesystem
## performance reasons in order not to have too many subdirectories in
## a given directory.
CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT = 5000
## CFG_WEBSUBMIT_ADDITIONAL_KNOWN_FILE_EXTENSIONS -- a comma-separated
## list of document extensions not listed in Python standard mimetype
## library that should be recognized by Invenio.
CFG_WEBSUBMIT_ADDITIONAL_KNOWN_FILE_EXTENSIONS = hpg,link,lis,llb,mat,mpp,msg,docx,docm,xlsx,xlsm,xlsb,pptx,pptm,ppsx,ppsm
## CFG_BIBDOCFILE_USE_XSENDFILE -- if your web server supports
## XSendfile header, you may want to enable this feature in order for
## to Invenio tell the web server to stream files for download (after
## proper authorization checks) by web server's means. This helps to
## liberate Invenio worker processes from being busy with sending big
## files to clients. The web server will take care of that. Note:
## this feature is still somewhat experimental. Note: when enabled
## (set to 1), then you have to also regenerate Apache vhost conf
## snippets (inveniocfg --update-config-py --create-apache-conf).
CFG_BIBDOCFILE_USE_XSENDFILE = 0
## CFG_BIBDOCFILE_MD5_CHECK_PROBABILITY -- a number between 0 and
## 1 that indicates probability with which MD5 checksum will be
## verified when streaming bibdocfile-managed files. (0.1 will cause
## the check to be performed once for every 10 downloads)
CFG_BIBDOCFILE_MD5_CHECK_PROBABILITY = 0.1
## CFG_OPENOFFICE_SERVER_HOST -- the host where an OpenOffice Server is
## listening to. If localhost an OpenOffice server will be started
## automatically if it is not already running.
## Note: if you set this to an empty value this will disable the usage of
## OpenOffice for converting documents.
## If you set this to something different than localhost you'll have to take
## care to have an OpenOffice server running on the corresponding host and
## to install the same OpenOffice release both on the client and on the server
## side.
## In order to launch an OpenOffice server on a remote machine, just start
## the usual 'soffice' executable in this way:
## $> soffice -headless -nologo -nodefault -norestore -nofirststartwizard \
## .. -accept=socket,host=HOST,port=PORT;urp;StarOffice.ComponentContext
CFG_OPENOFFICE_SERVER_HOST = localhost
## CFG_OPENOFFICE_SERVER_PORT -- the port where an OpenOffice Server is
## listening to.
CFG_OPENOFFICE_SERVER_PORT = 2002
## CFG_OPENOFFICE_USER -- the user that will be used to launch the OpenOffice
## client. It is recommended to set this to a user who don't own files, like
## e.g. 'nobody'. You should also authorize your Apache server user to be
## able to become this user, e.g. by adding to your /etc/sudoers the following
## line:
## "apache ALL=(nobody) NOPASSWD: ALL"
## provided that apache is the username corresponding to the Apache user.
## On some machine this might be apache2 or www-data.
CFG_OPENOFFICE_USER = nobody
#################################
## Part 6: BibIndex parameters ##
#################################
## This section contains some configuration parameters for BibIndex
## module. Please note that BibIndex is mostly configured on run-time
## via its BibIndex Admin web interface. The parameters below are the
## ones that you do not probably want to modify very often during the
## runtime.
## CFG_BIBINDEX_FULLTEXT_INDEX_LOCAL_FILES_ONLY -- when fulltext indexing, do
## you want to index locally stored files only, or also external URLs?
## Use "0" to say "no" and "1" to say "yes".
CFG_BIBINDEX_FULLTEXT_INDEX_LOCAL_FILES_ONLY = 0
## CFG_BIBINDEX_REMOVE_STOPWORDS -- when indexing, do we want to remove
## stopwords? Use "0" to say "no" and "1" to say "yes".
CFG_BIBINDEX_REMOVE_STOPWORDS = 0
## CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS -- characters considered as
## alphanumeric separators of word-blocks inside words. You probably
## don't want to change this.
CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS = \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
## CFG_BIBINDEX_CHARS_PUNCTUATION -- characters considered as punctuation
## between word-blocks inside words. You probably don't want to
## change this.
CFG_BIBINDEX_CHARS_PUNCTUATION = \.\,\:\;\?\!\"
## CFG_BIBINDEX_REMOVE_HTML_MARKUP -- should we attempt to remove HTML markup
## before indexing? Use 1 if you have HTML markup inside metadata
## (e.g. in abstracts), use 0 otherwise.
CFG_BIBINDEX_REMOVE_HTML_MARKUP = 0
## CFG_BIBINDEX_REMOVE_LATEX_MARKUP -- should we attempt to remove LATEX markup
## before indexing? Use 1 if you have LATEX markup inside metadata
## (e.g. in abstracts), use 0 otherwise.
CFG_BIBINDEX_REMOVE_LATEX_MARKUP = 0
## CFG_BIBINDEX_MIN_WORD_LENGTH -- minimum word length allowed to be added to
## index. The terms smaller then this amount will be discarded.
## Useful to keep the database clean, however you can safely leave
## this value on 0 for up to 1,000,000 documents.
CFG_BIBINDEX_MIN_WORD_LENGTH = 0
## CFG_BIBINDEX_URLOPENER_USERNAME and CFG_BIBINDEX_URLOPENER_PASSWORD --
## access credentials to access restricted URLs, interesting only if
## you are fulltext-indexing files located on a remote server that is
## only available via username/password. But it's probably better to
## handle this case via IP or some convention; the current scheme is
## mostly there for demo only.
CFG_BIBINDEX_URLOPENER_USERNAME = mysuperuser
CFG_BIBINDEX_URLOPENER_PASSWORD = mysuperpass
## CFG_INTBITSET_ENABLE_SANITY_CHECKS --
## Enable sanity checks for integers passed to the intbitset data
## structures. It is good to enable this during debugging
## and to disable this value for speed improvements.
CFG_INTBITSET_ENABLE_SANITY_CHECKS = False
## CFG_BIBINDEX_PERFORM_OCR_ON_DOCNAMES -- regular expression that matches
## docnames for which OCR is desired (set this to .* in order to enable
## OCR in general, set this to empty in order to disable it.)
CFG_BIBINDEX_PERFORM_OCR_ON_DOCNAMES = scan-.*
## CFG_BIBINDEX_SPLASH_PAGES -- key-value mapping where the key corresponds
## to a regular expression that matches the URLs of the splash pages of
## a given service and the value is a regular expression of the set of URLs
## referenced via <a> tags in the HTML content of the splash pages that are
## referring to documents that need to be indexed.
## NOTE: for backward compatibility reasons you can set this to a simple
## regular expression that will directly be used as the unique key of the
## map, with corresponding value set to ".*" (in order to match any URL)
CFG_BIBINDEX_SPLASH_PAGES = {
"http://documents\.cern\.ch/setlink\?.*": ".*",
"http://ilcagenda\.linearcollider\.org/subContributionDisplay\.py\?.*|http://ilcagenda\.linearcollider\.org/contributionDisplay\.py\?.*": "http://ilcagenda\.linearcollider\.org/getFile\.py/access\?.*|http://ilcagenda\.linearcollider\.org/materialDisplay\.py\?.*",
}
## CFG_BIBINDEX_AUTHOR_WORD_INDEX_EXCLUDE_FIRST_NAMES -- do we want
## the author word index to exclude first names to keep only last
## names? If set to True, then for the author `Bernard, Denis', only
## `Bernard' will be indexed in the word index, not `Denis'. Note
## that if you change this variable, you have to re-index the author
## index via `bibindex -w author -R'.
CFG_BIBINDEX_AUTHOR_WORD_INDEX_EXCLUDE_FIRST_NAMES = False
## CFG_BIBINDEX_SYNONYM_KBRS -- defines which knowledge bases are to
## be used for which index in order to provide index-time synonym
## lookup, and what massaging function should be used upon search
## pattern before performing the KB lookup. (Can be one of `exact',
## 'leading_to_comma', `leading_to_number'.)
CFG_BIBINDEX_SYNONYM_KBRS = {
'global': ['INDEX-SYNONYM-TITLE', 'exact'],
'title': ['INDEX-SYNONYM-TITLE', 'exact'],
}
#######################################
## Part 7: Access control parameters ##
#######################################
## This section contains some configuration parameters for the access
## control system. Please note that WebAccess is mostly configured on
## run-time via its WebAccess Admin web interface. The parameters
## below are the ones that you do not probably want to modify very
## often during the runtime. (If you do want to modify them during
## runtime, for example te deny access temporarily because of backups,
## you can edit access_control_config.py directly, no need to get back
## here and no need to redo the make process.)
## CFG_ACCESS_CONTROL_LEVEL_SITE -- defines how open this site is.
## Use 0 for normal operation of the site, 1 for read-only site (all
## write operations temporarily closed), 2 for site fully closed,
## 3 for also disabling any database connection.
## Useful for site maintenance.
CFG_ACCESS_CONTROL_LEVEL_SITE = 0
## CFG_ACCESS_CONTROL_LEVEL_GUESTS -- guest users access policy. Use
## 0 to allow guest users, 1 not to allow them (all users must login).
CFG_ACCESS_CONTROL_LEVEL_GUESTS = 0
## CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS -- account registration and
## activation policy. When 0, users can register and accounts are
## automatically activated. When 1, users can register but admin must
## activate the accounts. When 2, users cannot register nor update
## their email address, only admin can register accounts. When 3,
## users cannot register nor update email address nor password, only
## admin can register accounts. When 4, the same as 3 applies, nor
## user cannot change his login method. When 5, then the same as 4
## applies, plus info about how to get an account is hidden from the
## login page.
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS = 0
## CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN -- limit account
## registration to certain email addresses? If wanted, give domain
## name below, e.g. "cern.ch". If not wanted, leave it empty.
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN =
## CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS -- send a
## notification email to the administrator when a new account is
## created? Use 0 for no, 1 for yes.
CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS = 0
## CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT -- send a
## notification email to the user when a new account is created in order to
## to verify the validity of the provided email address? Use
## 0 for no, 1 for yes.
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT = 1
## CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION -- send a
## notification email to the user when a new account is activated?
## Use 0 for no, 1 for yes.
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION = 0
## CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION -- send a
## notification email to the user when a new account is deleted or
## account demand rejected? Use 0 for no, 1 for yes.
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION = 0
## CFG_APACHE_PASSWORD_FILE -- the file where Apache user credentials
## are stored. Must be an absolute pathname. If the value does not
## start by a slash, it is considered to be the filename of a file
## located under prefix/var/tmp directory. This is useful for the
## demo site testing purposes. For the production site, if you plan
## to restrict access to some collections based on the Apache user
## authentication mechanism, you should put here an absolute path to
## your Apache password file.
CFG_APACHE_PASSWORD_FILE = demo-site-apache-user-passwords
## CFG_APACHE_GROUP_FILE -- the file where Apache user groups are
## defined. See the documentation of the preceding config variable.
CFG_APACHE_GROUP_FILE = demo-site-apache-user-groups
###################################
## Part 8: WebSession parameters ##
###################################
## This section contains some configuration parameters for tweaking
## session handling.
## CFG_WEBSESSION_EXPIRY_LIMIT_DEFAULT -- number of days after which a session
## and the corresponding cookie is considered expired.
CFG_WEBSESSION_EXPIRY_LIMIT_DEFAULT = 2
## CFG_WEBSESSION_EXPIRY_LIMIT_REMEMBER -- number of days after which a session
## and the corresponding cookie is considered expired, when the user has
## requested to permanently stay logged in.
CFG_WEBSESSION_EXPIRY_LIMIT_REMEMBER = 365
## CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS -- when user requested
## a password reset, for how many days is the URL valid?
CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS = 3
## CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS -- when an account
## activation email was sent, for how many days is the URL valid?
CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS = 3
## CFG_WEBSESSION_NOT_CONFIRMED_EMAIL_ADDRESS_EXPIRE_IN_DAYS -- when
## user won't confirm his email address and not complete
## registeration, after how many days will it expire?
CFG_WEBSESSION_NOT_CONFIRMED_EMAIL_ADDRESS_EXPIRE_IN_DAYS = 10
## CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS -- when set to 1, the session
## system allocates the same uid=0 to all guests users regardless of where they
## come from. 0 allocate a unique uid to each guest.
CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS = 0
################################
## Part 9: BibRank parameters ##
################################
## This section contains some configuration parameters for the ranking
## system.
## CFG_BIBRANK_SHOW_READING_STATS -- do we want to show reading
## similarity stats? ('People who viewed this page also viewed')
CFG_BIBRANK_SHOW_READING_STATS = 1
## CFG_BIBRANK_SHOW_DOWNLOAD_STATS -- do we want to show the download
## similarity stats? ('People who downloaded this document also
## downloaded')
CFG_BIBRANK_SHOW_DOWNLOAD_STATS = 1
## CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS -- do we want to show download
## history graph? (0=no | 1=classic/gnuplot | 2=flot)
CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS = 1
## CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS_CLIENT_IP_DISTRIBUTION -- do we
## want to show a graph representing the distribution of client IPs
## downloading given document?
CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS_CLIENT_IP_DISTRIBUTION = 0
## CFG_BIBRANK_SHOW_CITATION_LINKS -- do we want to show the 'Cited
## by' links? (useful only when you have citations in the metadata)
CFG_BIBRANK_SHOW_CITATION_LINKS = 1
## CFG_BIBRANK_SHOW_CITATION_STATS -- de we want to show citation
## stats? ('Cited by M recors', 'Co-cited with N records')
CFG_BIBRANK_SHOW_CITATION_STATS = 1
## CFG_BIBRANK_SHOW_CITATION_GRAPHS -- do we want to show citation
## history graph? (0=no | 1=classic/gnuplot | 2=flot)
CFG_BIBRANK_SHOW_CITATION_GRAPHS = 1
####################################
## Part 10: WebComment parameters ##
####################################
## This section contains some configuration parameters for the
## commenting and reviewing facilities.
## CFG_WEBCOMMENT_ALLOW_COMMENTS -- do we want to allow users write
## public comments on records?
CFG_WEBCOMMENT_ALLOW_COMMENTS = 1
## CFG_WEBCOMMENT_ALLOW_REVIEWS -- do we want to allow users write
## public reviews of records?
CFG_WEBCOMMENT_ALLOW_REVIEWS = 1
## CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS -- do we want to allow short
## reviews, that is just the attribution of stars without submitting
## detailed review text?
CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS = 0
## CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN -- if users
## report a comment to be abusive, how many they have to be before the
## site admin is alerted?
CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN = 5
## CFG_WEBCOMMENT_NB_COMMENTS_IN_DETAILED_VIEW -- how many comments do
## we display in the detailed record page upon welcome?
CFG_WEBCOMMENT_NB_COMMENTS_IN_DETAILED_VIEW = 1
## CFG_WEBCOMMENT_NB_REVIEWS_IN_DETAILED_VIEW -- how many reviews do
## we display in the detailed record page upon welcome?
CFG_WEBCOMMENT_NB_REVIEWS_IN_DETAILED_VIEW = 1
## CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL -- do we notify the site
## admin after every comment?
CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL = 1
## CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS -- how many
## elapsed seconds do we consider enough when checking for possible
## multiple comment submissions by a user?
CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS = 20
## CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_REVIEWS_IN_SECONDS -- how many
## elapsed seconds do we consider enough when checking for possible
## multiple review submissions by a user?
CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_REVIEWS_IN_SECONDS = 20
## CFG_WEBCOMMENT_USE_RICH_EDITOR -- enable the WYSIWYG
## Javascript-based editor when user edits comments?
CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR = False
## CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL -- the email address from which the
## alert emails will appear to be sent:
CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL = info@invenio-software.org
## CFG_WEBCOMMENT_DEFAULT_MODERATOR -- if no rules are
## specified to indicate who is the comment moderator of
## a collection, this person will be used as default
CFG_WEBCOMMENT_DEFAULT_MODERATOR = info@invenio-software.org
## CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS -- do we want to allow the use
## of MathJax plugin to render latex input in comments?
CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS = 1
## CFG_WEBCOMMENT_AUTHOR_DELETE_COMMENT_OPTION -- allow comment author to
## delete its own comment?
CFG_WEBCOMMENT_AUTHOR_DELETE_COMMENT_OPTION = 1
##################################
## Part 11: BibSched parameters ##
##################################
## This section contains some configuration parameters for the
## bibliographic task scheduler.
## CFG_BIBSCHED_REFRESHTIME -- how often do we want to refresh
## bibsched monitor? (in seconds)
CFG_BIBSCHED_REFRESHTIME = 5
## CFG_BIBSCHED_LOG_PAGER -- what pager to use to view bibsched task
## logs?
CFG_BIBSCHED_LOG_PAGER = /bin/more
## CFG_BIBSCHED_GC_TASKS_OLDER_THAN -- after how many days to perform the
## gargbage collector of BibSched queue (i.e. removing/moving task to archive).
CFG_BIBSCHED_GC_TASKS_OLDER_THAN = 30
## CFG_BIBSCHED_GC_TASKS_TO_REMOVE -- list of BibTask that can be safely
## removed from the BibSched queue once they are DONE.
CFG_BIBSCHED_GC_TASKS_TO_REMOVE = bibindex,bibreformat,webcoll,bibrank,inveniogc
## CFG_BIBSCHED_GC_TASKS_TO_ARCHIVE -- list of BibTasks that should be safely
## archived out of the BibSched queue once they are DONE.
CFG_BIBSCHED_GC_TASKS_TO_ARCHIVE = bibupload,oaiarchive
## CFG_BIBSCHED_MAX_NUMBER_CONCURRENT_TASKS -- maximum number of BibTasks
## that can run concurrently.
## NOTE: concurrent tasks are still considered as an experimental
## feature. Please keep this value set to 1 on production environments.
CFG_BIBSCHED_MAX_NUMBER_CONCURRENT_TASKS = 1
## CFG_BIBSCHED_PROCESS_USER -- bibsched and bibtask processes must
## usually run under the same identity as the Apache web server
## process in order to share proper file read/write privileges. If
## you want to force some other bibsched/bibtask user, e.g. because
## you are using a local `invenio' user that belongs to your
## `www-data' Apache user group and so shares writing rights with your
## Apache web server process in this way, then please set its username
## identity here. Otherwise we shall check whether your
## bibsched/bibtask processes are run under the same identity as your
## Apache web server process (in which case you can leave the default
## empty value here).
CFG_BIBSCHED_PROCESS_USER =
###################################
## Part 12: WebBasket parameters ##
###################################
## CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS -- a safety limit for
## a maximum number of displayed baskets
CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS = 20
## CFG_WEBBASKET_USE_RICH_TEXT_EDITOR -- enable the WYSIWYG
## Javascript-based editor when user edits comments in WebBasket?
CFG_WEBBASKET_USE_RICH_TEXT_EDITOR = False
##################################
## Part 13: WebAlert parameters ##
##################################
## This section contains some configuration parameters for the
## automatic email notification alert system.
## CFG_WEBALERT_ALERT_ENGINE_EMAIL -- the email address from which the
## alert emails will appear to be sent:
CFG_WEBALERT_ALERT_ENGINE_EMAIL = info@invenio-software.org
## CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL -- how many records
## at most do we send in an outgoing alert email?
CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL = 20
## CFG_WEBALERT_MAX_NUM_OF_CHARS_PER_LINE_IN_ALERT_EMAIL -- number of
## chars per line in an outgoing alert email?
CFG_WEBALERT_MAX_NUM_OF_CHARS_PER_LINE_IN_ALERT_EMAIL = 72
## CFG_WEBALERT_SEND_EMAIL_NUMBER_OF_TRIES -- when sending alert
## emails fails, how many times we retry?
CFG_WEBALERT_SEND_EMAIL_NUMBER_OF_TRIES = 3
## CFG_WEBALERT_SEND_EMAIL_SLEEPTIME_BETWEEN_TRIES -- when sending
## alert emails fails, what is the sleeptime between tries? (in
## seconds)
CFG_WEBALERT_SEND_EMAIL_SLEEPTIME_BETWEEN_TRIES = 300
####################################
## Part 14: WebMessage parameters ##
####################################
## CFG_WEBMESSAGE_MAX_SIZE_OF_MESSAGE -- how large web messages do we
## allow?
CFG_WEBMESSAGE_MAX_SIZE_OF_MESSAGE = 20000
## CFG_WEBMESSAGE_MAX_NB_OF_MESSAGES -- how many messages for a
## regular user do we allow in its inbox?
CFG_WEBMESSAGE_MAX_NB_OF_MESSAGES = 30
## CFG_WEBMESSAGE_DAYS_BEFORE_DELETE_ORPHANS -- how many days before
## we delete orphaned messages?
CFG_WEBMESSAGE_DAYS_BEFORE_DELETE_ORPHANS = 60
##################################
## Part 15: MiscUtil parameters ##
##################################
## CFG_MISCUTIL_SQL_USE_SQLALCHEMY -- whether to use SQLAlchemy.pool
## in the DB engine of Invenio. It is okay to enable this flag
## even if you have not installed SQLAlchemy. Note that Invenio will
## loose some perfomance if this option is enabled.
CFG_MISCUTIL_SQL_USE_SQLALCHEMY = False
## CFG_MISCUTIL_SQL_RUN_SQL_MANY_LIMIT -- how many queries can we run
## inside run_sql_many() in one SQL statement? The limit value
## depends on MySQL's max_allowed_packet configuration.
CFG_MISCUTIL_SQL_RUN_SQL_MANY_LIMIT = 10000
## CFG_MISCUTIL_SMTP_HOST -- which server to use as outgoing mail server to
## send outgoing emails generated by the system, for example concerning
## submissions or email notification alerts.
CFG_MISCUTIL_SMTP_HOST = localhost
## CFG_MISCUTIL_SMTP_PORT -- which port to use on the outgoing mail server
## defined in the previous step.
CFG_MISCUTIL_SMTP_PORT = 25
## CFG_MISCUTILS_DEFAULT_PROCESS_TIMEOUT -- the default number of seconds after
## which a process launched trough shellutils.run_process_with_timeout will
## be killed. This is useful to catch runaway processes.
CFG_MISCUTIL_DEFAULT_PROCESS_TIMEOUT = 300
## CFG_MATHJAX_HOSTING -- if you plan to use MathJax to display TeX
## formulas on HTML web pages, you can specify whether you wish to use
## 'local' hosting or 'cdn' hosting of MathJax libraries. (If set to
## 'local', you have to run 'make install-mathjax-plugin' as described
## in the INSTALL guide.) If set to 'local', users will use your site
## to download MathJax sources. If set to 'cdn', users will use
## centralized MathJax CDN servers instead. Please note that using
## CDN is suitable only for small institutions or for MathJax
## sponsors; see the MathJax website for more details. (Also, please
## note that if you plan to use MathJax on your site, you have to
## adapt CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS and
## CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS configuration variables
## elsewhere in this file.)
CFG_MATHJAX_HOSTING = local
#################################
## Part 16: BibEdit parameters ##
#################################
## CFG_BIBEDIT_TIMEOUT -- when a user edits a record, this record is
## locked to prevent other users to edit it at the same time.
## How many seconds of inactivity before the locked record again will be free
## for other people to edit?
CFG_BIBEDIT_TIMEOUT = 3600
## CFG_BIBEDIT_LOCKLEVEL -- when a user tries to edit a record which there
## is a pending bibupload task for in the queue, this shouldn't be permitted.
## The lock level determines how thouroughly the queue should be investigated
## to determine if this is the case.
## Level 0 - always permits editing, doesn't look at the queue
## (unsafe, use only if you know what you are doing)
## Level 1 - permits editing if there are no queued bibedit tasks for this record
## (safe with respect to bibedit, but not for other bibupload maintenance jobs)
## Level 2 - permits editing if there are no queued bibupload tasks of any sort
## (safe, but may lock more than necessary if many cataloguers around)
## Level 3 - permits editing if no queued bibupload task concerns given record
## (safe, most precise locking, but slow,
## checks for 001/EXTERNAL_SYSNO_TAG/EXTERNAL_OAIID_TAG)
## The recommended level is 3 (default) or 2 (if you use maintenance jobs often).
CFG_BIBEDIT_LOCKLEVEL = 3
## CFG_BIBEDIT_PROTECTED_FIELDS -- a comma-separated list of fields that BibEdit
## will not allow to be added, edited or deleted. Wildcards are not supported,
## but conceptually a wildcard is added at the end of every field specification.
## Examples:
## 500A - protect all MARC fields with tag 500 and first indicator A
## 5 - protect all MARC fields in the 500-series.
## 909C_a - protect subfield a in tag 909 with first indicator C and empty
## second indicator
## Note that 001 is protected by default, but if protection of other
## identifiers or automated fields is a requirement, they should be added to
## this list.
CFG_BIBEDIT_PROTECTED_FIELDS =
## CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING -- maximum number of records
## that can be modified instantly using the multi-record editor. Above
## this limit, modifications will only be executed in limited hours.
CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING = 2000
## CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING -- maximum number of records
## that can be send for modification without having a superadmin role.
## If the number of records is between CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING
## and this number, the modifications will take place only in limited hours.
CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING = 20000
## CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING_TIME -- Allowed time to
## execute modifications on records, when the number exceeds
## CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING.
CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING_TIME = 22:00-05:00
###################################
## Part 17: BibUpload parameters ##
###################################
## CFG_BIBUPLOAD_REFERENCE_TAG -- where do we store references?
CFG_BIBUPLOAD_REFERENCE_TAG = 999
## CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG -- where do we store external
## system numbers? Useful for matching when our records come from an
## external digital library system.
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG = 970__a
## CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG -- where do we store OAI ID tags
## of harvested records? Useful for matching when we harvest stuff
## via OAI that we do not want to reexport via Invenio OAI; so records
## may have only the source OAI ID stored in this tag (kind of like
## external system number too).
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG = 035__a
## CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG -- where do we store OAI SRC
## tags of harvested records? Useful for matching when we harvest stuff
## via OAI that we do not want to reexport via Invenio OAI; so records
## may have only the source OAI SRC stored in this tag (kind of like
## external system number too). Note that the field should be the same of
## CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG.
CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG = 035__9
## CFG_BIBUPLOAD_STRONG_TAGS -- a comma-separated list of tags that
## are strong enough to resist the replace mode. Useful for tags that
## might be created from an external non-metadata-like source,
## e.g. the information about the number of copies left.
CFG_BIBUPLOAD_STRONG_TAGS = 964
## CFG_BIBUPLOAD_CONTROLLED_PROVENANCE_TAGS -- a comma-separated list
## of tags that contain provenance information that should be checked
## in the bibupload correct mode via matching provenance codes. (Only
## field instances of the same provenance information would be acted
## upon.) Please specify the whole tag info up to subfield codes.
CFG_BIBUPLOAD_CONTROLLED_PROVENANCE_TAGS = 6531_9
## CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS -- a comma-separated list of system
## paths from which it is allowed to take fulltextes that will be uploaded via
## FFT (CFG_TMPDIR is included by default).
CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS = /tmp,/home
## CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE -- do we want to serialize
## internal representation of records (Pythonic record structure) into
## the database? This can improve internal processing speed of some
## operations at the price of somewhat bigger disk space usage.
## If you change this value after some records have already been added
## to your installation, you may want to run:
## $ /opt/invenio/bin/inveniocfg --reset-recstruct-cache
## in order to either erase the cache thus freeing database space,
## or to fill the cache for all records that have not been cached yet.
CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE = 1
## CFG_BATCHUPLOADER_FILENAME_MATCHING_POLICY -- a comma-separated list
## indicating which fields match the file names of the documents to be
## uploaded.
## The matching will be done in the same order as the list provided.
CFG_BATCHUPLOADER_FILENAME_MATCHING_POLICY = reportnumber,recid
## CFG_BATCHUPLOADER_DAEMON_DIR -- Directory where the batchuploader daemon
## will look for the subfolders metadata and document by default.
## If path is relative, CFG_PREFIX will be joined as a prefix
CFG_BATCHUPLOADER_DAEMON_DIR = var/batchupload
## CFG_BATCHUPLOADER_WEB_ROBOT_AGENT -- Comma-separated list to specify the
## agents permitted when calling batch uploader web interface
## cdsweb.cern.ch/batchuploader/robotupload
## if using a curl, eg: curl xxx -A invenio_webupload
CFG_BATCHUPLOADER_WEB_ROBOT_AGENT = invenio_webupload
## CFG_BATCHUPLOADER_WEB_ROBOT_RIGHTS -- Access list specifying for each
## IP address, which collections are allowed using batch uploader robot
## interface.
CFG_BATCHUPLOADER_WEB_ROBOT_RIGHTS = {
'10.0.0.1': ['BOOK', 'REPORT'], # Example 1
'10.0.0.2': ['POETRY', 'PREPRINT'], # Example 2
}
####################################
## Part 18: BibCatalog parameters ##
####################################
## EXPERIMENTAL: Please do not use.
CFG_BIBCATALOG_SYSTEM =
CFG_BIBCATALOG_SYSTEM_RT_CLI = /usr/bin/rt
CFG_BIBCATALOG_SYSTEM_RT_URL = http://localhost/rt3
CFG_BIBCATALOG_QUEUES = General
####################################
## Part 19: BibFormat parameters ##
####################################
## CFG_BIBFORMAT_HIDDEN_TAGS -- comma-separated list of MARC tags that
## are not shown to users not having cataloging authorizations.
CFG_BIBFORMAT_HIDDEN_TAGS = 595
## CFG_BIBFORMAT_ADDTHIS_ID -- if you want to use the AddThis service from
## <http://www.addthis.com/>, set this value to the pubid parameter as
## provided by the service (e.g. ra-4ff80aae118f4dad).
## See also the bfe_addthis.py BibFormat element.
CFG_BIBFORMAT_ADDTHIS_ID =
####################################
## Part 20: BibMatch parameters ##
####################################
## CFG_BIBMATCH_LOCAL_SLEEPTIME -- Determines the amount of seconds to sleep
## between search queries on LOCAL system.
CFG_BIBMATCH_LOCAL_SLEEPTIME = 0.0
## CFG_BIBMATCH_REMOTE_SLEEPTIME -- Determines the amount of seconds to sleep
## between search queries on REMOTE systems.
CFG_BIBMATCH_REMOTE_SLEEPTIME = 2.0
## CFG_BIBMATCH_FUZZY_WORDLIMITS -- Determines the amount of words to extract
## from a certain fields value during fuzzy matching mode. Add/change field
## and appropriate number to the dictionary to configure this.
CFG_BIBMATCH_FUZZY_WORDLIMITS = {
'100__a': 2,
'245__a': 4
}
## CFG_BIBMATCH_FUZZY_EMPTY_RESULT_LIMIT -- Determines the amount of empty results
## to accept during fuzzy matching mode.
CFG_BIBMATCH_FUZZY_EMPTY_RESULT_LIMIT = 1
## CFG_BIBMATCH_QUERY_TEMPLATES -- Here you can set the various predefined querystrings
## used to standardize common matching queries. By default the following templates
## are given:
## title - standard title search. Taken from 245__a (default)
## title-author - title and author search (i.e. this is a title AND author a)
## Taken from 245__a and 100__a
## reportnumber - reportnumber search (i.e. reportnumber:REP-NO-123).
CFG_BIBMATCH_QUERY_TEMPLATES = {
'title' : '[title]',
'title-author' : '[title] [author]',
'reportnumber' : 'reportnumber:[reportnumber]'
}
######################################
## Part 21: BibAuthorID parameters ##
######################################
# CFG_BIBAUTHORID_MAX_PROCESSES is the max number of processes
# that may be spawned by the disambiguation algorithm
CFG_BIBAUTHORID_MAX_PROCESSES = 4
# CFG_BIBAUTHORID_PERSONID_SQL_MAX_THREADS is the max number of threads
# to parallelize sql queries during personID tables updates
CFG_BIBAUTHORID_PERSONID_SQL_MAX_THREADS = 4
# CFG_BIBAUTHORID_PERSONID_MIN_P_FROM_BCTKD_RA is the minimum confidence needed
# when backtracking automatically disambiguated authors to persons.
# Values in [0,1]
CFG_BIBAUTHORID_PERSONID_MIN_P_FROM_BCTKD_RA = 0.5
# CFG_BIBAUTHORID_PERSONID_MIN_P_FROM_NEW_RA is the threshold for
# the confidence in a paper by the disambiguation algorithm to have it
# automatically connected to a personID. Papers below the thresholds are
# left disconnected from persons if not already connected in other ways.
# values in [0,1]
CFG_BIBAUTHORID_PERSONID_MIN_P_FROM_NEW_RA = 0.5
# CFG_BIBAUTHORID_PERSONID_MAX_COMP_LIST_MIN_TRSH minimum threshold for
# disambiguated authors and persons: if less compatible than this the update
# process will create a new person to associate to the found disambiguated author.
CFG_BIBAUTHORID_PERSONID_MAX_COMP_LIST_MIN_TRSH = 0.5
# CFG_BIBAUTHORID_PERSONID_MAX_COMP_LIST_MIN_TRSH_P_N is a fallback mechanism
# to force a merge if a certain percentage of papers is compatible no matter
# what the confidences on the automatically disambiguated author looks like
CFG_BIBAUTHORID_PERSONID_MAX_COMP_LIST_MIN_TRSH_P_N = 0.5
# CFG_BIBAUTHORID_EXTERNAL_CLAIMED_RECORDS_KEY defines the user info
# keys for externally claimed records in an remote-login scenario--e.g. from arXiv.org
# e.g. "external_arxivids" for arXiv SSO
CFG_BIBAUTHORID_EXTERNAL_CLAIMED_RECORDS_KEY =
# CFG_BIBAUTHORID_ATTACH_VA_TO_MULTIPLE_RAS determines if the authorid
# algorithm is allowed to attach a virtual author to multiple
# real authors in the last run of the orphan processing.
# Comma separated list of values.
CFG_BIBAUTHORID_ATTACH_VA_TO_MULTIPLE_RAS = False
# CFG_BIBAUTHORID_AID_ENABLED
# Globally enable AuthorID Interfaces.
# If False: No guest, user or operator will have access to the system.
CFG_BIBAUTHORID_ENABLED = True
# CFG_BIBAUTHORID_AID_ON_AUTHORPAGES
# Enable AuthorID information on the author pages.
CFG_BIBAUTHORID_ON_AUTHORPAGES = True
# CFG_BIBAUTHORID_AUTHOR_TICKET_ADMIN_EMAIL defines the eMail address
# all ticket requests concerning authors will be sent to.
CFG_BIBAUTHORID_AUTHOR_TICKET_ADMIN_EMAIL = info@invenio-software.org
##########################
## THAT's ALL, FOLKS! ##
##########################
diff --git a/modules/bibcirculation/lib/bibcirculation_templates.py b/modules/bibcirculation/lib/bibcirculation_templates.py
index 446b0566a..a8defc99e 100644
--- a/modules/bibcirculation/lib/bibcirculation_templates.py
+++ b/modules/bibcirculation/lib/bibcirculation_templates.py
@@ -1,16594 +1,16595 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Templates for bibcirculation module """
__revision__ = "$Id$"
import datetime
import time
import cgi
from invenio.urlutils import create_html_link
from invenio.config import CFG_SITE_URL, CFG_SITE_LANG, \
- CFG_CERN_SITE, CFG_SITE_SECURE_URL
+ CFG_CERN_SITE, CFG_SITE_SECURE_URL, CFG_SITE_RECORD
from invenio.bibcirculation_config import CFG_BIBCIRCULATION_LIBRARIAN_EMAIL
from invenio.messages import gettext_set_language
import invenio.bibcirculation_dblayer as db
from invenio.bibcirculation_utils import get_book_cover, \
book_information_from_MARC, \
book_title_from_MARC, \
renew_loan_for_X_days, \
get_item_info_for_search_result, \
all_copies_are_missing, \
has_copies
_MENU_ = """
<div id="cdlhead">
<map name="Navigation_Bar" id="cdlnav">
<div id="bibcircmenu" class="cdsweb">
<h2><a name="localNavLinks">Main navigation links:</a></h2>
<ul>
<!-- <li>
<a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py">Home</a>
</li> -->
<li>
<a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1">Loan</a>
</li>
<li>
<a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/loan_return">Return</a>
</li>
<li>
<a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/borrower_search?redirect='yes'">Request</a>
</li>
<li>
<a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/borrower_search">Borrowers</a>
</li>
<!--
<li class="hassubmenu">
<a href="#">Borrowers</a>
<ul class="subsubmenu" style="width:16.5em;">
<li><a href = "%(url)s/admin/bibcirculation/bibcirculationadmin.py/borrower_search">Search...</a></li>
<li><a href = "%(url)s/admin/bibcirculation/bibcirculationadmin.py/borrower_notification">Notify</a></li>
<li><a href = "%(url)s/admin/bibcirculation/bibcirculationadmin.py/add_new_borrower_step1">Add new borrower</a></li>
<li><a href = "%(url)s/admin/bibcirculation/bibcirculationadmin.py/update_borrower_info_step1">Update info</a></li>
</ul>
</li>
-->
<li>
<a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/item_search">Items</a>
</li>
<!--
<li class="hassubmenu">
<a href="#">Items</a>
<ul class="subsubmenu" style="width:16.5em;">
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/item_search">Search...</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/new_item">Add new item</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/add_new_copy_step1">Add new copy</a></li>
<li><a href="#"># - Remove</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/update_item_info_step1">Update info</a></li>
</ul>
</li>
-->
<li class="hassubmenu">
<a href="#">Lists</a>
<ul class="subsubmenu" style="width:17.5em;">
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/all_loans">Last loans</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/all_expired_loans">Overdue loans</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/get_pending_requests">Items on shelf with holds</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/get_waiting_requests">Items on loan with holds</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/get_expired_loans_with_requests">Overdue loans with holds</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/ordered_books">Ordered books</a></li>
<!-- <li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/all_loans_test">TEST loans</a></li>
<li><a href="#"># - Stats</a></li> -->
</ul>
</li>
<!--
<li class="hassubmenu">
<a href="#">Loans</a>
<ul class="subsubmenu" style="width:16.5em;">
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1">On library desk</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/loan_return">Return book</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/all_loans">List of all loans</a></li>
<li><a href="#"># - Stats</a></li>
</ul>
</li>
<li class="hassubmenu">
<a href="#">Requests</a>
<ul class="subsubmenu" style="width:16.5em;">
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/get_pending_requests">List of pending requests</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/all_requests">List of hold requests</a></li>
<li><a href="#"># - Stats</a></li>
</ul>
</li>
-->
<li class="hassubmenu">
<a href="#">Libraries</a>
<ul class="subsubmenu" style="width:16.5em;">
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/search_library_step1">Search...</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/add_new_library_step1">Add new library</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/update_library_info_step1">Update info</a></li>
</ul>
</li>
<li class="hassubmenu">
<a href="#">Vendors</a>
<ul class="subsubmenu" style="width:16.5em;">
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/search_vendor_step1">Search...</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/add_new_vendor_step1">Add new vendor</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/update_vendor_info_step1">Update info</a></li>
</ul>
</li>
<li class="hassubmenu">
<a href="#">Acquisitions</a>
<ul class="subsubmenu" style="width:16.5em;">
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/ordered_books">List of ordered books</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/new_book_step1">Order new book</a></li>
</ul>
</li>
<li class="hassubmenu">
<a href="#">ILL</a>
<ul class="subsubmenu" style="width:17.5em;">
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/ill_search">Search...</a></li>
<!--<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/list_ill_request">All requests</a></li>-->
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/list_ill_request?status=new">New</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/list_ill_request?status=requested">Requested</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/list_ill_request?status=on loan">On loan</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/register_ill_book_request">Register Book request</a></li>
<li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/register_ill_article_request_step1">Register Article request</a></li>
</ul>
</li>
<li class="hassubmenu">
<a href="#">Help</a>
<ul class="subsubmenu" style="width:16.5em;">
<li><a href="%(url)s/help/admin/bibcirculation-admin-guide" target="_blank">Admin guide</a></li>
<!-- <li><a href="%(url)s/admin/bibcirculation/bibcirculationadmin.py/help_contactsupport">Contact Support</a></li> -->
</ul>
</li>
<div class="clear"></div>
</map>
</div>
""" % {'url': CFG_SITE_URL}
class Template:
"""Templates for BibCirculation module"""
def tmpl_infobox(self, infos, ln=CFG_SITE_LANG):
"""
Display len(infos) information fields
@param infos: list of strings
@param ln: language
@return html output
"""
_ = gettext_set_language(ln)
if not((type(infos) is list) or (type(infos) is tuple)):
infos = [infos]
infobox = ""
for info in infos:
infobox += "<div class=\"infobox\">"
lines = info.split("\n")
for line in lines[0:-1]:
infobox += line + "<br />\n"
infobox += lines[-1] + "</div><br />\n"
return infobox
def tmpl_holdings_information2(self, recid, req, holdings_info,
ln=CFG_SITE_LANG):
"""
This template is used in the user interface. In this template
it is possible to see all details (loan period, number of copies, location, etc)
about a book.
@param recid: identify the record. Primary key of bibrec.
@type recid: int
@param holdings_info: book's information (all copies)
@type holdings_info: list
"""
_ = gettext_set_language(ln)
if not book_title_from_MARC(recid):
out = """<div align="center"
<div class="infoboxmsg">
This record does not exist.
</div>"""
return out
elif not has_copies(recid):
out = """<div align="center"
<div class="infoboxmsg">
This record has no copies.
</div>"""
return out
# verify if all copies are missing
elif all_copies_are_missing(recid):
- ill_link = """<a href='%s/record/%s/holdings/ill_request_with_recid'>ILL services</a>""" % (CFG_SITE_URL, recid)
+ ill_link = """<a href='%s/%s/%s/holdings/ill_request_with_recid'>ILL services</a>""" % (CFG_SITE_URL, CFG_SITE_RECORD, recid)
out = """<div align="center"
<div class="infoboxmsg">
All the copies of <strong>%s</strong> are missing.You can request a copy using <strong>%s</strong>.
</div>""" % (book_title_from_MARC(recid), ill_link)
return out
# verify if there are no copies
elif not holdings_info:
out = """<div align="center"
<div class="infoboxmsg">
This item has no holdings.
</div>"""
return out
out = """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_holdings').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
"""
out += """
<table id="table_holdings" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (_("Barcode"), _("Library"), _("Collection"),
_("Location"), _("Description"), _("Loan period"),
_("Status"), _("Due date"), _("Option(s)"))
for (barcode, library, collection, location, description,
loan_period, status, due_date) in holdings_info:
if status == 'Not for loan':
request_button = '-'
else:
- request_button = """<input type=button onClick="location.href='%s/record/%s/holdings/request?barcode=%s'"
+ request_button = """<input type=button onClick="location.href='%s/%s/%s/holdings/request?barcode=%s'"
value='%s' class="bibcircbutton" onmouseover="this.className='bibcircbuttonover'"
- onmouseout="this.className='bibcircbutton'">""" % (CFG_SITE_URL, recid, barcode,
- _("Request"))
+ onmouseout="this.className='bibcircbutton'">""" % (CFG_SITE_URL, CFG_SITE_RECORD, recid,
+ barcode, _("Request"))
if status == 'missing':
out += """ """
else:
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td align='center'>%s</td>
</tr>
""" % (barcode, library, collection or '-', location,
description or '-', loan_period, status,
due_date or '-', request_button)
from invenio.bibcirculationadminlib import is_adminuser
(auth_code, _auth_message) = is_adminuser(req)
if auth_code != 0:
bibcirc_link = ''
else:
bibcirc_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
_("See this book on BibCirculation"))
out += """
</tbody>
</table>
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
""" % (bibcirc_link)
return out
def tmpl_book_not_for_loan(self):
message = """<div align="center"
<div class="infoboxmsg">
This item is not for loan.
</div>"""
return message
def tmpl_message_request_send_ok_cern(self):
message = "Your request has been registered and the document"\
" will be sent to you via internal mail."
return message
def tmpl_message_request_send_ok_other(self):
message = "Your request has been registered."
return message
def tmpl_message_request_send_fail_cern(self):
message = "It is not possible to validate your request. "\
"Your office address is not available. "\
"Please contact " + CFG_BIBCIRCULATION_LIBRARIAN_EMAIL
return message
def tmpl_message_request_send_fail_other(self):
message = "It is not possible to validate your request. "\
"Your office address is not available. "\
"Please contact " + CFG_BIBCIRCULATION_LIBRARIAN_EMAIL
return message
def tmpl_borrower_search_result(self, result, redirect='no', ln=CFG_SITE_LANG):
"""
When the admin features 'borrower_seach' is used, this template
show the result.
@param result: search result
@type result:list
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("0 borrower(s) found."))
else:
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
</form>
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirccontent">
<strong>%s borrower(s) found</strong>
</td>
</tr>
</table>
<br />
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<th align="center">%s</th>
""" % (len(result), _("Borrower(s)"))
for (borrower_id, name) in result:
if redirect == 'no':
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(name))
else:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/create_new_request_step1',
{'borrower_id': borrower_id, 'ln': ln},
(name))
out += """
<tr align="center">
<td width="70">%s
<input type=hidden name=uid value=%s></td>
</tr>
""" % (borrower_link, borrower_id)
out += """
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td><input type=button value=%s onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Back"))
return out
def tmpl_yourloans(self, loans, requests, borrower_id,
infos, ln=CFG_SITE_LANG):
"""
When an user is logged in, it is possible to check his loans.
In the section 'yourloans', it is also possible to renew a single
loan or all loans.
@param result: show all loans of an user who is logged in
@param uid: user ID
@param infos: display information about holdings
@param ln: language
"""
_ = gettext_set_language(ln)
renew_all_link = create_html_link(CFG_SITE_SECURE_URL +
'/yourloans/display',
{'borrower_id': borrower_id},
(_("Renew all loans")))
loanshistoricaloverview_link = create_html_link(CFG_SITE_SECURE_URL +
'/yourloans/loanshistoricaloverview',
{'ln': ln},
(_("Loans - historical overview")))
out = self.tmpl_infobox(infos, ln)
if len(loans) == 0:
out += """
<div class="bibcirctop_bottom">
<br />
<br />
<table class="bibcirctable_contents">
<td align="center" class="bibcirccontent">%s</td>
</table>
<br />
<br />
""" % (_("You don't have any book on loan."))
else:
out += """<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_loans').tablesorter()
});
</script>
<table class="tablesortermedium" id="table_loans" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (_("Item"),
_("Loaned on"),
_("Due date"),
_("Action(s)"))
for(recid, barcode, loaned_on, due_date, loan_type) in loans:
- record_link = "<a href=" + CFG_SITE_URL + "/record/%s>" % recid + \
+ record_link = "<a href=" + CFG_SITE_URL + "/%s/%s>" % (CFG_SITE_RECORD, recid) + \
(book_title_from_MARC(recid)) + "</a>"
if loan_type == 'ill':
renew_link = '-'
else:
renew_link = create_html_link(CFG_SITE_SECURE_URL +
'/yourloans/display',
{'barcode': barcode},
(_("Renew")))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (record_link,
loaned_on,
due_date,
renew_link)
out += """ </tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td width="430"></td>
<td class="bibcirccontent" width="700">%s</td>
</tr>
</table>
<br />
<br />
<br />
""" % (renew_all_link)
if len(requests) == 0:
out += """
<h1 class="headline">%s</h1>
<div class="bibcirctop">
<br /> <br />
<table class="bibcirctable_contents">
<td align="center" class="bibcirccontent">%s</td>
</table>
<br /> <br />
<hr>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="70">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s'" value='%s' class='formbutton'>
</td>
</tr>
</table>
<br />
""" % (_("Your Requests"),
_("You don't have any request (waiting or pending)."),
loanshistoricaloverview_link,
CFG_SITE_URL,
_("Back to home"))
else:
out +="""
<h1 class="headline">%s</h1>
<div class="bibcirctop">
<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_requests').tablesorter()
});
</script>
<table class="tablesortermedium" id="table_requests" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (_("Your Requests"),
_("Item"),
_("Request date"),
_("Status"),
_("Action(s)"))
for(request_id, recid, request_date, status) in requests:
- record_link = "<a href=" + CFG_SITE_URL + "/record/%s>" % recid + \
+ record_link = "<a href=" + CFG_SITE_URL + "/%s/%s>" % (CFG_SITE_RECORD, recid) + \
(book_title_from_MARC(recid)) + "</a>"
cancel_request_link = create_html_link(CFG_SITE_URL +
'/yourloans/display',
{'request_id': request_id},
(_("Cancel")))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (record_link, request_date,
status, cancel_request_link)
out +=""" </tbody>
</table>
<br />
<br />
<br />
<hr>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="70">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s'" value='%s' class='formbutton'>
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (loanshistoricaloverview_link,
CFG_SITE_URL,
_("Back to home"))
return out
def tmpl_loanshistoricaloverview(self, result, ln):
"""
In the section 'yourloans' it is possible to see the loans historical overview
of the user who is logged in. Bibcirculation display all loans where the status is
'returned'.
@param result: show all loans where status = 'returned'
@param ln: language
"""
_ = gettext_set_language(ln)
out = """<div class="bibcirctop_bottom">
<br /> <br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_hist').tablesorter()
});
</script>
<table class="tablesortermedium" id="table_hist" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (_("Item"),
_("Loaned"),
_("Returned"),
_("Renewalls"))
for(recid, loaned_on, returned_on, nb_renewalls) in result:
- record_link = "<a href=" + CFG_SITE_URL + "/record/%s>" % recid + \
+ record_link = "<a href=" + CFG_SITE_URL + "/%s/%s>" % (CFG_SITE_RECORD, recid) + \
(book_title_from_MARC(recid)) + "</a>"
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (record_link, loaned_on,
returned_on, nb_renewalls)
out += """</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_new_request(self, uid, recid, barcode, ln=CFG_SITE_LANG):
"""
This template is used when an user want to request a book. Here it is
possible to define the 'period of interest'
@param uid: user ID
@param recid: recID - Invenio record identifier
- @param barcode: book's barcode
+ @param barcode: book barcode
@param ln: language
"""
_ = gettext_set_language(ln)
today = datetime.date.today()
out = """
- <form name="request_form" action="%s/record/%s/holdings/send" method="get" >
+ <form name="request_form" action="%s/%s/%s/holdings/send" method="get" >
<div class="bibcirctableheader" align="center">%s</div>
<br />
<table class="bibcirctable_contents">
<tr>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader">%s</td>
</tr>
""" % (CFG_SITE_URL,
+ CFG_SITE_RECORD,
recid,
_("Enter your period of interest"),
_("From"),
_("Year"),
_("Month"),
_("Day"))
out += """
<tr>
<td class="bibcirccontent" width="30"></td>
<td class="bibcirccontent" width="30"><input size=4 style='border: 1px solid #cfcfcf' name="from_year" value=%(from_year)s></td>
<td class="bibcirccontent" width="30"><input size=2 style='border: 1px solid #cfcfcf' name="from_month" value=%(from_month)s></td>
<td class="bibcirccontent" width="30"><input size=2 style='border: 1px solid #cfcfcf' name="from_day" value=%(from_day)s></td>
</tr>
"""
out += """
<tr>
<td class="bibcirctableheader" width="30">%s</td>
<td class="bibcirctableheader" width="30">%s</td>
<td class="bibcirctableheader" width="30">%s</td>
<td class="bibcirctableheader" width="30">%s</td>
</tr>
""" % (_("To"),
_("Year"),
_("Month"),
_("Day"))
out += """
<tr>
<td class="bibcirccontent" width="30"></td>
<td class="bibcirccontent" width="30"><input size=4 style='border: 1px solid #cfcfcf' name="to_year" value=%(to_year)s></td>
<td class="bibcirccontent" width="30"><input size=2 style='border: 1px solid #cfcfcf' name="to_month" value=%(to_month)s></td>
<td class="bibcirccontent" width="30"><input size=2 style='border: 1px solid #cfcfcf' name="to_day" value=%(to_day)s></td>
</tr>
</table>
<br /> <br />
"""
out += """
<table class="bibcirctable_contents">
<tr>
<td align="center">
<input type=button value="Back" onClick="history.go(-1)" class="formbutton">
<input type="submit" name="submit_button" value="%(submit_button)s" class="formbutton">
<input type=hidden name=barcode value='%(barcode)s'>
</td>
</tr>
</table>
<br /> <br />
</form>
"""
out = out % {'url': CFG_SITE_URL,
'from_year' : today.year,
'from_month' : today.month,
'from_day': today.day,
'to_year': today.year + 1,
'to_month': today.month,
'to_day': today.day,
'submit_button': ('Confirm'),
'recid': recid,
'uid': uid,
'barcode': barcode
}
return out
def tmpl_new_request2(self, recid, barcode, ln=CFG_SITE_LANG):
"""
This template is used when an user want to request a book. Here it is
possible to define the 'period of interest'
@param uid: user ID
@param recid: recID - Invenio record identifier
@param barcode: book's barcode
@param ln: language
"""
_ = gettext_set_language(ln)
today = datetime.date.today()
gap = datetime.timedelta(days=180)
more_6_months = (today + gap).strftime('%Y-%m-%d')
out = """
<style type="text/css"> @import url("/img/jquery/tablesorter.css"); </style>
<link rel=\"stylesheet\" href=\"%s/img/jquery/jquery-ui.css\" type=\"text/css\" />
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
- <form name="request_form" action="%s/record/%s/holdings/send" method="get" >
+ <form name="request_form" action="%s/%s/%s/holdings/send" method="get" >
<br />
<div align=center>
<table class="bibcirctable_contents" align=center>
<tr>
<td class="bibcirctableheader" align=center>%s</td>
</tr>
</table>
<br />
<table align=center class="tablesorterborrower" width="100" border="0" cellspacing="1" align="center">
<tr align=center>
<th align=center>%s</th>
<td>
<script type="text/javascript">
$(function() {
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker1" name="period_from" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr align=center>
<th align=center>%s</th>
<td>
<script type="text/javascript">
$(function() {
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker2" name="period_to" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
</div>
<br />
<br />
<table class="bibcirctable_contents">
<input type=hidden name=barcode value='%s'>
<tr>
<td align="center">
<input type=button value='%s' onClick="history.go(-1)" class="formbutton">
<input type="submit" name="submit_button" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
""" % (CFG_SITE_URL, CFG_SITE_URL, CFG_SITE_URL,
- CFG_SITE_URL, recid,
+ CFG_SITE_URL, CFG_SITE_RECORD, recid,
_("Enter your period of interest"),
_("From"),CFG_SITE_URL, today, _("To"), CFG_SITE_URL, more_6_months,
barcode, _("Back"), _("Confirm"))
return out
def tmpl_new_request_send(self, message, ln=CFG_SITE_LANG):
"""
This template is used in the user interface.
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
<br /> <br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="30">%s</td>
</tr>
<tr>
<td class="bibcirccontent" width="30">%s<a href="%s">%s</a>%s</td>
</tr>
</table>
<br /> <br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s'" value='%s' class='formbutton'></td>
</table>
<br /> <br />
""" % (message,
_("You can see your loans "),
CFG_SITE_URL + '/yourloans/display',
_("here"),
_("."),
CFG_SITE_URL,
_("Back to home"))
return out
def tmpl_next_loan_request_done(self, ln=CFG_SITE_LANG):
"""
This template is used in the admin interface, when a request is
'waiting' in the queue.
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = _MENU_
out += """
<div class="bibcircbottom">
<br /> <br />
<table class="bibcirctable">
<td class="bibcirccontent" width="30">%s</td>
</table>
<br /> <br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value=%s class='formbutton'></td>
</table>
<br /> <br />
</div>
""" % (_("A new loan has been registered."),
CFG_SITE_URL,
_("Back to home"))
return out
def tmpl_get_pending_requests(self, result, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = _MENU_
out += """
<style type="text/css"> @import url("/js/jquery/tablesorter/themes/blue/style.css"); </style>
<style type="text/css"> @import url("/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.css"); </style>
<script src="/js/jquery/jquery.min.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/jquery.tablesorter.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#table_all_loans")
.tablesorter({sortList: [[4,0], [0,0]],widthFixed: true, widgets: ['zebra']})
.bind("sortStart",function(){$("#overlay").show();})
.bind("sortEnd",function(){$("#overlay").hide()})
.tablesorterPager({container: $("#pager"), positionFixed: false});
});
</script>
<br />
<div class="bibcircbottom">
"""
if len(result) == 0:
out += """
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value='%s' onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("No more requests are pending."),
_("Back"))
else:
out += """
<form name="borrower_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/all_loans" method="get" >
<br />
<table id="table_all_loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (CFG_SITE_URL,
_("Name"),
_("Item"),
_('Library'),
_("Location"),
_("From"),
_("To"),
_("Request date"),
_("Actions"))
for (request_id, recid, name, borrower_id, library, location, date_from, date_to, request_date) in result:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(name))
out += """
<script type="text/javascript">
function confirmation() {
var answer = confirm("Delete this request?")
if (answer){
window.location = "%s/admin/bibcirculation/bibcirculationadmin.py/get_pending_requests?request_id=%s";
}
else{
alert("Request not deleted.")
}
}
</script>
<tr>
<td width='150'>%s</td>
<td width='250'>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td algin='center'>
<input type="button" value='%s' style="background: url(/img/jquery/dialog-cancel.png)
no-repeat; width: 75px; text-align: right;"
onClick="confirmation()"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
class="bibcircbutton">
<input type=button style="background: url(/img/dialog-yes.png) no-repeat; width: 150px; text-align: right;"
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/associate_barcode?request_id=%s&recid=%s&borrower_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
""" % (CFG_SITE_URL,
request_id,
borrower_link,
title_link,
library,
location,
date_from,
date_to,
request_date,
_("Delete"),
CFG_SITE_URL,
request_id,
recid,
borrower_id,
_("Associate barcode"))
out+= """
</tbody>
</table>
</form>
<div id="pager" class="pager">
<form>
<br />
<img src="/img/sb.gif" class="first" />
<img src="/img/sp.gif" class="prev" />
<input type="text" class="pagedisplay" />
<img src="/img/sn.gif" class="next" />
<img src="/img/se.gif" class="last" />
<select class="pagesize">
<option value="10" selected="selected">10</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="40">40</option>
</select>
</form>
</div>
"""
out += """
<div class="back" style="position: relative; top: 5px;">
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s' onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
</form>
</div>
</div>
""" % (_("Back"))
return out
def tmpl_get_waiting_requests(self, result, ln=CFG_SITE_LANG):
"""
Template for the admin interface. Show pending requests(all, on loan, available)
@param result: items with status = 'pending'
@param ln: language
"""
_ = gettext_set_language(ln)
out = _MENU_
out += """
<style type="text/css"> @import url("/js/jquery/tablesorter/themes/blue/style.css"); </style>
<style type="text/css"> @import url("/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.css"); </style>
<script src="/js/jquery/jquery.min.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/jquery.tablesorter.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#table_all_loans")
.tablesorter({sortList: [[4,0], [0,0]],widthFixed: true, widgets: ['zebra']})
.bind("sortStart",function(){$("#overlay").show();})
.bind("sortEnd",function(){$("#overlay").hide()})
.tablesorterPager({container: $("#pager"), positionFixed: false});
});
</script>
<br />
<div class="bibcircbottom">
"""
if len(result) == 0:
out += """
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value='%s' onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("No more requests are pending."),
_("Back"))
else:
out += """
<form name="borrower_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/all_loans" method="get" >
<br />
<table id="table_all_loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (CFG_SITE_URL,
_("Name"),
_("Item"),
_('Library'),
_("Location"),
_("From"),
_("To"),
_("Request date"),
_("Options"))
for (request_id, recid, name, borrower_id, library, location,
date_from, date_to, request_date) in result:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(name))
out += """
<script type="text/javascript">
function confirmation() {
var answer = confirm("Delete this request?")
if (answer){
window.location = "%s/admin/bibcirculation/bibcirculationadmin.py/get_pending_requests?request_id=%s";
}
else{
alert("Request not deleted.")
}
}
</script>
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td align="center">
<input type="button" value='%s' style="background: url(/img/jquery/dialog-cancel.png)
no-repeat; width: 75px; text-align: right;"
onClick="confirmation()"
class="bibcircbutton">
<input type=button type=button style="background: url(/img/dialog-yes.png) no-repeat; width: 150px; text-align: right;" onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/associate_barcode?request_id=%s&recid=%s&borrower_id=%s'"
value='%s' class="bibcircbutton">
</td>
</tr>
""" % (CFG_SITE_URL,
request_id,
borrower_link,
title_link,
library,
location,
date_from,
date_to,
request_date,
_("Cancel"),
CFG_SITE_URL,
request_id,
recid,
borrower_id,
_("Associate barcode"))
out+= """
</tbody>
</table>
</form>
<div id="pager" class="pager">
<form>
<br />
<img src="/img/sb.gif" class="first" />
<img src="/img/sp.gif" class="prev" />
<input type="text" class="pagedisplay" />
<img src="/img/sn.gif" class="next" />
<img src="/img/se.gif" class="last" />
<select class="pagesize">
<option value="10" selected="selected">10</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="40">40</option>
</select>
</form>
</div>
"""
out += """
<div class="back" style="position: relative; top: 5px;">
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s' onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
</form>
</div>
</div>
""" % (_("Back"))
return out
def tmpl_get_next_waiting_loan_request(self, result, recid, barcode, ln=CFG_SITE_LANG):
"""
Template for the admin interface. Show the next request in the queue.
@param result: next request with status = 'waiting'
@param barcode: book's barcode
@param ln: language
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom">
<br /> <br /> <br /> <br />
<table class="bibcirctable_contents">
<td class="bibcirccontent" align="center">%s</td>
</table>
<br /> <br /> <br />
<table class="bibcirctable_contents">
<td align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value="Back to home" class='formbutton'>
</td>
</table>
<br />
</div>
""" % (_("No hold requests waiting."), CFG_SITE_URL)
else:
out += """
<form name="list_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_next_waiting_loan_request" method="get" >
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td width="10"><input type=hidden name=barcode value=%s></td>
<td width="10"><input type=hidden name=recid value=%s></td>
</tr>
"""% (CFG_SITE_URL,
_("Name"),
_("Item"),
_("Request status"),
_("From"),
_("To"),
_("Request date"),
_("Request options"),
barcode,
recid)
for (id_lr, name, recid, status, date_from, date_to, request_date) in result:
out += """
<tr onMouseOver="this.className='highlight'" onMouseOut="this.className='normal'">
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_next_waiting_loan_request?check_id=%s&recid=%s&barcode=%s'"
value='%s' class="formbutton">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/update_next_loan_request_status?check_id=%s&barcode=%s'"
value='%s' class="formbutton"></td>
</td>
</tr>
""" % (
name, book_title_from_MARC(recid),
status, date_from, date_to,
request_date, CFG_SITE_URL,
id_lr, recid, barcode,
_("Cancel"), CFG_SITE_URL,
id_lr, barcode,
_('Select hold request'))
out += """</table>
<br />
<br />
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_loan_return(self, infos, ln=CFG_SITE_LANG):
"""
Template for the admin interface. Used when a book return.
@param ln: language
"""
out = self.tmpl_infobox(infos, ln)
_ = gettext_set_language(ln)
out += _MENU_
out += """
<form name="return_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/loan_return_confirm" method="get">
<div class="bibcircbottom">
<br />
<br />
<br />
<table class="bibcirctable_contents">
<tr align="center">
<td class="bibcirctableheader">
%s
<input type="text" size=45 id="barcode" name="barcode" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
""" % (CFG_SITE_URL,
_("Barcode"))
out += """
<br />
<table class="bibcirctable_contents">
<tr align="center">
<td>
<input type="reset" name="reset_button" value=%s class="formbutton">
<input type="submit" name="ok_button" value=%s class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
</form>
""" % (_("Reset"),
_("OK"))
return out
def tmpl_loan_return_confirm(self, borrower_name, borrower_id, recid,
barcode, return_date, result, ln=CFG_SITE_LANG):
"""
@param borrower_name: person who returned the book
@param id_bibrec: book's recid
@param barcode: book's barcode
@param ln: language
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(borrower_name))
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<form name="return_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_next_waiting_loan_request" method="get" >
<div class="bibcircbottom">
<br />
<div class="infoboxsuccess">%s</div>
<br />
<table class="bibcirctable">
""" % (CFG_SITE_URL, _("The item %(x_title)s with barcode %(x_barcode)s has been returned with success." % \
{'x_title': book_title_from_MARC(recid), 'x_barcode': barcode}))
(_book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="350">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
<input type=hidden name=recid value=%s>
<input type=hidden name=barcode value=%s>
""" % (_("Loan informations"),
_("Borrower"), borrower_link,
_("Item"), title_link,
_("Author"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
_("Return date"), return_date,
str(book_cover),
recid,
barcode)
if result:
out += """
</table>
<div class="infoboxmsg">%s</div>
<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_requests').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="100">%s</td>
</tr>
</table>
<table id="table_requests" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
</tbody>
"""% (_("There %s request(s) on the book who has been returned." % len(result)),
_("Waiting requests"),
_("Name"),
_("Item"),
_("Request status"),
_("From"),
_("To"),
_("Request date"),
_("Request options"))
for (request_id, name, recid, status, date_from, date_to, request_date) in result:
out += """
<script type="text/javascript">
function confirmation() {
var answer = confirm("Delete this request?")
if (answer){
window.location = "%s/admin/bibcirculation/bibcirculationadmin.py/get_next_waiting_loan_request?check_id=%s&recid=%s&barcode=%s";
}
}
</script>
""" % (CFG_SITE_URL, request_id, recid, barcode)
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>
<input type=button onClick="confirmation()" value='%s' class="bibcircbutton"
style="background: url(/img/jquery/dialog-cancel.png) no-repeat; width: 75px; text-align: right;">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/update_next_loan_request_status?check_id=%s&barcode=%s'"
value='%s' class="bibcircbutton" style="background: url(/img/dialog-yes.png) no-repeat; width: 125px; text-align: right;"></td>
</td>
</tr>
""" % (
name, book_title_from_MARC(recid),
status, date_from, date_to,
request_date, _("Delete"),
CFG_SITE_URL, request_id, barcode,
_('Select request'))
out += """
</table>
"""
else:
out += """
</table>
<div class="infoboxmsg">%s</div>""" % (_("There are no requests waiting on the item <strong>%s</strong>." % book_title_from_MARC(recid)))
out += """
<br />
<br />
<br />
</div>
</form>
"""
return out
def tmpl_index(self, ln=CFG_SITE_LANG):
"""
Main page of the Admin interface.
@param pending_request: display the number of pending requests
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<div class="subtitle">
%s
</div>
<br />
""" % (_("Welcome to Invenio BibCirculation Admin"))
out += """
<br /><br />
<br /><br />
<br /><br />
<br /><br />
<br /><br />
</div>
"""
return out
def tmpl_borrower_search(self, infos, redirect='no', ln=CFG_SITE_LANG):
"""
Template for the admin interface. Search borrower.
@param ln: language
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<br /><br /> <br />
<form name="borrower_search" action="%s/admin/bibcirculation/bibcirculationadmin.py/borrower_search_result" method="get" >
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s
<input type="radio" name="column" value="id">ccid
<input type="radio" name="column" value="name" checked>name
<input type="radio" name="column" value="email">email
<input type="hidden" name="redirect" value="%s">
<br>
<br>
</td>
</tr>
<tr align="center">
<td><input type="text" size="45" name="string" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<form>
<br />
<br />
<br />
<br />
</div>
""" % (CFG_SITE_URL,
_("Search borrower by"),redirect,
_("Back"), _("Search"))
return out
def tmpl_item_search(self, infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="search_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/item_search_result" method="get" >
<br />
<br />
<br />
<input type=hidden value="0">
<input type=hidden value="10">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">Search item by
<input type="radio" name="f" value="" checked>any field
<input type="radio" name="f" value="barcode">barcode
<input type="radio" name="f" value="author">author
<input type="radio" name="f" value="title">title
<br />
<br />
</td>
</tr>
<tr align="center">
<td><input type="text" size="50" name="p" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
<form>
""" % (CFG_SITE_URL, _("Back"), _("Search"))
return out
def tmpl_item_search_result(self, result, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
if result == None:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("0 item(s) found."))
else:
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<strong>%s item(s) found</strong>
</td>
</tr>
</table>
<br />
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (len(result), _("Title"),
_("Author"), _("Publisher"),
_("No. Copies"))
### FIXME: If one result -> go ahead ###
for recid in result:
(book_author, book_editor, book_copies) = get_item_info_for_search_result(recid)
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (title_link, book_author,
book_editor, book_copies)
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_loan_on_desk_step1(self, result, key, string, infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1" method="get" >
<br />
<br />
<br />
<table class="bibcirctable" align="center">
""" % (CFG_SITE_URL)
if CFG_CERN_SITE == 1:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search user by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'name':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="ccid" checked>ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<tr>
<td align="center" class="bibcirctableheader">Search borrower by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'id':
out += """
<input type="radio" name="key" value="id" checked>id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
out += """
<br><br>
</td>
</tr>
<tr>
<td align="center">
<input type="text" size="40" id="string" name="string" value='%s' style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<td align="center">
<br>
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
</form>
""" % (string or '')
if result:
out += """
<br />
<form name="step1_form2" action="/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step2" method="get">
<table class="bibcirctable">
<tr width="200">
<td align="center">
<select name="user_info" size="8" style='border: 1px solid #cfcfcf; width:200'>
"""
for (ccid, name, email, phone, address, mailbox) in result:
out += """
<option value ='%s,%s,%s,%s,%s,%s'>%s
""" % (ccid, name, email, phone, address, mailbox, name)
out += """
</select>
</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td align="center">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
</form>
""" % (_("Select user"))
out += """
<br />
<br />
<br />
</div>
"""
return out
def tmpl_loan_on_desk_step2(self, user_info, infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
(ccid, name, email, phone, address, mailbox) = user_info
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="step2_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step3" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="77">%s</td>
</tr>
<tr>
<td><textarea rows="5" cols="43" name="barcode" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
<input type=hidden name="user_info" value="%s">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (CFG_SITE_URL, _("User information"),
_("ID"), ccid,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox,
_("Barcode(s)"), _("Back"),
_("Continue"),user_info)
return out
def tmpl_loan_on_desk_step3(self, user_info, list_of_books, infos,
ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
(ccid, name, email, phone, address, mailbox) = user_info
#user_info = [str(ccid), str(name), str(email), str(phone), str(address), str(mailbox)]
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="step3_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step4" method="get" >
<br />
<br />
<input type=hidden name="list_of_books" value="%s">
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th width="65">%s</th>
<th width="100">%s</th>
<th width="80">%s</th>
<th width="130">%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (CFG_SITE_URL, list_of_books, _("User information"),
_("ID"), ccid,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox,
_("List of borrowed books"),
CFG_SITE_URL,CFG_SITE_URL,
_("Item"), _("Barcode"),
_("Library"), _("Location"),
_("Due date"), _("Write note(s)"))
iterator = 1
for (recid, barcode, library_id, location) in list_of_books:
due_date = renew_loan_for_X_days(barcode)
library_name = db.get_library_name(library_id)
out +="""
<tr>
<td>%s</td>
<td width="65">%s</td>
<td width="100">%s</td>
<td width="80">%s</td>
<td width="130" class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("%s").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="%s" name="due_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
<td ><textarea name='note' rows="1" cols="40" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
""" % (book_title_from_MARC(recid), barcode,
library_name, location,
"#date_picker" + str(iterator), CFG_SITE_URL ,"date_picker" + str(iterator),due_date)
iterator += 1
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
<input type=hidden name="user_info" value="%s">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % ( _("Back"), _("Continue"), str(user_info))
return out
def tmpl_loan_on_desk_step4(self, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out +="""<div class="bibcircbottom">
<br />
<br />
<br />
<br />
A new loan has been registered.
<br />
<br />
<br />
<br />
<table class="bibcirctable_contents">
<td align="center">
<input type=button onClick="location.href='%s'" value="Back to home" class='formbutton'>
</td>
</table>
<br />
</div>
""" % (CFG_SITE_URL)
return out
def tmpl_send_notification(self, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """
<div class="bibcircbottom">
<br /> <br />
<table class="bibcirctable">
<td class="bibcirccontent" width="30">%s</td>
</table>
<br /> <br />
<table class="bibcirctable">
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value='Back to home' class='formbutton'>
</td>
</table>
<br /> <br />
</div>
""" % (_("Notification has been sent!"),
CFG_SITE_URL)
return out
def tmpl_register_new_loan(self, borrower_info, recid, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
(_id, name, email, phone, address, mailbox) = borrower_info
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<br />
<div class="infoboxsuccess">%s</div>
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<br />
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<br />
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<td><input type="button" onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/register_new_loan?print_data=true'"
value="%s" class="formbutton"></td>
</table>
<br />
<br />
</div>
""" % (_("A new loan has been registered."),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox,
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
CFG_SITE_URL,
_("Print loan information"))
return out
def tmpl_loan_on_desk_confirm(self, barcode,
borrower, infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page0
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
borrower_email = borrower.split(' [')[0]
borrower_id = borrower.split(' [')[1]
borrower_id = int(borrower_id[:-1])
out += """
<form name="return_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_new_loan" method="get" >
<div class="bibcircbottom">
<input type=hidden name=borrower_id value=%s>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="70">%s</td>
<td class="bibcirccontent" width="600">%s</td>
</tr>
""" % (CFG_SITE_URL,
borrower_id,
_("Borrower"),
borrower_email)
for (bar) in barcode:
recid = db.get_id_bibrec(bar)
out += """
<tr>
<td class="bibcirctableheader" width="70">%s</td>
<td class="bibcirccontent" width="600">%s</td>
</tr>
<input type=hidden name=barcode value=%s>
""" % (_("Item"),
book_title_from_MARC(recid),
bar)
out += """
</table>
<br />
<table class="bibcirctable_contents">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
</td>
</tr>
</table>
<br />
</div>
</form>
""" % (_("Back"),
_("Confirm"))
return out
def tmpl_all_requests(self, result, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """
<form name="all_requests_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/all_requests" method="get" >
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
</tr>
"""% (CFG_SITE_URL,
_("Borrower"),
_("Item"),
_("Status"),
_("From"),
_("To"),
_("Request date"),
_("Option(s)"))
for (id_lr, borid, name, recid, status, date_from, date_to, request_date) in result:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borid, 'ln': ln},
(name))
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr onMouseOver="this.className='highlight'" onMouseOut="this.className='normal'">
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/all_requests?request_id=%s'"
value='%s' class="formbutton">
</td>
</tr>
""" % (borrower_link,
title_link,
status,
date_from,
date_to,
request_date,
CFG_SITE_URL,
id_lr,
_("Cancel hold request"))
out += """
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value="%s" onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br /> <br />
</div>
</form>
""" % (_("Back"))
return out
def tmpl_get_item_requests_details(self, recid, result, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("There are no requests."))
else:
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_requests').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<form name="all_loans_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_loan_request_status" method="get" >
<div class="bibcircbottom">
<br />
<table id="table_requests" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (CFG_SITE_URL,
_("Borrower"),
_("Status"),
_("Library"),
_("Location"),
_("From"),
_("To"),
_("Request date"),
_("Option(s)"))
for (borrower_id, name, id_bibrec, status, library,
location, date_from, date_to, request_id,
request_date) in result:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(name))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_item_requests_details?recid=%s&request_id=%s'"
value='%s' class='formbutton'>
</td>
</tr>
""" % (borrower_link, status, library, location,
date_from, date_to, request_date, CFG_SITE_URL,
id_bibrec, request_id, _("Cancel hold request"))
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_item_details?recid=%s'"
value='%s' class='formbutton'></td>
</tr>
</table>
<br /><br /><br />
</div>
</form>
""" % (CFG_SITE_URL,
recid,
_("Back"))
return out
def tmpl_get_item_details(self, recid, copies, requests, loans, req_hist_overview,
loans_hist_overview, infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
try:
book_cover = get_book_cover(book_isbn)
except KeyError:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
- link_to_detailed_record = "<a href='%s/record/%s' target='_blank'>%s</a>" % (CFG_SITE_URL, recid, book_title)
+ link_to_detailed_record = "<a href='%s/%s/%s' target='_blank'>%s</a>" % (CFG_SITE_URL, CFG_SITE_RECORD, recid, book_title)
out += """
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
- <input type=button onClick="window.open('%s/record/%s/edit')"
+ <input type=button onClick="window.open('%s/%s/%s/edit')"
value='%s' class="formbutton">
</td>
<td>
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
""" % (_("Item details"),
_("Name"), link_to_detailed_record,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
- CFG_SITE_URL, recid,
+ CFG_SITE_URL, CFG_SITE_RECORD, recid,
_("Edit this record"),
str(book_cover),
_("Additional details"))
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_copies').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<table class="tablesorter" id="table_copies" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tboby>
""" % (_("Barcode"),
_("Status"),
_("Due date"),
_("Library"),
_("Location"),
_("Loan period"),
_("No of loans"),
_("Collection"),
_("Description"),
_("Action(s)"))
for (barcode, loan_period, library_name, library_id,
location, nb_requests, status, collection,
description, due_date) in copies:
library_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_library_details',
{'library_id': library_id, 'ln': ln},
(library_name))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
"""% (barcode, status, due_date or '-', library_link, location,
loan_period, nb_requests, collection or '-',
description or '-')
if status == 'on loan':
out += """
<td align="center">
<SELECT style='border: 1px solid #cfcfcf' ONCHANGE="location = this.options[this.selectedIndex].value;">
<OPTION VALUE="">Select an action
<OPTION VALUE="update_item_info_step4?barcode=%s">Update
<OPTION VALUE="place_new_request_step1?barcode=%s">New request
<OPTION VALUE="" DISABLED>New loan
</SELECT>
</td>
</tr>
""" % (barcode, barcode)
elif status == 'missing':
out += """
<td align="center">
<SELECT style='border: 1px solid #cfcfcf' ONCHANGE="location = this.options[this.selectedIndex].value;">
<OPTION VALUE="">Select an action
<OPTION VALUE="update_item_info_step4?barcode=%s">Update
<OPTION VALUE="" DISABLED>New request
<OPTION VALUE="" DISABLED>New loan
</SELECT>
</td>
</tr>
""" % (barcode)
elif status == 'Not for loan':
out += """
<td align="center">
<SELECT style='border: 1px solid #cfcfcf' ONCHANGE="location = this.options[this.selectedIndex].value;">
<OPTION VALUE="">Select an action
<OPTION VALUE="update_item_info_step4?barcode=%s">Update
<OPTION VALUE="place_new_request_step1?barcode=%s">New request
<OPTION VALUE="place_new_loan_step1?barcode=%s">New loan
</SELECT>
</td>
</tr>
""" % (barcode, barcode, barcode)
else:
out += """
<td align="center">
<SELECT style='border: 1px solid #cfcfcf' ONCHANGE="location = this.options[this.selectedIndex].value;">
<OPTION VALUE="">Select an action
<OPTION VALUE="update_item_info_step4?barcode=%s">Update
<OPTION VALUE="place_new_request_step1?barcode=%s">New request
<OPTION VALUE="place_new_loan_step1?barcode=%s">New loan
</SELECT>
</td>
</tr>
""" % (barcode, barcode, barcode)
out += """
</tbody>
</table>
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/add_new_copy_step3?recid=%s'"
value='%s' class="formbutton">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/order_new_copy_step1?recid=%s'"
value='%s' class="formbutton">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_step0?recid=%s'"
value='%s'class="formbutton">
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s %s</td>
</tr>
</table>
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button" value='%s'
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_item_requests_details?recid=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
class="bibcircbutton">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button" value='%s'
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_item_loans_details?recid=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
class="bibcircbutton">
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button" value='%s'
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_item_req_historical_overview?recid=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
class="bibcircbutton">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button" value='%s'
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_item_loans_historical_overview?recid=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
class="bibcircbutton">
</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL, recid, _("Add new copy"),
CFG_SITE_URL, recid, _("Order new copy"),
CFG_SITE_URL, recid, _("ILL request"),
_("Hold requests and loans overview on"), time.ctime(),
_("Hold requests"), len(requests), _("More details"), CFG_SITE_URL, recid,
_("Loans"), len(loans), _("More details"), CFG_SITE_URL, recid,
_("Historical overview"),
_("Hold requests"), len(req_hist_overview), _("More details"), CFG_SITE_URL, recid,
_("Loans"), len(loans_hist_overview), _("More details"), CFG_SITE_URL, recid)
out += """
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_bor_requests_historical_overview(self, req_hist_overview, ln=CFG_SITE_LANG):
"""
Return the historical requests overview of a borrower.
req_hist_overview: list of old requests.
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
if len(req_hist_overview) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("There are no requests."))
else:
out += """<div class="bibcircbottom">
<br /> <br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_requests').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<table id="table_requests" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
<thead>
<tbody>
""" % (_("Item"), _("Barcode"), _("Library"),
_("Location"), _("From"),
_("To"), _("Request date"))
for (recid, barcode, library_name, location, req_from, req_to, req_date) in req_hist_overview:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """ <tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (title_link, barcode, library_name, location, req_from, req_to, req_date)
out += """
</tbody>
</table>
<br />
"""
out += """
<table class="bibcirctable">
<tr>
<td><input type=button value='%s'
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_bor_loans_historical_overview(self, loans_hist_overview, ln=CFG_SITE_LANG):
"""
Return the historical loans overview of a borrower.
loans_hist_overview: list of old loans.
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
if len(loans_hist_overview) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("There are no loans."))
else:
out += """<div class="bibcircbottom">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_loans').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<br /> <br />
<table id="table_loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (_("Item"),
_("Barcode"),
_("Library"),
_("Location"),
_("Loaned on"),
_("Due date"),
_("Returned on"),
_("Renewals"),
_("Overdue letters"))
(recid, barcode, library_name, location, loaned_on, due_date,
returned_on, nb_renew, nb_overdueletters) = None
for (recid, barcode, library_name, location, loaned_on, due_date,
returned_on, nb_renew, nb_overdueletters) in loans_hist_overview:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """ <tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (title_link, barcode,
library_name, location,
loaned_on, due_date,
returned_on, nb_renew,
nb_overdueletters)
out += """
</table>
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s'
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</div>
""" % ("Back")
return out
def tmpl_get_item_req_historical_overview(self, req_hist_overview,
ln=CFG_SITE_LANG):
"""
Return the historical requests overview of a item.
req_hist_overview: list of old borrowers.
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
if len(req_hist_overview) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("There are no requests."))
else:
out += """
<div class="bibcircbottom">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_holdings').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<br />
<br />
<table id="table_holdings" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
</tbody>
""" % (_("Borrower"),
_("Barcode"),
_("Library"),
_("Location"),
_("From"),
_("To"),
_("Request date"))
for (name, borrower_id, barcode, library_name,
location, req_from, req_to, req_date) in req_hist_overview:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(name))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (borrower_link, barcode, library_name,
location, req_from, req_to, req_date)
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s'
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_get_item_loans_historical_overview(self, loans_hist_overview,
ln=CFG_SITE_LANG):
"""
Return the historical loans overview of a item.
loans_hist_overview: list of old borrowers.
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """<div class="bibcircbottom">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_loans').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<br />
<br />
<table id="table_loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (_("Borrower"),
_("Barcode"),
_("Library"),
_("Location"),
_("Loaned on"),
_("Due date"),
_("Returned on"),
_("Renewals"),
_("Overdue letters"))
for (name, borrower_id, barcode, library_name, location, loaned_on, due_date, returned_on, nb_renew, nb_overdueletters) in loans_hist_overview:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(name))
out += """ <tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (borrower_link, barcode, library_name,
location, loaned_on,
due_date, returned_on, nb_renew,
nb_overdueletters)
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s'
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_library_details(self, library_details, library_items,
ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<br />
"""
(library_id, name, address, email, phone, notes) = library_details
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_library_notes',
{'library_id': library_id},
(_("No notes")))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_library_notes',
{'library_id': library_id},
(_("Notes about this library")))
if notes == "":
notes_link = no_notes_link
else:
notes_link = see_notes_link
out += """
<table class="bibcirctable">
<tr>
<td width="80" class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<table>
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/update_library_info_step3?library_id=%s'" onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value=%s class="bibcircbutton">
</td>
</tr>
</table>
""" % (_("Library details"),
_("Name"), name,
_("Address"), address,
_("Email"), email,
_("Phone"), phone,
_("Notes"), notes_link,
_("No of items"), len(library_items),
CFG_SITE_URL, library_id, _("Update"))
out += """
</table>
<br />
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s'
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_borrower_details(self, borrower, requests, loans, notes,
ill, req_hist, loans_hist, ill_hist,
ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
(borrower_id, name, email, phone, address, mailbox) = borrower
#req_link = create_html_link(CFG_SITE_URL +
# '/admin/bibcirculation/bibcirculationadmin.py/get_borrower_requests_details',
# {'borrower_id': borrower_id},
# (_("More details")))
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_notes',
{'borrower_id': borrower_id},
(_("No notes")))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_notes',
{'borrower_id': borrower_id},
(_("Notes about this borrower")))
#loans_link = create_html_link(CFG_SITE_URL +
# '/admin/bibcirculation/bibcirculationadmin.py/get_borrower_loans_details',
# {'borrower_id': borrower_id},
# (_("More details")))
#
#ill_link = create_html_link(CFG_SITE_URL +
# '/admin/bibcirculation/bibcirculationadmin.py/get_borrower_ill_details',
# {'borrower_id': borrower_id},
# (_("More details")))
#
#req_hist_link = create_html_link(CFG_SITE_URL +
# '/admin/bibcirculation/bibcirculationadmin.py/bor_requests_historical_overview',
# {'borrower_id': borrower_id},
# (_("More details")))
#
#loans_hist_link = create_html_link(CFG_SITE_URL +
# '/admin/bibcirculation/bibcirculationadmin.py/bor_loans_historical_overview',
# {'borrower_id': borrower_id},
# (_("More details")))
#
#ill_hist_link = create_html_link(CFG_SITE_URL +
# '/admin/bibcirculation/bibcirculationadmin.py/bor_ill_historical_overview',
# {'borrower_id': borrower_id},
# (_("More details")))
if notes == "" or str(notes) == '{}':
check_notes = no_notes_link
else:
check_notes = see_notes_link
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="borrower_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/borrower_notification" method="get" >
<div class="bibcircbottom">
<input type=hidden name=borrower_id value=%s>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
</form>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
""" % (CFG_SITE_URL,
borrower_id,
_("Personal details"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox,
_("Notes"), check_notes)
nb_requests = len(requests)
nb_loans = len(loans)
nb_ill = len(ill)
nb_req_hist = len(req_hist)
nb_loans_hist = len(loans_hist)
nb_ill_hist = len(ill_hist)
out += """
</table>
<!-- <table class="bibcirctable">
<tr>
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/update_borrower_info_step3?borrower_id=%s'"
value=%s class='formbutton'></td>
</tr>
</table> -->
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step2?user_info=%s,%s,%s,%s,%s,%s'"
value='%s' class='formbutton'>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/create_new_request_step1?borrower_id=%s'"
value='%s' class='formbutton'>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_book_request_from_borrower_page?borrower_id=%s'"
value='%s' class='formbutton'>
<input type='submit' name='notify_button' value='%s' class='formbutton'>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s %s</td>
</tr>
</table>
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button"
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_requests_details?borrower_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button"
onClick="location.href='%s//admin/bibcirculation/bibcirculationadmin.py/get_borrower_loans_details?borrower_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button"
onClick="location.href='%s//admin/bibcirculation/bibcirculationadmin.py/get_borrower_ill_details?borrower_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button"
onClick="location.href='%s//admin/bibcirculation/bibcirculationadmin.py/bor_requests_historical_overview?borrower_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button"
onClick="location.href='%s//admin/bibcirculation/bibcirculationadmin.py/bor_loans_historical_overview?borrower_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td width="50">%s</td>
<td>
<input type="button"
onClick="location.href='%s//admin/bibcirculation/bibcirculationadmin.py/bor_ill_historical_overview?borrower_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s'
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
</div>
""" % (CFG_SITE_URL, borrower_id, _("Update"),
CFG_SITE_URL, borrower_id, name, email, phone, address, mailbox, _("New loan"),
CFG_SITE_URL, borrower_id, _("New request"),
CFG_SITE_URL, borrower_id, _("New ILL request"),
_("Notify this borrower"),
_("Requests, Loans and ILL overview on"), time.ctime(),
_("Requests"), nb_requests, CFG_SITE_URL, borrower_id, _("More details"),
_("Loans"), nb_loans, CFG_SITE_URL, borrower_id, _("More details"),
_("ILL"), nb_ill, CFG_SITE_URL, borrower_id, _("More details"),
_("Historical overview"),
_("Requests"), nb_req_hist, CFG_SITE_URL, borrower_id, _("More details"),
_("Loans"), nb_loans_hist, CFG_SITE_URL, borrower_id, _("More details"),
_("ILL"), nb_ill_hist, CFG_SITE_URL, borrower_id, _("More details"),
_("Back"))
return out
def tmpl_borrower_request_details(self, result, borrower_id,
ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("There are no requests."))
else:
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_requests').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<form name="borrower_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_requests_details" method="get" >
<div class="bibcircbottom">
<br />
<table id="table_requests" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
</form>
"""% (CFG_SITE_URL,
_("Item"),
_("Request status"),
_("Library"),
_("Location"),
_("From"),
_("To"),
_("Request date"),
_("Request option(s)"))
for (recid, status, library, location, date_from, date_to, request_date, request_id) in result:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td align="center">
<input type="button" value='%s' style="background: url(/img/jquery/dialog-cancel.png)
no-repeat; width: 75px; text-align: right;"
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_pending_requests?request_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
class="bibcircbutton">
</td>
</tr>
""" % (title_link, status, library, location, date_from,
date_to, request_date, _("Cancel"),
CFG_SITE_URL, request_id)
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details?borrower_id=%s'"
value='%s' class='formbutton'>
</td>
</tr>
</table>
<br />
</div>
""" % (CFG_SITE_URL,
borrower_id,
_("Back"))
return out
def tmpl_borrower_loans_details(self, borrower_loans, borrower_id, infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
if len(borrower_loans) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("There are no loans."))
else:
out += """
<form name="borrower_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_loans_details?submit_changes=true" method="get" >
<input type=hidden name=borrower_id value=%s>
<div class="bibcircbottom">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_loans').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<br />
<table id="table_loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (CFG_SITE_URL,
borrower_id,
_("Item"),
_("Barcode"),
_("Loan date"),
_("Due date"),
_("Renewals"),
_("Overdue letters"),
_("Type"),
_("Loan notes"),
_("Loans status"),
_("Loan options"))
for (recid, barcode, loaned_on, due_date, nb_renewall, nb_overdue, date_overdue, loan_type, notes, loan_id, status) in borrower_loans:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("No notes")))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("See notes")))
if notes == "":
check_notes = no_notes_link
else:
check_notes = see_notes_link
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s - %s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td align="center">
<SELECT style='border: 1px solid #cfcfcf' ONCHANGE="location = this.options[this.selectedIndex].value;">
<OPTION VALUE="">Select an action
<OPTION VALUE="get_borrower_loans_details?borrower_id=%s&barcode=%s&loan_id=%s&recid=%s">Renew
<OPTION VALUE="loan_return_confirm?barcode=%s">Return
<OPTION VALUE="change_due_date_step1?loan_id=%s&borrower_id=%s">Change due date
<OPTION VALUE="claim_book_return?borrower_id=%s&recid=%s&loan_id=%s&template=claim_return">Send recall
</SELECT>
</td>
<input type=hidden name=barcode value=%s>
<input type=hidden name=loan_id value=%s>
</tr>
""" % (title_link, barcode, loaned_on,
due_date, nb_renewall,
nb_overdue, date_overdue, loan_type,
check_notes, status,
borrower_id, barcode,
loan_id, recid,
barcode, loan_id, borrower_id,
borrower_id, recid, loan_id,
barcode, loan_id)
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" align="right" width="100">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_loans_details?borrower_id=%s&renewall=true'"
value='%s' class='bibcircbutton'onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"></td>
</tr>
</table>
""" % (CFG_SITE_URL,
borrower_id,
_("Renew all loans"))
out += """
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details?borrower_id=%s'"
value='%s' class='formbutton'></td>
</tr>
</table>
<br />
</div>
</form>
""" % (CFG_SITE_URL,
borrower_id,
_("Back"))
return out
def tmpl_all_loans(self, result, infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<style type="text/css"> @import url("/js/jquery/tablesorter/themes/blue/style.css"); </style>
<style type="text/css"> @import url("/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.css"); </style>
<script src="/js/jquery/jquery.min.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/jquery.tablesorter.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#table_all_loans")
.tablesorter({sortList: [[3,1], [0,0]],widthFixed: true, widgets: ['zebra']})
.bind("sortStart",function(){$("#overlay").show();})
.bind("sortEnd",function(){$("#overlay").hide()})
.tablesorterPager({container: $("#pager"), positionFixed: false});
});
</script>
<br />
<div class="bibcircbottom">
"""
if len(result) == 0:
out += """
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value='%s' onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("No result for your search."),
_("Back"))
else:
out += """
<form name="borrower_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/all_loans" method="get" >
<br />
<table id="table_all_loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th></th>
</tr>
</thead>
<tbody>
"""% (CFG_SITE_URL,
_("Borrower"),
_("Item"),
_("Barcode"),
_("Loaned on"),
_("Due date"),
_("Renewals"),
_("Overdue letters"),
_("Loan Notes"))
for (borrower_id, borrower_name, recid, barcode,
loaned_on, due_date, nb_renewall, nb_overdue,
date_overdue, notes, loan_id) in result:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(borrower_name))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("see notes")))
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("no notes")))
if notes == "":
check_notes = no_notes_link
elif str(notes) == '{}':
check_notes = no_notes_link
else:
check_notes = see_notes_link
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s - %s</td>
<td>%s</td>
<td align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/claim_book_return?borrower_id=%s&recid=%s&loan_id=%s&template=claim_return'" onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class='bibcircbutton'></td>
</tr>
""" % (borrower_link, title_link, barcode,
loaned_on, due_date,
nb_renewall, nb_overdue, date_overdue,
check_notes, CFG_SITE_URL,
borrower_id, recid, loan_id, _("Send recall"))
out+= """
</tbody>
</table>
</form>
<div id="pager" class="pager">
<form>
<br />
<img src="/img/sb.gif" class="first" />
<img src="/img/sp.gif" class="prev" />
<input type="text" class="pagedisplay" />
<img src="/img/sn.gif" class="next" />
<img src="/img/se.gif" class="last" />
<select class="pagesize">
<option value="10" selected="selected">10</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="40">40</option>
</select>
</form>
</div>
"""
out += """
<div class="back" style="position: relative; top: 5px;">
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s' onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
</div>
</div>
""" % (_("Back"))
return out
def tmpl_all_expired_loans(self, result, infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<style type="text/css"> @import url("/js/jquery/tablesorter/themes/blue/style.css"); </style>
<style type="text/css"> @import url("/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.css"); </style>
<script src="/js/jquery/jquery.min.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/jquery.tablesorter.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#table_all_loans")
.tablesorter({sortList: [[3,1], [0,0]],widthFixed: true, widgets: ['zebra']})
.bind("sortStart",function(){$("#overlay").show();})
.bind("sortEnd",function(){$("#overlay").hide()})
.tablesorterPager({container: $("#pager"), positionFixed: false});
});
</script>
<br />
<div class="bibcircbottom">
"""
if len(result) == 0:
out += """
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value='%s' onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("No result for your search."),
_("Back"))
else:
out += """
<form name="borrower_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/all_loans" method="get" >
<br />
<table id="table_all_loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th></th>
</tr>
</thead>
<tbody>
"""% (CFG_SITE_URL,
_("Borrower"),
_("Item"),
_("Barcode"),
_("Loaned on"),
_("Due date"),
_("Renewals"),
_("Overdue letters"),
_("Loan Notes"))
for (borrower_id, borrower_name, recid, barcode,
loaned_on, due_date, nb_renewall, nb_overdue,
date_overdue, notes, loan_id) in result:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(borrower_name))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("see notes")))
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("no notes")))
if notes == "":
check_notes = no_notes_link
elif str(notes) == '{}':
check_notes = no_notes_link
else:
check_notes = see_notes_link
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s - %s</td>
<td>%s</td>
<td align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/claim_book_return?borrower_id=%s&recid=%s&loan_id=%s&template=claim_return'" onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class='bibcircbutton'></td>
</tr>
""" % (borrower_link, title_link, barcode,
loaned_on, due_date,
nb_renewall, nb_overdue, date_overdue,
check_notes, CFG_SITE_URL,
borrower_id, recid, loan_id, _("Send recall"))
out+= """
</tbody>
</table>
</form>
<div id="pager" class="pager">
<form>
<br />
<img src="/img/sb.gif" class="first" />
<img src="/img/sp.gif" class="prev" />
<input type="text" class="pagedisplay" />
<img src="/img/sn.gif" class="next" />
<img src="/img/se.gif" class="last" />
<select class="pagesize">
<option value="10" selected="selected">10</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="40">40</option>
</select>
</form>
</div>
"""
out += """
<div class="back" style="position: relative; top: 5px;">
<br />
<table class="bibcirctable">
<tr>
<td><input type=button value='%s' onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
</form>
</div>
</div>
""" % (_("Back"))
return out
def tmpl_borrower_notification(self, email, subject, template, borrower_id,
ln=CFG_SITE_LANG):
"""
@param result: template used for the notification
@param ln: language of the page
"""
if subject is None:
subject = ""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="borrower_notification" action="%s/admin/bibcirculation/bibcirculationadmin.py/borrower_notification" method="get" >
<div class="bibcircbottom">
<input type=hidden name=borrower_id value=%s>
<br />
<table class="tablesortermedium" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="50">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="50">%s</th>
"""% (CFG_SITE_URL,
borrower_id,
_("From"),
_("CERN Library"),
_("To"))
out += """
<td>
<input type="text" name="borrower_email" size="60" style='border: 1px solid #cfcfcf' value="%s">
</td>
</tr>
""" % (email)
out += """
<tr>
<th width="50">%s</th>
<td><input type="text" name="subject" size="60" value="%s" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="tablesortermedium" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="500">%s</th>
<th>%s</th>
</tr>
<tr>
<td><textarea rows="10" cols="100" name="message" style='border: 1px solid #cfcfcf'>%s</textarea></td>
""" % (_("Subject"),
subject,
_("Message"),
_("Choose a template"),
template)
out += """
<td>
<select name="template" style='border: 1px solid #cfcfcf'>
<option value ="">%s</option>
<option value ="overdue_letter">%s</option>
<option value ="reminder">%s</option>
<option value ="notification">%s</option>
<option value ="claim_return">%s</option>
</select>
<br /><br />
<input type="submit" name="load_template" value=%s class="formbutton">
</td>
</tr>
</table>
""" % (_("Templates"),
_("Overdue letter"),
_("Reminder"),
_("Notification"),
_("Send recall"),
_("Load"))
out += """
<br /> <br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="reset" name="reset_button" value=%s class="formbutton">
<input type="submit" name="send_message" value=%s class="formbutton">
</td>
</tr>
</table>
<br /> <br />
</div>
</form>
""" % (_("Back"),
_("Reset"),
_("Send"))
return out
def tmpl_all_loans_test(self, result, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.pager.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#tablesorter-loans')
.tablesorter({widthFixed: true, widgets: ['zebra']})
.tablesorterPager({container: $('#pager')});
});
</script>
<table id="tablesorter-loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (_("Borrower"),
_("Item"),
_("Barcode"),
_("Loaned on"),
_("Due date"),
_("Renewals"),
_("Overdue letters"),
_("Loan Notes"))
for (borrower_id, borrower_name, recid, barcode,
loaned_on, due_date, nb_renewall, nb_overdue,
date_overdue, notes, loan_id) in result:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(borrower_name))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("see notes")))
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("no notes")))
if notes == "":
check_notes = no_notes_link
else:
check_notes = see_notes_link
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s - %s</td>
<td>%s</td>
</tr>
""" % (borrower_link, title_link,
barcode, loaned_on, due_date,
nb_renewall, nb_overdue,
date_overdue, check_notes)
out += """ </tbody>
</table>
<div id="pager" class="pager">
<form>
<img src="/js/first.png" class="first"/>
<img src="/js/prev.png" class="prev"/>
<input type="text" class="pagedisplay"/>
<img src="/js/next.png" class="next"/>
<img src="/js/last.png" class="last"/>
<select class="pagesize">
<option selected="selected" value="25">25</option>
<option value="40">40</option>
<option value="60">60</option>
</select>
</form>
</div>
"""
return out
def tmpl_get_item_loans_details(self, result, recid, infos,
ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("There are no loans."))
else:
out += """
<div class="bibcircbottom">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_loans').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<br />
<form name="borrower_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_item_loans_details" method="get" >
<input type=hidden name=recid value=%s>
""" % (CFG_SITE_URL,
recid)
out += """
<br />
<table id="table_loans" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (_("Borrower"),
_("Barcode"),
_("Loaned on"),
_("Due date"),
_("Renewals"),
_("Overdue letter"),
_("Loan status"),
_("Loan notes"),
_("Loan options"))
for (borrower_id, borrower_name, barcode, loaned_on,
due_date, nb_renewall, nb_overdue, date_overdue,
status, notes, loan_id) in result:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(borrower_name))
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("No notes")))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'loan_id': loan_id, 'recid': recid, 'ln': ln},
(_("See notes")))
if notes == "":
check_notes = no_notes_link
else:
check_notes = see_notes_link
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s - %s</td>
<td>%s</td>
<td>%s</td>
<td align="center">
<SELECT style='border: 1px solid #cfcfcf' ONCHANGE="location = this.options[this.selectedIndex].value;">
<OPTION VALUE="">Select an action
<OPTION VALUE="get_item_loans_details?barcode=%s&loan_id=%s&recid=%s">Renew
<OPTION VALUE="loan_return_confirm?barcode=%s">Return
<OPTION VALUE="change_due_date_step1?loan_id=%s">Change due date
<OPTION VALUE="claim_book_return?recid=%s&template=claim_return">Send recall
</SELECT>
</td>
</tr>
<input type=hidden name=loan_id value=%s>
<input type=hidden name=loan_id value=%s>
""" % (borrower_link, barcode,
loaned_on, due_date,
nb_renewall, nb_overdue,
date_overdue, status, check_notes,
borrower_id, barcode, loan_id, recid,
barcode, loan_id, recid,
loan_id)
out += """
<tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_item_details?recid=%s'"
value='%s' class='formbutton'>
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
</form>
""" % (CFG_SITE_URL,
recid,
_("Back"))
return out
def tmpl_associate_barcode(self, request_id, recid, borrower,
infos, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
(book_title, _book_year, _book_author, book_isbn, _book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
(borrower_id, name, email, phone, address, mailbox) = borrower
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="return_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_new_loan" method="get" >
<div class="bibcircbottom">
<input type=hidden name=borrower_id value=%s>
<input type=hidden name=request_id value=%s>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
</form>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
"""% (CFG_SITE_URL,
borrower_id,
request_id,
_("Personal details"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox)
out +="""
<br />
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th>%s</th>
</tr>
<tr>
<td>%s</td>
</tr>
<tr algin='center'>
<td><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
<tr>
<th>%s</th>
</tr>
<tr>
<td><input type="text" size="66" name="barcode" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
""" % (_("Item"),
book_title,
str(book_cover),
_("Barcode"))
out += """
<br />
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th>%s</th>
</tr>
<tr>
<td><textarea name='new_note' rows="4" cols="57" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<br />
""" % (_("Write notes"))
out += """
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="bibcircbutton"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'">
<input type="submit" value=%s class="bibcircbutton"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'">
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
</form>
""" % (_("Back"),
_("Confirm"))
return out
def tmpl_borrower_notes(self, borrower_notes, borrower_id,
ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
if not borrower_notes:
borrower_notes = {}
else:
borrower_notes = eval(borrower_notes)
out = """ """
out += _MENU_
out +="""
<div class="bibcircbottom">
<form name="borrower_notes" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_notes" method="get" >
<input type=hidden name=borrower_id value='%s'>
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td>
<table class="bibcircnotes">
""" % (CFG_SITE_URL, borrower_id,
_("Notes about borrower"))
key_array = borrower_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_notes',
{'delete_key': key, 'borrower_id': borrower_id, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, borrower_notes[key], delete_note)
out += """
</table>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">
<textarea name="library_notes" rows="5" cols="90" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details?borrower_id=%s'"
value=%s class='formbutton'>
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Write new note"),
CFG_SITE_URL,
borrower_id,
_("Back"),
_("Confirm"))
return out
def tmpl_get_loans_notes(self, loans_notes, loan_id,
referer, back="", ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
if back == "":
back=referer
if not loans_notes:
loans_notes = {}
else:
loans_notes = eval(loans_notes)
out = """ """
out += _MENU_
out +="""
<div class="bibcircbottom">
<form name="loans_notes" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes" method="get" >
<input type="hidden" name="loan_id" value="%s">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td>
<table class="bibcircnotes">
""" % (CFG_SITE_URL, loan_id,
_("Notes about loan"))
key_array = loans_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_loans_notes',
{'delete_key': key, 'loan_id': loan_id, 'ln': ln, 'back': cgi.escape(back, True)},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, loans_notes[key], delete_note)
out += """
</table>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">
<textarea name="library_notes" rows="5" cols="90" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value="%s" onClick="window.location='%s'" class="formbutton">
<input type="submit" value="%s" class="formbutton">
<input type="hidden" name="back" value="%s">
</td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Write new note"),
_("Back"),
cgi.escape(back,True),
_("Confirm"),
cgi.escape(back, True))
return out
def tmpl_new_item(self, book_info=None, errors=None, ln=CFG_SITE_LANG):
"""
No more in use.
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
if book_info:
out += """
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="110" valign="top">%s</td>
<td class="bibcirccontent">
<textarea style='border: 1px solid #cfcfcf' rows="3" cols="43" name="title">%s</textarea>
</td>
</tr>
<tr>
<td width="110"></td>
<td class="bibcirccontent"></td>
</tr>
<tr>
<td width="110"></td>
<td class="bibcirccontent">
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
<tr>
<td width="110"></td>
<td class="bibcirccontent"></td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="%s" name="author">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="%s" name="ean">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="%s" name="isbn">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" size=45 style='border: 1px solid #cfcfcf' value="%s" name="publisher">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" size=45 style='border: 1px solid #cfcfcf' value="%s" name="pub_date">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="" name="pub_place">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="%s" name="edition">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="%s" name="nb_pages">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="%s" name="sub_library">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="" name="location">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<select name="loan_period" style='border: 1px solid #cfcfcf'>
<option value ="Not for loan">Not for loan</option>
<option value ="4 weeks loan">4 weeks loan</option>
</select></td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="" name="barcode">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="" name="collection">
</td>
</tr>
<tr>
<td width="110">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 value="" name="description">
</td>
</tr>
</table>
<br />
<br />
""" % (_("Book Information"),
_("Title"), book_info[6],
book_info[8],
_("Author"), book_info[0],
_("EAN"), book_info[1],
_("ISBN"), book_info[2],
_("Publisher"), book_info[3],
_("Publication date"), book_info[5],
_("Publication place"),
_("Edition"), book_info[7],
_("Number of pages"), book_info[4],
_("Sub-library"),
_("CERN Central Library"),
_("Location"),
_("Loan period"),
_("Barcode"),
_("Collection"),
_("Description"))
elif errors:
out += """
<div class="bibcircbottom">
<form name="list_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/new_item" method="get" >
<br />
<br />
<br />
<table class="bibcirctable_contents">
<tr align="center">
<td>ERROR: %s. %s</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, errors[0], errors[1])
else:
out += """
<div class="bibcircbottom">
<form name="list_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/new_item" method="get" >
<br />
<br />
<br />
<table class="bibcirctable_contents">
<tr align="center">
<td class="bibcirctableheader">'%s'
<input type="text" style='border: 1px solid #cfcfcf' size=25 name="isbn">
</td>
</tr>
</table>
<br />
<table class="bibcirctable_contents">
<tr align="center">
<td><input type="submit" value="Retrieve book information" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("ISBN"))
return out
def tmpl_add_new_borrower_step1(self, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="add_new_borrower_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_borrower_step2" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="name">
</td>
</tr>
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="email">
</td>
</tr>
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="phone">
</td>
</tr>
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="address">
</td>
</tr>
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="mailbox">
</td>
</tr>
<tr>
<td width="70" valign="top">%s</td>
<td class="bibcirccontent">
<textarea name="notes" rows="5" cols="39" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("Name"), _("Email"),
_("Phone"), _("Address"), _("Mailbox"), _("Notes"),
_("Back"), _("Continue"))
return out
def tmpl_add_new_borrower_step2(self, tup_infos, infos, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
(name, email, phone, address, mailbox, notes) = tup_infos
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="add_new_borrower_step2_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_borrower_step3" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
</table>
""" % (CFG_SITE_URL,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox,
_("Notes"), notes)
if infos:
out += """
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (_("Back"))
else:
out += """
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
<input type=hidden name=tup_infos value="%s">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (_("Back"), _("Continue"),
tup_infos)
return out
def tmpl_add_new_borrower_step3(self, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick= onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("A new borrower has been registered."),
_("Back to home"),
CFG_SITE_URL)
return out
def tmpl_update_borrower_info_step1(self, ln=CFG_SITE_LANG):
"""
Template for the admin interface. Search borrower.
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br /><br /> <br />
<form name="update_borrower_info_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_borrower_info_step2" method="get" >
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s
<input type="radio" name="column" value="id">ccid
<input type="radio" name="column" value="name" checked>name
<input type="radio" name="column" value="email">email
<br><br>
</td>
</tr>
<tr align="center">
<td><input type="text" size="45" name="string" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value="Back" onClick="history.go(-1)" class="formbutton">
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
<form>
<br /><br />
<br />
<br />
</div>
""" % (CFG_SITE_URL,
_("Search borrower by"))
return out
def tmpl_update_borrower_info_step2(self, result, ln=CFG_SITE_LANG):
"""
@param result: search result
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirccontent">
%s borrowers found
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
""" % (len(result))
for (borrower_id, name) in result:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/update_borrower_info_step3',
{'borrower_id': borrower_id, 'ln': ln},
(name))
out += """
<tr align="center">
<td class="bibcirccontent" width="70">%s
<input type=hidden name=uid value=%s></td>
</tr>
""" % (borrower_link, borrower_id)
out += """
</table>
<br />
"""
out += """
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Back"))
return out
def tmpl_update_borrower_info_step3(self, result, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
(_borrower_id, name, email, phone, address, mailbox) = result
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="update_borrower_info_step3_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_borrower_info_step4" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="name" value="%s">
</td>
</tr>
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="email" value="%s">
</td>
</tr>
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="phone" value="%s">
</td>
</tr>
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="address" value="%s">
</td>
</tr>
<tr>
<td width="70">%s</td>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="mailbox" value="%s">
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("Borrower information"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox,
_("Back"), _("Continue"))
return out
def tmpl_update_borrower_info_step4(self, tup_infos, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
(name, email, phone, address, mailbox) = tup_infos
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="update_borrower_info_step4_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_borrower_info_step5" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
<input type=hidden name=tup_infos value="%s">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("Borrower information"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox,
_("Back"), _("Confirm"),
tup_infos)
return out
def tmpl_update_borrower_info_step5(self, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value='%s' onClick= onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("The information has been updated."),
_("Back to home"),
CFG_SITE_URL)
return out
def tmpl_add_new_library_step1(self, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<form name="add_new_library_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_library_step2" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=50 name="name">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=50 name="email">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=50 name="phone">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=50 name="address">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td>
<select name="type" style='border: 1px solid #cfcfcf'>
<option value ="internal">internal</option>
<option value ="external">external</option>
</select>
</td>
</tr>
<tr>
<th width="70" valign="top">%s</th>
<td>
<textarea name="notes" rows="5" cols="39" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("New library information"), _("Name"),
_("Email"), _("Phone"), _("Address"), _("Type"), _("Notes"),
_("Back"), _("Continue"))
return out
def tmpl_add_new_library_step2(self, tup_infos, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
(name, email, phone, address, lib_type, notes) = tup_infos
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="add_new_library_step2_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_library_step3" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="70">%s</td> <td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
<input type=hidden name=tup_infos value="%s">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("New library information"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Type"), lib_type,
_("Notes"), notes,
_("Back"), _("Confirm"),
tup_infos)
return out
def tmpl_add_new_library_step3(self, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("A new library has been registered."),
_("Back to home"),
CFG_SITE_URL)
return out
def tmpl_update_library_info_step1(self, infos, ln=CFG_SITE_LANG):
"""
Template for the admin interface. Search borrower.
@param ln: language
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<br /><br /> <br />
<form name="update_library_info_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_library_info_step2" method="get" >
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s
<input type="radio" name="column" value="name" checked>name
<input type="radio" name="column" value="email">email
<br>
<br>
</td>
</tr>
<tr align="center">
<td><input type="text" size="45" name="string" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
<form>
<br /><br />
<br />
<br />
</div>
""" % (CFG_SITE_URL,
_("Search library by"),
_("Back"))
return out
def tmpl_update_library_info_step2(self, result, ln=CFG_SITE_LANG):
"""
@param result: search result
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirccontent">
<strong>%s library(ies) found</strong>
</td>
</tr>
</table>
<br />
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<th align="center">%s</th>
""" % (len(result), _("Library(ies)"))
for (library_id, name) in result:
library_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/update_library_info_step3',
{'library_id': library_id, 'ln': ln},
(name))
out += """
<tr align="center">
<td class="bibcirccontent" width="70">%s
<input type=hidden name=library_id value=%s></td>
</tr>
""" % (library_link, library_id)
out += """
</table>
<br />
"""
out += """
<table class="bibcirctable">
<tr align="center">
<td><input type=button value=%s
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Back"))
return out
def tmpl_update_library_info_step3(self, library_info, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
(library_id, name, address, email, phone, _notes) = library_info
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<form name="update_library_info_step3_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_library_info_step4" method="get" >
<input type=hidden name=library_id value=%s>
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=50 name="name" value="%s">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=50 name="email" value="%s">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=50 name="phone" value="%s">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=50 name="address" value="%s">
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, library_id, _("Library information"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Back"), _("Continue"))
return out
def tmpl_update_library_info_step4(self, tup_infos, ln=CFG_SITE_LANG):
(_library_id, name, email, phone, address) = tup_infos
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<form name="update_library_info_step4_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_library_info_step5" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th> <td>%s</td>
</tr>
<tr>
<th width="70">%s</th> <td>%s</td>
</tr>
<tr>
<th width="70">%s</th> <td>%s</td>
</tr>
<tr>
<th width="70">%s</th> <td>%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
<input type=hidden name=tup_infos value="%s">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("Library information"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Back"), _("Continue"),
tup_infos)
return out
def tmpl_update_library_info_step5(self, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick= onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("The information has been updated."),
_("Back to home"),
CFG_SITE_URL)
return out
def tmpl_new_book_step1(self, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
out = _MENU_
out += """
<br />
<br />
<div class="bibcircbottom" align="center">
<br />
<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="display_ill_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/new_book_step2" method="get">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="title" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="authors" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="place" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="publisher" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="year" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="edition" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="isbn" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
_("Item details"),
_("Book title"),
_("Author(s)"),
_("Place"),
_("Publisher"),
_("Year"),
_("Edition"),
_("ISBN"))
#conditions_link = """<a href="http://library.web.cern.ch/library/Library/ill_faq.html" target="_blank">conditions</a>"""
out += """
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("Back"), _("Continue"))
return out
def tmpl_new_book_step2(self, ln=CFG_SITE_LANG):
### FIXME ###
return "Y aquí se hace algo..."
def tmpl_add_new_copy_step1(self):
"""
@param ln: language of the page
"""
out = _MENU_
out += """
<div class="bibcircbottom">
<form name="add_new_copy_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_copy_step2" method="get" >
<br />
<br />
<br />
<input type=hidden name=start value="0">
<input type=hidden name=end value="10">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">Search item by
<input type="radio" name="f" value="" checked>any field
<input type="radio" name="f" value="name">year
<input type="radio" name="f" value="author">author
<input type="radio" name="f" value="title">title
<br />
<br />
</td>
<tr align="center">
<td>
<input type="text" size="50" name="p" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value="Back"
onClick="history.go(-1)" class="formbutton">
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
<form>
""" % (CFG_SITE_URL)
return out
def tmpl_add_new_copy_step2(self, result, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirccontent">
<strong>%s items found</strong>
</td>
</tr>
</table>
<table class="bibcirctable">
</tr>
""" % (len(result))
for recid in result:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/add_new_copy_step3',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr align="center">
<td class="contents">%s</td>
</tr>
""" % (title_link)
out += """
</table>
<br />
"""
out += """
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value="Back" onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
"""
return out
def tmpl_add_new_copy_step3(self, recid, result, libraries,
infos, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_copies').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<form name="add_new_copy_step3_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_copy_step4" method="get" >
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
""" % (CFG_SITE_URL,
_("Item details"),
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn,
str(book_cover),
_("Copies of %s" % book_title))
out += """<table id="table_copies" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>""" % (_("Barcode"),
_("Status"),
_("Due date"),
_("Library"),
_("Location"),
_("Loan period"),
_("No of loans"),
_("Collection"),
_("Description"))
for (barcode, loan_period, lib_name, libid, location, nb_requests,
status, collection, description, due_date) in result:
library_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_library_details',
{'library_id': libid, 'ln': ln},
(lib_name))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (barcode, status, due_date, library_link, location,
loan_period, nb_requests, collection or '-',
description or '-')
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=35 name="barcode">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="library" style='border: 1px solid #cfcfcf'>
""" % (_("New copy details"), _("Barcode"), _("Library"))
for(library_id, name) in libraries:
out +="""<option value ="%s">%s</option>""" % (library_id, name)
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=35 name="location">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="collection" style='border: 1px solid #cfcfcf'>
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=35 name="description">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="loan_period" style='border: 1px solid #cfcfcf'>
<option value ="4 weeks">4 weeks</option>
<option value ="1 week">1 week</option>
<option value ="reference">reference</option>
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="status" style='border: 1px solid #cfcfcf'>
<option value ="available">available</option>
<option value ="missing">missing</option>
</select>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value="Back"
onClick="history.go(-1)" class="formbutton">
<input type="submit" value="Continue" class="formbutton">
<input type=hidden name=recid value=%s>
</td>
</tr>
</table>
<br />
<br />
</div>
</form>
""" % (_("Location"), _("Collection"), _("Description"),
_("Loan period"), _("Status"), recid)
return out
def tmpl_add_new_copy_step4(self, tup_infos, ln=CFG_SITE_LANG):
"""
@param tup_info: item's informations
@type tup_info: tuple
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="add_new_copy_step4_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_copy_step5" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td width="90">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="90">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="90">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="90">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="90">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="90">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="90">%s</td> <td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
<input type=hidden name=tup_infos value="%s">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL,
_("Barcode"), tup_infos[0],
_("Library"), tup_infos[2],
_("Location"), tup_infos[3],
_("Collection"), tup_infos[4],
_("Description"), tup_infos[5],
_("Loan period"), tup_infos[6],
_("Status"), tup_infos[7],
_("Back"), _("Continue"), tup_infos)
return out
def tmpl_add_new_copy_step5(self, recid, ln=CFG_SITE_LANG):
"""
@param recid: identify the record. Primary key of bibrec.
@type recid: int
"""
_ = gettext_set_language(ln)
item_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(_("new copy")))
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value='%s'
onClick= onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("A %s has been added." % (item_link)),
_("Back to home"),
CFG_SITE_URL)
return out
def tmpl_update_item_info_step1(self):
"""
@param ln: language of the page
"""
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="update_item_info_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_item_info_step2" method="get" >
<br />
<br />
<br />
<input type=hidden name=start value="0">
<input type=hidden name=end value="10">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">Search item by
<input type="radio" name="f" value="" checked>any field
<input type="radio" name="f" value="name">year
<input type="radio" name="f" value="email">author
<input type="radio" name="f" value="email">title
<br /><br />
</td>
<tr align="center">
<td><input type="text" size="50" name="p" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value="Back"
onClick="history.go(-1)" class="formbutton">
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
<form>
""" % (CFG_SITE_URL)
return out
def tmpl_update_item_info_step2(self, result, ln=CFG_SITE_LANG):
"""
@param result: list with recids
@type result: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirccontent">
<strong>%s items found</strong>
</td>
</tr>
</table>
<table class="bibcirctable">
</tr>
""" % (len(result))
for recid in result:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/update_item_info_step3',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr align="center">
<td class="contents">%s</td>
</tr>
""" % (title_link)
out += """
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value="Back" onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
"""
return out
def tmpl_update_item_info_step3(self, recid, result, ln=CFG_SITE_LANG):
"""
@param recid: identify the record. Primary key of bibrec.
@type recid: int
@param result: book's information
@type result: tuple
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<form name="update_item_info_step3_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_item_info_step4" method="get" >
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
_("Item details"),
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn,
str(book_cover))
out += """<table class="bibcirctable">
<tr>
<td>%s</td>
<td align="center">%s</td>
<td align="center">%s</td>
<td align="center">%s</td>
<td align="center">%s</td>
<td align="center">%s</td>
<td align="center">%s</td>
<td align="center">%s</td>
<td align="center"></td>
<td width="350"></td>
</tr>""" % (_("Barcode"),
_("Status"),
_("Library"),
_("Location"),
_("Loan period"),
_("No of loans"),
_("Collection"),
_("Description"))
for (barcode, loan_period, lib_name, libid, location, nb_requests, status, collection, description) in result:
library_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_library_details',
{'library_id': libid, 'ln': ln},
(lib_name))
out += """
<tr>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/update_item_info_step4?barcode=%s'"
value=%s class="formbutton">
</td>
<td class="bibcirccontent" width="350"></td>
</tr>
""" % (barcode, status, library_link, location, loan_period,
nb_requests, collection, description, CFG_SITE_URL,
barcode, _("Update"))
out += """
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value="%s"
onClick="history.go(-1)" class="formbutton">
<input type=hidden name=recid value=%s></td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("Back"), recid)
return out
def tmpl_update_item_info_step4(self, recid, result, libraries, ln=CFG_SITE_LANG):
"""
@param recid: identify the record. Primary key of bibrec
@type recid: int
@param result: book's information
@type result: tuple
@param libraries: list of libraries
@type libraries: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="update_item_info_step4_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_item_info_step5" method="get" >
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
_("Item details"),
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn,
str(book_cover))
out += """
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=barcode value=%s>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="library" style='border: 1px solid #cfcfcf'>
""" % (_("Update copy information"),
_("Barcode"), result[0], result[0],
_("Library"))
for(library_id, name) in libraries:
if library_id == result[1]:
out +="""<option value ="%s" selected>%s</option>""" % (library_id, name)
else:
out +="""<option value ="%s">%s</option>""" % (library_id, name)
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td><input type="text" style='border: 1px solid #cfcfcf' size=35 name="location" value="%s"></td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="collection" style='border: 1px solid #cfcfcf'>
""" % (_("Location"), result[4],
_("Collection"))
if result[3] == 'Monography':
out += """
<option value = "Monography" selected>Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Reference':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference" selected>Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Archives':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives" selected>Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Library':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library" selected>Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Conference':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference" selected>Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'LSL Depot':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot" selected>LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Oversize':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize" selected>Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Official':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official" selected>Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Pamphlet':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet" select>Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'CDROM':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM" selected>CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Standarts':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards" selected>Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
elif result[3] == 'Video & Trainings':
out += """
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings" selected>Video & Trainings</option>
"""
else:
out += """
<option value = "">-</option>
<option value = "Monography">Monography</option>
<option value = "Reference">Reference</option>
<option value = "Archives">Archives</option>
<option value = "Library">Library</option>
<option value = "Conference">Conference</option>
<option value = "LSL Depot">LSL Depot</option>
<option value = "Oversize">Oversize</option>
<option value = "Official">Official</option>
<option value = "Pamphlet">Pamphlet</option>
<option value = "CDROM">CDROM</option>
<option value = "Standards">Standards</option>
<option value = "Video & Trainings">Video & Trainings</option>
"""
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td><input type="text" style='border: 1px solid #cfcfcf' size=35 name="description" value="%s"></td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="loan_period" style='border: 1px solid #cfcfcf'>
""" % (_("Description"), result[5] or '-',
_("Loan period"))
if result[6] == '4 weeks':
out += """
<option value ="4 weeks" selected>4 weeks</option>
<option value ="1 week">1 week</option>
<option value ="reference">reference</option>
"""
elif result[6] == '1 week':
out += """
<option value ="4 weeks">4 weeks</option>
<option value ="1 week" selected>1 week</option>
<option value ="reference">reference</option>
"""
else:
out += """
<option value ="4 weeks">4 weeks</option>
<option value ="1 week">1 week</option>
<option value ="reference" selected>reference</option>
"""
out += """</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="status" style='border: 1px solid #cfcfcf'>
""" % (_("Status"))
if result[7] == 'available':
out += """
<option value ="available" selected>available</option>
<option value ="missing">missing</option>
"""
elif result[7] == 'on loan':
out += """
<option value ="available">available</option>
<option value ="on loan" selected>on loan</option>
<option value ="missing">missing</option>
"""
elif result[7] == 'requested':
out += """
<option value ="available">available</option>
<option value ="missing">missing</option>
<option value ="requested" selected>requested</option>
"""
else:
out += """
<option value ="available">available</option>
<option value ="missing" selected>missing</option>
"""
out += """ </select>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="history.go(-1)"
value='%s' class='formbutton'>
<input type="submit" value='%s' class="formbutton">
<input type=hidden name=recid value=%s>
</td>
</tr>
</table>
<br />
<br />
</div>
</form>
""" % (_("Back"), _("Continue"), recid)
return out
def tmpl_update_item_info_step5(self, tup_infos, ln=CFG_SITE_LANG):
"""
@param tup_info: item's informations
@type tup_info: tuple
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<form name="update_item_info_step5_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_item_info_step6" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th> <td>%s</td>
</tr>
<tr>
<th width="100">%s</th> <td>%s</td>
</tr>
<tr>
<th width="100">%s</th> <td>%s</td>
</tr>
<tr>
<th width="100">%s</th> <td>%s</td>
</tr>
<tr>
<th width="100">%s</th> <td>%s</td>
</tr>
<tr>
<th width="100">%s</th> <td>%s</td>
</tr>
<tr>
<th width="100">%s</th> <td>%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
<input type=hidden name=tup_infos value="%s">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("New copy information"),
_("Barcode"), tup_infos[0],
_("Library"), tup_infos[2],
_("Location"), tup_infos[3],
_("Collection"), tup_infos[4],
_("Description"), tup_infos[5],
_("Loan period"), tup_infos[6],
_("Status"), tup_infos[7],
_("Back"), _("Confirm"), tup_infos)
return out
def tmpl_update_item_info_step6(self, ln=CFG_SITE_LANG):
"""
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value='%s'
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("This item has been updated."),
_("Back to home"),
CFG_SITE_URL)
return out
def tmpl_search_library_step1(self, infos, ln=CFG_SITE_LANG):
"""
Template for the admin interface. Search borrower.
@param infos: informations
@type infos: list
@param ln: language
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<br /><br /> <br />
<form name="search_library_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/search_library_step2" method="get" >
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s
<input type="radio" name="column" value="name" checked>name
<input type="radio" name="column" value="email">email
<br>
<br>
</td>
</tr>
<tr align="center">
<td><input type="text" size="45" name="string" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<form>
<br />
<br />
<br />
<br />
</div>
""" % (CFG_SITE_URL,
_("Search library by"),
_("Back"),
_("Search"))
return out
def tmpl_search_library_step2(self, result, ln=CFG_SITE_LANG):
"""
@param result: search result about libraries
@type result: list
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("0 library(ies) found."))
else:
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirccontent">
<strong>%s library(ies) found</strong>
</td>
</tr>
</table>
<br />
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<th align="center">%s</th>
""" % (len(result), _("Library(ies)"))
for (library_id, name) in result:
library_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_library_details',
{'library_id': library_id, 'ln': ln},
(name))
out += """
<tr align="center">
<td width="70">%s
<input type=hidden name=library_id value=%s></td>
</tr>
""" % (library_link, library_id)
out += """
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Back"))
return out
def tmpl_library_notes(self, library_notes, library_id,
ln=CFG_SITE_LANG):
"""
@param library_notes: notes about a library
@type library_notes: dictionnary
@param library_id: identify the library. Primary key of crcLIBRARY
@type library_id: int
@param ln: language of the page
"""
_ = gettext_set_language(ln)
if not library_notes:
library_notes = {}
else:
library_notes = eval(library_notes)
out = """ """
out += _MENU_
out +="""
<div class="bibcircbottom">
<form name="library_notes" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_library_notes" method="get" >
<input type=hidden name=library_id value='%s'>
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td>
<table class="bibcircnotes">
""" % (CFG_SITE_URL, library_id,
_("Notes about library"))
key_array = library_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_library_notes',
{'delete_key': key, 'library_id': library_id, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, library_notes[key], delete_note)
out += """
</table>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">
<textarea name="library_notes" rows="5" cols="90" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_library_details?library_id=%s'"
value=%s class='formbutton'>
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Write new note"),
CFG_SITE_URL,
library_id,
_("Back"),
_("Confirm"))
return out
def tmpl_change_due_date_step1(self, loan_details, loan_id, borrower_id, ln=CFG_SITE_LANG):
"""
Return the form where the due date can be changed.
@param loan_details: the information related with the loan.
@type loan_details: tuple
@param loan_id: identify the loan. Primary key of crcLOAN.
@type loan_id: int
@param borrower_id: identify the borrower. Primary key of crcBORROWER.
@type borrower_id: int
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
(recid, barcode, loaned_on, due_date, loan_status, loan_period, item_status) = loan_details
if item_status == 'requested':
request_status = 'Yes'
else:
request_status = 'No'
out +="""
<div class="bibcircbottom">
<form name="borrower_notes" action="%s/admin/bibcirculation/bibcirculationadmin.py/change_due_date_step2" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="100">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="80">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="80">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="80">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="80">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="80">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="80">%s</td> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="80">%s</td> <td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL, _("Loan information"),
_("Title"), book_title_from_MARC(recid),
_("Barcode"), barcode,
_("Loan date"), loaned_on,
_("Due date"), due_date,
_("Loan status"), loan_status,
_("Loan period"), loan_period,
_("Requested ?"), request_status)
out += """
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
<table class="bibcirctable">
<tr align="left">
<td width="230" class="bibcirctableheader">%s
<script type="text/javascript">
$(function(){
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker1" name="period_from" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL, CFG_SITE_URL,
_("New due date: "), CFG_SITE_URL, due_date)
out += """
<table class="bibcirctable">
<tr>
<td>
<input type=hidden name=loan_id value=%s>
<input type=hidden name=borrower_id value=%s>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value="%s" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (loan_id, borrower_id,
_("Back"), _("Submit new due date"))
return out
def tmpl_change_due_date_step2(self, due_date, borrower_id, ln=CFG_SITE_LANG):
"""
Return a page with the new due date.
@param due_date: new due date
@type due_date: string
@param borrower_id: identify the borrower. Primary key of crcBORROWER.
@type borrower_id: int
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_loans_details?borrower_id=%s'"
value=%s class='formbutton'>
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("The due date has been updated. New due date: %s" % (due_date)),
CFG_SITE_URL, borrower_id, _("Back borrower's loans"))
return out
def tmpl_create_new_loan_step1(self, borrower, infos, ln=CFG_SITE_LANG):
"""
Display the borrower's information and a form where it is
possible to search for an item.
@param borrower: borrower's information.
@type borrower: tuple
@param infos: informations
@type infos: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
(borrower_id, name, email, phone, address, mailbox) = borrower
out += """
<form name="create_new_loan_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/create_new_loan_step2" method="get" >
<div class="bibcircbottom">
<input type=hidden name=borrower_id value=%s>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
</form>
<table class="bibcirctable">
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
</table>
"""% (CFG_SITE_URL,
borrower_id,
_("Personal details"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox)
out +="""
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
<tr>
<td><input type="text" size="50" name="barcode" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
""" % (_("Barcode"))
out += """
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
<tr>
<td><textarea name='new_note' rows="4" cols="43" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<br />
""" % (_("Write notes"))
out += """
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
</form>
""" % (_("Back"),
_("Confirm"))
return out
def tmpl_create_new_request_step1(self, borrower, infos, result, p, f, ln=CFG_SITE_LANG):
"""
Display the borrower's information and the form where it is
possible to search for an item.
@param borrower: borrower's information.
@type borrower: tuple
@param infos: informations
@type infos: list
@param result: result of searching for an item, using p and f.
@type result: list
@param p: pattern who will be used in the search process.
@type p: string
@param f: field who will be used in the search process.
@type f: string
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
(borrower_id, name, email, phone, address, mailbox) = borrower
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tbody>
<tr>
<td width="500" valign="top">
<form name="create_new_loan_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/create_new_request_step1" method="get" >
<input type=hidden name=borrower_id value=%s>
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">Search item by
"""%(CFG_SITE_URL, borrower_id)
if f == 'barcode':
out += """
<input type="radio" name="f" value="">any field
<input type="radio" name="f" value="barcode" checked>barcode
<input type="radio" name="f" value="author">author
<input type="radio" name="f" value="title">title
"""
elif f == 'author':
out += """
<input type="radio" name="f" value="">any field
<input type="radio" name="f" value="barcode">barcode
<input type="radio" name="f" value="author" checked>author
<input type="radio" name="f" value="title">title
"""
elif f == 'title':
out += """
<input type="radio" name="f" value="">any field
<input type="radio" name="f" value="barcode">barcode
<input type="radio" name="f" value="author">author
<input type="radio" name="f" value="title" checked>title
"""
else:
out += """
<input type="radio" name="f" value="" checked>any field
<input type="radio" name="f" value="barcode">barcode
<input type="radio" name="f" value="author">author
<input type="radio" name="f" value="title">title
"""
out += """
<br />
<br />
</td>
</tr>
<tr align="center">
<td><input type="text" size="50" name="p" value='%s' style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s' onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' name='search' class="formbutton">
</td>
</tr>
</table>
</form>
""" % (p or '', _("Back"), _("Search"))
if result:
out += """
<br />
<form name="form2" action="%s/admin/bibcirculation/bibcirculationadmin.py/create_new_request_step2" method="get" >
<table class="bibcirctable">
<tr width="200">
<td align="center">
<select name="recid" size="12" style='border: 1px solid #cfcfcf; width:77%%'>
""" % (CFG_SITE_URL)
for recid in result:
out += """
<option value ='%s'>%s
""" % (recid, book_title_from_MARC(recid))
out += """
</select>
</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td ALIGN="center">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<input type=hidden name=borrower_id value=%s>
</form>
""" % (_("Select item"), borrower_id)
out += """
</td>
<td width="200" align="center" valign="top">
<td align="center" valign="top">
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
</form>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
</tr>
<br />
"""% (_("Borrower details"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox)
out += """
</table>
<br />
<br />
<br />
<br />
<br />
</div>
"""
return out
def tmpl_create_new_request_step2(self, user_info, holdings_information, recid, ln=CFG_SITE_LANG):
"""
@param borrower_id: identify the borrower. Primary key of crcBORROWER.
@type borrower_id: int
@param holdings_information: information about holdings
@type holdings_information: list
@param recid: identify the record. Primary key of bibrec.
@type recid: int
"""
_ = gettext_set_language(ln)
if not holdings_information:
return _("This item has no holdings.")
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader" align="center">%s</td>
<td class="bibcirctableheader "align="center">%s</td>
<td class="bibcirctableheader "align="center">%s</td>
<td class="bibcirctableheader"></td>
</tr>
""" % (_("Barcode"), _("Library"), _("Collection"),
_("Location"), _("Description"), _("Loan period"),
_("Status"), _("Due date"))
for (barcode, library, collection, location, description, loan_period, status, due_date) in holdings_information:
out += """
<tr onMouseOver="this.className='highlight'" onMouseOut="this.className='normal'">
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="right">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/place_new_request_step2?barcode=%s&recid=%s&user_info=%s,%s,%s,%s,%s,%s'"
value='%s' class="formbutton"></td>
</tr>
""" % (barcode, library, collection, location,
description, loan_period, status, due_date,
CFG_SITE_URL, barcode, recid, user_info[0],user_info[1],user_info[2],user_info[3],user_info[4],user_info[5],
_("Request"))
out += """
</table>
<br />
<br />
<br />
</div>
"""
return out
def tmpl_create_new_request_step3(self, borrower_id, barcode, recid, ln=CFG_SITE_LANG):
"""
@param borrower_id: identify the borrower. Primary key of crcBORROWER.
@type borrower_id: int
@param barcode: identify the item. Primary key of crcITEM.
@type barcode: string
@param recid: identify the record. Primary key of bibrec.
@type recid: int
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
<form name="request_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/create_new_request_step4" method="get" >
<div class="bibcircbottom">
<br />
<br />
<br />
<table class="bibcirctable_contents">
<tr class="bibcirctableheader" align='center'>
<td>%s</td>
</tr>
</table>
<br />
<table class="bibcirctable_contents">
<tr>
<td width="90" class="bibcirctableheader" align='right'>%s</td>
<td align='left'>
<script type="text/javascript">
$(function(){
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker1" name="period_from" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<table class="bibcirctable_contents">
<tr>
<td width="90" class="bibcirctableheader" align='right'>%s</td>
<td align='left'>
<script type="text/javascript">
$(function(){
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker2" name="period_to" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
<br />
""" % (CFG_SITE_URL, CFG_SITE_URL, CFG_SITE_URL,
_("Enter the period of interest"),
_("From: "), CFG_SITE_URL, datetime.date.today().strftime('%Y-%m-%d'),
_("To: "), CFG_SITE_URL, (datetime.date.today() + datetime.timedelta(days=365)).strftime('%Y-%m-%d'))
out += """
<table class="bibcirctable_contents">
<tr>
<td align="center">
<input type=hidden name=barcode value='%s'>
<input type=hidden name=borrower_id value='%s'>
<input type=hidden name=recid value='%s'>
<input type=button value="Back" onClick="history.go(-1)" class="formbutton">
<input type="submit" name="submit_button" value="%s" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (barcode, borrower_id, recid, _('Confirm'))
return out
def tmpl_create_new_request_step4(self, ln=CFG_SITE_LANG):
"""
Last step of the request procedure.
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="30">%s</td>
</tr>
</table>
<br />
<br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value='%s' class='formbutton'></td>
</table>
<br />
<br />
</div>
""" % (_("A new request has been registered with success."),
CFG_SITE_URL, _("Back to home"))
return out
def tmpl_place_new_request_step1(self, result, key, string, barcode,
recid, infos, ln=CFG_SITE_LANG):
"""
@param result: borrower's information
@type result: list
@param key: field (name, email, etc...)
@param key: string
@param string: pattern
@type string: string
@param barcode: identify the item. Primary key of crcITEM.
@type barcode: string
@param recid: identify the record. Primary key of bibrec
@type recid: int
@param infos: informations
@type infos: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td width="500" valign='top'>
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td width="200" align='center' valign='top'>
<table>
<tr>
<td>
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
</table>
</td>
""" % (_("Item details"),
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
_("Barcode"), barcode,
str(book_cover))
out += """
<td valign='top' align='center'>
<form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/place_new_request_step1" method="get" >
<input type=hidden name=barcode value='%s'>
<input type=hidden name=recid value='%s'>
<table>
""" % (CFG_SITE_URL, barcode, recid)
if CFG_CERN_SITE == 1:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search user by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'name':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="ccid" checked>ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search borrower by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'id':
out += """
<input type="radio" name="key" value="id" checked>id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
out += """
<br><br>
</td>
</tr>
<tr>
<td align="center">
<input type="text" size="40" id="string" name="string" value='%s' style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<td align="center">
<br>
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
</form>
""" % (string or '')
if result:
out += """
<br />
<form name="step1_form2" action="%s/admin/bibcirculation/bibcirculationadmin.py/place_new_request_step2" method="get" >
<input type=hidden name=barcode value='%s'>
<input type=hidden name=recid value='%s'>
<table class="bibcirctable">
<tr width="200">
<td align="center">
<select name="user_info" size="8" style='border: 1px solid #cfcfcf; width:40%%'>
""" % (CFG_SITE_URL, barcode, recid)
for (ccid, name, email, phone, address, mailbox) in result:
out += """
<option value ='%s,%s,%s,%s,%s,%s'>%s
""" % (ccid, name, email, phone, address, mailbox, name)
out += """
</select>
</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td ALIGN="center">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
</form>
""" % (_("Select user"))
out += """
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
"""
return out
def tmpl_place_new_request_step2(self, barcode, recid, user_info, infos, ln=CFG_SITE_LANG):
"""
@param barcode: identify the item. Primary key of crcITEM.
@type barcode: string
@param recid: identify the record. Primary key of bibrec
@type recid: int
@param user_info: user's informations
@type user_info: tuple
@param infos: informations
@type infos: list
@param ln: language of the page
"""
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
(ccid, name, email, phone, address, mailbox) = user_info
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<form name="step2_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/place_new_request_step3" method="get" >
<input type=hidden name=barcode value='%s'>
<input type=hidden name=recid value='%s'>
<input type=hidden name=user_info value="%s">
<br />
<table class="bibcirctable">
<tr>
<td width="500" valign="top">
<table class="bibcirctable">
<tr class="bibcirctableheader">
<td>%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td width="200" align='center' valign='top'>
<table>
<tr>
<td>
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
</table>
</td>
<br />
""" % (CFG_SITE_URL, barcode,
recid, user_info,
_("Item details"),
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
_("Barcode"), barcode,
str(book_cover))
out += """
<td align='center' valign="top">
<table class="bibcirctable">
<tr class="bibcirctableheader">
<td>%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
</table>
""" % (_("Borrower details"),
_("ID"), ccid,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox)
out += """
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
<table class="bibcirctable">
<tr class="bibcirctableheader">
<td>%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>
<script type="text/javascript">
$(function(){
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker1" name="period_from" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<script type="text/javascript">
$(function(){
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker2" name="period_to" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (CFG_SITE_URL ,CFG_SITE_URL,
_("Enter the period of interest"),
_("From: "), CFG_SITE_URL, datetime.date.today().strftime('%Y-%m-%d'),
_("To: "), CFG_SITE_URL, (datetime.date.today() + datetime.timedelta(days=365)).strftime('%Y-%m-%d'),
_("Back"), _("Continue"))
return out
def tmpl_place_new_request_step3(self, ln=CFG_SITE_LANG):
"""
Last step of the request procedure.
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<div class="infoboxsuccess">%s</div>
<br />
<br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value='%s' class='formbutton'></td>
</table>
<br />
<br />
</div>
""" % (_("A new request has been registered with success."),
CFG_SITE_URL, _("Back to home"))
return out
def tmpl_place_new_loan_step1(self, result, key, string, barcode,
recid, infos, ln=CFG_SITE_LANG):
"""
@param result: borrower's information
@type result: list
@param key: field (name, email, etc...)
@param key: string
@param string: pattern
@type string: string
@param barcode: identify the item. Primary key of crcITEM.
@type barcode: string
@param recid: identify the record. Primary key of bibrec
@type recid: int
@param infos: informations
@type infos: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="500" valign='top'>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td width="200" align='center' valign='top'>
<table>
<tr>
<td>
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
</table>
</td>
""" % (_("Item details"),
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
_("Barcode"), barcode,
str(book_cover))
out += """
<td valign='top' align='center'>
<form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/place_new_loan_step1" method="get" >
<input type=hidden name=barcode value='%s'>
<input type=hidden name=recid value='%s'>
<table>
""" % (CFG_SITE_URL, barcode, recid)
if CFG_CERN_SITE == 1:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search user by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'name':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="ccid" checked>ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search borrower by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'id':
out += """
<input type="radio" name="key" value="id" checked>id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
out += """
<br><br>
</td>
</tr>
<tr>
<td align="center">
<input type="text" size="40" id="string" name="string" value='%s' style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<td align="center">
<br>
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
</form>
""" % (string or '')
#<input type=hidden name=recid value='%s'>
if result:
out += """
<br />
<form name="step1_form2" action="%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step3" method="get" >
<input type=hidden name=barcode value='%s'>
<table class="bibcirctable">
<tr width="200">
<td align="center">
<select name="user_info" size="8" style='border: 1px solid #cfcfcf; width:40%%'>
""" % (CFG_SITE_URL, barcode)
for (ccid, name, email, phone, address, mailbox) in result:
out += """
<option value ="[\'%s\',\'%s\',\'%s\',\'%s\',\'%s\',\'%s\']">%s
""" % (ccid, name, email, phone, address, mailbox, name)
out += """
</select>
</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td ALIGN="center">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
</form>
""" % (_("Select user"))
out += """
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
"""
return out
def tmpl_place_new_loan_step2(self, barcode, recid, user_info, ln=CFG_SITE_LANG):
"""
@param barcode: identify the item. Primary key of crcITEM.
@type barcode: string
@param recid: identify the record. Primary key of bibrec.
@type recid: int
@param user_info: user's informations
@type user_info: tuple
@param ln: language of the page
"""
(book_title, book_year, book_author, book_isbn,
book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
(ccid, name, email, phone, address, mailbox) = user_info.split(',')
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<form name="step2_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/place_new_loan_step3" method="get" >
<input type=hidden name=barcode value='%s'>
<input type=hidden name=recid value='%s'>
<input type=hidden name=email value='%s'>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td>
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL, barcode,
recid, email,
_("Item details"),
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
_("Barcode"), barcode,
str(book_cover))
out += """
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
</table>
<br />
""" % (_("Borrower details"),
_("ID"), ccid,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox)
out += """
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
<table class="bibcirctable">
<tr class="bibcirctableheader">
<td>%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="70">%s</th>
<td align='left'>
<script type="text/javascript">
$(function(){
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker1" name="due_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
<br />
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th>%s</th>
</tr>
<tr>
<td><textarea name='notes' rows="5" cols="57" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
<tr>
<td>This note will be associate to this new loan, not to the borrower.</td>
</tr>
</table>
<br />
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (CFG_SITE_URL, CFG_SITE_URL,
_("Loan information"),
_("Loan date"), datetime.date.today().strftime('%Y-%m-%d'),
_("Due date"), CFG_SITE_URL, renew_loan_for_X_days(barcode),
_("Write notes"), _("Back"), _("Continue"))
return out
def tmpl_order_new_copy_step1(self, recid, list_of_vendors,
libraries, ln=CFG_SITE_LANG):
"""
@param recid: identify the record. Primary key of bibrec.
@type recid: int
@param list_of_vendors: list with all the vendores
@type list_of_vendors: list
@param libraries: all libraries
@type libraries: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script type="text/javascript" src="/js/jquery/jquery.validate.js"></script>
<script>
$(document).ready(function(){
$('#order_new_copy_step1_form').validate();
});
</script>
<style type="text/css">
label { width: 10em; float: left; }
label.error { float: none; color: red; padding-left: .5em; vertical-align: top; }
p { clear: both; }
.submit { margin-left: 12em; }
em { font-weight: bold; padding-right: 1em; vertical-align: top; }
</style>
<form name="order_new_copy_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/order_new_copy_step2" method="get" >
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400"><input type=hidden name=recid value='%s'>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
_("Item details"),
recid,
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn,
str(book_cover))
out += """
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesortermedium" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>
<input class="required" type="text" size="20" name="barcode" style='border: 1px solid #cfcfcf' />
<em>*</em>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="vendor_id" style='border: 1px solid #cfcfcf'>
""" % (CFG_SITE_URL, CFG_SITE_URL,
_("Order details"), _("Barcode"), _("Vendor"))
for(vendor_id, name) in list_of_vendors:
out +="""<option value ="%s">%s</option>""" % (vendor_id, name)
today = datetime.date.today()
gap = datetime.timedelta(days=14)
more_2_weeks = (today + gap).strftime('%Y-%m-%d')
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" style='border: 1px solid #cfcfcf' size=12 name="cost">
<select name="currency" style='border: 1px solid #cfcfcf'>
<option value ="EUR">EUR</option>
<option value ="CHF">CHF</option>
<option value ="USD">USD</option>
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="status" style='border: 1px solid #cfcfcf'>
<option value ="ordered">ordered</option>
<option value ="cancelled">cancelled</option>
<option value ="not arrived">arrived</option>
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<script type="text/javascript">
$(function(){
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker1" name="order_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<script type="text/javascript">
$(function(){
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker2" name="expected_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<select name="library_id" style='border: 1px solid #cfcfcf'>
""" % (_("Price"), _("Status"),
_("Order date"), CFG_SITE_URL, today,
_("Expected date"), CFG_SITE_URL, more_2_weeks,
_("Library"))
for(library_id, name) in libraries:
out +="""<option value ="%s">%s</option>""" % (library_id, name)
out += """
</select>
</td>
</tr>
<tr>
<th valign="top" width="100">%s</th>
<td><textarea name='notes' rows="6" cols="30" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("Notes"), _("Back"), _("Continue"))
return out
def tmpl_order_new_copy_step2(self, order_info, ln=CFG_SITE_LANG):
"""
@param order_info: order's informations
@type order_info: tuple
@param ln: language of the page
"""
(recid, barcode, vendor_id, cost, currency, status,
order_date, expected_date, library_id, notes) = order_info
(book_title, book_year, book_author,
book_isbn, book_editor) = book_information_from_MARC(int(recid))
vendor_name = db.get_vendor_name(vendor_id)
library_name = db.get_library_name(library_id)
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """
<div class="bibcircbottom">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="step2_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/order_new_copy_step3" method="get" >
<input type=hidden name=order_info value="%s">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent">
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
order_info,
_("Item details"),
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
str(book_cover))
out += """
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s %s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td><i>%s</i></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("Order details"),
_("Barcode"), barcode,
_("Vendor"), vendor_name,
_("Price"), cost, currency,
_("Status"), status,
_("Order date"), order_date,
_("Expected date"), expected_date,
_("Library"), library_name,
_("Notes"), notes,
_("Back"), _("Continue"))
return out
def tmpl_order_new_copy_step3(self, ln=CFG_SITE_LANG):
"""
Last step of the request procedure.
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="30">%s</td>
</tr>
</table>
<br />
<br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value='%s' class='formbutton'></td>
</table>
<br />
<br />
</div>
""" % (_("A new purchase has been registered with success."),
CFG_SITE_URL, _("Back to home"))
return out
def tmpl_ordered_books(self, ordered_books, ln=CFG_SITE_LANG):
"""
Display list with all ordered books.
@param ordered_books: list with all the ordered books
@type ordered_books: list
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_orders').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<div class="bibcircbottom">
<br />
<table id="table_orders" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (_("Item"),
_("Vendor"),
_("Ordered date"),
_("Price"),
_("Status"),
_("Expected date"),
_("Notes"),
_("Option(s)"))
for (purchase_id, recid, vendor_id, ordered_date, expected_date, price, status, notes) in ordered_books:
vendor_name = db.get_vendor_name(vendor_id)
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_purchase_notes',
{'purchase_id': purchase_id},
(_("No notes")))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_purchase_notes',
{'purchase_id': purchase_id},
(_("See notes")))
if notes == "":
notes_link = no_notes_link
else:
notes_link = see_notes_link
out += """
<tr onMouseOver="this.className='highlight'" onMouseOut="this.className='normal'">
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td class="bibcirccontent" align="center">%s</td>
<td align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/ordered_books_details_step1?purchase_id=%s'" onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value=%s class='bibcircbutton'>
</td>
</tr>
""" % (title_link, vendor_name, ordered_date,
price, status, expected_date, notes_link,
CFG_SITE_URL, purchase_id, _("select"))
out += """
</tbody>
</table>
<br />
<br />
</div>
"""
return out
def tmpl_purchase_notes(self, purchase_notes, purchase_id, ln=CFG_SITE_LANG):
"""
@param purchase_notes: notes about a given purchase
@type purchase_notes: dictionnary
@param purchase_id: identify the purchase. Primary key of crcPURCHASE
@type purchase_id: int
"""
_ = gettext_set_language(ln)
if not purchase_notes:
purchase_notes = {}
else:
purchase_notes = eval(purchase_notes)
out = """ """
out += _MENU_
out +="""
<div class="bibcircbottom">
<form name="borrower_notes" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_purchase_notes" method="get" >
<input type=hidden name=purchase_id value='%s'>
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td>
<table class="bibcircnotes">
""" % (CFG_SITE_URL, purchase_id,
_("Notes about acquisition"))
key_array = purchase_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_purchase_notes',
{'delete_key': key, 'purchase_id': purchase_id, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, purchase_notes[key], delete_note)
out += """
</table>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">
<textarea name="library_notes" rows="5" cols="90" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/ordered_books'"
value=%s class='formbutton'>
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Write new note"),
CFG_SITE_URL,
_("Back"),
_("Confirm"))
return out
def tmpl_register_ill_request_step0(self, result, infos, key, string, recid, ln=CFG_SITE_LANG):
"""
@param result: borrower's information
@type result: list
@param infos: informations
@type infos: list
@param key: field (name, email, etc...)
@param key: string
@param string: pattern
@type string: string
@param recid: identify the record. Primary key of bibrec.
@type recid: int
@param ln: language of the page
"""
_ = gettext_set_language(ln)
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="500" valign='top'>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td width="200" align='center' valign='top'>
<table>
<tr>
<td>
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
</table>
</td>
""" % (_("Item details"),
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
str(book_cover))
out += """
<td valign='top' align='center'>
<form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_step0" method="get" >
<input type=hidden name=recid value='%s'>
<table>
""" % (CFG_SITE_URL, recid)
if CFG_CERN_SITE == 1:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search user by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'name':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="ccid" checked>ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search borrower by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'id':
out += """
<input type="radio" name="key" value="id" checked>id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
out += """
<br><br>
</td>
</tr>
<tr>
<td align="center">
<input type="text" size="40" id="string" name="string" value='%s' style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<td align="center">
<br>
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
</form>
""" % (string or '')
if result:
out += """
<br />
<form name="step1_form2" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_step1" method="get" >
<input type=hidden name=recid value='%s'>
<table class="bibcirctable">
<tr width="200">
<td align="center">
<select name="user_info" size="8" style='border: 1px solid #cfcfcf; width:40%%'>
""" % (CFG_SITE_URL, recid)
for (ccid, name, email, phone, address, mailbox) in result:
out += """
<option value ='%s,%s,%s,%s,%s,%s'>%s
""" % (ccid, name, email, phone, address, mailbox, name)
out += """
</select>
</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td ALIGN="center">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
</form>
""" % (_("Select user"))
out += """
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
"""
return out
def tmpl_register_ill_request_step1(self, recid, user_info, ln=CFG_SITE_LANG):
"""
@param recid: identify the record. Primary key of bibrec.
@type recid: int
@param user_info: user's informations
@type user_info: tuple
"""
(ccid, name, email, phone, address, mailbox) = user_info.split(',')
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="update_item_info_step4_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_step2" method="get" >
<div class="bibcircbottom" align="center">
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<input type=hidden name=recid value='%s'>
<input type=hidden name=user_info value="%s">
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
_("Item details"),
recid, user_info,
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn)
out += """
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar.js"></script>
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar-setup.js"></script>
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar-en.js"></script>
<style type="text/css"> @import url("/jsCalendar/calendar-blue.css"); </style>
<br />
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">
<input type="text" size="12" id="%s" name="period_of_interest_from" value="" style='border: 1px solid #cfcfcf'>
<img src="/jsCalendar/jsCalendar.gif" alt="select period of interest" id="%s"
onmouseover="this.style.background='red';" onmouseout="this.style.background=''">
<script type="text/javascript" language='JavaScript'>
Calendar.setup({
inputField : '%s',
ifFormat : '%%Y-%%m-%%d',
button : '%s'
});
</script>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">
<input type="text" size="12" id="%s" name="period_of_interest_to" value="" style='border: 1px solid #cfcfcf'>
<img src="/jsCalendar/jsCalendar.gif" alt="select period of interest" id="%s"
onmouseover="this.style.background='red';" onmouseout="this.style.background=''">
<script type="text/javascript" language='JavaScript'>
Calendar.setup({
inputField : '%s',
ifFormat : '%%Y-%%m-%%d',
button : '%s'
});
</script>
</td>
</tr>
<tr>
<th valign="top" width="150">%s</th>
<td><textarea name='notes' rows="6" cols="30"
style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<table class="bibcirctable">
<tr align="center">
<td>
<input name="only_edition" type="checkbox" value="Yes" />%s</td>
</tr>
</table>
</td>
<td>
""" % (_("Borrower details"),
_("ID"), ccid,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox,
_("ILL request details"),
_("Period of interest - From"), _("period_of_interest_from"),
_("jsCal3"), _("period_of_interest_from"), _("jsCal3"),
_("Period of interest - To"), _("period_of_interest_to"),
_("jsCal4"), _("period_of_interest_to"), _("jsCal4"),
_("Additional comments"),
_("Borrower wants only this edition?"))
out += """
<table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("Back"), _("Continue"))
return out
def tmpl_register_ill_request_step2(self, user_info, request_info, ln=CFG_SITE_LANG):
"""
@param user_info: user's informations
@type user_info: tuple
@param request_info: request's informations
@type request_info: tuple
@param ln: language of the page
"""
(recid, period_of_interest_from, period_of_interest_to,
notes, only_edition) = request_info
(book_title, book_year, book_author,
book_isbn, book_editor) = book_information_from_MARC(int(recid))
(borrower_id, name, email, phone, address, mailbox) = user_info
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class=bibcircbottom align="center">
<br />
<br />
<form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_step3" method="get" >
<table>
<tr align="center">
<td class="bibcirctableheader">%s</td>
<input type=hidden name=borrower_id value="%s">
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<table>
<tr align="center">
<td class="bibcirctableheader">%s</td>
<input type=hidden name=request_info value="%s">
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<table>
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
""" % (CFG_SITE_URL,
_("Item details"), borrower_id,
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
_("ILL request details"), request_info,
_("Period of interest - From"), period_of_interest_from,
_("Period of interest - To"), period_of_interest_to,
_("Additional comments"), notes,
_("Only this edition"), only_edition,
_("Borrower details"),
_("ID"), borrower_id,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox)
out += """<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>""" % (_("Back"), _("Continue"))
return out
(_ccid, name, email, phone, address, mailbox) = user_info.split(',')
return out
def tmpl_register_ill_request_step3(self, ln=CFG_SITE_LANG):
"""
Last step of the request procedure.
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="30">%s</td>
</tr>
</table>
<br />
<br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value='%s' class='formbutton'></td>
</table>
<br />
<br />
</div>
""" % (_("A new ILL request has been registered with success."),
CFG_SITE_URL, _("Back to home"))
return out
def tmpl_ill_request_with_recid(self, recid, infos, ln=CFG_SITE_LANG):
"""
@param recid: identify the record. Primary key of bibrec.
@type recid: int
@param infos: informations
@type infos: list
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
today = datetime.date.today()
within_six_months = (datetime.date.today() + datetime.timedelta(days=182)).strftime('%Y-%m-%d')
out += """
<div align="center">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
- <form name="update_item_info_step4_form" action="%s/record/%s/holdings/ill_register_request_with_recid" method="get" >
+ <form name="update_item_info_step4_form" action="%s/%s/%s/holdings/ill_register_request_with_recid" method="get" >
<table class="bibcirctable">
<tr align="center">
<td><h1 class="headline">%s</h1></td>
</tr>
</table>
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<input type=hidden name=recid value='%s'>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<br />
<br />
- """ % (CFG_SITE_URL, recid,
+ """ % (CFG_SITE_URL, CFG_SITE_RECORD, recid,
_('Interlibrary loan request for books'),
_("Item details"),
recid,
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn)
conditions_link = """<a href="http://library.web.cern.ch/library/Library/ill_faq.html" target="_blank">conditions</a>"""
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/ui.datepicker.min.js"></script>
"""% (CFG_SITE_URL, CFG_SITE_URL)
out += """
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="150">%s</th>
<td>
<script type="text/javascript">
$(function() {
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker1" name="period_of_interest_from" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td>
<script type="text/javascript">
$(function() {
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker2" name="period_of_interest_to" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th valign="top" width="150">%s</th>
<td><textarea name='additional_comments' rows="6" cols="30"
style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<table class="bibcirctable">
<tr align="center">
<td>
<input name="conditions" type="checkbox" value="accepted" />%s</td>
</tr>
<tr align="center">
<td>
<input name="only_edition" type="checkbox" value="Yes" />%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("ILL request details"),
_("Period of interest - From"), CFG_SITE_URL, today,
_("Period of interest - To"), CFG_SITE_URL, within_six_months,
_("Additional comments"),
_("I accept the %s of the service in particular the return of books in due time." % (conditions_link)),
_("I want this edition only."),
_("Back"), _("Continue"))
return out
def tmpl_ill_register_request_with_recid(self, message, ln=CFG_SITE_LANG):
"""
@param message: information to the borrower
@type message: string
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
<br /> <br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="30">%s</td>
</tr>
<tr>
<td class="bibcirccontent" width="30">%s<a href="%s">%s</a>%s</td>
</tr>
</table>
<br /> <br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s'" value='%s' class='formbutton'></td>
</table>
<br /> <br />
""" % (message,
_("You can see your loans "),
CFG_SITE_URL + '/yourloans/display',
_("here"),
_("."),
CFG_SITE_URL,
_("Back to home"))
return out
def tmpl_display_ill_form(self, infos, ln=CFG_SITE_LANG):
"""
@param infos: informations
@type infos: list
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += """
<div align="center">
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="display_ill_form" action="%s/ill/register_request" method="get">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="title" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="authors" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="place" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="publisher" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="year" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="edition" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="isbn" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
_("Item details"),
_("Title"),
_("Author(s)"),
_("Place"),
_("Publisher"),
_("Year"),
_("Edition"),
_("ISBN"))
conditions_link = """<a href="http://library.web.cern.ch/library/Library/ill_faq.html" target="_blank">conditions</a>"""
out += """
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar.js"></script>
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar-setup.js"></script>
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar-en.js"></script>
<style type="text/css"> @import url("/jsCalendar/calendar-blue.css"); </style>
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="150">%s</th>
<td>
<input type="text" size="12" id="%s" name="period_of_interest_from"
value="" style='border: 1px solid #cfcfcf'>
<img src="/jsCalendar/jsCalendar.gif" alt="select period of interest" id="%s"
onmouseover="this.style.background='red';" onmouseout="this.style.background=''"
>
<script type="text/javascript" language='JavaScript'>
Calendar.setup({
inputField : '%s',
ifFormat : '%%Y-%%m-%%d',
button : '%s'
});
</script>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td>
<input type="text" size="12" id="%s" name="period_of_interest_to"
value="" style='border: 1px solid #cfcfcf'>
<img src="/jsCalendar/jsCalendar.gif" alt="select period of interest" id="%s"
onmouseover="this.style.background='red';" onmouseout="this.style.background=''"
>
<script type="text/javascript" language='JavaScript'>
Calendar.setup({
inputField : '%s',
ifFormat : '%%Y-%%m-%%d',
button : '%s'
});
</script>
</td>
</tr>
<tr>
<th valign="top" width="150">%s</th>
<td><textarea name='additional_comments' rows="6" cols="30"
style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<table class="bibcirctable">
<tr align="center">
<td>
<input name="conditions" type="checkbox" value="accepted" />%s</td>
</tr>
<tr align="center">
<td>
<input name="only_edition" type="checkbox" value="True" />%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("ILL request details"), _("Period of interest - From"), _("period_of_interest_from"),
_("jsCal1"), _("period_of_interest_from"), _("jsCal1"),
_("Period of interest - To"), _("period_of_interest_to"), _("jsCal2"), _("period_of_interest_to"), _("jsCal2"),
_("Additional comments"),
_("I accept the %s of the service in particular the return of books in due time." % (conditions_link)),
_("I want this edition only."),
_("Back"), _("Continue"))
return out
def tmpl_list_ill_request(self, ill_req, ln=CFG_SITE_LANG):
"""
@param ill_req: informations about a given ILL request
@type ill_req: tuple
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_ill').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<div class="bibcircbottom">
<br />
<table id="table_ill" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th width="200">%s</th>
<th width="300">%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (_("Borrower"),
_("Item"),
_("Supplier"),
_("Status"),
_("ID"),
_("Interest from"),
_("Type"),
_("Option(s)"))
for (ill_request_id, borrower_id, borrower_name, library_id,
ill_status, period_from, _period_to, item_info, request_type) in ill_req:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(borrower_name))
if library_id:
library_name = db.get_library_name(library_id)
else:
library_name = '-'
item_info = eval(item_info)
try:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': item_info['recid'], 'ln': ln},
(book_title_from_MARC(int(item_info['recid']))))
except KeyError:
if request_type == 'book':
title_link = item_info['title']
else:
title_link = item_info['periodical_title']
out += """
<tr>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td class="bibcirccontent">%s</td>
<td align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step1?ill_request_id=%s'" onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value=%s class='bibcircbutton'>
</td>
</tr>
""" % (borrower_link, title_link, library_name, ill_status,ill_request_id,
period_from, request_type, CFG_SITE_URL, ill_request_id, _('select'))
out += """
</tbody>
</table>
</div>
"""
return out
def tmpl_ill_request_details_step1(self, ill_request_id, ill_request_details, libraries, ill_request_borrower_details,ln=CFG_SITE_LANG):
"""
@param ill_request_id: identify the ILL request. Primary key of crcILLREQUEST
@type ill_request_id: int
@param ill_req_details: informations about a given ILL request
@type ill_req_details: tuple
@param libraries: list of libraries
@type libraries: list
@param ill_status: status of an ILL request
@type ill_status: string
@param ill_request_borrower_details: borrower's informations
@type ill_request_borrower_details: tuple
"""
_ = gettext_set_language(ln)
out = _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
"""% (CFG_SITE_URL, CFG_SITE_URL)
(_borrower_id, borrower_name, borrower_email, borrower_mailbox,
period_from, period_to, item_info, borrower_comments, only_this_edition, request_type) = ill_request_borrower_details
(library_id, request_date, expected_date, arrival_date, due_date, return_date,
cost, barcode, library_notes, ill_status) = ill_request_details
if cost:
(value, currency) = cost.split()
else:
(value, currency) = (0, 'EUR')
if library_notes == '' or library_notes == None:
previous_library_notes = {}
else:
previous_library_notes = eval(library_notes)
key_array = previous_library_notes.keys()
key_array.sort()
item_info = eval(item_info)
today = datetime.date.today()
within_a_week = (datetime.date.today() + datetime.timedelta(days=7)).strftime('%Y-%m-%d')
within_a_month = (datetime.date.today() + datetime.timedelta(days=30)).strftime('%Y-%m-%d')
notes=''
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step1',
{'delete_key': key, 'ill_request_id': ill_request_id, 'ln': ln},
(_("[delete]")))
notes += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, previous_library_notes[key], delete_note)
if library_id:
library_name = db.get_library_name(library_id)
else:
library_name = '-'
try:
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(item_info['recid']))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<form name="ill_req_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2" method="get" >
<div class="bibcircbottom">
<input type=hidden name=ill_request_id value=%s>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
ill_request_id,
_("Item details"),
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn,
str(book_cover))
except KeyError:
try:
book_cover = get_book_cover(item_info['isbn'])
except KeyError:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
f=open("/tmp/item_info",'w')
f.write(str(item_info)+'\n')
f.close()
if str(request_type) == 'book':
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="ill_req_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2" method="get" >
<div class="bibcircbottom">
<input type=hidden name=ill_request_id value=%s>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
ill_request_id,
_("Item details"),
_("Title"),
item_info['title'],
_("Author(s)"),
item_info['authors'],
_("Place"),
item_info['place'],
_("Publisher"),
item_info['publisher'],
_("Year"),
item_info['year'],
_("Edition"),
item_info['edition'],
_("ISBN"),
item_info['isbn'],
str(book_cover))
# for articles
elif str(request_type) == 'article':
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="ill_req_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2" method="get" >
<div class="bibcircbottom">
<input type=hidden name=ill_request_id value=%s>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
ill_request_id,
_("Item details"),
_("Periodical Title"),
item_info['periodical_title'],
_("Article Title"),
item_info['title'],
_("Author(s)"),
item_info['authors'],
_("Volume, Issue, Page"),
item_info['volume'],
_("ISSN"),
item_info['issn'],
_("Place"),
item_info['place'],
_("Publisher"),
item_info['publisher'],
_("Year"),
item_info['year'],
str(book_cover))
else:
out+= """aqui falta algo, no?"""
out += """
<table class="bibcirctable">
<tr valign='top'>
<td width="550">
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="150">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td width="350"><i>%s</i></td>
</tr>
<tr>
<th width="150">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
""" % (_("Borrower request"), _("Name"), borrower_name,
_("Email"), borrower_email,
_("Mailbox"), borrower_mailbox,
_("Period of interest (From)"), period_from,
_("Period of interest (To)"), period_to,
_("Borrower comments"), borrower_comments or '-',
_("Only this edition?"), only_this_edition or 'No',
_("ILL request details"))
#### NEW ####
if ill_status == 'new' or ill_status == None or ill_status == '':
if request_type == 'book':
out += """
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<input type=hidden name=new_status value="new">
<th width="100">%s</th>
<td>
<select style='border: 1px solid #cfcfcf' onchange="location = this.options[this.selectedIndex].value;">
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=new" selected>
New
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=requested">
Requested
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=on loan">
On loan
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=returned">
Returned
</option>
</select>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (_("Status"), ill_request_id, ill_request_id,
ill_request_id, ill_request_id, _("ILL request ID"), ill_request_id, _("Previous notes"))
if request_type == 'article':
out += """
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<input type=hidden name=new_status value="new">
<th width="100">%s</th>
<td>
<select style='border: 1px solid #cfcfcf' onchange="location = this.options[this.selectedIndex].value;">
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=new" selected>
New
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=requested">
Requested
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=received">
Received
</option>
</select>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (_("Status"), ill_request_id, ill_request_id,
ill_request_id, _("ILL request ID"), ill_request_id, _("Previous notes"))
out += notes
out += """
</table>
</td>
</tr>
<tr>
<th valign="top" width="100">%s</th>
<td><textarea name='library_notes' rows="6" cols="74" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"))
############# REQUESTED ##############
elif ill_status == 'requested':
if request_type == 'book':
out += """
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<input type=hidden name=new_status value="requested">
<th width="150">%s</th>
<td class="bibcirccontent">
<select style='border: 1px solid #cfcfcf' onchange="location = this.options[this.selectedIndex].value;">
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=new">
New
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=requested" selected>
Requested
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=on loan">
On loan
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=returned">
Returned
</option>
</select>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">
<select name="library_id" style='border: 1px solid #cfcfcf'>
""" % (_("Status"), ill_request_id, ill_request_id,
ill_request_id, ill_request_id, _("ILL request ID"), ill_request_id,
_("Library/Supplier"))
if request_type == 'article':
out += """
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<input type=hidden name=new_status value="requested">
<th width="150">%s</th>
<td class="bibcirccontent">
<select style='border: 1px solid #cfcfcf' onchange="location = this.options[this.selectedIndex].value;">
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=new">
New
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=requested" selected>
Requested
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=received">
Received
</option>
</select>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">
<select name="library_id" style='border: 1px solid #cfcfcf'>
""" % (_("Status"), ill_request_id, ill_request_id,
ill_request_id, _("ILL request ID"), ill_request_id,
_("Library/Supplier"))
for(library_id, name) in libraries:
out += """<option value ="%s">%s</option>""" % (library_id, name)
out += """
</select>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker1" name="request_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker2" name="expected_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">
<input type="text" size="12" name="cost" value="%s" style='border: 1px solid #cfcfcf'>
<select name="currency" style='border: 1px solid #cfcfcf'>
""" % (_("Request date"),
CFG_SITE_URL, today,
_("Expected date"),
CFG_SITE_URL, within_a_week,
_("Cost"), value)
if currency == 'EUR':
out += """
<option value="EUR" selected>EUR</option>
<option value="CHF">CHF</option>
<option value="USD">USD</option> """
elif currency == 'CHF':
out += """
<option value="EUR">EUR</option>
<option value="CHF" selected>CHF</option>
<option value="USD">USD</option> """
else:
out +="""
<option value="EUR">EUR</option>
<option value="CHF">CHF</option>
<option value="USD" selected>USD</option> """
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><input type="text" size="12" name="barcode" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ %(_("Barcode"), barcode or 'No barcode asociated',
_("Previous notes"))
out += notes
out += """
</table>
</td>
</tr>
<tr>
<th valign="top" width="150">%s</th>
<td><textarea name='library_notes' rows="6" cols="74" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"))
##### ON LOAN ##############
elif ill_status == 'on loan':
out += """
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<input type=hidden name=new_status value="on loan">
<th width="100">%s</th>
<td class="bibcirccontent">
<select style='border: 1px solid #cfcfcf' onchange="location = this.options[this.selectedIndex].value;">
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=new">
New
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=requested">
Requested
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=on loan" selected>
On loan
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=returned">
Returned
</option>
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
""" % (_("Status"), ill_request_id, ill_request_id,
ill_request_id, ill_request_id, _("ILL request ID"), ill_request_id,
_("Library"), library_name,
_("Request date"), request_date, _("Expected date"),
expected_date)
if str(arrival_date)=='0000-00-00':
date1=today
else:
date1=arrival_date
if str(due_date)=='0000-00-00':
date2=within_a_month
else:
date2=due_date
out += """
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker1" name="arrival_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker2" name="due_date" value="%s" style='border: 1px solid #cfcfcf'>
<input type="hidden" name="request_date" value="%s">
<input type="hidden" name="expected_date" value="%s">
</td>
</tr>
""" % (_("Arrival date"), CFG_SITE_URL, date1, _("Due date"), CFG_SITE_URL, date2, request_date, expected_date)
out += """
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><input type="text" size="12" name="cost" value="%s" style='border: 1px solid #cfcfcf'>
<select name="currency" style='border: 1px solid #cfcfcf'>
""" % (_("Cost"), value)
if currency == 'EUR':
out += """
<option value="EUR" selected>EUR</option>
<option value="CHF">CHF</option>
<option value="USD">USD</option> """
elif currency == 'CHF':
out += """
<option value="EUR">EUR</option>
<option value="CHF" selected>CHF</option>
<option value="USD">USD</option> """
else:
out +="""
<option value="EUR">EUR</option>
<option value="CHF">CHF</option>
<option value="USD" selected>USD</option> """
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><input type="text" size="12" name="barcode" value="%s" style='border: 1px solid #cfcfcf'>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (_("Barcoce"), barcode, _("Previous notes"))
out += notes
out += """
</table>
</td>
</tr>
<tr>
<th valign="top" width="100">%s</th>
<td><textarea name='library_notes' rows="6" cols="74" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"))
##### RETURNED ##############
elif ill_status == 'returned':
out += """
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<input type=hidden name=new_status value="returned">
<th width="100">%s</th>
<td class="bibcirccontent">
<select style='border: 1px solid #cfcfcf' onchange="location = this.options[this.selectedIndex].value;">
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=new">
New
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=requested">
Requested
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=on loan">
On loan
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=returned" selected>
Returned
</option>
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker1" name="return_date" value="%s" style='border: 1px solid #cfcfcf'>
<input type="hidden" name="request_date" value="%s">
<input type="hidden" name="expected_date" value="%s">
<input type="hidden" name="arrival_date" value="%s">
<input type="hidden" name="due_date" value="%s">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><input type="text" size="12" name="cost" value="%s" style='border: 1px solid #cfcfcf'>
<select name="currency" style='border: 1px solid #cfcfcf'>
""" % ( _("Status"), ill_request_id,ill_request_id,ill_request_id,ill_request_id, _("ILL request ID"), ill_request_id,
_("Library"), library_name,
_("Request date"), request_date, _("Expected date"), expected_date,
_("Arrival date"), arrival_date, _("Due date"), due_date,
_("Return date"), CFG_SITE_URL, return_date,
request_date, expected_date,arrival_date,due_date,
_("Cost"), value)
if currency == 'EUR':
out += """
<option value="EUR" selected>EUR</option>
<option value="CHF">CHF</option>
<option value="USD">USD</option> """
elif currency == 'CHF':
out += """
<option value="EUR">EUR</option>
<option value="CHF" selected>CHF</option>
<option value="USD">USD</option> """
else:
out +="""
<option value="EUR">EUR</option>
<option value="CHF">CHF</option>
<option value="USD" selected>USD</option> """
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (_("Barcode"), barcode, _("Previous notes"))
out += notes
out += """
</table>
</td>
</tr>
<tr>
<th valign="top" width="100">%s</th>
<td><textarea name='library_notes' rows="6" cols="74" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"))
##### RECEIVED ##############
elif ill_status == 'received':
if str(arrival_date)=='0000-00-00':
date1=today
else:
date1=arrival_date
out += """
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<input type=hidden name=new_status value="received">
<th width="100">%s</th>
<td class="bibcirccontent">
<select style='border: 1px solid #cfcfcf' onchange="location = this.options[this.selectedIndex].value;">
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=new">
New
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=requested">
Requested
</option>
<option value ="ill_request_details_step1?ill_request_id=%s&new_status=received" selected>
Received
</option>
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker1" name="arrival_date" value="%s" style='border: 1px solid #cfcfcf'>
<input type="hidden" name="request_date" value="%s">
<input type="hidden" name="expected_date" value="%s">
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><input type="text" size="12" name="cost" value="%s" style='border: 1px solid #cfcfcf'>
<select name="currency" style='border: 1px solid #cfcfcf'>
""" % ( _("Status"), ill_request_id,ill_request_id,ill_request_id, _("ILL request ID"), ill_request_id,
_("Library"), library_name,
_("Request date"), request_date, _("Expected date"), expected_date,
_("Arrival date"), CFG_SITE_URL, date1,
request_date, expected_date,
_("Cost"), value)
if currency == 'EUR':
out += """
<option value="EUR" selected>EUR</option>
<option value="CHF">CHF</option>
<option value="USD">USD</option> """
elif currency == 'CHF':
out += """
<option value="EUR">EUR</option>
<option value="CHF" selected>CHF</option>
<option value="USD">USD</option> """
else:
out +="""
<option value="EUR">EUR</option>
<option value="CHF">CHF</option>
<option value="USD" selected>USD</option> """
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (_("Barcode"), barcode, _("Previous notes"))
out += notes
out += """
</table>
</td>
</tr>
<tr>
<th valign="top" width="100">%s</th>
<td><textarea name='library_notes' rows="6" cols="74" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"))
###### END STATUSES ######
out += """
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</div>
</form>
<br />
<br />
""" % (_("Back"), _("Continue"))
return out
def tmpl_ill_request_details_step2(self, ill_req_details, request_info, ill_status, ill_request_borrower_details, ln=CFG_SITE_LANG):
"""
@param ill_req_details: informations about a given ILL request
@type ill_req_details: tuple
@param request_info:
@type request_info: tuple
@param ill_status: status of an ILL request
@type ill_status: string
@param ill_request_borrower_details: borrower's informations
@type ill_request_borrower_details: tuple
"""
_ = gettext_set_language(ln)
out = _MENU_
(_borrower_id, borrower_name, borrower_email, borrower_mailbox,
period_from, period_to, book_info, borrower_comments, only_this_edition) = ill_request_borrower_details
book_info = eval(book_info)
try:
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(book_info['recid']))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="ill_req_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step3" method="get" >
<div class="bibcircbottom">
<input type=hidden name=request_info value="%s">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
request_info,
_("Item details"),
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn,
str(book_cover))
except KeyError:
try:
book_cover = get_book_cover(book_info['isbn'])
except KeyError:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="ill_req_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step3" method="get" >
<div class="bibcircbottom">
<input type=hidden name=request_info value="%s">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
request_info,
_("Item details"),
_("Name"),
book_info['title'],
_("Author(s)"),
book_info['authors'],
_("Place"),
book_info['place'],
_("Publisher"),
book_info['publisher'],
_("Year"),
book_info['year'],
_("Edition"),
book_info['edition'],
_("ISBN"),
book_info['isbn'],
str(book_cover))
out += """
<table class="bibcirctable">
<tr valign='top'>
<td width="550">
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="150">%s</th>
<td width="350" class="bibcirccontent"><i>%s</i></td>
</tr>
<tr>
<th width="150">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
""" % (_("Borrower request"), _("Name"), borrower_name,
_("Email"), borrower_email,
_("Mailbox"), borrower_mailbox,
_("Period of interest - From"), period_from,
_("Period of interest - To"), period_to,
_("Borrower comments"), borrower_comments or '-',
_("Only this edition?"), only_this_edition,
_("ILL request details"))
if ill_status == 'new':
if not ill_req_details:
previous_library_notes = {}
else:
previous_library_notes = eval(ill_req_details[8])
(ill_request_id, library_notes) = request_info
out += """
<input type=hidden name=ill_status value="%s">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><b>%s</b></td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (ill_status, _("Status"), ill_status,
_("ILL request ID"), ill_request_id,
_("Previous notes"))
key_array = previous_library_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2',
{'delete_key': key, 'ill_request_id': ill_request_id, 'ill_status': ill_status,
'library_notes': library_notes, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, previous_library_notes[key], delete_note)
out += """
</table>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><i>%s</i></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"), library_notes or '-')
elif ill_status == 'requested':
if not ill_req_details:
previous_library_notes = {}
else:
previous_library_notes = eval(ill_req_details[8])
(ill_request_id, library_id, request_date, expected_date,
cost, currency, barcode, library_notes) = request_info
if library_id:
library_name = db.get_library_name(library_id)
else:
library_name = '-'
out += """
<input type=hidden name=ill_status value="%s">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><b>%s</b></td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s %s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (ill_status, _("Status"), ill_status,
_("ILL request ID"), ill_request_id,
_("Library/Supplier"), library_name,
_("Request date"), request_date,
_("Expected date"),expected_date,
_("Cost"), cost, currency,
_("Barcode"), barcode,
_("Previous notes"))
key_array = previous_library_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2',
{'delete_key': key, 'ill_request_id': ill_request_id, 'ill_status': ill_status,
'library_notes': library_notes, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, previous_library_notes[key], delete_note)
out += """
</table>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><i>%s</i></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"), library_notes or '-')
elif ill_status == 'request cancelled':
(library_id, request_date, expected_date, previous_library_notes) = ill_req_details
if not previous_library_notes:
previous_library_notes = {}
else:
previous_library_notes = eval(previous_library_notes)
(ill_request_id, cost, currency, barcode, library_notes) = request_info
if library_id:
library_name = db.get_library_name(library_id)
else:
library_name = '-'
out += """
<input type=hidden name=ill_status value="%s">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><b>%s</b></td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s %s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (ill_status, _("Status"), ill_status,
_("ILL request ID"), ill_request_id,
_("Library/Supplier"), library_name,
_("Request date"), request_date,
_("Expected date"),expected_date,
_("Cost"), cost, currency,
_("Barcode"), barcode,
_("Previous notes"))
key_array = previous_library_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2',
{'delete_key': key, 'ill_request_id': ill_request_id, 'ill_status': ill_status,
'library_notes': library_notes, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, previous_library_notes[key], delete_note)
out += """
</table>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><i>%s</i></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"), library_notes or '-')
elif ill_status == 'item received, due date defined':
(library_id, request_date, expected_date, previous_library_notes) = ill_req_details
if not previous_library_notes:
previous_library_notes = {}
else:
previous_library_notes = eval(previous_library_notes)
(ill_request_id, arrival_date, due_date, cost, currency, barcode, library_notes) = request_info
if library_id:
library_name = db.get_library_name(library_id)
else:
library_name = '-'
out += """
<input type=hidden name=ill_status value="%s">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><b>%s</b></td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s %s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (ill_status, _("Status"), ill_status,
_("ILL request ID"), ill_request_id,
_("Library/Supplier"), library_name,
_("Request date"), request_date,
_("Expected date"), expected_date,
_("Arrival date"), arrival_date,
_("Due date"), due_date,
_("Cost"), cost, currency,
_("Barcode"), barcode,
_("Previous notes"))
key_array = previous_library_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2',
{'delete_key': key, 'ill_request_id': ill_request_id, 'ill_status': ill_status,
'library_notes': library_notes, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, previous_library_notes[key], delete_note)
out += """
</table>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><i>%s</i></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"), library_notes or '-')
elif ill_status == 'item returned':
(library_id, request_date, expected_date, arrival_date, due_date, barcode, previous_library_notes) = ill_req_details
previous_library_notes = eval(previous_library_notes)
(ill_request_id, return_date, cost, currency, library_notes) = request_info
library_name = db.get_library_name(library_id)
out += """
<input type=hidden name=ill_status value="%s">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><b>%s</b></td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s %s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (ill_status, _("Status"), ill_status,
_("ILL request ID"), ill_request_id,
_("Library/Supplier"), library_name,
_("Request date"), request_date,
_("Expected date"), expected_date,
_("Arrival date"), arrival_date,
_("Due date"), due_date,
_("Return date"), return_date,
_("Cost"), cost, currency,
_("Barcode"), barcode,
_("Previous notes"))
key_array = previous_library_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2',
{'delete_key': key, 'ill_request_id': ill_request_id, 'ill_status': ill_status,
'library_notes': library_notes, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, previous_library_notes[key], delete_note)
out += """
</table>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><i>%s</i></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"), library_notes or '-')
else:
(library_id, request_date, expected_date, arrival_date, due_date, return_date, cost, barcode, previous_library_notes) = ill_req_details
previous_library_notes = eval(previous_library_notes)
(value, currency) = cost.split()
(ill_request_id, library_notes) = request_info
library_name = db.get_library_name(library_id)
out += """
<input type=hidden name=ill_status value="%s">
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><b>%s</b></td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s %s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes"> """ % (ill_status, _("Status"), ill_status,
_("ILL request ID"), ill_request_id,
_("Library/Supplier"), library_name,
_("Request date"), request_date,
_("Expected date"), expected_date,
_("Arrival date"), arrival_date,
_("Due date"), due_date,
_("Return date"), return_date,
_("Cost"), value, currency,
_("Barcode"), barcode,
_("Previous notes"))
key_array = previous_library_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/ill_request_details_step2',
{'delete_key': key, 'ill_request_id': ill_request_id, 'ill_status': ill_status,
'library_notes': library_notes, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, previous_library_notes[key], delete_note)
out += """
</table>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent"><i>%s</i></td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Library notes"), library_notes or '-')
out += """
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</div>
</form>
<br />
<br />
""" % (_("Back"), _("Continue"))
return out
def tmpl_ill_request_details_step3(self, ln=CFG_SITE_LANG):
"""
Last step of the request procedure.
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="30">%s</td>
</tr>
</table>
<br />
<br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value='%s' class='formbutton'></td>
</table>
<br />
<br />
</div>
""" % (_("An ILL request has been updated with success."),
CFG_SITE_URL, _("Back to home"))
return out
def tmpl_ordered_book_details_step1(self, order_details, list_of_vendors, ln=CFG_SITE_LANG):
"""
@param order_details: informations about a given order.
@type order_details: tuple
@param list_of_vendors: list with all the vendors
@type list_of_vendors: list
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
(purchase_id, recid, vendor, order_date, expected_date, price, status, notes) = order_details
purchase_notes = eval(notes)
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
(value, currency) = price.split()
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="update_item_info_step4_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/ordered_books_details_step2" method="get" >
<div class="bibcircbottom">
<input type=hidden name=purchase_id value="%s">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400"><input type=hidden name=recid value='%s'>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent"><img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/></td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
purchase_id,
_("Item details"),
recid,
_("Name"),
book_title,
_("Author(s)"),
book_author,
_("Year"),
book_year,
_("Publisher"),
book_editor,
_("ISBN"),
book_isbn,
str(book_cover))
out += """
<script type="text/javascript" language='JavaScript' src="%s/js/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/ui.datepicker.min.js"></script>
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesortermedium" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">
<select name="vendor_id" style='border: 1px solid #cfcfcf'>
""" % (CFG_SITE_URL, CFG_SITE_URL, _("Order details"), _("Vendor"))
for(vendor_id, name) in list_of_vendors:
if vendor_id == vendor:
out +="""<option value="%s" selected>%s</option>""" % (vendor_id, name)
else:
out +="""<option value="%s">%s</option>""" % (vendor_id, name)
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">
<input type="text" size="12" name="cost" value="%s" style='border: 1px solid #cfcfcf'>
<select name="currency" style='border: 1px solid #cfcfcf'>
""" % (_("Cost"), value,)
if currency == 'EUR':
out += """
<option value="EUR" selected>EUR</option>
<option value="CHF">CHF</option>
<option value="USD">USD</option> """
elif currency == 'CHF':
out += """
<option value="EUR">EUR</option>
<option value="CHF" selected>CHF</option>
<option value="USD">USD</option> """
else:
out +="""
<option value="EUR">EUR</option>
<option value="CHF">CHF</option>
<option value="USD" selected>USD</option> """
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">
<select name="status" style='border: 1px solid #cfcfcf'>
""" % (_("Status"))
if status == 'ordered':
out += """
<option value ="ordered" selected>ordered</option>
<option value ="cancelled">cancelled</option>
<option value ="not arrived">not arrived</option>
"""
elif status == 'cancelled':
out += """
<option value ="ordered">ordered</option>
<option value ="cancelled" selected>cancelled</option>
<option value ="not arrived">not arrived</option>
"""
else:
out += """
<option value ="ordered">ordered</option>
<option value ="cancelled">cancelled</option>
<option value ="not arrived" selected>not arrived</option>
"""
out += """
</select>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker1" name="order_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td class="bibcirccontent">
<script type="text/javascript">
$(function() {
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker2" name="expected_date" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100" valign="top">%s</th>
<td>
<table class="bibcircnotes">
""" % (_("Order date"), CFG_SITE_URL, order_date,
_("Expected date"), CFG_SITE_URL, expected_date,
_("Previous notes"))
key_array = purchase_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/ordered_books_details_step1',
{'delete_key': key, 'purchase_id': purchase_id, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, purchase_notes[key], delete_note)
out += """
</table></td>
</tr>
<input type=hidden name="purchase_notes" value="%s">
<tr>
<th width="100" valign="top">%s</th>
<td class="bibcirccontent">
<textarea name="library_notes" rows="5" cols="90" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (purchase_notes, _("Notes"), _("Back"), _("Continue"))
return out
def tmpl_ordered_book_details_step2(self, order_details, ln=CFG_SITE_LANG):
"""
@param order_details: informations about a given order.
@type order_details: tuple
@param ln: language of the page
"""
(purchase_id, recid, vendor_id, cost, currency, status,
order_date, expected_date, purchase_notes, library_notes) = order_details
vendor_name = db.get_vendor_name(vendor_id)
(book_title, book_year, book_author, book_isbn, book_editor) = book_information_from_MARC(int(recid))
if book_isbn:
book_cover = get_book_cover(book_isbn)
else:
book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """
<div class="bibcircbottom">
<form name="step2_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/ordered_books_details_step3" method="get" >
<input type=hidden name=purchase_id value="%s">
<input type=hidden name=recid value="%s">
<input type=hidden name=vendor_id value="%s">
<input type=hidden name=cost value="%s">
<input type=hidden name=currency value="%s">
<input type=hidden name=status value="%s">
<input type=hidden name=order_date value="%s">
<input type=hidden name=expected_date value="%s">
<input type=hidden name=purchase_notes value="%s">
<input type=hidden name=library_notes value="%s">
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr valign='top'>
<td width="400">
<table>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
</table>
</td>
<td class="bibcirccontent">
<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/>
</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
purchase_id, recid,
vendor_id, cost,
currency, status,
order_date,
expected_date,
purchase_notes,
library_notes,
_("Item details"),
_("Name"), book_title,
_("Author(s)"), book_author,
_("Year"), book_year,
_("Publisher"), book_editor,
_("ISBN"), book_isbn,
str(book_cover))
out += """
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s %s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<td width="100" valign="top">%s</td>
<td><table class="bibcircnotes">
""" % (_("Order details"),
_("Vendor"), vendor_name,
_("Price"), cost, currency,
_("Status"), status,
_("Order date"), order_date,
_("Expected date"), expected_date,
_("Previous notes"))
purchase_notes = eval(purchase_notes)
key_array = purchase_notes.keys()
key_array.sort()
for key in key_array:
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>[%s]</b></td>
<td width="400"><i>%s</i></td>
</tr>
""" % (key, purchase_notes[key])
out += """
</table>
</td>
</tr>
<tr>
<td width="100">%s</td>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("Notes"), library_notes,
_("Back"), _("Continue"))
return out
def tmpl_ordered_book_details_step3(self, ln=CFG_SITE_LANG):
"""
Last step of the request procedure.
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent" width="30">%s</td>
</tr>
</table>
<br />
<br />
<table class="bibcirctable">
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value='%s' class='formbutton'></td>
</table>
<br />
<br />
</div>
""" % (_("Purchase information updated with success."),
CFG_SITE_URL, _("Back to home"))
return out
def tmpl_add_new_vendor_step1(self, ln=CFG_SITE_LANG):
"""
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<form name="add_new_vendor_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_vendor_step2" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="name">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="email">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="phone">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="address">
</td>
</tr>
<tr>
<th width="70" valign="top">%s</th>
<td class="bibcirccontent">
<textarea name="notes" rows="5" cols="39" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("New vendor information"), _("Name"),
_("Email"), _("Phone"), _("Address"), _("Notes"),
_("Back"), _("Continue"))
return out
def tmpl_add_new_vendor_step2(self, tup_infos, ln=CFG_SITE_LANG):
"""
@param tup_infos: borrower's informations
@type tup_infos: tuple
@param ln: language
"""
_ = gettext_set_language(ln)
(name, email, phone, address, notes) = tup_infos
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<form name="add_new_vendor_step2_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/add_new_vendor_step3" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s onClick="history.go(-1)" class="formbutton">
<input type="submit" value=%s class="formbutton">
<input type=hidden name=tup_infos value="%s">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("New vendor information"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Notes"), notes,
_("Back"), _("Confirm"),
tup_infos)
return out
def tmpl_add_new_vendor_step3(self, ln=CFG_SITE_LANG):
"""
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("A new vendor has been registered."),
_("Back to home"),
CFG_SITE_URL)
return out
def tmpl_update_vendor_info_step1(self, infos, ln=CFG_SITE_LANG):
"""
@param infos: informations
@type infos: list
@param ln: language
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<br /><br /> <br />
<form name="update_vendor_info_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_vendor_info_step2" method="get" >
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s
<input type="radio" name="column" value="name" checked>name
<input type="radio" name="column" value="email">email
<br>
<br>
</td>
</tr>
<tr align="center">
<td><input type="text" size="45" name="string" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
<form>
<br /><br />
<br />
<br />
</div>
""" % (CFG_SITE_URL,
_("Search vendor by"),
_("Back"))
return out
def tmpl_update_vendor_info_step2(self, result, ln=CFG_SITE_LANG):
"""
@param result: search result
@type result: list
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirccontent">
<strong>%s vendor(s) found</strong>
</td>
</tr>
</table>
<br />
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr align="center">
<th>%s</th>
</tr>
""" % (len(result), _("Vendor(s)"))
for (vendor_id, name) in result:
vendor_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/update_vendor_info_step3',
{'vendor_id': vendor_id, 'ln': ln},
(name))
out += """
<tr align="center">
<td class="bibcirccontent" width="70">%s
<input type=hidden name=vendor_id value=%s></td>
</tr>
""" % (vendor_link, vendor_id)
out += """
</table>
<br />
"""
out += """
<table class="bibcirctable">
<tr align="center">
<td><input type=button value=%s
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Back"))
return out
def tmpl_update_vendor_info_step3(self, vendor_info, ln=CFG_SITE_LANG):
"""
@param vendor_infos: information about a given vendor
@type vendor_infos: tuple
"""
_ = gettext_set_language(ln)
(vendor_id, name, address, email, phone, _notes) = vendor_info
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<form name="update_vendor_info_step3_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_vendor_info_step4" method="get" >
<input type=hidden name=vendor_id value=%s>
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="name" value="%s">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="email" value="%s">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="phone" value="%s">
</td>
</tr>
<tr>
<th width="70">%s</th>
<td class="bibcirccontent">
<input type="text" style='border: 1px solid #cfcfcf' size=45 name="address" value="%s">
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, vendor_id, _("Vendor information"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Back"), _("Continue"))
return out
def tmpl_update_vendor_info_step4(self, tup_infos, ln=CFG_SITE_LANG):
"""
@param tup_infos: information about a given vendor
@type tup_infos: tuple
"""
(_vendor_id, name, email, phone, address) = tup_infos
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<form name="update_vendor_info_step4_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/update_vendor_info_step5" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="70">%s</th> <td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
<input type=hidden name=tup_infos value="%s">
</td>
</tr>
</table>
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL, _("Vendor information"),
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Back"), _("Continue"),
tup_infos)
return out
def tmpl_update_vendor_info_step5(self, ln=CFG_SITE_LANG):
"""
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<div class="bibcircbottom">
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button value=%s
onClick= onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
class="formbutton">
</td>
</tr>
</table>
<br />
<br />
</div>
""" % (_("The information has been updated."),
_("Back to home"),
CFG_SITE_URL)
return out
def tmpl_search_vendor_step1(self, infos, ln=CFG_SITE_LANG):
"""
@param infos: informations
@type infos: list
@param ln: language
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class="bibcircbottom">
<br /><br /> <br />
<form name="search_vendor_step1_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/search_vendor_step2" method="get" >
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s
<input type="radio" name="column" value="name" checked>name
<input type="radio" name="column" value="email">email
<br>
<br>
</td>
</tr>
<tr align="center">
<td><input type="text" size="45" name="string" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<form>
<br />
<br />
<br />
<br />
</div>
""" % (CFG_SITE_URL,
_("Search vendor by"),
_("Back"),
_("Search"))
return out
def tmpl_search_vendor_step2(self, result, ln=CFG_SITE_LANG):
"""
@param result: search result
@type result:list
@param ln: language
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom" align="center">
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirccontent">
<strong>%s vendor(s) found</strong>
</td>
</tr>
</table>
<br />
<table class="tablesortersmall" border="0" cellpadding="0" cellspacing="1">
<tr align="center">
<th>%s</th>
</tr>
""" % (len(result), _("Vendor(s)"))
for (vendor_id, name) in result:
vendor_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_vendor_details',
{'vendor_id': vendor_id, 'ln': ln},
(name))
out += """
<tr align="center">
<td class="bibcirccontent" width="70">%s
<input type=hidden name=library_id value=%s></td>
</tr>
""" % (vendor_link, vendor_id)
out += """
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Back"))
return out
def tmpl_vendor_details(self, vendor_details, ln=CFG_SITE_LANG):
"""
@param vendor_details: details about a given vendor
@type vendor_details: tuple
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """
<div class="bibcircbottom" align="center">
<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
"""
(vendor_id, name, address, email, phone, notes) = vendor_details
no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_vendor_notes',
{'vendor_id': vendor_id},
(_("No notes")))
see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_vendor_notes',
{'vendor_id': vendor_id},
(_("Notes about this vendor")))
if notes == "":
notes_link = no_notes_link
else:
notes_link = see_notes_link
out += """
<table class="bibcirctable">
<tr align="center">
<td width="80" class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
<tr>
<th width="80">%s</th>
<td class="bibcirccontent">%s</td>
</tr>
</table>
<table>
<tr>
<td><input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/update_vendor_info_step3?vendor_id=%s'" onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value=%s class="bibcircbutton">
</td>
</tr>
</table>
""" % (_("Vendor details"),
_("Name"), name,
_("Address"), address,
_("Email"), email,
_("Phone"), phone,
_("Notes"), notes_link,
CFG_SITE_URL, vendor_id, _("Update"))
out += """
</table>
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td><input type=button value='%s'
onClick="history.go(-1)" class="formbutton"></td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Back"))
return out
def tmpl_vendor_notes(self, vendor_notes, vendor_id, add_notes,
ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out +="""
<div class="bibcircbottom">
<form name="vendor_notes" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_vendor_notes" method="get" >
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
""" % (CFG_SITE_URL,
_("Notes about this vendor"))
notes = vendor_notes.split('\n')
for values in notes:
out += """ <tr>
<td class="bibcirccontent">%s</td>
</tr>
""" % (values)
if add_notes:
out += """
<tr>
<td><textarea name='new_note' rows="10" cols="60" style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
<tr>
<td>
<input type='submit' name='confirm_note' value='%s' class='formbutton'>
<input type=hidden name=vendor_id value=%s>
</td>
</tr>
</table>
""" % (_("Confirm"), vendor_id)
else:
out += """
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
<tr>
<td>
<input type='submit' name='add_notes' value='%s' class='formbutton'>
<input type=hidden name=vendor_id value=%s>
</td>
</tr>
</table>
""" % (_("Add notes"), vendor_id)
out += """
<br />
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_vendor_details?vendor_id=%s'"
value=%s class='formbutton'>
</td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (CFG_SITE_URL,
vendor_id,
_("Back"))
return out
def tmpl_register_ill_request_with_no_recid_step1(self, infos, ln=CFG_SITE_LANG):
"""
@param infos: informations
@type infos: list
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<br />
<br />
<div class="bibcircbottom" align="center">
<div class="infoboxmsg"><strong>%s<br />%s</strong></div>
<br />
<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="display_ill_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_with_no_recid_step2" method="get">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="title" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="authors" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="place" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="publisher" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="year" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="edition" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="isbn" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
""" % (_("Book does not exists on Invenio."),_("Please fill the following form."),
CFG_SITE_URL,
_("Item details"),
_("Book title"),
_("Author(s)"),
_("Place"),
_("Publisher"),
_("Year"),
_("Edition"),
_("ISBN"))
conditions_link = """<a href="http://library.web.cern.ch/library/Library/ill_faq.html" target="_blank">conditions</a>"""
out += """
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th valign="center" width="100">%s</th>
<td valign="center" width="250">
<script type="text/javascript">
$(function() {
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker1" name="period_of_interest_from" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
"""% (CFG_SITE_URL, CFG_SITE_URL,
_("ILL request details"),
_("Period of interest (From)"),
CFG_SITE_URL,
datetime.date.today().strftime('%Y-%m-%d'))
out += """
<tr>
<th valign="top" width="100">%s</th>
<td width="250">
<script type="text/javascript">
$(function() {
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="10" id="date_picker2" name="period_of_interest_to" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th valign="top" width="100">%s</th>
<td width="250"><textarea name='additional_comments' rows="6" cols="34"
style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<table class="bibcirctable">
<!--<tr>
<td>
<input name="conditions" type="checkbox" value="accepted" />%s</td>
</tr> -->
<tr align="center">
<td>
<input name="only_edition" type="checkbox" value="Yes" />%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("Period of interest (To)"), CFG_SITE_URL, (datetime.date.today() + datetime.timedelta(days=365)).strftime('%Y-%m-%d'),
_("Additional comments"),
_("Borrower accepts the %s of the service in particular the return of books in due time." % (conditions_link)),
_("Borrower wants this edition only."),
_("Back"), _("Continue"))
return out
def tmpl_register_ill_request_with_no_recid_step2(self, book_info, request_details,
result, key, string, infos, ln):
"""
@param book_info: book's informations
@type book_info: tuple
@param request_details: details about a given request
@type request_details: tuple
@param result: borrower's informations
@type result: list
@param key: field (name, email, etc...)
@param key: string
@param string: pattern
@type string: string
@param infos: informations
@type infos: list
"""
(title, authors, place, publisher, year, edition, isbn) = book_info
(period_of_interest_from, period_of_interest_to,
additional_comments, only_edition)= request_details
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
#if isbn:
# book_cover = get_book_cover(isbn)
#else:
# book_cover = "%s/img/book_cover_placeholder.gif" % (CFG_SITE_URL)
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_with_no_recid_step2" method="get" >
<br />
<table class="bibcirctable">
<tr>
<td width="500" valign='top'>
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=title value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=authors value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=place value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=publisher value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=year value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=edition value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=isbn value="%s">
</tr>
</table>
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=period_of_interest_from value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=period_of_interest_to value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=additional_comments value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=only_edition value="%s">
</tr>
</table>
</td>
<td width="200" align='center' valign='top'>
<table>
<tr>
<td>
</td>
</tr>
</table>
</td>
""" % (CFG_SITE_URL,
_("Item details"),
_("Name"), title, title,
_("Author(s)"), authors, authors,
_("Place"), place, place,
_("Year"), year, year,
_("Publisher"), publisher, publisher,
_("Edition"), edition, edition,
_("ISBN"), isbn, isbn,
_("ILL request details"),
_("Period of interest - From"), period_of_interest_from, period_of_interest_from,
_("Period of interest - To"), period_of_interest_to, period_of_interest_to,
_("Additional comments"), additional_comments, additional_comments,
_("Only this edition."), only_edition, only_edition)
#<img style='border: 1px solid #cfcfcf' src="%s" alt="Book Cover"/> ,
# str(book_cover)
out += """
<td valign='top' align='center'>
<table>
"""
if CFG_CERN_SITE == 1:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search user by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'name':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="ccid" checked>ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search borrower by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'id':
out += """
<input type="radio" name="key" value="id" checked>id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
out += """
<br><br>
</td>
</tr>
<tr>
<td align="center">
<input type="text" size="40" id="string" name="string" value='%s' style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<td align="center">
<br>
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
</form>
""" % (string or '')
if result:
out += """
<br />
<form name="step1_form2" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_with_no_recid_step3" method="get" >
<input type=hidden name=book_info value="%s">
<table class="bibcirctable">
<tr width="200">
<td align="center">
<select name="user_info" size="8" style='border: 1px solid #cfcfcf; width:80%%'>
""" % (CFG_SITE_URL, book_info)
for (ccid, name, email, phone, address, mailbox) in result:
out += """
<option value ='%s,%s,%s,%s,%s,%s'>%s
""" % (ccid, name, email, phone, address, mailbox, name)
out += """
</select>
</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td align="center">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<input type=hidden name=request_details value="%s">
</form>
""" % (_("Select user"), request_details)
out += """
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
"""
return out
def tmpl_register_ill_request_with_no_recid_step3(self, book_info, user_info, request_details, ln):
"""
@param book_info: book's informations
@type book_info: tuple
@param user_info: user's informations
@type user_info: tuple
@param request_details: details about a given request
@type request_details: tuple
"""
(title, authors, place, publisher, year, edition, isbn) = book_info
(_borrower_id, name, email, phone, address, mailbox) = user_info
(period_of_interest_from, period_of_interest_to,
additional_comments, only_edition)= request_details
_ = gettext_set_language(ln)
out = _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<form name="step3_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_with_no_recid_step4" method="get" >
<br />
<table class="bibcirctable">
<tr>
<td width="200" valign='top'>
<input type=hidden name=book_info value="%s">
<input type=hidden name=request_details value="%s">
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
<td width="50" valign='top'>
<table>
<tr>
<td>
</td>
</tr>
</table>
</td>
""" % (CFG_SITE_URL, book_info, request_details,
_("Item details"),
_("Name"), title,
_("Author(s)"), authors,
_("Place"), place,
_("Year"), year,
_("Publisher"), publisher,
_("Edition"), edition,
_("ISBN"), isbn,
_("ILL request details"),
_("Period of interest (From)"), period_of_interest_from,
_("Period of interest (To)"), period_of_interest_to,
_("Additional comments"), additional_comments,
_("Only this edition"), only_edition)
out += """
<td width="200" valign='top'>
<table>
<tr align="center">
<td class="bibcirctableheader">%s</td>
<input type=hidden name=user_info value="%s">
</tr>
</table>
<table class="tablesorter" width="200" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
</td>
</tr>
</table>
""" % (_("Borrower details"), user_info,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox)
#out += """
#<style type="text/css"> @import url("/img/tablesorter.css"); </style>
#<div class=bibcircbottom align="center">
# <form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_with_no_recid_step4" method="get" >
# <br />
# <table class="bibcirctable">
# <tr>
# <td width="500" valign='top'>
# <table>
# <tr align="center">
# <td class="bibcirctableheader">%s</td>
# <input type=hidden name=book_info value="%s">
# </tr>
# </table>
# <table class="tablesorterborrower" width:100%% border="0" cellpadding="0" cellspacing="1">
# <tr width:100%%>
# <th>%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="300">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="300">%s</th>
# <td width="300">%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# </table>
# """ %(CFG_SITE_URL,
# _("Item details"), book_info,
# _("Name"), title,
# _("Author(s)"), authors,
# _("Place"), place,
# _("Year"), year,
# _("Publisher"), publisher,
# _("Edition"), edition,
# _("ISBN"), isbn,)
#out += """
# <table>
# <tr align="center">
# <td class="bibcirctableheader">%s</td>
# <input type=hidden name=request_details value="%s">
# </tr>
# </table>
# <table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# </table>
# </td>
# <td width="200" align='center' valign='top'>
# <table>
# <tr>
# <td>
#
# </td>
# </tr>
# </table>
# </td>
# <td valign='top' align='center'>
#
# <table>
# <tr align="center">
# <td class="bibcirctableheader">%s</td>
# <input type=hidden name=user_info value="%s">
# </tr>
# </table>
# <table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# <tr>
# <th width="100">%s</th>
# <td>%s</td>
# </tr>
# </table>
#
# </td>
# </tr>
# </table>
# """ % (
# _("ILL request details"), request_details,
# _("Period of interest - From"), period_of_interest_from,
# _("Period of interest - To"), period_of_interest_to,
# _("Additional comments"), additional_comments,
# _("Only this edition"), only_edition,
# _("Borrower details"), user_info,
# _("Name"), name,
# _("Email"), email,
# _("Phone"), phone,
# _("Address"), address,
# _("Mailbox"), mailbox)
out += """<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>""" % (_("Back"), _("Continue"))
return out
def tmpl_borrower_ill_details(self, result, borrower_id,
ill_id, ln=CFG_SITE_LANG):
"""
@param result: ILL request's informations:
@type result: list
@param borrower_id: identify the borrower. Primary key of crcBORROWER.
@type borrower_id: int
@param ill_id: identify the ILL request. Primray key of crcILLREQUEST
@type ill_id: int
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """
"""
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<script src="/js/jquery/jquery.js" type="text/javascript"></script>
<script src="/js/jquery/jquery.tablesorter.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#table_ill').tablesorter({widthFixed: true, widgets: ['zebra']})
});
</script>
<div class="bibcircbottom">
<br />
<table id="table_ill" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (_("Item"),
_("Supplier"),
_("Request date"),
_("Expected date"),
_("Arrival date"),
_("Due date"),
_("Status"),
_("Library notes"))
for (ill_id, book_info, supplier_id, request_date,
expected_date, arrival_date, due_date, status,
library_notes) in result:
#get supplier name
if supplier_id:
library_name = db.get_library_name(supplier_id)
library_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_library_details',
{'library_id': supplier_id, 'ln': ln},
(library_name))
else:
library_link = '-'
#get book title
book_info = eval(book_info)
try:
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': book_info['recid'], 'ln': ln},
(book_title_from_MARC(int(book_info['recid']))))
except KeyError:
title_link = book_info['title']
# links to notes pages
lib_no_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_ill_library_notes',
{'ill_id': ill_id},
(_("No notes")))
lib_see_notes_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_ill_library_notes',
{'ill_id': ill_id},
(_("Notes about this ILL")))
if library_notes == "":
notes_link = lib_no_notes_link
else:
notes_link = lib_see_notes_link
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (title_link, library_link, request_date,
expected_date, arrival_date, due_date, status,
notes_link)
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details?borrower_id=%s'"
value='%s' class='formbutton'>
</td>
</tr>
</table>
<br />
</div>
""" % (CFG_SITE_URL,
borrower_id,
_("Back"))
return out
def tmpl_ill_notes(self, ill_notes, ill_id, ln=CFG_SITE_LANG):
"""
@param ill_notes: notes about an ILL request
@type ill_notes: dictionnary
@param ill_id: identify the ILL request. Primray key of crcILLREQUEST
@type ill_id: int
"""
_ = gettext_set_language(ln)
if not ill_notes:
ill_notes = {}
else:
ill_notes = eval(ill_notes)
out = """ """
out += _MENU_
out +="""
<div class="bibcircbottom">
<form name="borrower_notes" action="%s/admin/bibcirculation/bibcirculationadmin.py/get_ill_library_notes" method="get" >
<input type=hidden name=ill_id value='%s'>
<br />
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td>
<table class="bibcircnotes">
""" % (CFG_SITE_URL, ill_id,
_("Notes about acquisition"))
key_array = ill_notes.keys()
key_array.sort()
for key in key_array:
delete_note = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_ill_library_notes',
{'delete_key': key, 'ill_id': ill_id, 'ln': ln},
(_("[delete]")))
out += """<tr class="bibcirccontent">
<td class="bibcircnotes" width="160" valign="top" align="center"><b>%s</b></td>
<td width="400"><i>%s</i></td>
<td width="65" align="center">%s</td>
</tr>
""" % (key, ill_notes[key], delete_note)
out += """
</table>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td class="bibcirccontent">
<textarea name="library_notes" rows="5" cols="90" style='border: 1px solid #cfcfcf'></textarea>
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/ordered_books'"
value=%s class='formbutton'>
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Write new note"),
CFG_SITE_URL,
_("Back"),
_("Confirm"))
return out
def tmpl_get_expired_loans_with_requests(self, result, ln=CFG_SITE_LANG):
"""
@param result: loans' informations:
@param result: list
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom">
<br /> <br /> <br /> <br />
<table class="bibcirctable_contents">
<td class="bibcirccontent" align="center">%s</td>
</table>
<br /> <br /> <br />
<table class="bibcirctable_contents">
<td align="center">
<input type=button onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/loan_on_desk_step1'"
value='%s' class='formbutton'>
</td>
</table>
<br />
</div>
""" % (_("No more requests are pending or waiting."),
CFG_SITE_URL,
_("Back to home"))
else:
out += """
<style type="text/css"> @import url("/js/jquery/tablesorter/themes/blue/style.css"); </style>
<style type="text/css"> @import url("/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.css"); </style>
<script src="/js/jquery/jquery.min.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/jquery.tablesorter.js" type="text/javascript"></script>
<script src="/js/jquery/tablesorter/addons/pager/jquery.tablesorter.pager.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#table_requests")
.tablesorter({sortList: [[4,0], [0,0]],widthFixed: true, widgets: ['zebra']})
.bind("sortStart",function(){$("#overlay").show();})
.bind("sortEnd",function(){$("#overlay").hide()})
.tablesorterPager({container: $("#pager"), positionFixed: false});
});
</script>
<div class="bibcircbottom">
<br />
<br />
<table id="table_requests" class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
"""% (_("Name"),
_("Item"),
_('Library'),
_("Location"),
_("From"),
_("To"),
_("Request date"),
_("Actions"))
for (request_id, recid, borrower_id, library_id, location, date_from, date_to, request_date) in result:
borrower_name = db.get_borrower_name(borrower_id)
library_name = db.get_library_name(library_id)
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
if borrower_name:
borrower_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_borrower_details',
{'borrower_id': borrower_id, 'ln': ln},
(borrower_name))
else:
borrower_link = str(borrower_id)
out += """
<script type="text/javascript">
function confirmation(id){
var answer = confirm("Delete this request?")
if (answer){
window.location = "%s/admin/bibcirculation/bibcirculationadmin.py/get_expired_loans_with_requests?request_id="+id;
}
else{
alert("Request not deleted.")
}
}
</script>
<tr>
<td width='150'>%s</td>
<td width='250'>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td algin='center'>
<input type="button" value='%s' style="background: url(/img/jquery/dialog-cancel.png)
no-repeat; width: 75px; text-align: right;"
onClick="confirmation(%s)"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
class="bibcircbutton">
<input type=button style="background: url(/img/dialog-yes.png) no-repeat; width: 150px; text-align: right;"
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/associate_barcode?request_id=%s&recid=%s&borrower_id=%s'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
""" % (CFG_SITE_URL,
borrower_link,
title_link,
library_name,
location,
date_from,
date_to,
request_date,
_("Delete"),
request_id,
CFG_SITE_URL,
request_id,
recid,
borrower_id,
_("Associate barcode"))
out += """
</tbody>
</table>
<div id="pager" class="pager">
<form>
<br />
<img src="/img/sb.gif" class="first" />
<img src="/img/sp.gif" class="prev" />
<input type="text" class="pagedisplay" />
<img src="/img/sn.gif" class="next" />
<img src="/img/se.gif" class="last" />
<select class="pagesize">
<option value="10" selected="selected">10</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="40">40</option>
</select>
</form>
</div>
<br />
<table class="bibcirctable">
<tr>
<td>
<input type=button style="background: url(/img/document-print.png) no-repeat; width: 135px; text-align: right;"
onClick="location.href='%s/admin/bibcirculation/bibcirculationadmin.py/get_pending_requests?print_data=true'"
onmouseover="this.className='bibcircbuttonover'" onmouseout="this.className='bibcircbutton'"
value='%s' class="bibcircbutton">
</td>
</tr>
</table>
<br />
</div>
""" % (CFG_SITE_URL,
_("Printable format"))
return out
def tmpl_register_ill_book_request(self, infos, ln=CFG_SITE_LANG):
"""
@param infos: informations
@type infos: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class=bibcircbottom align="center">
<form name="search_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_book_request_result"
method="get" >
<br />
<br />
<div class="infoboxmsg"><strong>%s</strong></div>
<br />
<input type=hidden name=start value="0">
<input type=hidden name=end value="10">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">Search item by
<input type="radio" name="f" value="" checked>any field
<input type="radio" name="f" value="barcode">barcode
<input type="radio" name="f" value="author">author
<input type="radio" name="f" value="title">title
<br />
<br />
</td>
</tr>
<tr align="center">
<td><input type="text" size="50" name="p" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
</form>
""" % (CFG_SITE_URL, _("Check if the book already exists on Invenio,"\
+ " before to send your ILL request."),
_("Back"), _("Search"))
return out
def tmpl_register_ill_book_request_result(self, result, ln=CFG_SITE_LANG):
"""
@param result: book's information
@type result: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom" align="center">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("0 items found."))
else:
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<form name="search_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_with_no_recid_step1" method="get" >
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<strong>%s item(s) found</strong>
</td>
</tr>
</table>
<br />
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (CFG_SITE_URL,len(result), _("Title"),
_("Author"), _("Publisher"),
_("No. Copies"))
for recid in result:
(book_author, book_editor, book_copies) = get_item_info_for_search_result(recid)
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (title_link, book_author,
book_editor, book_copies)
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</form>
</div>
""" % (_("Back"), _("Proceed anyway"))
return out
def tmpl_register_ill_book_request_from_borrower_page(self, infos, borrower_id, ln=CFG_SITE_LANG):
"""
@param infos: informations
@type infos: list
@param borrower_id: identify the borrower. Primary key of crcBORROWER.
@type borrower_id: int
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<div class=bibcircbottom align="center">
<form name="search_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_book_request_from_borrower_page_result"
method="get" >
<input type=hidden name=borrower_id value=%s>
<br />
<br />
<div class="infoboxmsg"><strong>%s</strong></div>
<br />
<input type=hidden name=start value="0">
<input type=hidden name=end value="10">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">Search item by
<input type="radio" name="f" value="" checked>any field
<input type="radio" name="f" value="barcode">barcode
<input type="radio" name="f" value="author">author
<input type="radio" name="f" value="title">title
<br />
<br />
</td>
</tr>
<tr align="center">
<td><input type="text" size="50" name="p" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
<form>
""" % (CFG_SITE_URL, borrower_id,
_("Check if the book already exists on Invenio,"\
" before to send your ILL request."),
_("Back"), _("Search"))
return out
def tmpl_register_ill_book_request_from_borrower_page_result(self, result, ln=CFG_SITE_LANG):
"""
@param result: book's information
@type result: list
@param borrower_id: identify the borrower. Primary key of crcBORROWER.
@type borrower_id: int
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
if len(result) == 0:
out += """
<div class="bibcircbottom" align="center">
<br />
<div class="infoboxmsg">%s</div>
<br />
""" % (_("0 items found."))
else:
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<strong>%s item(s) found</strong>
</td>
</tr>
</table>
<br />
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
</thead>
<tbody>
""" % (len(result), _("Title"),
_("Author"), _("Publisher"),
_("No. Copies"))
for recid in result:
(book_author, book_editor, book_copies) = get_item_info_for_search_result(recid)
title_link = create_html_link(CFG_SITE_URL +
'/admin/bibcirculation/bibcirculationadmin.py/get_item_details',
{'recid': recid, 'ln': ln},
(book_title_from_MARC(recid)))
out += """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
""" % (title_link, book_author,
book_editor, book_copies)
out += """
</tbody>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
</div>
""" % (_("Back"))
return out
def tmpl_register_ill_request_from_borrower_page_step1(self, infos, borrower_id, ln=CFG_SITE_LANG):
"""
@param infos: informations
@type infos: list
@param borrower_id: identify the borrower. Primary key of crcBORROWER.
@type borrower_id: int
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<br />
<br />
<div class="bibcircbottom" align="center">
<div class="infoboxmsg"><strong>%s</strong></div>
<br />
<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="display_ill_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_from_borrower_page_step2" method="get">
<input type=hidden name=borrower_id value=%s>
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="title" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="authors" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="place" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="publisher" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="year" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="edition" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="isbn" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
""" % (_("Book does not exists on Invenio. Please fill the following form."),
CFG_SITE_URL, borrower_id,
_("Item details"),
_("Book title"),
_("Author(s)"),
_("Place"),
_("Publisher"),
_("Year"),
_("Edition"),
_("ISBN"))
conditions_link = """<a href="http://library.web.cern.ch/library/Library/ill_faq.html" target="_blank">conditions</a>"""
out += """
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar.js"></script>
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar-setup.js"></script>
<script type="text/javascript" language='JavaScript' src="/jsCalendar/calendar-en.js"></script>
<style type="text/css"> @import url("/jsCalendar/calendar-blue.css"); </style>
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="150">%s</th>
<td>
<input type="text" size="12" id="%s" name="period_of_interest_from" value="" style='border: 1px solid #cfcfcf'>
<img src="/jsCalendar/jsCalendar.gif" alt="select period of interest" id="%s"
onmouseover="this.style.background='red';" onmouseout="this.style.background=''"
>
<script type="text/javascript" language='JavaScript'>
Calendar.setup({
inputField : '%s',
ifFormat : '%%Y-%%m-%%d',
button : '%s'
});
</script>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td>
<input type="text" size="12" id="%s" name="period_of_interest_to" value="" style='border: 1px solid #cfcfcf'>
<img src="/jsCalendar/jsCalendar.gif" alt="select period of interest" id="%s"
onmouseover="this.style.background='red';" onmouseout="this.style.background=''"
>
<script type="text/javascript" language='JavaScript'>
Calendar.setup({
inputField : '%s',
ifFormat : '%%Y-%%m-%%d',
button : '%s'
});
</script>
</td>
</tr>
<tr>
<th valign="top" width="150">%s</th>
<td><textarea name='additional_comments' rows="6" cols="30"
style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<table class="bibcirctable">
<!--<tr>
<td>
<input name="conditions" type="checkbox" value="accepted" />%s</td>
</tr> -->
<tr align="center">
<td>
<input name="only_edition" type="checkbox" value="Yes" />%s</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (_("ILL request details"), _("Period of interest - From"),
_("period_of_interest_from"),
_("jsCal1"), _("period_of_interest_from"), _("jsCal1"),
_("Period of interest - To"), _("period_of_interest_to"),
_("jsCal2"), _("period_of_interest_to"), _("jsCal2"),
_("Additional comments"),
_("Borrower accepts the %s of the service in particular the return of books in due time." % (conditions_link)),
_("Borrower wants this edition only."),
_("Back"), _("Continue"))
return out
def tmpl_register_ill_request_from_borrower_page_step3(self, book_info, user_info, request_details, ln):
"""
@param book_info: book's informations
@type book_info: tuple
@param user_info: user's informations
@type user_info: tuple
@param request_details: details about a given request
@type request_details: tuple
"""
(title, authors, place, publisher, year, edition, isbn) = book_info
(_borrower_id, name, email, phone, address, mailbox) = user_info
(period_of_interest_from, period_of_interest_to,
additional_comments, only_edition)= request_details
_ = gettext_set_language(ln)
out = """ """
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class=bibcircbottom align="center">
<br />
<br />
<form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_request_from_borrower_page_step4" method="get" >
<table>
<tr align="center">
<td class="bibcirctableheader">%s</td>
<input type=hidden name=book_info value="%s">
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<table>
<tr align="center">
<td class="bibcirctableheader">%s</td>
<input type=hidden name=request_details value="%s">
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
<table>
<tr align="center">
<td class="bibcirctableheader">%s</td>
<input type=hidden name=user_info value="%s">
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td>
</tr>
</table>
""" % (CFG_SITE_URL,
_("Item details"), book_info,
_("Name"), title,
_("Author(s)"), authors,
_("Place"), place,
_("Year"), year,
_("Publisher"), publisher,
_("Edition"), edition,
_("ISBN"), isbn,
_("ILL request details"), request_details,
_("Period of interest - From"), period_of_interest_from,
_("Period of interest - To"), period_of_interest_to,
_("Additional comments"), additional_comments,
_("Only this edition"), only_edition or 'No',
_("Borrower details"), user_info,
_("Name"), name,
_("Email"), email,
_("Phone"), phone,
_("Address"), address,
_("Mailbox"), mailbox)
out += """<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>""" % (_("Back"), _("Continue"))
return out
def tmpl_register_ill_article_request_step1(self, infos, ln=CFG_SITE_LANG):
"""
@param infos: informations
@type infos: list
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<br />
<br />
<div class="bibcircbottom" align="center">
<br />
<br />
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<form name="display_ill_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_article_request_step2" method="get">
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="periodical_title" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="article_title" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="45" name="author" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="report_number" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="volume" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="issue" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="page" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="year" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="budget_code" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="100">%s</th>
<td>
<input type="text" size="30" name="issn" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
<br />
""" % (CFG_SITE_URL,
_("Article details"),
_("Periodical title"),
_("Article title"),
_("Author(s)"),
_("Report number"),
_("Volume"),
_("Issue"),
_("Page"),
_("Year"),
_("Budget code"),
_("ISSN"))
#conditions_link = """<a href="http://library.web.cern.ch/library/Library/ill_faq.html" target="_blank">conditions</a>"""
out += """
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/jquery/jquery.ui.datepicker.min.js"></script>
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorterborrower" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="150">%s</th>
<td>
<script type="text/javascript">
$(function(){
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker1" name="period_of_interest_from" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th width="150">%s</th>
<td>
<script type="text/javascript">
$(function(){
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/jquery/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker2" name="period_of_interest_to" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<th valign="top" width="150">%s</th>
<td><textarea name='additional_comments' rows="6" cols="30"
style='border: 1px solid #cfcfcf'></textarea></td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value=%s
onClick="history.go(-1)" class="formbutton">
<input type="submit"
value=%s class="formbutton">
</td>
</tr>
</table>
</form>
<br />
<br />
</div>
""" % (CFG_SITE_URL,CFG_SITE_URL,_("ILL request details"),
_("Period of interest - From"), CFG_SITE_URL, datetime.date.today().strftime('%Y-%m-%d'),
_("Period of interest - To"), CFG_SITE_URL, (datetime.date.today() + datetime.timedelta(days=365)).strftime('%Y-%m-%d'),
_("Additional comments"),
_("Back"), _("Continue"))
return out
def tmpl_register_ill_article_request_step2(self, article_info, request_details,
result, key, string, infos, ln):
"""
@param article_info: information about article
@type article_info: tuple
@param request_details: details about a given ILL request
@type request_details: tuple
@param result: result with borrower's information
@param result: list
@param key: field (name, email, etc...)
@param key: string
@param string: pattern
@type string: string
@param infos: informations
@type infos: list
"""
(periodical_title, article_title, author, report_number,
volume, issue, page, year, issn) = article_info
(period_of_interest_from, period_of_interest_to,
additional_comments)= request_details
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
out += """
<style type="text/css"> @import url("/img/tablesorter.css"); </style>
<div class="bibcircbottom">
<form name="step1_form1" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_article_request_step2" method="get" >
<br />
<table class="bibcirctable">
<tr>
<td class="bibcirctableheader" width="10">%s</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td width="500" valign='top'>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=periodical_title value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=article_title value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=author value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=report_number value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=volume value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=issue value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=page value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=year value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=issn value="%s">
</tr>
</table>
<table>
<tr>
<td class="bibcirctableheader">%s</td>
</tr>
</table>
<table class="tablesorter" border="0" cellpadding="0" cellspacing="1">
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=period_of_interest_from value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=period_of_interest_to value="%s">
</tr>
<tr>
<th width="100">%s</th>
<td>%s</td><input type=hidden name=additional_comments value="%s">
</tr>
</table>
</td>
<td width="200" align='center' valign='top'>
</td>
""" % (CFG_SITE_URL,
_("Item details"),
_("Periodical title"), periodical_title, periodical_title,
_("Article title"), article_title, article_title,
_("Author(s)"), author, author,
_("Report number"), report_number, report_number,
_("Volume"), volume, volume,
_("Issue"), issue, issue,
_("Page"), page, page,
_("Year"), year, year,
_("ISSN"), issn, issn,
_("ILL request details"),
_("Period of interest - From"), period_of_interest_from, period_of_interest_from,
_("Period of interest - To"), period_of_interest_to, period_of_interest_to,
_("Additional comments"), additional_comments, additional_comments)
out += """
<td valign='top' align='center'>
<table>
"""
if CFG_CERN_SITE == 1:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search user by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'name':
out += """
<input type="radio" name="key" value="ccid">ccid
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="ccid" checked>ccid
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<tr>
<td class="bibcirctableheader" align="center">Search borrower by
"""
if key == 'email':
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email" checked>email
"""
elif key == 'id':
out += """
<input type="radio" name="key" value="id" checked>id
<input type="radio" name="key" value="name">name
<input type="radio" name="key" value="email">email
"""
else:
out += """
<input type="radio" name="key" value="id">id
<input type="radio" name="key" value="name" checked>name
<input type="radio" name="key" value="email">email
"""
out += """
<br><br>
</td>
</tr>
<tr>
<td align="center">
<input type="text" size="40" id="string" name="string" value='%s' style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr>
<td align="center">
<br>
<input type="submit" value="Search" class="formbutton">
</td>
</tr>
</table>
</form>
""" % (string or '')
if result:
out += """
<br />
<form name="step1_form2" action="%s/admin/bibcirculation/bibcirculationadmin.py/register_ill_article_request_step3" method="get" >
<input type=hidden name=book_info value="%s">
<table class="bibcirctable">
<tr width="200">
<td align="center">
<select name="user_info" size="8" style='border: 1px solid #cfcfcf; width:40%%'>
""" % (CFG_SITE_URL, article_info)
for (ccid, name, email, phone, address, mailbox) in result:
out += """
<option value ='%s,%s,%s,%s,%s,%s'>%s
""" % (ccid, name, email, phone, address, mailbox, name)
out += """
</select>
</td>
</tr>
</table>
<table class="bibcirctable">
<tr>
<td align="center">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<input type=hidden name=request_details value="%s">
</form>
""" % (_("Select user"), request_details)
out += """
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
"""
return out
def tmpl_ill_search(self, infos, ln=CFG_SITE_LANG):
"""
Display form for ILL search
@param infos: informations
@type infos: list
@param ln: language of the page
"""
_ = gettext_set_language(ln)
out = self.tmpl_infobox(infos, ln)
out += _MENU_
#<input type=hidden name=start value="0">
#<input type=hidden name=end value="10">
out += """
<div class="bibcircbottom">
<link rel=\"stylesheet\" href=\"%s/img/jquery-ui.css\" type=\"text/css\" />
<script type="text/javascript" language='JavaScript' src="%s/js/jquery.min.js"></script>
<script type="text/javascript" language='JavaScript' src="%s/js/ui.datepicker.min.js"></script>
<form name="search_form" action="%s/admin/bibcirculation/bibcirculationadmin.py/ill_search_result" method="get" >
<br />
<br />
<br />
<table class="bibcirctable">
<tr align="center">
<td class="bibcirctableheader">Search ILL request by
<input type="radio" name="f" value="title" checked>title
<input type="radio" name="f" value="ILL_request_ID">ill_request_id
</td>
</tr>
</table>
<br />
<table class="bibcirctable">
<tr align="center" width=10>
<td width=10><input type="text" size="50" name="p" style='border: 1px solid #cfcfcf'></td>
</tr>
</table>
""" % (CFG_SITE_URL,CFG_SITE_URL,CFG_SITE_URL,CFG_SITE_URL)
out += """
<br />
<table align="center">
<tr align="center">
<td class="bibcirctableheader" align="right">date restriction: </td>
<td class="bibcirctableheader" align="right">From</td>
<td align="left">
<script type="text/javascript">
$(function(){
$("#date_picker1").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker1" name="date_from" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
<tr align="center">
<td class="bibcirctableheader" align="right"></td>
<td class="bibcirctableheader" align="right">To</td>
<td align="left">
<script type="text/javascript">
$(function(){
$("#date_picker2").datepicker({dateFormat: 'yy-mm-dd', showOn: 'button', buttonImage: "%s/img/calendar.gif", buttonImageOnly: true});
});
</script>
<input type="text" size="12" id="date_picker2" name="date_to" value="%s" style='border: 1px solid #cfcfcf'>
</td>
</tr>
</table>
""" % (CFG_SITE_URL,_("the beginning"),CFG_SITE_URL,_("now"))
out += """
<br />
<table class="bibcirctable">
<tr align="center">
<td>
<input type=button value='%s'
onClick="history.go(-1)" class="formbutton">
<input type="submit" value='%s' class="formbutton">
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
</div>
<form>
""" % (_("Back"), _("Search"))
return out
diff --git a/modules/bibcirculation/lib/bibcirculation_webinterface.py b/modules/bibcirculation/lib/bibcirculation_webinterface.py
index 11361d1cc..507e16085 100644
--- a/modules/bibcirculation/lib/bibcirculation_webinterface.py
+++ b/modules/bibcirculation/lib/bibcirculation_webinterface.py
@@ -1,749 +1,752 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Bibcirculation web interface """
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
# others invenio imports
from invenio.config import CFG_SITE_LANG, \
CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
- CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS
-
+ CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS, \
+ CFG_SITE_RECORD
from invenio.webuser import getUid, page_not_authorized, isGuestUser, \
collect_user_info
from invenio.webpage import page, pageheaderonly, pagefooteronly
from invenio.search_engine import create_navtrail_links, \
guess_primary_collection_of_a_record, \
get_colID, check_user_can_view_record, \
record_exists
from invenio.urlutils import redirect_to_url, \
make_canonical_urlargd
from invenio.messages import gettext_set_language
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
websearch_templates = invenio.template.load('websearch')
# bibcirculation imports
bibcirculation_templates = invenio.template.load('bibcirculation')
from invenio.bibcirculation import perform_new_request, \
perform_new_request_send, \
perform_get_holdings_information, \
perform_borrower_loans, \
perform_loanshistoricaloverview, \
display_ill_form, \
ill_register_request, \
ill_request_with_recid, \
ill_register_request_with_recid
class WebInterfaceYourLoansPages(WebInterfaceDirectory):
"""Defines the set of /yourloans pages."""
_exports = ['', 'display', 'loanshistoricaloverview']
def index(self, req, form):
""" The function called by default
"""
redirect_to_url(req, "%s/yourloans/display?%s" % (CFG_SITE_URL,
req.args))
def display(self, req, form):
"""
Displays all loans of a given user
@param ln: language
@return the page for inbox
"""
argd = wash_urlargd(form, {'barcode': (str, ""),
'borrower_id': (int, 0),
'request_id': (int, 0)})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourloans/display" % \
(CFG_SITE_URL,),
navmenuid="yourloans")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourloans/display%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_useloans']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use loans."))
body = perform_borrower_loans(uid=uid,
barcode=argd['barcode'],
borrower_id=argd['borrower_id'],
request_id=argd['request_id'],
ln=argd['ln'])
return page(title = _("Your Loans"),
body = body,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
navmenuid = "yourloans",
secure_page_p=1)
def loanshistoricaloverview(self, req, form):
"""
Show loans historical overview.
"""
argd = wash_urlargd(form, {})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourloans/loanshistoricaloverview" % \
(CFG_SITE_URL,),
navmenuid="yourloans")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourloans/loanshistoricaloverview%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_useloans']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use loans."))
body = perform_loanshistoricaloverview(uid=uid,
ln=argd['ln'])
return page(title = _("Loans - historical overview"),
body = body,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
navmenuid = "yourloans",
secure_page_p=1)
class WebInterfaceILLPages(WebInterfaceDirectory):
"""Defines the set of /ill pages."""
_exports = ['', 'display', 'register_request']
def index(self, req, form):
""" The function called by default
"""
redirect_to_url(req, "%s/ill/display?%s" % (CFG_SITE_URL,
req.args))
def display(self, req, form):
"""
Displays all loans of a given user
@param ln: language
@return the page for inbox
"""
argd = wash_urlargd(form, {})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/ill/display" % \
(CFG_SITE_URL,),
navmenuid="ill")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/ill/display%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_useloans']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use ill."))
body = display_ill_form(ln=argd['ln'])
return page(title = _("Interlibrary loan request for books"),
body = body,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
navmenuid = "ill")
def register_request(self, req, form):
"""
Displays all loans of a given user
@param ln: language
@return the page for inbox
"""
argd = wash_urlargd(form, {'ln': (str, ""),
'title': (str, ""),
'authors': (str, ""),
'place': (str, ""),
'publisher': (str, ""),
'year': (str, ""),
'edition': (str, ""),
'isbn': (str, ""),
'period_of_interest_from': (str, ""),
'period_of_interest_to': (str, ""),
'additional_comments': (str, ""),
'conditions': (str, ""),
'only_edition': (str, ""),
})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/ill/register_request" % \
(CFG_SITE_URL,),
navmenuid="ill")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/ill/register_request%s" % (
CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
_ = gettext_set_language(argd['ln'])
user_info = collect_user_info(req)
if not user_info['precached_useloans']:
return page_not_authorized(req, "../", \
text = _("You are not authorized to use ill."))
body = ill_register_request(uid=uid,
title=argd['title'],
authors=argd['authors'],
place=argd['place'],
publisher=argd['publisher'],
year=argd['year'],
edition=argd['edition'],
isbn=argd['isbn'],
period_of_interest_from = argd['period_of_interest_from'],
period_of_interest_to = argd['period_of_interest_to'],
additional_comments = argd['additional_comments'],
conditions = argd['conditions'],
only_edition = argd['only_edition'],
request_type='book',
ln=argd['ln'])
return page(title = _("Interlibrary loan request for books"),
body = body,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
navmenuid = "ill")
class WebInterfaceHoldingsPages(WebInterfaceDirectory):
"""Defines the set of /holdings pages."""
_exports = ['', 'display', 'request', 'send', 'ill_request_with_recid', 'ill_register_request_with_recid']
def __init__(self, recid=-1):
self.recid = recid
def index(self, req, form):
"""
Redirects to display function
"""
return self.display(req, form)
def display(self, req, form):
"""
Show the tab 'holdings'.
"""
argd = wash_urlargd(form, {'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'voted': (int, -1),
'reported': (int, -1),
})
_ = gettext_set_language(argd['ln'])
record_exists_p = record_exists(self.recid)
if record_exists_p != 1:
if record_exists_p == -1:
msg = _("The record has been deleted.")
else:
msg = _("Requested record does not seem to exist.")
msg = '<span class="quicknote">' + msg + '</span>'
title, description, keywords = \
websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])
return page(title = title,
show_title_p = False,
body = msg,
description = description,
keywords = keywords,
uid = getUid(req),
language = argd['ln'],
req = req,
navmenuid='search')
body = perform_get_holdings_information(self.recid, req, argd['ln'])
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
+ '%s/%s/%s/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
- navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
+ navtrail += ' &gt; <a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
metaheaderadd = "<link rel=\"stylesheet\" href=\"%s/img/jquery/jquery-ui.css\" type=\"text/css\" />" % CFG_SITE_URL,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
- # Return the same page wether we ask for /record/123 or /record/123/
+ # Return the same page wether we ask for /CFG_SITE_RECORD/123 or /CFG_SITE_RECORD/123/
__call__ = index
def request(self, req, form):
"""
Show new hold request form.
"""
argd = wash_urlargd(form, {'ln': (str, ""), 'barcode': (str, "")})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
body = perform_new_request(recid=self.recid,
barcode=argd['barcode'],
ln=argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../holdings/request",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
- 'referer' : "%s/record/%s/holdings/request%s" % (
+ 'referer' : "%s/%s/%s/holdings/request%s" % (
CFG_SITE_URL,
+ CFG_SITE_RECORD,
self.recid,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})), norobot=True)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
+ '%s/%s/%s/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
- navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
+ navtrail += ' &gt; <a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
metaheaderadd = "<link rel=\"stylesheet\" href=\"%s/img/jquery/jquery-ui.css\" type=\"text/css\" />" % CFG_SITE_URL,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
def send(self, req, form):
"""
Create a new hold request.
"""
argd = wash_urlargd(form, {'period_from': (str, ""),
'period_to': (str, ""),
'barcode': (str, "")
})
uid = getUid(req)
body = perform_new_request_send(recid=self.recid,
uid=uid,
period_from=argd['period_from'],
period_to=argd['period_to'],
barcode=argd['barcode'])
ln = CFG_SITE_LANG
_ = gettext_set_language(ln)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=ln)
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
+ '%s/%s/%s/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
- navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
+ navtrail += ' &gt; <a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__,
language=argd['ln'], req=req)
def ill_request_with_recid(self, req, form):
"""
Show ILL request form.
"""
argd = wash_urlargd(form, {'ln': (str, "")})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
body = ill_request_with_recid(recid=self.recid,
ln=argd['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../holdings/ill_request_with_recid",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
- 'referer' : "%s/record/%s/holdings/ill_request_with_recid%s" % (
+ 'referer' : "%s/%s/%s/holdings/ill_request_with_recid%s" % (
CFG_SITE_URL,
+ CFG_SITE_RECORD,
self.recid,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
+ '%s/%s/%s/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
- navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
+ navtrail += ' &gt; <a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
metaheaderadd = "<link rel=\"stylesheet\" href=\"%s/img/jquery/jquery-ui.css\" type=\"text/css\" />" % CFG_SITE_URL,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
def ill_register_request_with_recid(self, req, form):
"""
Register ILL request.
"""
argd = wash_urlargd(form, {'ln': (str, ""),
'period_of_interest_from': (str, ""),
'period_of_interest_to': (str, ""),
'additional_comments': (str, ""),
'conditions': (str, ""),
'only_edition': (str, ""),
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
body = ill_register_request_with_recid(recid=self.recid,
uid=uid,
period_of_interest_from = argd['period_of_interest_from'],
period_of_interest_to = argd['period_of_interest_to'],
additional_comments = argd['additional_comments'],
conditions = argd['conditions'],
only_edition = argd['only_edition'],
ln=argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../holdings/ill_request_with_recid",
navmenuid = 'yourbaskets')
if isGuestUser(uid):
if not CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
- 'referer' : "%s/record/%s/holdings/ill_request_with_recid%s" % (
+ 'referer' : "%s/%s/%s/holdings/ill_request_with_recid%s" % (
CFG_SITE_URL,
+ CFG_SITE_RECORD,
self.recid,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
+ '%s/%s/%s/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, tab_id, link_ln), \
tab_id in ['holdings'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, _order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])[0]
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
- navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
+ navtrail += ' &gt; <a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
diff --git a/modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc b/modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc
index 4e3ecaaa3..fc4784106 100644
--- a/modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc
+++ b/modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc
@@ -1,232 +1,232 @@
## -*- mode: html; coding: utf-8; -*-
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibClassify Admin Guide -->
<!-- WebDoc-Page-Navtrail: <a class="navtrail"
href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.1 <a href="#1.1">Thesaurus</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.2 <a href="#1.2">Keyword extraction</a><br />
<strong>2. <a href="#2">Running BibClassify</a></strong><br />
<a name="1"></a><h2>1. Overview</h2>
<p>BibClassify automatically extracts keywords from fulltext documents.
The automatic assignment of keywords to textual documents has clear
benefits in the digital library environment as it aids
catalogization, classification and retrieval of documents.</p>
<a name="1.1"></a><h3>1.1 Thesaurus</h3>
<p> BibClassify performs an extraction of keywords based on the
recurrence of specific terms, taken from a controlled vocabulary. A
controlled vocabulary is a thesaurus of all the terms that are
relevant in a specific context. When a context is defined by a
discipline or branch of knowledge then the vocabulary is said to be a
<em>subject thesaurus</em>. Various existing subject thesauri can be found
<a href="http://www.fbi.fh-koeln.de/institut/labor/Bir/thesauri_new/thesen.htm">here</a>.</p>
<p> A subject thesaurus can be expressed in several different
formats. Different institutions/disciplines have developed different
ways of representing their vocabulary systems. The taxonomy used by
bibclassify is expressed in RDF/SKOS. It allows not only to list keywords but
to specify relations between the keywords and alternative ways to represent the
same keyword.</b><br />
<blockquote>
<pre>
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#scalar"&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.fieldtheoryscalar"/&gt;
&lt;prefLabel xml:lang="en"&gt;scalar&lt;/prefLabel&gt;
&lt;note xml:lang="en"&gt;nostandalone&lt;/note&gt;
&lt;/Concept&gt;
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#fieldtheory"&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.fieldtheoryscalar"/&gt;
&lt;prefLabel xml:lang="en"&gt;field theory&lt;/prefLabel&gt;
&lt;altLabel xml:lang="en"&gt;QFT&lt;/altLabel&gt;
&lt;hiddenLabel xml:lang="en"&gt;/field theor\w*/&lt;/hiddenLabel&gt;
&lt;note xml:lang="en"&gt;nostandalone&lt;/note&gt;
&lt;/Concept&gt;
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#Composite.fieldtheoryscalar"&gt;
&lt;compositeOf rdf:resource="http://cern.ch/thesauri/HEP.rdf#scalar"/&gt;
&lt;compositeOf rdf:resource="http://cern.ch/thesauri/HEP.rdf#fieldtheory"/&gt;
&lt;prefLabel xml:lang="en"&gt;field theory: scalar&lt;/prefLabel&gt;
&lt;altLabel xml:lang="en"&gt;scalar field&lt;/altLabel&gt;
&lt;/Concept&gt;
</pre>
</blockquote>
</li></ul>
In RDF/SKOS, every keyword is wrapped around a <em>concept</em> which
encapsulates the full semantics and hierarchical status of a term - including
synonyms, alternative forms, broader concepts, notes and so on - rather than
just a plain keyword.
<p> The specification of the SKOS language and
<a href="http://www.w3.org/TR/2005/WD-swbp-thesaurus-pubguide-20050517/">various manuals</a>
that aid the building of a semantic thesaurus can be found at the
<a href="http://www.w3.org/TR/2005/WD-swbp-skos-core-guide-20051102/">SKOS W3C website</a>.
Furthermore, BibClassify can function on top of an extended version of SKOS,
which includes special elements such as key chains, composite keywords and
special annotations. The extension of the SKOS language is documented in the
<a href="<CFG_SITE_URL>/help/hacking/bibclassify-internals">hacking guide</a>.</p>
<a name="1.2"></a><h3>1.2 Keyword extraction</h3>
<p>BibClassify computes the keywords of a fulltext document based on the
frequency of thesaurus terms in it. In other words, it calculates how many
times a thesaurus keyword (and its alternative and hidden labels, defined in
the taxonomy) appears in a text and it ranks the results. Unlike other similar
systems, BibClassify does not use any machine learning or AI methodologies - a
just plain phrase matching using
<a href="http://en.wikipedia.org/wiki/Regex">regular expressions</a>:
it exploits the conformation and richness of the thesaurus to produce accurate
results. It is then clear that BibClassify performs best on top of rich,
well-structured, subject thesauri expressed in the RDF/SKOS language.</p>
<p>A detailed account of the phrase matching mechanisms used by BibClassify is
included in the
<a href="<CFG_SITE_URL>/help/hacking/bibclassify-internals">hacking guide</a>.</p>
<a name="2"></a><h2>2. Running BibClassify</h2>
<p><span class="adminbox">&nbsp;<b>Dependencies.</b> BibClassify requires
Python <a href="http://rdflib.net/">RDFLib</a> in order to process the
RDF/SKOS taxonomy.</span></p>
<p>In order to extract relevant keywords from a document
<code>fulltext.pdf</code> based on a controlled vocabulary
<code>thesaurus.rdf</code>, you would run BibClassify as follows:</p>
<blockquote>
<pre>
$ bibclassify.py -k thesaurus.rdf fulltext.pdf
</pre>
</blockquote>
<p>Launching <code>bibclassify --help</code> shows the options
available for BibClassify:</p>
<pre><code>
Usage: bibclassify [OPTION]... [FILE/URL]...
bibclassify [OPTION]... [DIRECTORY]...
Searches keywords in FILEs and/or files in DIRECTORY(ies). If a directory is
specified, BibClassify will generate keywords for all PDF documents contained
in the directory. Can also run in a daemon mode, in which case the files to
be run are looked for from the database (=records modified since the last run).
General options:
-h, --help display this help and exit
-V, --version output version information and exit
-v, --verbose=LEVEL sets the verbose to LEVEL (=0)
-k, --taxonomy=NAME sets the taxonomy NAME. It can be a simple
controlled vocabulary or a descriptive RDF/SKOS
and can be located in a local file or URL.
Standalone file mode options:
-o, --output-mode=TYPE changes the output format to TYPE (text, marcxml or
html) (=text)
-s, --spires outputs keywords in the SPIRES format
-n, --keywords-number=INT sets the number of keywords displayed (=20), use 0
to set no limit
-m, --matching-mode=TYPE changes the search mode to TYPE (full or partial)
(=full)
--detect-author-keywords detect keywords that are explicitely written in the
document
Daemon mode options:
-i, --recid=RECID extract keywords for a record and store into DB
(=all necessary ones for pre-defined taxonomies)
-c, --collection=COLL extract keywords for a collection and store into DB
(=all necessary ones for pre-defined taxonomies)
Taxonomy management options:
--check-taxonomy checks the taxonomy and reports warnings and errors
--rebuild-cache ignores the existing cache and regenerates it
--no-cache don't cache the taxonomy
Backward compatibility options (discouraged):
-q equivalent to -s
-f FILE URL sets the file to read the keywords from
Examples (standalone file mode):
$ bibclassify -k HEP.rdf http://arxiv.org/pdf/0808.1825
$ bibclassify -k HEP.rdf article.pdf
$ bibclassify -k HEP.rdf directory/
Examples (daemon mode):
$ bibclassify -u admin -s 24h -L 23:00-05:00
$ bibclassify -u admin -i 1234
$ bibclassify -u admin -c Preprints
</code></pre>
<p><span class="adminbox">&nbsp;<b>NB.</b> BibClassify can run as a CDS
Invenio module or as a standalone program. If you already run a server with a
Invenio installation, you can simply run
<em>/opt/invenio/bin/bibclassify [options]</em>. Otherwise, you can run from
BibClassify sources <em>bibclassify [options]</em>.</li>
</span></p>
<p>As an example, running BibClassify on document
-<a href="http://cdsweb.cern.ch/record/547024">nucl-th/0204033</a> using the
+<a href="http://cdsweb.cern.ch/<CFG_SITE_RECORD>/547024">nucl-th/0204033</a> using the
high-energy physics RDF/SKOS taxonomy (<code>HEP.rdf</code>) would yield the
following results (based on the HEP taxonomy from October 10th 2008):
<pre><code>
Input file: 0204033.pdf
Author keywords:
Dense matter
Saturation
Unstable nuclei
Composite keywords:
10 nucleus: stability [36, 14]
6 saturation: density [25, 31]
6 energy: symmetry [35, 11]
4 nucleon: density [13, 31]
3 energy: Coulomb [35, 3]
2 energy: density [35, 31]
2 nuclear matter: asymmetry [21, 2]
1 n: matter [54, 36]
1 n: density [54, 31]
1 n: mass [54, 16]
Single keywords:
61 K0
23 equation of state
12 slope
4 mass number
4 nuclide
3 nuclear model
3 mass formula
2 charge distribution
2 elastic scattering
2 binding energy
</pre></code>
or, the following keyword-cloud HTML visualization:<br />
<br />
<img src="<CFG_SITE_URL>/img/admin/bibclassify-admin-guide-cloud.jpeg" alt="tag-cloud for document nucl-th/0204033" border="0" />
</p>
diff --git a/modules/bibedit/doc/admin/bibedit-admin-guide.webdoc b/modules/bibedit/doc/admin/bibedit-admin-guide.webdoc
index 1e11dce91..20fdb84d3 100644
--- a/modules/bibedit/doc/admin/bibedit-admin-guide.webdoc
+++ b/modules/bibedit/doc/admin/bibedit-admin-guide.webdoc
@@ -1,436 +1,436 @@
## -*- mode: html; coding: utf-8; -*-
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibEdit Admin Guide -->
<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
<strong>2. <a href="#2">Edit records via Web interface</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.1. <a href="#2.1">Keyboard shortcuts</a><br />
<strong>3. <a href="#3">Edit multiple records via Web interface</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1. <a href="#3.1">Multi-Record Editor user guide</a><br />
<strong>4. <a href="#4">Edit records via command line</a></strong><br />
<strong>5. <a href="#5">Delete records via command line</a></strong><br />
<strong>6. <a href="#6">Delete all records</a></strong><br />
<strong>7. <a href="#7">Access record history</a></strong><br />
<a name="1"></a><h2>1. Overview</h2>
<p>BibEdit enables you to directly manipulate bibliographic data, edit a
single record, do global replacements, and other cataloguing tasks.</p>
<a name="2"></a><h2>2. Edit records via Web interface</h2>
<p>To edit records via the web interface, please go to <a
-href="<CFG_SITE_URL>/record/edit/">BibEdit Admin
+href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/edit/">BibEdit Admin
Interface</a>. This interface will let you to add, change or
delete fields in a record.</p>
<p>If you want to change several records at once, please use the batch
command-line techniques describe below.</p>
<p>Please note that 8564 tags pointing to fulltext files managed by Invenio
can't be manipulated via BibEdit. In order to modify them please use the
<a href="bibupload-admin-guide#3.5">FFT tags through BibUpload</a> or the
bibdocfile command line tool.</p>
<a name="2.1"></a><h3>2.1 Keyboard shortcuts</h3>
<h4>Basic record actions</h4>
<table border="1" width="600px">
<thead>
<tr> <th>Shortcut</th> <th>Definition</th> <th>Action</th> </tr>
</thead>
<tbody>
<tr> <td>g</td> <td>Select</td> <td>Go to the record selection
field</td> </tr>
<tr> <td>Ctrl+Right</td> <td>Next</td> <td>Go to next record
(in search results).</td> </tr>
<tr> <td>Ctrl+Left</td> <td>Previous</td> <td>Go to previous record
(in search results).</td> </tr>
<tr> <td>Shift+s</td> <td>Submit</td> <td>Submit the record.</td> </tr>
<tr> <td>Shift+c</td> <td>Cancel</td> <td>Cancel editing of the
record.</td> </tr>
<tr> <td>Shift+d</td> <td>Delete</td> <td>Delete the record.</td> </tr>
<tr> <td>Shift+t</td> <td>Tags</td> <td>Toggle MARC/human tags.</td> </tr>
<tr> <td>a</td> <td>Add field</td> <td>Add a new, empty field.</td> </tr>
<tr> <td>Del</td> <td>Delete selected</td> <td>Deletes all selected
fields.</td> </tr>
</tbody>
</table>
<h4>Focused (clicked) subfield</h4>
<table border="1" width="600px">
<thead>
<tr> <th>Shortcut</th> <th>Definition</th> <th>Action</th> </tr>
</thead>
<tbody>
<tr> <td>Tab</td> <td>Next</td> <td>Move focus to next subfield.</td> </tr>
<tr> <td>Shift+Tab</td> <td>Previous</td> <td>Move focus to previous
subfield.</td> </tr>
<tr> <td>Return</td> <td>Edit</td> <td>Edit focused subfield.</td> </tr>
<tr> <td>Space</td> <td>Select</td> <td>Select focused subfield.</td> </tr>
<tr> <td>Shift+Space</td> <td>Select field</td> <td>Select parent field of
focused subfield.</td> </tr>
<tr> <td>Ctrl+Up</td> <td>Move up</td> <td>Move focused subfield up.</td> </tr>
<tr> <td>Ctrl+Down</td> <td>Move down</td> <td>Move focused subfield
down.</td> </tr>
<tr> <td>Ctrl+Shift+e</td> <td>Add subfield</td> <td>Add an additional
subfield at the end.</td> </tr>
<tr> <td>Ctrl+Shift+d</td> <td>Remove subfield</td> <td>Remove a subfield
from the end.</td> </tr>
</tbody>
</table>
<h4>Input field/form</h4>
<table border="1" width="600px">
<thead>
<tr> <th>Shortcut</th> <th>Definition</th> <th>Action</th> </tr>
</thead>
<tbody>
<tr> <td>Ctrl+Shift+s</td> <td>Save</td> <td>Save content of
field/form.</td> </tr>
<tr> <td>Ctrl+Shift+s</td> <td>Cancel</td> <td>Cancel editing of
field/form.</td> </tr>
<tr> <td>Ctrl+Shift+s</td> <td>Clear</td> <td>Clear content of
field/form.</td> </tr>
<tr> <td>Ctrl+Shift+e</td> <td>Add subfield</td> <td>Add an additional
subfield at the end.</td> </tr>
<tr> <td>Ctrl+Shift+d</td> <td>Remove subfield</td> <td>Remove a subfield
from the end.</td> </tr>
</tbody>
</table>
<h4>Other functionality</h4>
<table border="1" width="600px">
<thead>
<tr> <th>Shortcut</th> <th>Definition</th> <th>Action</th> </tr>
</thead>
<tbody>
<tr> <td>s</td> <td>Selection mode</td> <td>Toggle selection mode.</td> </tr>
</tbody>
</table>
<a name="3"></a><h2>3. Edit multiple records via web interface</h2>
-<p>The purpose of the <a href="<CFG_SITE_URL>/record/multiedit/">Multi-Record Editor Web interface</a> is to allow cataloguers to easily edit more than one record in one go.</p>
+<p>The purpose of the <a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/multiedit/">Multi-Record Editor Web interface</a> is to allow cataloguers to easily edit more than one record in one go.</p>
<p>The Multi-Record Editor allows cataloguers to easily look up various records in
the system in order to find record sets upon which to operate, and then to allow
some easy replacement procedures on these records in one go, e.g. a substring
substitution of some field value in some field tags.
</p>
<a name="3.1"></a><h3>3.1 Multi-Record Editor user guide</h3>
<p>While working with the Multi-Record Editor, the first step is to filter the set
of records that are going to be modified.<p>
<p>In order to do that, three options are available in the interface:<p>
<blockquote>
<pre>
Search criteria: [ ]
Filter collection: [ ]
Output tags: [ ]
[Search]
</pre>
</blockquote>
<p>
<ul>
<li>Search criteria allows to search records using the
same syntax offered by Invenio's web search.
<li>These records can be filtered by the desired collection, thus narrowing the
search results.
<li>Finally, for convenience, the tags displayed for each record can be specified.
The tags have to be separated by commas.
</ul>
</p>
<p>After clicking the <code>Search</code> button, the set of records that will
be affected by the changes will be visible at the bottom of the interface. It is
possible to specify whether to visualize them in <code>MARC</code> format or in
<code>HTML Brief</code> format.</p>
<p>The next step is to specify the desired changes to be made on the records.
When defining a new field action, the field tag and its indicators (if necessary)
have to be specified and one of the three actions (Add field, Delete field,
Update field) selected.</p>
<blockquote>
<pre>
Field
[ tag ][ind1][ind2] [Select action[V]]
</pre>
</blockquote>
<p>After that, as many actions on subfields as needed can be defined. The subfield
tag has to be specified and one action (<code>Add subfield, Delete subfield, Replace
full content, Replace substring</code>) selected. Depending on the field action
selected some actions for subfields will not be available.</p>
<p>The difference between <code>Replace full content</code> and <code>Replace
substring</code> resides in that the former deletes all the content present in
a subfield and writes the specified value on it whereas the latter looks for a string
and substitutes it by a new string.</p>
<p>All subfield actions have the <code>Apply only to specific field instances</code>
option. This is useful, for example, in cases where there are multiple authors
(<code>700__</code> tags) and we do not want to act in all of them.</p>
<p>In that case one could add the condition that only fields where the tag
<code>$a</code> is equal to <code>Ellis A.</code> should be modified.</p>
<blockquote>
<pre>
700__ Update Field
[u] [Replace full content]
[Ellis J.]
when other subfield [u] is equal to [Ellis A.]
</pre>
</blockquote>
<p>Every subfield action defined has to be saved using the correspondent button
before applying the changes.</p>
<p>Once all the actions for fields and subfields have been specified the modifications
can be previewed using the corresponding button.</p>
<p>Finally, when clicking on the <code>Apply changes</code> button all modifications
will be sent to the server and will be visible after some time.</p>
<a name="4"></a><h2>4. Edit records via command line</h2>
<p>The idea is to download record in XML MARC format, edit it by using
any editor, and upload the changes back. Note that you can edit any
number of records at the same time: for example, you can download all
records written by <code>Qllis, J</code>, open the file in your
favourite text editor, and change globally the author name to the
proper form <code>Ellis, J</code>.</p>
<p>You therefore continue as follows:</p>
<ol>
<li> Download the record in XML MARC. For example, download record ID 1234:
<pre>
- $ wget -O z.xml 'http://your.site/record/1234?of=xm'
+ $ wget -O z.xml 'http://your.site/<CFG_SITE_RECORD>/1234?of=xm'
</pre>
or download latest 5,000 public documents written by <code>Qllis, J</code>:
<pre>
$ wget -O z.xml 'http://your.site/search?p=Qllis%2C+J&f=author&of=xm&rg=5000'
</pre>
Note also that you can access history of records as covered in
a <a href="#6">access record history</a> section below.<p/>
<li> Edit the metadata as necessary:
<pre>
$ emacs z.xml
</pre>
<li> Upload changes back:
<pre>
$ bibupload -r z.xml
</pre>
<li> See the progress of the treatment of the file via BibSched:
<pre>
$ bibsched
</pre>
If you do not want to wait for the next wake-up time of indexing
and formatting daemons, launch them manually now:
<pre>
$ bibindex
$ bibreformat
$ webcoll
</pre>
and watch the progress via <code>bibsched</code>.
</ol>
<p>After which the record(s) should be fully modified and formatted and
all indexes and collections updated, as necessary.</p>
<a name="5"></a><h2>5. Delete records via command line</h2>
<p>Once a record has been uploaded, we prefer not to *destroy* it fully
anymore (i.e. to wipe it out and to reuse its record ID for another
record) for a variety of reasons. For example, some users may have
put this record already into their baskets in the meantime, or the
record might have already been announced by alert emails to the
external world, or the OAI harvestors might have harvested it already,
etc. We usually prefer only to *mark* records as deleted, so that our
record IDs are ensured to stay permanent.</p>
<p>Thus said, the canonical way to delete the record #1234 in Invenio
v0.1.x development branch is to download its XML MARC:
<pre>
- $ wget -O z.xml 'http://your.site/record/1234?of=xm'
+ $ wget -O z.xml 'http://your.site/<CFG_SITE_RECORD>/1234?of=xm'
</pre>
and to mark it as deleted by adding the indicator ``DELETED'' into the
MARC 980 $$c tag:
<pre>
$ emacs z.xml
[...]
&lt;datafield tag="980" ind1=" " ind2=" "&gt;
&lt;subfield code="a"&gt;PREPRINT&lt;/subfield&gt;
&lt;subfield code="c"&gt;DELETED&lt;/subfield&gt;
&lt;/datafield&gt;
[...]
</pre>
and upload thusly modified record in the `replace' mode:
<pre>
$ bibupload -r z.xml
</pre>
and watch the progress via <code>bibsched</code>, as mentioned in the
<a href="#3">section 3</a>.
</p>
<p>This procedure will remove the record from the collection cache so
that the record won't be findable anymore. In addition, if the users
try to access this record via direct URL such as distributed by the
-alert engine (record/1234) or via their baskets, they will
+alert engine (<CFG_SITE_RECORD>/1234) or via their baskets, they will
see a message ``This record has been deleted''. Please note though
that the original MARCXML of the record stays kept in the database,
for example you can access it by:
<pre>
$ python -c "from zlib import decompress; \\
from invenio.dbquery import run_sql; \\
print decompress(run_sql('SELECT value FROM bibfmt \\
WHERE id_bibrec=1234 AND format=\'xm\'')[0][0])"
</pre>
<p>In some cases you may want to hide the record from the searches,
but to leave it accessible via direct URLs or via baskets. In this
case the best it to alter its collection tag (980) to some
non-existent collection, for example:
<pre>
- $ wget -O z.xml 'http:://localhost/record/1234?of=xm'
+ $ wget -O z.xml 'http:://localhost/<CFG_SITE_RECORD>/1234?of=xm'
$ perl -pi -e 's,<subfield code="a">ARTICLE</subfield>,<subfield code="a">HIDDENARTICLE</subfield>,g' z.xml
$ bibupload -r z.xml
</pre>
This will make the record non-existent as far as the search engine is
concerned, because it won't belong to any existing collection, but the
record will exist ``on its own'' and the users knowing its recID will
be able to access it.
</p>
<p>P.S. Note that the ``bibXXx'' tables will keep having entries for the
deleted records. These entries are to be cleaned from time to
time by the BibEdit garbage collector. This GC isn't part of
Invenio yet; moreover in the future we plan to abolish all the
bibXXx tables, so that this won't be necessary anymore.</p>
<a name="6"></a><h2>6. Delete all records</h2>
<p>If you want to wipe out all the existing bibliographic content of
your site, for example to start uploading the documents from
scratch again, you can launch:
<pre>
$ /opt/invenio/bin/dbexec &lt; /opt/invenio/src/invenio-0.90/modules/miscutil/sql/tabbibclean.sql
$ rm -rf /opt/invenio/var/data/files/*
$ /opt/invenio/bin/webcoll
$ /opt/invenio/bin/bibindex --reindex
</pre>
Note that you may also want to delete the fulltext files and the
submission counters in <code>/opt/invenio/var/data</code>
subdirectories, if you use WebSubmit.
</p>
<a name="7"></a><h2>7. Access record history</h2>
<p>Every revision of the metadata of a record is stored in the
"history" table containing all previous MARCXML master formats of the
record. You can access them via the <code>bibedit</code> command line
utility.
<p>To list previous revisions of record ID 1:
<pre>
$ /opt/invenio/bin/bibedit --list-revisions 1
1.20080319193118
1.20080318172536
1.20080311020315
</pre>
<p>To get MARCXML of the revision 1.20080318172536 (record ID 1,
revision date 2008-03-18 17:25:36):
<pre>
$/opt/invenio/bin/bibedit --get-revision 1.20080318172536 | head -5
&lt;record>
&lt;controlfield tag="001">1&lt;/controlfield>
&lt;datafield tag="037" ind1=" " ind2=" ">
&lt;subfield code="a">CERN-EX-0106015&lt;/subfield>
&lt;/datafield>
[...]
</pre>
<p>To compare the differences between the two last revisions:
<pre>
$ /opt/invenio/bin/bibedit --diff-revisions 1.20080318172536 1.20080319193118
--- 1.20080318172536
+++ 1.20080319193118
@@ -4,7 +4,7 @@
&lt;subfield code="a">CERN-EX-0106015&lt;/subfield>
&lt;/datafield>
&lt;datafield tag="100" ind1=" " ind2=" ">
- &lt;subfield code="a">Photolab&lt;/subfield>
+ &lt;subfield code="a">Photolab SOME TEST EDIT HERE&lt;/subfield>
&lt;/datafield>
&lt;datafield tag="245" ind1=" " ind2=" ">
&lt;subfield code="a">ALEPH experiment: Candidate of Higgs boson production&lt;/subfield>
@@ -26,7 +26,7 @@
&lt;/datafield>
&lt;datafield tag="650" ind1="1" ind2="7">
&lt;subfield code="2">SzGeCERN&lt;/subfield>
- &lt;subfield code="a">Experiments and Tracks&lt;/subfield>
+ &lt;subfield code="a">Experiments and Tracks SOME TEST EDIT THERE&lt;/subfield>
&lt;/datafield>
&lt;datafield tag="653" ind1="1" ind2=" ">
&lt;subfield code="a">LEP&lt;/subfield>
</pre>
diff --git a/modules/bibedit/lib/bibedit_display.js b/modules/bibedit/lib/bibedit_display.js
index c835b8e2a..ede990b92 100644
--- a/modules/bibedit/lib/bibedit_display.js
+++ b/modules/bibedit/lib/bibedit_display.js
@@ -1,1138 +1,1138 @@
/*
* This file is part of Invenio.
* Copyright (C) 2009, 2010, 2011 CERN.
*
* Invenio 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.
*
* Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* This is the BibEdit Javascript for generation of webpage elements and HTML.
*/
function displayRecord(){
/*
* Create the main content table.
*/
var table = '' +
'<table id="bibEditTable">' +
'<col span="1" class="bibEditColFieldBox"/>' +
'<col span="1" id="bibEditColFieldTag"/>' +
'<col span="1" class="bibEditColFieldBox" />' +
'<col span="1" id="bibEditColSubfieldTag" />' +
'<col span="1" />' +
'<col span="1" id="bibEditColSubfieldAdd" />' +
// Create a dummy row to hack layout in like FF..
'<tbody style="border: 0px;">' +
'<tr>' +
'<td style="padding: 0px; max-width: 14px;"></td>' +
'<td style="padding: 0px; max-width: 100px;"></td>' +
'<td style="padding: 0px; max-width: 14px;"></td>' +
'<td style="padding: 0px; max-width: 80px;"></td>' +
'<td style="padding: 0px"></td>' +
'<td style="padding: 0px; max-width: 16px;"></td>' +
'</tr>' +
'</tbody>';
var tags = getTagsSorted(), tag, fields;
// For each controlfield, create row.
for (var i=0, n=tags.length; i<n; i++){
tag = tags[i];
// If not controlfield, move on.
if (!validMARC.reControlTag.test(tag))
break;
fields = gRecord[tag];
for (var j = 0, m = fields.length; j < m; j++)
table += createControlField(tag, fields[j], j);
}
// For each instance of each field, create row(s).
for (n=tags.length; i<n; i++){
tag = tags[i];
fields = gRecord[tag];
for (var j = 0, m = fields.length; j < m; j++){
table += createField(tag, fields[j], j);
}
}
// Close and display table.
table += '</table>';
$('#bibEditContent').append(table);
// now displaying the remaining controls
for (changeNr in gHoldingPenChanges){
addChangeControl(changeNr, false);
}
colorFields();
}
function createControlField(tag, field, fieldPosition){
/*
* Create control field row.
*/
var fieldID = tag + '_' + fieldPosition;
var cellContentClass = 'class="bibEditCellContentProtected" ';
if (!fieldIsProtected(tag))
cellContentClass = '';
return '' +
'<tbody id="rowGroup_' + fieldID + '">' +
'<tr id="row_' + fieldID + '" >' +
'<td class="bibEditCellField">' +
input('checkbox', 'boxField_' + fieldID, 'bibEditBoxField',
{onclick: 'onFieldBoxClick(this)', tabindex: -1}) +
'</td>' +
'<td id="fieldTag_' + fieldID + '" class="bibEditCellFieldTag">' +
getFieldTag(tag) +
'</td>' +
'<td></td>' +
'<td></td>' +
'<td id="content_' + fieldID + '" ' + cellContentClass +
'colspan="2" tabindex="0">' + escapeHTML(field[3]) +
'</td>' +
'</tr>' +
'</tbody>';
}
function createField(tag, field, fieldPosition){
/*
* Create field row(s).
*/
var subfields = field[0], ind1 = field[1], ind2 = field[2];
var fieldID = tag + '_' + fieldPosition;
ind1 = (ind1 != ' ' && ind1 !== '') ? ind1 : '_';
ind2 = (ind2 != ' ' && ind2 !== '') ? ind2 : '_';
var protectedField = fieldIsProtected(tag + ind1 + ind2);
var subfieldsLength = subfields.length;
var result = '<tbody ' + 'id="rowGroup_' + fieldID + '">';
for (var i=0, n=subfields.length; i<n; i++){
var subfield = subfields[i];
result += createRow(tag, ind1, ind2, subfield[0], escapeHTML(subfield[1]),
fieldID, i, subfieldsLength, protectedField);
}
return result + '</tbody>';
}
function createRow(tag, ind1, ind2, subfieldCode, subfieldValue, fieldID,
subfieldIndex, subfieldsLength, protectedField){
/*
* Create single row (not controlfield).
*/
var MARC = tag + ind1 + ind2 + subfieldCode;
var protectedSubfield = (protectedField) ? true : fieldIsProtected(MARC);
var subfieldID = fieldID + '_' + subfieldIndex;
var boxField = '', cellFieldTagAttrs = 'class="bibEditCellField"',
fieldTagToPrint = '',
cellContentClass = 'bibEditCellContentProtected',
cellContentTitle='',
cellContentOnClick = '';
var autosuggestkeypress = "";
var autosuggest = false;
var autocomplete = false;
var autokeyword = false;
for (var i = 0; i < gAUTOSUGGEST_TAGS.length; i++) {
if (MARC == gAUTOSUGGEST_TAGS[i]) {
autosuggest = true;
}
}
for (var i = 0; i < gAUTOCOMPLETE_TAGS.length; i++) {
if (MARC == gAUTOCOMPLETE_TAGS[i]) {
autocomplete = true;
}
}
if (MARC == gKEYWORD_TAG) { autokeyword = true; }
if (!protectedField){
// Enable features for unprotected fields.
if (!protectedSubfield){
cellContentClass = 'bibEditCellContent';
cellContentTitle = 'title="Click to edit" ';
if (autosuggest || autokeyword) {
cellContentTitle = 'title="Click to edit (suggest values: ctrl-shift-a or ctrl-9) " ';
}
if (autocomplete) {
cellContentTitle = 'title="Click to edit (complete values: ctrl-shift-a or ctrl-9) " ';
}
cellContentOnClick = 'onclick="onContentClick(this)" ';
}
}
cellContentAdditionalClass = "";
if (subfieldValue.substring(0,9) == "VOLATILE:"){
subfieldValue = subfieldValue.substring(9);
cellContentAdditionalClass += " bibEditVolatileSubfield";
}
var boxSubfield = input('checkbox', 'boxSubfield_' + subfieldID,
'bibEditBoxSubfield', {onclick: 'onSubfieldBoxClick(this)', tabindex: -1});
var subfieldTagToPrint = getSubfieldTag(MARC);
var btnAddSubfield = '';
// If first subfield, add tag and selection box, remove up arrow.
if (subfieldIndex === 0){
boxField = input('checkbox', 'boxField_' + fieldID, 'bibEditBoxField',
{onclick: 'onFieldBoxClick(this)', tabindex: -1});
cellFieldTagAttrs = 'id="fieldTag_' + fieldID +
'" class="bibEditCellFieldTag ' + cellContentClass + '" ' +
cellContentTitle + cellContentOnClick + 'tabindex="0" ';
fieldTagToPrint = getFieldTag(MARC);
}
// If last subfield, remove down arrow, add 'Add subfield' button.
if (subfieldIndex == subfieldsLength - 1){
if (!protectedField)
btnAddSubfield = img('/img/add.png', 'btnAddSubfield_' + fieldID, '',
{title: 'Add subfield', onclick: 'onAddSubfieldsClick(this)'});
}
myelement = '' +
'<tr id="row_' + subfieldID + '">' +
'<td class="bibEditCellField">' + boxField + '</td>' +
'<td ' + cellFieldTagAttrs + '>' + fieldTagToPrint + '</td>' +
'<td class="bibEditCellSubfield">' + boxSubfield + '</td>' +
'<td id="subfieldTag_' + subfieldID +
'" class="bibEditCellSubfieldTag ' + cellContentClass + '" ' +
cellContentTitle + cellContentOnClick + 'tabindex="0">' + subfieldTagToPrint +
'</td>' +
'<td id="content_' + subfieldID + '" class="' + cellContentClass + cellContentAdditionalClass + '" ' +
cellContentTitle + autosuggestkeypress + cellContentOnClick + 'tabindex="0">' +
subfieldValue +
'</td>' +
'<td class="bibEditCellAddSubfields">' + btnAddSubfield + '</td>' +
'</tr>';
/*add a place where the autosuggest box goes, if needed*/
if (autosuggest || autokeyword) {
myelement = myelement +
'<tr><td></td><td></td><td></td><td></td><td tabindex="0" id="autosuggest_' + subfieldID + '">' + '<td></td></td></tr>';
}
return myelement;
}
function redrawFields(tag, skipAddFileds){
/*
* Redraw all fields for a given tag.
* skipAddFileds - forces to skip drawing the controls corresponding to the
* change of adding a field
*
* WARNING: if we have added two (or more) fields with completely new tags a, b
* where a > b in the lexicographical order, redrawFields(b) has to be executed before
* redrawFields(a)
*/
var rowGroup = $('#rowGroup_' + tag + '_0'), prevRowGroup;
if (rowGroup.length){
// Remove the fields from view.
prevRowGroup = rowGroup.prev();
prevRowGroup.nextAll('[id^=rowGroup_' + tag + ']').remove();
}
else{
// New tag. Determine previous sibling.
var prevTag = getPreviousTag(tag);
var lastIndex = gRecord[prevTag].length - 1;
prevRowGroup = $('#rowGroup_' + prevTag + '_' + lastIndex);
}
// Redraw all fields and append to table.
if (gRecord[tag]){
var fields = gRecord[tag];
var result = '', i, n;
if (validMARC.reControlTag.test(tag)){
for (i=0, n=fields.length; i<n; i++)
result += createControlField(tag, fields[i], i);
}
else{
for (i=0, n=fields.length; i<n; i++)
result += createField(tag, fields[i], i);
}
prevRowGroup.after(result);
}
// Now redraw all the Holding Pen changes connected controls
for (changeNr in gHoldingPenChanges){
if (gHoldingPenChanges[changeNr]["tag"] == tag){
addChangeControl(changeNr, skipAddFileds);
}
}
}
/// The Holding Pen changes connected functions
/// rendering the field content
function removeAddFieldControl(changeNo){
/** A function removing the interface element associated with the Add Field
Holding Pen change
Arguments:
changeNo: a number of the change, the control is associated with
*/
$("#changeBox_" + changeNo).remove();
}
/// generating the changes controls
function getApplyChangeButton(functionName, changeNo){
/*Returning the HTML code of the Apply change button
* functionName - the name of the function called when the button is clicked
* The function has to take one argument being the number of the
* change
* changeNo - the number of the change the button is associated with
*/
return "<button onClick=\"return " + functionName + "(" + changeNo + ");\"><img src=\"/img/wsignout.gif\"></img></button>";
}
function getRejectChangeButton(changeNo){
/*The button allowing to reject the change - and remove the control from the user interface*/
result = "<button onClick=\"onRejectChangeClicked(" +
changeNo + ");\"><img src=\"/img/iconcross2.gif\"></img></button>";
return result;
}
function getAddInsteadOfChangeButton(functionName, changeNo){
/*The button allowing to reject the change - and remove the control from the user interface*/
result = "<button onClick=\"" + functionName + "(" +
changeNo + ");\"><img src=\"/img/plus_orange.png\"></img></button>";
return result;
}
function addSubfieldChangedControl(changeNo){
fieldId = gHoldingPenChanges[changeNo]["tag"];
fieldPos = gHoldingPenChanges[changeNo]["field_position"];
sfPos = gHoldingPenChanges[changeNo]["subfield_position"];
content = gHoldingPenChanges[changeNo]["subfield_content"];
applyButton = getApplyChangeButton("applySubfieldChanged", changeNo);
rejectButton = getRejectChangeButton(changeNo);
addButton = getAddInsteadOfChangeButton("applyFieldAdded", changeNo); // apply the change as if it was adding the subfield ( the data is the same)
/*adds a control allowing to change the subfield content*/
newel = "<div class=\"bibeditHPCorrection\"><span> New value: " + content +
"</span>&nbsp;&nbsp;" +
applyButton +
rejectButton +
addButton +
"</div>";
$("#content_" + fieldId + "_" + fieldPos + "_" + sfPos).append(newel);
}
function addSubfieldAddedControl(changeNo){
/*adds a control allowing to add a new subfield using the holding pen*/
fieldId = gHoldingPenChanges[changeNo].tag;
fieldPos = gHoldingPenChanges[changeNo].field_position;
subfieldCode = gHoldingPenChanges[changeNo].subfield_code;
subfieldContent = gHoldingPenChanges[changeNo].subfield_content;
applyButton = getApplyChangeButton("applySubfieldAdded", changeNo);
rejectButton = getRejectChangeButton(changeNo);
subfieldPreview = "$$" + subfieldCode + "&nbsp;&nbsp;&nbsp;" + subfieldContent;
newel = "<tr><td></td><td></td><td></td><td></td><td>" +
"<div class=\"bibeditHPCorrection\"><span>Subfield added: " + subfieldPreview + "</span>" +
"<div>" + applyButton + rejectButton +"</div></div></td><td></td></tr>";
$("#rowGroup_" + fieldId + "_" + fieldPos).append(newel);
}
function addSubfieldRemovedControl(changeNo){
/*adds a control allowing to apply the change of removing the subfield */
fieldId = gHoldingPenChanges[changeNo]["tag"];
fieldPos = gHoldingPenChanges[changeNo]["field_position"];
sfPos = gHoldingPenChanges[changeNo]["subfield_position"];
applyButton = getApplyChangeButton("applySubfieldRemoved", changeNo);
rejectButton = getRejectChangeButton(changeNo);
newel = "<div class=\"bibeditHPCorrection\"><span> The subfield has been removed " +
"</span>" + applyButton + rejectButton + "</div>";
$("#content_" + fieldId + "_" + fieldPos + "_" + sfPos).append(newel);
}
function addFieldRemovedControl(changeNo){
/*adds a control allowing the change of removing the Field*/
fieldId = gHoldingPenChanges[changeNo]["tag"];
fieldPos = gHoldingPenChanges[changeNo]["field_position"];
applyButton = getApplyChangeButton("applyFieldRemoved", changeNo);
rejectButton = getRejectChangeButton(changeNo);
newel = "<tr><td></td><td></td><td></td><td></td><td><div class=\"bibeditHPCorrection\">Field has been removed in the Holding Pen. " +
applyButton + rejectButton +
"</div></td><td></td></tr>";
$("#rowGroup_" + fieldId + "_" + fieldPos).append(newel);
}
function addFieldChangedControl(changeNo){
/*adds a control allowing the change of removing the Field*/
fieldId = gHoldingPenChanges[changeNo]["tag"];
indicators = gHoldingPenChanges[changeNo]["indicators"];
fieldPos = gHoldingPenChanges[changeNo]["field_position"];
fieldContent = gHoldingPenChanges[changeNo]["field_content"];
applyButton = getApplyChangeButton("applyFieldChanged", changeNo);
rejectButton = getRejectChangeButton(changeNo);
addButton = getAddInsteadOfChangeButton("applyFieldAdded", changeNo);
fieldPreview = createFieldPreview(fieldId, indicators, fieldContent);
newel = "<tr><td></td><td></td><td></td><td></td><td><div class=\"bibeditHPCorrection\">Field structure has changed. New value: " +
fieldPreview + "<br>" + applyButton + rejectButton + addButton +
"</div></td><td></td></tr>";
$("#rowGroup_" + fieldId + "_" + fieldPos).append(newel);
}
function addFieldAddedControl(changeNo){
/*adds a control allowing the change of adding the Field*/
fieldId = gHoldingPenChanges[changeNo]["tag"];
indicators = gHoldingPenChanges[changeNo]["indicators"];
field = gHoldingPenChanges[changeNo]["field_content"];
fieldContent = createFieldPreview(fieldId, indicators, field);
//fieldContent = returnASummaryOfTheField(fieldId, field);
applyButton = getApplyChangeButton("applyFieldAdded", changeNo);
rejectButton = getRejectChangeButton(changeNo);
content = "<div class=\"bibeditHPCorrection\" id=\"changeBox_" + changeNo + "\">" +
"<div>A field has been added in the Holding Pen entry </div> " +
"<div>" + fieldContent + "</div>" +
"<div>" +
applyButton + rejectButton +
"</div></div>";
$('#bibEditContent').append(content);
}
function removeAllChangeControls(){
/** Removing all the change controls from the interface
*/
$(".bibeditHPCorrection").remove();
}
function addChangeControl(changeNum, skipAddedField){
/** creates a web controls responsible for applying a particular change
changeNum is the number of the change - it is the same as the index
in gHoldingPenChanges global array */
if (gHoldingPenChanges[changeNum].applied_change === true){
return;
}
changeType = gHoldingPenChanges[changeNum]["change_type"];
if ( changeType == "field_added" && skipAddedField !== true){
addFieldAddedControl(changeNum);
}
if ( changeType == "subfield_changed"){
addSubfieldChangedControl(changeNum);
}
if ( changeType == "subfield_removed"){
addSubfieldRemovedControl(changeNum);
}
if ( changeType == "subfield_added"){
addSubfieldAddedControl(changeNum);
}
if ( changeType == "field_removed"){
addFieldRemovedControl(changeNum);
}
if ( changeType == "field_changed"){
addFieldChangedControl(changeNum);
}
}
/// the functions for creating the previews
function createFieldPreviewCore(tag, indicators, subfields){
/** A function creating a HTML preview of the record part */
headerData = tag + indicators + "&nbsp;&nbsp;&nbsp;";
var result = "";
for (subfield in subfields){
result += "<tr><td>" + headerData + "</td><td>$$" + subfields[subfield][0] +
"&nbsp;&nbsp;&nbsp;</td><td>" + subfields[subfield][1] + "</td></tr>";
headerData = "";
}
return result;
}
function createFieldPreview(tag, indicators, subfields){
/** Creating a preview of a single field*/
return "<table>" + createFieldPreviewCore(tag, indicators, subfields) + "</table>";
}
function createRecordPreview(recordData){
/**A function creating a preview of the record*/
// 1) sorting Tags
unsortedTags = [];
for (tag in recordData){
unsortedTags.push(tag);
}
tags = unsortedTags.sort();
result = "<table>";
for (tagIndex in tags){
tag = tags[tagIndex];
// now we have to sort the fields by the indicators
unsortedIndicators = [];
indicatorLists = {};
for (field in recordData[tag]){
indicators = (recordData[tag][field][1] == ' ' ? '_' : recordData[tag][field][1] ) +
(recordData[tag][field][2] == ' ' ? '_' : recordData[tag][field][2] );
if (indicatorLists[indicators] == undefined){
indicatorLists[indicators] = [];
unsortedIndicators.push(indicators);
}
indicatorLists[indicators].push(field);
}
sortedIndicators = unsortedIndicators.sort();
// traversing all the fields in previously indicated order
for (indicatorsInd in sortedIndicators){
indicators = sortedIndicators[indicatorsInd];
for (fieldInd in indicatorLists[indicators]){
field = indicatorLists[indicators][fieldInd];
result += createFieldPreviewCore(tag, indicators, recordData[tag][field][0]);
}
}
}
result += "</table>";
return result;
}
/// the entries on the left, in the panel
function createHoldingPenChangePreview(record){
/** A function creating the content
*
* Parameters:
* record - A content of the record that should be previewed
*/
return createRecordPreview(record) +
"<br><button onClick=\"onToggleDetailsVisibility(" + changesetNumber + ");\">Hide preview</button>";
}
function createHoldingPenPanelEntry(changesetNumber, changesetDatetime){
/**
* A function creating the panel entry describing one changeset
* Parameters:
*
* changesetNumber - the internal changeset number ( from the Holding Pen )
* changesetDatetime - the data of harvesting the changeset
*
* Returns: The HTML code of the control
*/
manipulationControlsSection = //"<div class=\"bibeditHPEntryControlsPanel\">" +
button("<img src=\"/img/add.png\">",
id = ("bibeditHPApplyChange" + changesetNumber),
_class = "bibeditHPControl",
attrs = {
"onClick" :
("return holdingPenPanelApplyChangeSet("+ changesetNumber + ");")
}) +
button("<img src=\"/img/delete.png\">",
id = ("bibeditHPRemoveChange" + changesetNumber),
_class = "bibeditHPControl",
attrs = {
"onClick" :
("return holdingPenPanelRemoveChangeSet("+ changesetNumber + ");")
});
//"</div>";
numberSection = "<div class=\"bibeditHPEntryNumber\">No" + changesetNumber + "</div>";
datetimeSection = "<div class=\"bibeditHPEntryDateSection\">" + changesetDatetime + "</div>";
previewOpener = "<div id=\"holdingPenToggleDetailsVisibility_" + changesetNumber +
"\" onClick=\"onToggleDetailsVisibility(" + changesetNumber +
");\" class=\"bibeditHPDetailsOpener\">+</div>";
previewLayer = "<div class=\"bibeditHPContentPreviewBox bibeditHPHiddenElement\" id=\"holdingPenPreview_" + changesetNumber + "\">" +
"Loading... <br>" +
"<br><button onClick=\"onToggleDetailsVisibility(" + changesetNumber + ");\">Hide preview</button></div>"; // a toggle button ;
previewSection = previewOpener + previewLayer;
result = "<div class=\"bibeditHPPanelEntry\" id=\"bibeditHoldingPenPanelEntry_" + changesetNumber + "\">" +
"<div class=\"bibeditHPEntryCol1\">" +
"<div class=\"bibeditHPEntryRow1\">" + numberSection + previewSection + "</div>" +
"<div class=\"bibeditHPEntryRow2\">" + datetimeSection + "</div>" +
"</div><div class=\"bibeditHPEntryCol2\">"+ manipulationControlsSection + "</div>" +
"</div>";
return result;
//informationsSection = "<div class=\"bibeditHPInformationsSection\">" + numberSection + previewSection + datetimeSection + "</div>";
//return "<div class=\"bibeditHPPanelEntry\" id=\"bibeditHoldingPenPanelEntry_" +
// changesetNumber + "\">" + informationsSection + manipulationControlsSection + "</div>";
}
function createGeneralControlsPanel(){
/** Generating a panel that allows to perform global operations on the previewed changes*/
result = "<div id=\"bibeditHoldingPenGC\">";
result += "<button onClick=\"onAcceptAllChanges();\">Apply all the changes</button>";
result += "<button onClick=\"onRejectAllChanges();\"> Reject all the changes</button>";
result += "</div>";
return result;
}
function createTopToolbar(){
/* Generate BibEdit top toolbar */
$('.navtrailboxbody').after('<td class="revisionLine"></td>');
// When Special modes are available there will be a loop through all of
// them and the appropriate icons will be added
var toolbar_html = "<div id='topToolbarRight'><img id='img_preview' class='bibEditImgCtrlDisabled' src='/img/document-preview.png' ";
toolbar_html += "width='40px' height='40px' title='Preview record' /></div>";
toolbar_html += "<div id='top_toolbar_hr'><hr></div>"
$('.headline_div').html(toolbar_html);
$('#img_preview').bind('click', onPreviewClick);
$('#img_preview').unbind('click').removeClass(
'bibEditImgCtrlEnabled').addClass('bibEditImgCtrlDisabled');
}
function updateToolbar(enable) {
if (enable === true) {
$('#img_preview').bind('click', onPreviewClick).removeClass(
'bibEditImgCtrlDisabled').addClass('bibEditImgCtrlEnabled');
}
else {
$('#img_preview').unbind('click', onPreviewClick).removeClass(
'bibEditImgCtrlEnabled').addClass('bibEditImgCtrlDisabled');
}
}
/// end of the Holding Pen Connected functions
function createAddFieldForm(fieldTmpNo, fieldTemplateNo){
/*
* Create an 'Add field' form.
* fieldTmpNo - temporary field number
* fieldTemplateNo - a number of template that should be selected by default
*/
fieldTemplatesData = [];
for (templatePos in fieldTemplates){
fieldTemplatesData.push({"value" : templatePos , "description": fieldTemplates[templatePos].name});
}
return '' +
'<tbody id="rowGroupAddField_' + fieldTmpNo + '">' +
'<tr>' +
'<td></td>' +
'<td><b>New</b></td>' +
'<td></td>' +
'<td></td>' +
'<td><div class="bibEditAddFieldManipulationsBar"><div class="bibEditAddFieldFormSelectTemplate">Add field: ' +
select('selectAddFieldTemplate_' + fieldTmpNo, fieldTemplatesData, fieldTemplateNo) +
'</div><div class="bibEditAddFieldFormCreateSimilar"> Add ' +
input('text', 'selectAddFieldTemplateTimes_' + fieldTmpNo, "addFieldAddSimilarInput", {"maxlength" : 4, "size": 1}) +
button('similar', 'selectAddSimilarFields_' + fieldTmpNo, "", {}) +
'</div></div></td>' +
'<td>' +
img('/img/add.png', 'btnAddFieldAddSubfield_' + fieldTmpNo, '', {
title: 'Add subfield'}) +
'</td>' +
'</tr>' +
createAddFieldRow(fieldTmpNo, 0) +
// adding a row used to insert at the end without repositioning the tag and indicators
'<tr>' +
'<td></td>' +
'<td></td>' +
'<td></td>' +
'<td></td>' +
'<td>' +
'</td>' +
'<td></td>' +
'</tr>' +
'</tbody>';
}
function createAddFieldRow(fieldTmpNo, subfieldTmpNo, defaultCode, defaultValue){
/*
* Create a row in the 'Add field' form.
* optional parameters:
* defaultCode - the subfield code displayed by default
* defaultValue - the subfield value displayed by default
*/
var fieldCode = "";
var fieldValue = "";
if (defaultCode != undefined && defaultCode != " "){
fieldCode = defaultCode;
}
if (defaultValue != undefined && defaultValue != " "){
fieldValue = defaultValue;
}
var isVolatile = (defaultValue != undefined && defaultValue.substring(0, 9) == "VOLATILE:");
var additionalClass = "";
if (isVolatile){
additionalClass = " bibEditVolatileSubfield";
fieldValue = fieldValue.substring(9);
}
var txtAddFieldTag = '', txtAddFieldInd1 = '', txtAddFieldInd2 = '',
btnAddFieldRemove = '';
if (subfieldTmpNo === 0){
txtAddFieldTag = input('text', 'txtAddFieldTag_' + fieldTmpNo,
'bibEditTxtTag', {maxlength: 3});
txtAddFieldInd1 = input('text', 'txtAddFieldInd1_' + fieldTmpNo,
'bibEditTxtInd', {maxlength: 1});
txtAddFieldInd2 = input('text', 'txtAddFieldInd2_' + fieldTmpNo,
'bibEditTxtInd', {maxlength: 1});
}
else
btnAddFieldRemove = img('/img/delete.png', 'btnAddFieldRemove_' +
fieldTmpNo + '_' + subfieldTmpNo, '', {title: 'Remove subfield'});
return '' +
'<tr id="rowAddField_' + fieldTmpNo + '_' + subfieldTmpNo + '">' +
'<td></td>' +
'<td>' +
txtAddFieldTag + txtAddFieldInd1 + txtAddFieldInd2 +
'</td>' +
'<td></td>' +
'<td class="bibEditCellAddSubfieldCode">' +
input('text', 'txtAddFieldSubfieldCode_' + fieldTmpNo + '_' +
subfieldTmpNo, 'bibEditTxtSubfieldCode', {maxlength: 1, value: fieldCode}) +
'</td>' +
'<td>' +
input('text', 'txtAddFieldValue_' + fieldTmpNo + '_' +
subfieldTmpNo, 'bibEditTxtValue' + additionalClass, {value : fieldValue}) +
'</td>' +
'<td>' + btnAddFieldRemove + '</td>' +
'</tr>';
}
function createAddSubfieldsForm(fieldID, defSubCode, defValue){
/*
* Create an 'Add subfields' form.
*/
return '' +
createAddSubfieldsRow(fieldID, 0, defSubCode, defValue) +
'<tr id="rowAddSubfieldsControls_' + fieldID + '">' +
'<td></td>' +
'<td></td>' +
'<td></td>' +
'<td></td>' +
'<td>' +
'</td>' +
'<td></td>' +
'</tr>';
}
function createAddSubfieldsRow(fieldID, subfieldTmpNo, defSubCode, defValue){
/*
* Create a row in the 'Add subfields' form.
*/
var subfieldID = fieldID + '_' + subfieldTmpNo;
var btnRemove = (subfieldTmpNo === 0) ? '' : img('/img/delete.png',
'btnAddSubfieldsRemove_' + subfieldID, '', {title: 'Remove subfield'});
return '' +
'<tr id="rowAddSubfields_' + subfieldID + '">' +
'<td></td>' +
'<td></td>' +
'<td></td>' +
'<td class="bibEditCellAddSubfieldCode">' +
input('text', 'txtAddSubfieldsCode_' + subfieldID,
'bibEditTxtSubfieldCode', {maxlength: 1}, defSubCode) +
'</td>' +
'<td>' +
input('text', 'txtAddSubfieldsValue_' + subfieldID, 'bibEditTxtValue', {}, defValue) +
'</td>' +
'<td>' + btnRemove + '</td>' +
'</tr>';
}
function displayMessage(msgCode, keepContent, args){
/*
* Display message in the main work area. Messages codes returned from the
* server (positive integers) are as specified in the BibEdit configuration.
*/
var msg;
switch (msgCode){
case -1:
msg = 'Search term did not match any records.';
break;
case 0:
msg = 'A server error has occured. Please contact your system ' +
'administrator.<br />' +
'Error code: <b>' + msgCode + '</b>';
break;
case 4:
msg = 'Your modifications have now been submitted. ' +
'They will be processed as soon as the task queue is empty.';
break;
case 6:
msg = 'The record will be deleted as soon as the task queue is empty.';
break;
case 101:
msg = 'Could not access record. Permission denied.';
break;
case 102:
msg = 'This record does not exist. Please try another record ID.';
break;
case 103:
msg = 'Cannot edit deleted record.';
break;
case 104:
msg = 'This record is currently being edited by another user. Please ' +
'try again later.';
break;
case 105:
msg = 'This record cannot be safely edited at the moment. Please ' +
'try again in a few minutes.';
break;
case 106:
msg = 'A server error has occured. You may have lost your changes to ' +
'this record.<br />' +
'Error code: <b>' + msgCode + '</b> (missing cache file)';
break;
case 107:
msg = 'It appears that you have opened this record in another editor, ' +
'perhaps in a different window or on a different computer. A record ' +
'can only be edited in one place at the time.<br />' +
'Do you want to ' +
'<b><a href="#"id="lnkGetRecord">reopen the record</a></b> here?';
break;
case 108:
msg = 'Could not find record template file. Please notify your system ' +
'administrator.';
break;
case 109:
msg = 'The record template file is invalid. Please notify your system ' +
'administrator';
break;
case 110:
msg = 'The record contains invalid content. Remove the invalid content ' +
'and resubmit the record.<br />' +
'Errors: <b>' + args[0] + '</b><br /><br />';
break;
case 111:
msg = 'Internal error. Cache file format is incorrect. Try to open the record again';
break;
default:
msg = 'Result code: <b>' + msgCode + '</b>';
break;
}
if (!keepContent)
$('#bibEditContent').html('<div id="bibEditMessage">' + msg + '</div>');
else
$('#bibEditContent').prepend('<div id="bibEditMessage">' + msg + '</div>');
}
function displayNewRecordScreen(){
/*
* Display options for creating a new record: An empty record or a template
* selected from a list of templates.
*/
var msg = '<ul><li style="padding-bottom: 20px;">' +
'<a href="#" id="lnkNewEmptyRecord"><b>Empty record</b></a></li>' +
'<li style="padding-bottom: 10px;">Use record template:' +
'<table>';
var templatesCount = gRECORD_TEMPLATES.length;
if (!templatesCount)
msg += '<tr><td style="padding-left: 10px;">No record templates found' +
'</td></tr>';
else{
for (var i=0, n=templatesCount; i<n; i++)
msg += '<tr style="border-width: 1px;">' +
'<td style="padding-left: 10px; padding-right: 10px;">' +
'<a href="#" id="lnkNewTemplateRecord_' + i + '"><b>' +
gRECORD_TEMPLATES[i][1] + '</b></a></td>' +
'<td style="padding-left: 10px; padding-right: 10px;">' +
'<td>' + gRECORD_TEMPLATES[i][2] + '</td></tr>';
}
msg += '</table></li>';
$('#bibEditContent').html(msg);
}
function displayCacheOutdatedScreen(requestType){
/*
* Display options to resolve the outdated cache scenario (DB record updated
* during editing). Options differ depending on wether the situation was
* discovered when fetching or when submitting the record.
*/
$('#bibEditMessage').remove();
- var recordURL = gSITE_URL + '/record/' + gRecID + '/';
+ var recordURL = gSITE_URL + '/'+ gSITE_RECORD +'/' + gRecID + '/';
var viewMARCURL = recordURL + '?of=hm';
var viewMARCXMLURL = recordURL + '?of=xm';
var msg = '';
if (requestType == 'submit')
msg = 'Someone has changed this record while you were editing. ' +
'You can:<br /><ul>' +
'<li>View (<b><a href="' + recordURL + '" target="_blank">HTML</a></b>,' +
' <b><a href="' + viewMARCURL + '" target="_blank">MARC</a></b>,' +
' <b><a href="' + viewMARCXMLURL + '" target="_blank">MARCXML</a></b>' +
') the latest version</li>' +
'<li><a href="#" id="lnkMergeCache"><b>Merge</b></a> your changes ' +
'with the latest version by using the merge interface</li>' +
'<li><a href="#" id="lnkForceSubmit"><b>Force your changes</b></a> ' +
'(<b>Warning: </b>overwrites the latest version)</li>' +
'<li><a href="#" id="lnkDiscardChanges><b>Discard your changes</b></a> ' +
'(keep the latest version)</li>' +
'</ul>';
else if (requestType == 'getRecord')
msg = 'You have unsubmitted changes to this record, but someone has ' +
'changed the record while you were editing. You can:<br /><ul>' +
'<li>View (<b><a href="' + recordURL + '" target="_blank">HTML</a></b>,' +
' <b><a href="' + viewMARCURL + '" target="_blank">MARC</a></b>,' +
' <b><a href="' + viewMARCXMLURL + '" target="_blank">MARCXML</a></b>' +
') the latest version</li>' +
'<li><a href="#" id="lnkMergeCache"><b>Merge</b></a> your changes ' +
'with the latest version by using the merge interface</li>' +
'<li><a href="#" id="lnkDiscardChanges"><b>Get the latest version' +
'</b></a> (<b>Warning: </b>discards your changes)</li>' +
'<li>Keep editing. When submitting you will be offered to overwrite ' +
'the latest version. Click <a href="#" id="lnkRemoveMsg">here' +
'</a> to remove this message.</li>' +
'</ul>';
$('#bibEditContent').prepend('<div id="bibEditMessage">' + msg + '</div>');
}
function displayAlert(msgType, args){
/*
* Display pop-up of type alert or confirm.
* args can be an array with additional arguments.
*/
var msg;
var popUpType = 'alert';
switch (msgType){
case 'confirmClone':
msg = 'Clone this record?\n\n';
popUpType = 'confirm';
break;
case 'confirmSubmit':
msg = 'Submit your changes to this record?\n\n';
popUpType = 'confirm';
break;
case 'confirmRevert':
msg = 'Are you sure, you want to revert to this record revision?\n\n';
popUpType = 'confirm';
break;
case 'confirmCancel':
msg = 'You have unsubmitted changes to this record.\n\n' +
'Discard your changes?';
popUpType = 'confirm';
break;
case 'confirmDeleteRecord':
msg = 'Really delete this record?\n\n';
popUpType = 'confirm';
break;
case 'confirmInvalidOrEmptyInput':
msg = 'WARNING: Some subfields contain invalid MARC or are empty. \n' +
'Click Cancel to go back and correct. \n' +
'Click OK to ignore and continue (only valid subfields will be saved).';
popUpType = 'confirm';
break;
case 'confirmLeavingChangedRecord':
msg = '******************** WARNING ********************\n' +
' You have unsubmitted changes.\n\n' +
'You should go back to the record and click either:\n' +
' * Submit (to save your changes permanently)\n or\n' +
' * Cancel (to discard your changes)\n\n' +
'Press OK to continue, or Cancel to stay on the current record.';
popUpType = 'confirm';
break;
case 'alertCriticalInput':
msg = 'ERROR: Your input had critical errors. Please go back and ' +
'correct any fields with invalid MARC (red border) or fields that ' +
'should not be empty.';
break;
case 'alertAddProtectedField':
msg = 'ERROR: Cannot add protected field ' + args[0] + '.';
break;
case 'alertAddProtectedSubfield':
msg = 'ERROR: Cannot add protected subfield ' + args[0] + '.';
break;
case 'alertEmptySubfieldsList':
msg = "The field has to contain at least one non-empty subfield.";
break;
case 'alertDeleteProtectedField':
msg = 'ERROR: Cannot delete protected field ' + args[0] + '.';
break;
case 'errorPhysicalCopiesExist':
msg = "ERROR: Cannot delete record when physical copies exist. First remove the copies in the BibCirculation module and then try again";
break;
default:
msg = msgType;
break;
}
if (popUpType == 'confirm') {
return confirm(msg);
}
else {
alert(msg);
return 1; // the equivalent of clicking ok in the confirm dialog
}
}
function notImplemented(event){
/*
* Handle unimplemented function.
*/
alert('Sorry, this function is not implemented yet!');
event.preventDefault();
}
function button(value, id, _class, attrs){
/*
* Create a button tag with specified attributes.
*/
value = (value != undefined) ? value : '';
id = id ? 'id="' + id + '" ' : '';
_class = _class ? 'class="' + _class + '" ' : '';
var strAttrs = '';
for (var attr in attrs){
strAttrs += attr + '="' + attrs[attr] + '" ';
}
return '<button ' + id + _class + strAttrs + '>' + value + '</button>';
}
function img(src, id, _class, attrs){
/*
* Create an image tag with specified attributes.
*/
src = 'src="' + src + '" ';
id = id ? 'id="' + id + '" ' : '';
_class = _class ? 'class="' + _class + '" ' : '';
var strAttrs = '';
for (var attr in attrs){
strAttrs += attr + '="' + attrs[attr] + '" ';
}
return '<img ' + src + id + _class + strAttrs + '/>';
}
function input(type, id, _class, attrs, defvalue){
/*
* Create an input tag with specified attributes.
*/
type = 'type="' + type + '" ';
id = id ? 'id="' + id + '" ' : '';
_class = _class ? 'class="' + _class + '" ' : '';
var strAttrs = '';
for (var attr in attrs){
strAttrs += attr + '="' + attrs[attr] + '" ';
}
myval = '';
if ((defvalue !== null) && (defvalue !== "")) {
myval = ' value="' + defvalue + '" ';
}
return '<input ' + type + id + _class + strAttrs + myval + '/>';
}
function select(id, options, selectedOption){
/*
* Create the select input -> it has a different structure than most of the
* inputs
* options: a list of options appearing under the same id. Each option is a
* dictionary describing the value associated and the description
* a sample entry of the options : {value: "1", description: "option1"}
*/
optionsHTML = "";
for (optionNr in options){
optionsHTML += "<option value=\"" + options[optionNr].value + "\"";
if (selectedOption == options[optionNr].value){
optionsHTML += " selected";
}
optionsHTML += ">" + options[optionNr].description + "</option>";
}
return "<select id=\"" + id + "\">" + optionsHTML + "</select>";
}
function getRevisionDate(revisionTs){
var result = {};
result.year = revisionTs.substr(0,4);
result.month = revisionTs.substr(4,2);
result.day = revisionTs.substr(6,2);
result.hour = revisionTs.substr(8,2);
result.minute = revisionTs.substr(10,2);
result.second = revisionTs.substr(12,2);
return result;
}
function formatDateTime(dt){
return dt.year + '.' + dt.month + '.' + dt.day +
' ' + dt.hour + ':' + dt.minute + ':' + dt.second;
}
function displayRevisionHistoryEntry(recId, revisionId){
var entryClass = (revisionId == gRecRev) ?
"bibEditRevHistorySelectedEntry" : "bibEditRevHistoryEntry";
var timeString = formatDateTime(getRevisionDate(revisionId));
/* var additionalAttrs = {};
if (revisionId == gRecRev){
additionalAttrs.disabled = "disabled"
}
additionalAttrs.title = "Merge with the newest revision";
*/
compareButtonId = 'btnCompareRevision_' + revisionId;
mergeImgId = 'imgMergeWithNewest_' + revisionId;
compareImgId = 'imgCompareWithCurrent_' + revisionId;
revertImgId = 'imgRevert_' + revisionId;
- var mergeUrl = '/record/merge#recid1=' + recId + '&recid2=' + recId + '.' + revisionId;
+ var mergeUrl = '/'+ gSITE_RECORD +'/merge#recid1=' + recId + '&recid2=' + recId + '.' + revisionId;
var mergeWithNewestControl = '<a href="' + mergeUrl +
'" title="Merge with the newest revision" class="bibEditRevHistoryLink">' +
img('/img/merge-small.png', mergeImgId, 'bibEditRevHistoryLinkImg') + '</a>';
var compareWithCurrentControl =
'<a href="#" title="Compare with currently viewed version" class="bibEditRevHistoryLink">' +
img('/img/compare.png', compareImgId, 'bibEditRevHistoryLinkImg') + '</a>';
var revertToRevisionControl = '<a href="#" title="Revert to this revision" class="bibEditRevHistoryLink">' +
img('/img/replace.png', revertImgId, 'bibEditRevHistoryLinkImg') +
'</a>';
var resultHTML = '<div class="' + entryClass + '">\n' +
'<div class="bibEditRevHistoryEntryContent" id="bibEditRevHistoryEntry_' +
revisionId+ '">' + timeString +
'</div><div class="bibEditRevHistoryEntryControls"><div style="display:table-row;">' +
mergeWithNewestControl + compareWithCurrentControl + revertToRevisionControl + "</div></div></div>\n";
return {
"HTML" : resultHTML,
"compareImgId" : compareImgId,
"revertImgId" : revertImgId
};
}
function escapeHTML(value){
/*
* Replace special characters '&', '<' and '>' with HTML-safe sequences.
* This functions is called on content before displaying it.
*/
value = value.replace(/&/g, '&amp;'); // Must be done first!
value = value.replace(/</g, '&lt;');
value = value.replace(/>/g, '&gt;');
return value;
}
diff --git a/modules/bibedit/lib/bibedit_engine.js b/modules/bibedit/lib/bibedit_engine.js
index 79d7e9d79..f0c746c70 100644
--- a/modules/bibedit/lib/bibedit_engine.js
+++ b/modules/bibedit/lib/bibedit_engine.js
@@ -1,4679 +1,4679 @@
/*
* This file is part of Invenio.
* Copyright (C) 2009, 2010, 2011 CERN.
*
* Invenio 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.
*
* Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* This is the main BibEdit Javascript.
*/
/* ************************* Table of contents ********************************
*
* 1. Global variables
*
* 2. Initialization
* - $()
* - initJeditable
* - initMisc
*
* 3. Ajax
* - initAjax
* - createReq
* - onAjaxError
* - onAjaxSuccess
*
* 4. Hash management
* - initStateFromHash
* - deserializeHash
* - changeAndSerializeHash
*
* 5. Data logic
* - getTagsSorted
* - getFieldPositionInTag
* - getPreviousTag
* - deleteFieldFromTag
* - cmpFields
* - fieldIsProtected
* - containsProtected
* - getMARC
* - getFieldTag
* - getSubfieldTag
* - validMARC
*
* 6. Record UI
* - onNewRecordClick
* - getRecord
* - onGetRecordSuccess
* - onSubmitClick
* - onPreviewClick
* - onCancelClick
* - onCloneRecordClick
* - onDeleteRecordClick
* - onMergeClick
* - bindNewRecordHandlers
* - cleanUp
*
* 7. Editor UI
* - colorFields
* - reColorFields
* - onMARCTagsClick
* - onHumanTagsClick
* - updateTags
* - onFieldBoxClick
* - onSubfieldBoxClick
* - onAddFieldClick
* - onAddFieldControlfieldClick
* - onAddFieldChange
* - onAddFieldSave
* - onAddSubfieldsClick
* - onAddSubfieldsChange
* - onAddSubfieldsSave
* - onDoubleClick
* - onContentChange
* - onMoveSubfieldClick
* - onDeleteClick
*/
/*
* **************************** 1. Global variables ****************************
*/
// Record data
var gRecID = null;
var gRecIDLoading = null;
var gRecRev = null;
var gRecRevAuthor = null;
var gRecLatestRev = null;
var gRecord = null;
// Search results (record IDs)
var gResultSet = null;
// Current index in the result set
var gResultSetIndex = null;
// Tag format.
var gTagFormat = null;
// Has the record been modified?
var gRecordDirty = false;
// Last recorded cache modification time
var gCacheMTime = null;
// Are we navigating a set of records?
var gNavigatingRecordSet = false;
// The current hash (fragment part of the URL).
var gHash;
// The current hash deserialized to an object.
var gHashParsed;
// Hash check timer ID.
var gHashCheckTimerID;
// The previous and current state (this is not exactly the same as the state
// parameter, but an internal state control mechanism).
var gPrevState;
var gState;
// A current status
var gCurrentStatus;
// a global array of visible changes associated with a currently viewed record
// This array is cleared always when a new changes set is applied... then it is used
// for redrawing the change fields
// The index in this array is used when referring to a particular change [ like finding an appropriate box]
var gHoldingPenChanges = [];
// A global variable used to avoid multiple retrieving of the same changes stored in the Holding Pen
// this is the dictionary indexed by the HoldingPen entry identifiers and containing the javascript objects
// representing the records
// due to this mechanism, applying previously previewed changes, as well as previewing the change for the
// second time, can be made much faster
var gHoldingPenLoadedChanges = {};
// The changes that have been somehow processed and should not be displayed as already processed
var gDisabledHpEntries = {};
// is the read-only mode enabled ?
var gReadOnlyMode = false;
// revisions history
var gRecRevisionHistory = [];
var gUndoList = []; // list of possible undo operations
var gRedoList = []; // list of possible redo operations
// number of bibcirculation copies from the retrieval time
var gPhysCopiesNum = 0;
var gBibCircUrl = null;
var gDisplayBibCircPanel = false;
/*
* **************************** 2. Initialization ******************************
*/
window.onload = function(){
if (typeof(jQuery) == 'undefined'){
alert('ERROR: jQuery not found!\n\n' +
'The Record Editor requires jQuery, which does not appear to be ' +
'installed on this server. Please alert your system ' +
'administrator.\n\nInstructions on how to install jQuery and other ' +
"required plug-ins can be found in Invenio's INSTALL file.");
var imgError = document.createElement('img');
imgError.setAttribute('src', '/img/circle_red.png');
var txtError = document.createTextNode('jQuery missing');
var cellIndicator = document.getElementById('cellIndicator');
cellIndicator.replaceChild(imgError, cellIndicator.firstChild);
var cellStatus = document.getElementById('cellStatus');
cellStatus.replaceChild(txtError, cellStatus.firstChild);
}
};
$(function(){
/*
* Initialize all components.
*/
initMenu();
initJeditable();
initAjax();
initMisc();
createTopToolbar();
initStateFromHash();
gHashCheckTimerID = setInterval(initStateFromHash, gHASH_CHECK_INTERVAL);
initHotkeys();
initClipboardLibrary();
initClipboard();
});
function failInReadOnly(){
/** Function checking if the current BibEdit mode is read-only. In sucha a case, a warning
dialog is displayed and true returned.
If bibEdit is in read/write mode, false is returned
*/
if (gReadOnlyMode === true){
alert("It is impossible to perform this operation in the Read/Only mode. Please switch to Read-write mode before trying again");
return true;
}
else{
return false;
}
}
function initJeditable(){
/* Initialize Jeditable with the Autogrow extension. Used for in-place
* content editing.
*/
$.editable.addInputType('autogrow', {
element: function(settings, original){
var textarea = $('<textarea>');
if (settings.rows){
textarea.attr('rows', settings.rows);
} else {
textarea.height(settings.height);
}if (settings.cols) {
textarea.attr('cols', settings.cols);
} else {
textarea.width(settings.width);
}
$(this).append(textarea);
return(textarea);
},
plugin: function(settings, original){
$('textarea', this).autogrow(settings.autogrow);
}
});
}
function initClipboard(){
// attaching the events -> handlers are stored in bibedit_engine.js file
$(document).bind("copy", onPerformCopy);
$(document).bind("paste", onPerformPaste);
}
function initMisc(){
/*
* Miscellaneous initialization operations.
*/
// CERN allows for capital MARC indicators.
if (gCERN_SITE){
validMARC.reIndicator1 = /[\dA-Za-z]{1}/;
validMARC.reIndicator2 = /[\dA-Za-z]{1}/;
}
// Warn user if BibEdit is being closed while a record is open.
window.onbeforeunload = function(){
if (gRecID && gRecordDirty){
return '******************** WARNING ********************\n' +
' You have unsubmitted changes.\n\n' +
'You should go back to the page and click either:\n' +
' * Submit (to save your changes permanently)\n or\n' +
' * Cancel (to discard your changes)';
}
};
//Initialising the BibCircualtion integration plugin
$("#bibEditBibCirculationBtn").bind("click", onBibCirculationBtnClicked);
}
/*
* **************************** 3. Ajax ****************************************
*/
function initAjax(){
/*
* Initialize Ajax.
*/
$.ajaxSetup(
{cache: false,
dataType: 'json',
error: onAjaxError,
type: 'POST',
- url: '/record/edit/'
+ url: '/'+ gSITE_RECORD +'/edit/'
}
);
}
function createReq(data, onSuccess, asynchronous){
/*
* Create Ajax request.
*/
if (asynchronous == undefined){
asynchronous = true;
}
// Include and increment transaction ID.
var tID = createReq.transactionID;
createReq.transactionID+=1;
createReq.transactions[tID] = data['requestType'];
data.ID = tID;
// Include cache modification time if we have it.
if (gCacheMTime){
data.cacheMTime = gCacheMTime;
}
// Send the request.
$.ajax({data: {jsondata: JSON.stringify(data)},
success: function(json){
onAjaxSuccess(json, onSuccess);
},
async: asynchronous
});
}
// Transactions data.
createReq.transactionID = 0;
createReq.transactions = [];
function createBulkReq(reqsData, onSuccess, optArgs){
/* optArgs is a disctionary containning the optional arguments
possible keys include:
asynchronous : if the request should be asynchronous
undoRedo : handler for the undo operation
*/
// creating a bulk request ... the cache timestamp is not saved
var data = {'requestType' : 'applyBulkUpdates',
'requestsData' : reqsData,
'recID' : gRecID};
if (optArgs.undoRedo != undefined){
data.undoRedo = optArgs.undoRedo;
}
createReq(data, onSuccess, optArgs.asynchronous);
}
function onAjaxError(XHR, textStatus, errorThrown){
/*
* Handle Ajax request errors.
*/
alert('Request completed with status ' + textStatus +
'\nResult: ' + XHR.responseText +
'\nError: ' + errorThrown);
}
function onAjaxSuccess(json, onSuccess){
/*
* Handle server response to Ajax requests, in particular error situations.
* See BibEdit config for result codes.
* If a function onSuccess is specified this will be called in the end,
* if no error was encountered.
*/
var resCode = json['resultCode'];
var recID = json['recID'];
if (resCode == 100){
// User's session has timed out.
gRecID = null;
gRecIDLoading = null;
- window.location = recID ? gSITE_URL + '/record/' + recID + '/edit/' :
- gSITE_URL + '/record/edit/';
+ window.location = recID ? gSITE_URL + '/'+ gSITE_RECORD +'/' + recID + '/edit/' :
+ gSITE_URL + '/'+ gSITE_RECORD +'/edit/';
return;
}
else if ($.inArray(resCode, [101, 102, 103, 104, 105, 106, 107, 108, 109]) !=
-1){
cleanUp(!gNavigatingRecordSet, null, null, true, true);
if ($.inArray(resCode, [108, 109]) == -1)
$('.headline').text('Record Editor: Record #' + recID);
displayMessage(resCode);
if (resCode == 107)
return;
$('#lnkGetRecord').bind('click', function(event){
getRecord(recID);
event.preventDefault();
});
updateStatus('error', gRESULT_CODES[resCode]);
}
else if (resCode == 110){
displayMessage(resCode, true, [json['errors'].toString()]);
updateStatus('error', gRESULT_CODES[resCode]);
}
else{
var cacheOutdated = json['cacheOutdated'];
var requestType = createReq.transactions[json['ID']];
if (cacheOutdated && requestType == 'submit'){
// User wants to submit, but cache is outdated. Outdated means that the
// DB version of the record has changed after the cache was created.
displayCacheOutdatedScreen(requestType);
$('#lnkMergeCache').bind('click', onMergeClick);
$('#lnkForceSubmit').bind('click', function(event){
onSubmitClick.force = true;
onSubmitClick();
event.preventDefault();
});
$('#lnkDiscardChanges').bind('click', function(event){
onCancelClick();
event.preventDefault();
});
updateStatus('error', 'Error: Record cache is outdated');
}
else{
if (requestType != 'getRecord'){
// On getRecord requests the below actions will be performed in
// onGetRecordSuccess (after cleanup).
var cacheMTime = json['cacheMTime'];
if (cacheMTime)
// Store new cache modification time.
gCacheMTime = cacheMTime;
var cacheDirty = json['cacheDirty'];
if (cacheDirty){
// Cache is dirty. Enable submit button.
gRecordDirty = cacheDirty;
$('#btnSubmit').removeAttr('disabled');
$('#btnSubmit').css('background-color', 'lightgreen');
}
}
if (onSuccess)
// No critical errors; call onSuccess function.
onSuccess(json);
}
}
}
function resetBibeditState(){
/** A function clearing the state of the bibEdit (all the panels content)
*/
gHoldingPenLoadedChanges = {};
gHoldingPenChanges = [];
gDisabledHpEntries = {};
gReadOnlyMode = false;
gRecRevisionHistory = [];
gUndoList = [];
gRedoList = [];
gPhysCopiesNum = 0;
gBibCircUrl = null;
updateRevisionsHistory();
updateInterfaceAccordingToMode();
updateRevisionsHistory();
updateUrView();
updateBibCirculationPanel();
holdingPenPanelRemoveEntries();
}
/*
* **************************** 4. Hash management *****************************
*/
function initStateFromHash(){
/*
* Initialize or update page state from hash.
* Any program functions changing the hash should use changeAndSerializeHash()
* which circumvents this function, meaning this function should only run on
* page load and when browser navigation buttons (ie. Back and Forward) are
* clicked. Any invalid hashes entered by the user will be ignored.
*/
if (window.location.hash == gHash)
// Hash is the same as last time we checked, do nothing.
return;
gHash = window.location.hash;
gHashParsed = deserializeHash(gHash);
gPrevState = gState;
var tmpState = gHashParsed.state;
var tmpRecID = gHashParsed.recid;
var tmpRecRev = gHashParsed.recrev;
var tmpReadOnlyMode = gHashParsed.romode;
// Find out which internal state the new hash leaves us with
if (tmpState && tmpRecID){
// We have both state and record ID.
if ($.inArray(tmpState, ['edit', 'submit', 'cancel', 'deleteRecord']) != -1)
gState = tmpState;
else
// Invalid state, fail...
return;
}
else if (tmpState){
// We only have state.
if (tmpState == 'edit')
gState = 'startPage';
else if (tmpState == 'newRecord')
gState = 'newRecord';
else
// Invalid state, fail... (all states but 'edit' and 'newRecord' are
// illegal without record ID).
return;
}
else
// Invalid hash, fail...
return;
if (gState != gPrevState ||
(gState == 'edit' && parseInt(tmpRecID, 10) != gRecID) ||
(tmpRecRev != undefined && tmpRecRev != gRecRev) ||
(tmpRecRev == undefined && gRecRev != gRecLatestRev) ||
(tmpReadOnlyMode != gReadOnlyMode)){
/* Tested cases:
different record number
different revision
latest revision requested but another open
switched between read-only and read-write modes
*/
// We have an actual and legal change of state. Clean up and update the
// page.
updateStatus('updating');
if (gRecID && !gRecordDirty && !tmpReadOnlyMode)
// If the record is unchanged, delete the cache.
createReq({recID: gRecID, requestType: 'deleteRecordCache'});
switch (gState){
case 'startPage':
cleanUp(true, '', 'recID', true, true);
updateStatus('ready');
break;
case 'edit':
var recID = parseInt(tmpRecID, 10);
if (isNaN(recID)){
// Invalid record ID.
cleanUp(true, tmpRecID, 'recID', true);
$('.headline').text('Record Editor: Record #' + tmpRecID);
displayMessage(102);
updateStatus('error', gRESULT_CODES[102]);
}
else{
cleanUp(true, recID, 'recID');
gReadOnlyMode = tmpReadOnlyMode;
if (tmpRecRev !== undefined && tmpRecRev !== 0){
getRecord(recID, tmpRecRev);
} else {
getRecord(recID);
}
}
break;
case 'newRecord':
cleanUp(true, '', null, null, true);
$('.headline').text('Record Editor: Create new record');
displayNewRecordScreen();
bindNewRecordHandlers();
updateStatus('ready');
break;
case 'submit':
cleanUp(true, '', null, true);
$('.headline').text('Record Editor: Record #' + tmpRecID);
displayMessage(4);
updateStatus('ready');
break;
case 'cancel':
cleanUp(true, '', null, true, true);
updateStatus('ready');
break;
case 'deleteRecord':
cleanUp(true, '', null, true);
$('.headline').text('Record Editor: Record #' + tmpRecID);
displayMessage(6);
updateStatus('ready');
break;
default:
break;
}
}
// else
// What changed was not of interest, continue as if nothing happened.
}
function deserializeHash(aHash){
/*
* Deserializes a string (given as parameter or taken from the window object)
* into the hash object.
*/
if (aHash == undefined){
aHash = window.location.hash;
}
var hash = {};
var args = aHash.slice(1).split('&');
var tmpArray;
for (var i=0, n=args.length; i<n; i++){
tmpArray = args[i].split('=');
if (tmpArray.length == 2)
hash[tmpArray[0]] = tmpArray[1];
}
return hash;
}
function changeAndSerializeHash(updateData){
/*
* Change the hash object to use the data from the object given as parameter.
* Then update the hash accordingly, WITHOUT invoking initStateFromHash().
*/
clearTimeout(gHashCheckTimerID);
gHashParsed = {};
for (var key in updateData){
gHashParsed[key.toString()] = updateData[key].toString();
}
gHash = '#';
for (key in gHashParsed){
gHash += key + '=' + gHashParsed[key] + '&';
}
gHash = gHash.slice(0, -1);
gState = gHashParsed.state;
window.location.hash = gHash;
gHashCheckTimerID = setInterval(initStateFromHash, gHASH_CHECK_INTERVAL);
}
/*
* **************************** 5. Data logic **********************************
*/
function getTagsSorted(){
/*
* Return field tags in sorted order.
*/
var tags = [];
for (var tag in gRecord){
tags.push(tag);
}
return tags.sort();
}
function getFieldPositionInTag(tag, field){
/*
* Determine the local (in tag) position of a new field.
*/
var fields = gRecord[tag];
if (fields){
var fieldLength = fields.length, i = 0;
while (i < fieldLength && cmpFields(field, fields[i]) != -1)
i++;
return i;
}
else
return 0;
}
function getPreviousTag(tag){
/*
* Determine the previous tag in the record (if the given tag is the first
* tag, 0 will be returned).
*/
var tags = getTagsSorted();
var tagPos = $.inArray(tag, tags);
if (tagPos == -1){
tags.push(tag);
tags.sort();
tagPos = $.inArray(tag, tags);
}
if (tagPos > 0)
return tags[tagPos-1];
return 0;
}
function deleteFieldFromTag(tag, fieldPosition){
/*
* Delete a specified field.
*/
var field = gRecord[tag][fieldPosition];
var fields = gRecord[tag];
fields.splice($.inArray(field, fields), 1);
// If last field, delete tag.
if (fields.length === 0){
delete gRecord[tag];
}
}
function cmpFields(field1, field2){
/*
* Compare fields by indicators (tag assumed equal).
*/
if (field1[1].toLowerCase() > field2[1].toLowerCase())
return 1;
else if (field1[1].toLowerCase() < field2[1].toLowerCase())
return -1;
else if (field1[2].toLowerCase() > field2[2].toLowerCase())
return 1;
else if (field1[2].toLowerCase() < field2[2].toLowerCase())
return -1;
return 0;
}
function insertFieldToRecord(record, fieldId, ind1, ind2, subFields){
/**Inserting a new field on the client side and returning the position of the newly created field*/
newField = [subFields, ind1, ind2, '', 0];
if (record[fieldId] == undefined){
record[fieldId] = [newField];
return 0;
} else {
record[fieldId].push(newField);
return (record[fieldId].length-1);
}
}
function transformRecord(record){
/**Transforming a bibrecord to a form that is easier to compare that is a dictionary
* field identifier -> field indices -> fields list -> [subfields list, position in the record]
*
* The data is enriched with the positions inside the record in a following manner:
* each field consists of:
* */
result = {};
for (fieldId in record){
result[fieldId] = {};
indicesList = []; // a list of all the indices ... utilised later when determining the positions
for (fieldIndex in record[fieldId]){
indices = "";
if (record[fieldId][fieldIndex][1] == ' '){
indices += "_";
}else{
indices += record[fieldId][fieldIndex][1];
}
if (record[fieldId][fieldIndex][2] == ' '){
indices += "_";
}else{
indices += record[fieldId][fieldIndex][2];
}
if (result[fieldId][indices] == undefined){
result[fieldId][indices] = []; // a future list of fields sharing the same indice
indicesList.push(indices);
}
result[fieldId][indices].push([record[fieldId][fieldIndex][0], 0]);
}
// now calculating the positions within a field identifier ( utilised on the website )
position = 0;
indices = indicesList.sort();
for (i in indices){
for (fieldInd in result[fieldId][indices[i]]){
result[fieldId][indices[i]][fieldInd][1] = position;
position ++;
}
}
}
return result;
}
function filterChanges(changeset){
/*Filtering the changes list -> removing the changes related to the fields
* that should never be changed */
unchangableTags = {"001" : true}; // a dictionary of the fields that should not be modified
result = [];
for (changeInd in changeset){
change = changeset[changeInd];
if ((change.tag == undefined) || (!(change.tag in unchangableTags))){
result.push(change);
}
}
return result;
}
///// Functions generating easy to display changes list
function compareFields(fieldId, indicators, fieldPos, field1, field2){
result = [];
for (sfPos in field2){
if (field1[sfPos] == undefined){
// adding the subfield at the end of the record can be treated in a more graceful manner
result.push(
{"change_type" : "subfield_added",
"tag" : fieldId,
"indicators" : indicators,
"field_position" : fieldPos,
"subfield_code" : field2[sfPos][0],
"subfield_content" : field2[sfPos][1]});
}
else
{
// the subfield exists in both the records
if (field1[sfPos][0] != field2[sfPos][0]){
// a structural change ... we replace the entire field
return [{"change_type" : "field_changed",
"tag" : fieldId,
"indicators" : indicators,
"field_position" : fieldPos,
"field_content" : field2}];
} else
{
if (field1[sfPos][1] != field2[sfPos][1]){
result.push({"change_type" : "subfield_changed",
"tag" : fieldId,
"indicators" : indicators,
"field_position" : fieldPos,
"subfield_position" : sfPos,
"subfield_code" : field2[sfPos][0],
"subfield_content" : field2[sfPos][1]});
}
}
}
}
for (sfPos in field1){
if (field2[sfPos] == undefined){
result.push({"change_type" : "subfield_removed",
"tag" : fieldId,
"indicators" : indicators,
"field_position" : fieldPos,
"subfield_position" : sfPos});
}
}
return result;
}
function compareIndicators(fieldId, indicators, fields1, fields2){
/*a helper function allowing to compare inside one indicator
* excluded from compareRecords for the code clarity reason*/
result = [];
for (fieldPos in fields2){
if (fields1[fieldPos] == undefined){
result.push({"change_type" : "field_added",
"tag" : fieldId,
"indicators" : indicators,
"field_content" : fields2[fieldPos][0]});
} else { // comparing the content of the subfields
result = result.concat(compareFields(fieldId, indicators, fields1[fieldPos][1], fields1[fieldPos][0], fields2[fieldPos][0]));
}
}
for (fieldPos in fields1){
if (fields2[fieldPos] == undefined){
fieldPosition = fields1[fieldPos][1];
result.push({"change_type" : "field_removed",
"tag" : fieldId,
"indicators" : indicators,
"field_position" : fieldPosition});
}
}
return result;
}
function compareRecords(record1, record2){
/*Compares two bibrecords, producing a list of atom changes that can be displayed
* to the user if for example applying the Holding Pen change*/
// 1) This is more convenient to have a different structure of the storage
r1 = transformRecord(record1);
r2 = transformRecord(record2);
result = [];
for (fieldId in r2){
if (r1[fieldId] == undefined){
for (indicators in r2[fieldId]){
for (field in r2[fieldId][indicators]){
result.push({"change_type" : "field_added",
"tag" : fieldId,
"indicators" : indicators,
"field_content" : r2[fieldId][indicators][field][0]});
}
}
}
else
{
for (indicators in r2[fieldId]){
if (r1[fieldId][indicators] == undefined){
for (field in r2[fieldId][indicators]){
result.push({"change_type" : "field_added",
"tag" : fieldId,
"indicators" : indicators,
"field_content" : r2[fieldId][indicators][field][0]});
}
}
else{
result = result.concat(compareIndicators(fieldId, indicators,
r1[fieldId][indicators], r2[fieldId][indicators]));
}
}
for (indicators in r1[fieldId]){
if (r2[fieldId][indicators] == undefined){
for (fieldInd in r1[fieldId][indicators]){
fieldPosition = r1[fieldId][indicators][fieldInd][1];
result.push({"change_type" : "field_removed",
"tag" : fieldId,
"field_position" : fieldPosition});
}
}
}
}
}
for (fieldId in r1){
if (r2[fieldId] == undefined){
for (indicators in r1[fieldId]){
for (field in r1[fieldId][indicators])
{
// field position has to be calculated here !!!
fieldPosition = r1[fieldId][indicators][field][1]; // field position inside the mark
result.push({"change_type" : "field_removed",
"tag" : fieldId,
"field_position" : fieldPosition});
}
}
}
}
return result;
}
function fieldIsProtected(MARC){
/*
* Determine if a MARC field is protected or part of a protected group of
* fields.
*/
do{
var i = MARC.length - 1;
if ($.inArray(MARC, gPROTECTED_FIELDS) != -1)
return true;
MARC = MARC.substr(0, i);
i--;
}
while (i >= 1)
return false;
}
function containsProtectedField(fieldData){
/*
* Determine if a field data structure contains protected elements (useful
* when checking if a deletion command is valid).
* The data structure must be an object with the following levels
* - Tag
* - Field position
* - Subfield index
*/
var fieldPositions, subfieldIndexes, MARC;
for (var tag in fieldData){
fieldPositions = fieldData[tag];
for (var fieldPosition in fieldPositions){
subfieldIndexes = fieldPositions[fieldPosition];
if (subfieldIndexes.length === 0){
MARC = getMARC(tag, fieldPosition);
if (fieldIsProtected(MARC))
return MARC;
}
else{
for (var i=0, n=subfieldIndexes.length; i<n; i++){
MARC = getMARC(tag, fieldPosition, subfieldIndexes[i]);
if (fieldIsProtected(MARC))
return MARC;
}
}
}
}
return false;
}
function getMARC(tag, fieldPosition, subfieldIndex){
/*
* Return the MARC representation of a field or a subfield.
*/
var field = gRecord[tag][fieldPosition];
var ind1, ind2;
if (validMARC.reControlTag.test(tag)){
ind1 = '';
ind2 = '';
}
else {
ind1 = (field[1] == ' ' || !field[1]) ? '_' : field[1];
ind2 = (field[2] == ' ' || !field[2]) ? '_' : field[2];
}
if (subfieldIndex == undefined)
return tag + ind1 + ind2;
else
return tag + ind1 + ind2 + field[0][subfieldIndex][0];
}
function getFieldTag(MARC){
/*
* Get the tag name of a field in format as specified by gTagFormat.
*/
MARC = MARC.substr(0, 5);
if (gTagFormat == 'human'){
var tagName = gTAG_NAMES[MARC];
if (tagName != undefined)
// Direct hit. Return it.
return tagName;
else{
// Start looking for wildcard hits.
if (MARC.length == 3){
// Controlfield
tagName = gTAG_NAMES[MARC.substr(0, 2) + '%'];
if (tagName != undefined && tagName != MARC + 'x')
return tagName;
}
else{
// Regular field, try finding wildcard hit by shortening expression
// gradually. Ignores wildcards which gives values like '27x'.
var term = MARC + '%', i = 5;
do{
tagName = gTAG_NAMES[term];
if (tagName != undefined){
if (tagName != MARC.substr(0, i) + 'x')
return tagName;
break;
}
i--;
term = MARC.substr(0, i) + '%';
}
while (i >= 3)
}
}
}
return MARC;
}
function getSubfieldTag(MARC){
/*
* Get the tag name of a subfield in format as specified by gTagFormat.
*/
if (gTagFormat == 'human'){
var subfieldName = gTAG_NAMES[MARC];
if (subfieldName != undefined)
return subfieldName;
}
return MARC.charAt(5);
}
function validMARC(datatype, value){
/*
* Validate a value of given datatype according to the MARC standard. The
* value should be restricted/extended to it's expected size before being
* passed to this function.
* Datatype can be 'ControlTag', 'Tag', 'Indicator' or 'SubfieldCode'.
* Returns a boolean.
*/
return eval('validMARC.re' + datatype + '.test(value)');
}
// MARC validation REs
validMARC.reControlTag = /00[1-9A-Za-z]{1}/;
validMARC.reTag = /(0([1-9A-Z][0-9A-Z])|0([1-9a-z][0-9a-z]))|(([1-9A-Z][0-9A-Z]{2})|([1-9a-z][0-9a-z]{2}))/;
validMARC.reIndicator1 = /[\da-z]{1}/;
validMARC.reIndicator2 = /[\da-z]{1}/;
//validMARC.reSubfieldCode = /[\da-z!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?{}_^`~\[\]\\]{1}/;
validMARC.reSubfieldCode = /[\da-z!&quot;#$%&amp;'()*+,-.\/:;&lt;=&gt;?{}_^`~\[\]\\]{1}/;
/*
* **************************** 6. Record UI ***********************************
*/
function onNewRecordClick(event){
/*
* Handle 'New' button (new record).
*/
updateStatus('updating');
if (gRecordDirty){
if (!displayAlert('confirmLeavingChangedRecord')){
updateStatus('ready');
event.preventDefault();
return;
}
}
else
// If the record is unchanged, erase the cache.
if (gReadOnlyMode === false){
createReq({recID: gRecID, requestType: 'deleteRecordCache'});
}
changeAndSerializeHash({state: 'newRecord'});
cleanUp(true, '');
$('.headline').text('Record Editor: Create new record');
displayNewRecordScreen();
bindNewRecordHandlers();
updateStatus('ready');
event.preventDefault();
}
function getRecord(recID, recRev, onSuccess){
/* A function retrieving the bibliographic record, using an AJAX request.
*
* recID : the identifier of a record to be retrieved from the server
* recRev : the revision of the record to be retrieved (0 or undefined
* means retrieving the newest version )
* onSuccess : The callback to be executed upon retrieval. The default
* callback loads the retrieved record into the bibEdit user
* interface
*/
// Temporary store the record ID by attaching it to the onGetRecordSuccess
// function.
if (onSuccess == undefined)
onSuccess = onGetRecordSuccess;
if (recRev !== undefined && recRev !== 0){
changeAndSerializeHash({state: 'edit', recid: recID, recrev: recRev});
}
else{
changeAndSerializeHash({state: 'edit', recid: recID});
}
gRecIDLoading = recID;
reqData = {recID: recID,
requestType: 'getRecord',
deleteRecordCache:
getRecord.deleteRecordCache,
clonedRecord: getRecord.clonedRecord,
inReadOnlyMode: gReadOnlyMode};
if (recRev !== undefined && recRev !== 0){
reqData.recordRevision = recRev;
reqData.inReadOnlyMode = true;
}
resetBibeditState();
createReq(reqData, onSuccess);
onHoldingPenPanelRecordIdChanged(recID); // reloading the Holding Pen toolbar
getRecord.deleteRecordCache = false;
getRecord.clonedRecord = false;
}
// Enable this flag to delete any existing cache before fetching next record.
getRecord.deleteRecordCache = false;
// Enable this flag to tell that we are fetching a record that has just been
// cloned (enables proper feedback, highlighting).
getRecord.clonedRecord = false;
function onGetRecordSuccess(json){
/*
* Handle successfull 'getRecord' requests.
*/
cleanUp(!gNavigatingRecordSet);
// Store record data.
gRecID = json['recID'];
gRecIDLoading = null;
gRecRev = json['recordRevision'];
gRecRevAuthor = json['revisionAuthor'];
gPhysCopiesNum = json['numberOfCopies'];
gBibCircUrl = json['bibCirculationUrl'];
gDisplayBibCircPanel = json['canRecordHavePhysicalCopies'];
var revDt = formatDateTime(getRevisionDate(gRecRev));
var recordRevInfo = "record revision: " + revDt;
var revAuthorString = gRecRevAuthor;
/*$('.headline').html(
'Record Editor: Record #<span id="spnRecID">' + gRecID + '</span>' +
'<div style="margin-left: 5px; font-size: 0.5em; color: #36c;">' +
recordRevInfo + ' ' + revAuthorString + '</div>').css('white-space', 'nowrap');*/
$('.revisionLine').html(recordRevInfo + ' ' + revAuthorString)
gRecord = json['record'];
gTagFormat = json['tagFormat'];
gRecordDirty = json['cacheDirty'];
gCacheMTime = json['cacheMTime'];
if (json['cacheOutdated']){
// User had an existing outdated cache.
displayCacheOutdatedScreen('getRecord');
$('#lnkMergeCache').bind('click', onMergeClick);
$('#lnkDiscardChanges').bind('click', function(event){
getRecord.deleteRecordCache = true;
getRecord(gRecID);
event.preventDefault();
});
$('#lnkRemoveMsg').bind('click', function(event){
$('#bibEditMessage').remove();
event.preventDefault();
});
}
gHoldingPenChanges = json['pendingHpChanges'];
gDisabledHpEntries = json['disabledHpChanges'];
gHoldingPenLoadedChanges = {};
adjustHPChangesetsActivity();
updateBibCirculationPanel();
// updating the undo/redo lists
gUndoList = json['undoList'];
gRedoList = json['redoList'];
updateUrView();
// Display record.
displayRecord();
// Activate menu record controls.
activateRecordMenu();
// the current mode should is indicated by the result from the server
gReadOnlyMode = (json['inReadOnlyMode'] != undefined) ? json['inReadOnlyMode'] : false;
gRecLatestRev = (json['latestRevision'] != undefined) ? json['latestRevision'] : null;
gRecRevisionHistory = (json['revisionsHistory'] != undefined) ? json['revisionsHistory'] : null;
updateInterfaceAccordingToMode();
if (gRecordDirty){
$('#btnSubmit').removeAttr('disabled');
$('#btnSubmit').css('background-color', 'lightgreen');
}
if (gTagFormat == 'MARC')
$('#btnHumanTags').bind('click', onHumanTagsClick).removeAttr('disabled');
else
$('#btnMARCTags').bind('click', onMARCTagsClick).removeAttr('disabled');
// Unfocus record selection field (to facilitate hotkeys).
$('#txtSearchPattern').blur();
if (json['resultCode'] == 9)
$('#spnRecID').effect('highlight', {color: gCLONED_RECORD_COLOR},
gCLONED_RECORD_COLOR_FADE_DURATION);
updateStatus('report', gRESULT_CODES[json['resultCode']]);
updateRevisionsHistory();
adjustGeneralHPControlsVisibility();
createReq({recID: gRecID, requestType: 'getTickets'}, onGetTicketsSuccess);
updateToolbar(true);
}
function onGetTemplateSuccess(json) {
onGetRecordSuccess(json);
}
function onSubmitClick(){
/*
* Handle 'Submit' button (submit record).
*/
updateStatus('updating');
if (displayAlert('confirmSubmit')){
createReq({recID: gRecID, requestType: 'submit',
force: onSubmitClick.force}, function(json){
// Submission was successful.
changeAndSerializeHash({state: 'submit', recid: gRecID});
var resCode = json['resultCode'];
cleanUp(!gNavigatingRecordSet, '', null, true);
updateStatus('report', gRESULT_CODES[resCode]);
displayMessage(resCode);
updateToolbar(false);
resetBibeditState()
});
onSubmitClick.force = false;
resetBibeditState();
}
else
updateStatus('ready');
}
// Enable this flag to force the next submission even if cache is outdated.
onSubmitClick.force = false;
function onPreviewClick(){
/*
* Handle 'Preview' button (preview record).
*/
createReq({recID: gRecID, requestType: 'preview'
}, function(json){
// Preview was successful.
var html_preview = json['html_preview'];
var preview_window = window.open('', '', 'width=768,height=768,resizeable,scrollbars');
preview_window.document.write(html_preview);
preview_window.document.close(); // needed for chrome and safari
});
}
function onCancelClick(){
/*
* Handle 'Cancel' button (cancel editing).
*/
updateStatus('updating');
if (!gRecordDirty || displayAlert('confirmCancel')) {
createReq({
recID: gRecID,
requestType: 'cancel'
}, function(json){
// Cancellation was successful.
changeAndSerializeHash({
state: 'cancel',
recid: gRecID
});
cleanUp(!gNavigatingRecordSet, '', null, true, true);
updateStatus('report', gRESULT_CODES[json['resultCode']]);
});
holdingPenPanelRemoveEntries();
gUndoList = [];
gRedoList = [];
gReadOnlyMode = false;
gRecRevisionHistory = [];
gHoldingPenLoadedChanges = [];
gHoldingPenChanges = [];
gPhysCopiesNum = 0;
gBibCircUrl = null;
// making the changes visible
updateBibCirculationPanel();
updateInterfaceAccordingToMode();
updateRevisionsHistory();
updateUrView();
updateToolbar(false);
}
else {
updateStatus('ready');
}
}
function onCloneRecordClick(){
/*
* Handle 'Clone' button (clone record).
*/
updateStatus('updating');
if (!displayAlert('confirmClone')){
updateStatus('ready');
return;
}
else if (!gRecordDirty)
// If the record is unchanged, erase the cache.
createReq({recID: gRecID, requestType: 'deleteRecordCache'});
createReq({requestType: 'newRecord', newType: 'clone', recID: gRecID},
function(json){
var newRecID = json['newRecID'];
$('#txtSearchPattern').val(newRecID);
getRecord.clonedRecord = true;
getRecord(newRecID);
});
}
function onDeleteRecordClick(){
/*
* Handle 'Delete record' button.
*/
if (gPhysCopiesNum > 0){
displayAlert('errorPhysicalCopiesExist');
return;
}
if (displayAlert('confirmDeleteRecord')){
updateStatus('updating');
createReq({recID: gRecID, requestType: 'deleteRecord'}, function(json){
// Record deletion was successful.
changeAndSerializeHash({state: 'deleteRecord', recid: gRecID});
cleanUp(!gNavigatingRecordSet, '', null, true);
var resCode = json['resultCode'];
// now cleaning the interface - removing holding pen entries and record history
resetBibeditState();
updateStatus('report', gRESULT_CODES[resCode]);
displayMessage(resCode);
updateToolbar(false);
});
}
}
function onMergeClick(event){
/*
* Handle click on 'Merge' link (to merge outdated cache with current DB
* version of record).
*/
notImplemented(event);
updateStatus('updating');
createReq({recID: gRecID, requestType: 'prepareRecordMerge'}, function(json){
// Null gRecID to avoid warning when leaving page.
gRecID = null;
var recID = json['recID'];
- window.location = gSITE_URL + '/record/merge/#recid1=' + recID + '&recid2=' +
+ window.location = gSITE_URL + '/'+ gSITE_RECORD +'/merge/#recid1=' + recID + '&recid2=' +
'tmp';
});
event.preventDefault();
}
function bindNewRecordHandlers(){
/*
* Bind event handlers to links on 'Create new record' page.
*/
$('#lnkNewEmptyRecord').bind('click', function(event){
updateStatus('updating');
createReq({requestType: 'newRecord', newType: 'empty'}, function(json){
getRecord(json['newRecID']);
});
event.preventDefault();
});
for (var i=0, n=gRECORD_TEMPLATES.length; i<n; i++)
$('#lnkNewTemplateRecord_' + i).bind('click', function(event){
updateStatus('updating');
var templateNo = this.id.split('_')[1];
createReq({requestType: 'newRecord', newType: 'template',
templateFilename: gRECORD_TEMPLATES[templateNo][0]}, function(json){
getRecord(json['newRecID'], 0, onGetTemplateSuccess); // recRev = 0 -> current revision
});
event.preventDefault();
});
}
function cleanUp(disableRecBrowser, searchPattern, searchType,
focusOnSearchBox, resetHeadline){
/*
* Clean up display and data.
*/
// Deactivate controls.
deactivateRecordMenu();
if (disableRecBrowser){
disableRecordBrowser();
gResultSet = null;
gResultSetIndex = null;
gNavigatingRecordSet = false;
}
// Clear main content area.
/*if (resetHeadline)
$('.headline').text('Record Editor');*/
$('#bibEditContent').empty();
// Clear search area.
if (typeof(searchPattern) == 'string' || typeof(searchPattern) == 'number')
$('#txtSearchPattern').val(searchPattern);
if ($.inArray(searchType, ['recID', 'reportnumber', 'anywhere']) != -1)
$('#sctSearchType').val(searchPattern);
if (focusOnSearchBox)
$('#txtSearchPattern').focus();
// Clear tickets.
$('#tickets').empty();
// Clear data.
gRecID = null;
gRecord = null;
gTagFormat = null;
gRecordDirty = false;
gCacheMTime = null;
gSelectionMode = false;
gReadOnlyMode = false;
gHoldingPenLoadedChanges = null;
gHoldingPenChanges = null;
gUndoList = [];
gRedoList = [];
gBibCircUrl = null;
gPhysCopiesNum = 0;
}
/*
* **************************** 7. Editor UI ***********************************
*/
function colorFields(){
/*
* Color every other field (rowgroup) gray to increase readability.
*/
$('#bibEditTable tbody:even').each(function(){
$(this).addClass('bibEditFieldColored');
});
}
function reColorFields(){
/*
* Update coloring by removing existing, then recolor.
*/
$('#bibEditTable tbody').each(function(){
$(this).removeClass('bibEditFieldColored');
});
colorFields();
}
function onMARCTagsClick(event){
/*
* Handle 'MARC' link (MARC tags).
*/
$(this).unbind('click').attr('disabled', 'disabled');
createReq({recID: gRecID, requestType: 'changeTagFormat', tagFormat: 'MARC'});
gTagFormat = 'MARC';
updateTags();
$('#btnHumanTags').bind('click', onHumanTagsClick).removeAttr('disabled');
event.preventDefault();
}
function onHumanTagsClick(event){
/*
* Handle 'Human' link (Human tags).
*/
$(this).unbind('click').attr('disabled', 'disabled');
createReq({recID: gRecID, requestType: 'changeTagFormat',
tagFormat: 'human'});
gTagFormat = 'human';
updateTags();
$('#btnMARCTags').bind('click', onMARCTagsClick).removeAttr('disabled');
event.preventDefault();
}
function onLnkSpecialSymbolsClick(){
var special_char_list = ['&#192;','&#193;','&#194;','&#195;','&#196;','&#197;',
'&#198;','&#199;','&#200;','&#201;','&#202;','&#203;',
'&#204;','&#205;','&#206;','&#207;','&#208;','&#209;',
'&#210;','&#211;','&#212;','&#213;','&#214;','&#215;',
'&#216;','&#217;','&#218;','&#219;','&#220;','&#221;',
'&#222;','&#223;','&#224;','&#225;','&#226;','&#227;',
'&#228;','&#229;','&#230;','&#231;','&#232;','&#233;',
'&#234;','&#235;','&#236;','&#237;','&#238;','&#239;',
'&#240;','&#241;','&#242;','&#243;','&#244;','&#245;',
'&#246;','&#247;','&#248;','&#249;','&#250;','&#251;',
'&#252;','&#253;','&#254;','&#255;'];
var html_content;
html_content = '<html><head><title>Special Symbols</title>';
html_content += '<style type="text/css">';
html_content += '#char_table_div { padding: 20px 0px 0px 20px; }';
html_content += '#symbol_table { border: 1px solid black; border-collapse:collapse;}';
html_content += 'td { border: 1px solid black; padding: 5px 5px 5px 5px;}';
html_content += '</style>';
html_content += '</head><body>';
html_content += '<div id="char_table_div"><table id="symbol_table"><tr>';
var char_list_length = special_char_list.length;
for (var i=0; i<char_list_length; i++) {
html_content += '<td>' + special_char_list[i] + '</td>';
if ((i+1)%10 == 0) {
html_content += '</tr><tr>';
}
}
html_content += '</tr></table></div></body></html>';
var special_char_window = window.open('', '', 'width=310,height=310,resizeable,scrollbars');
special_char_window.document.write(html_content);
special_char_window.document.close(); // needed for chrome and safari
}
function updateTags(){
/*
* Check and update all tags (also subfield codes) against the currently
* selected tag format.
*/
$('.bibEditCellFieldTag').each(function(){
var currentTag = $(this).text();
var tmpArray = this.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2];
var newTag = getFieldTag(getMARC(tag, fieldPosition));
if (newTag != currentTag)
$(this).text(newTag);
});
$('.bibEditCellSubfieldTag').each(function(){
var currentTag = $(this).text();
var tmpArray = this.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2],
subfieldIndex = tmpArray[3];
var newTag = getSubfieldTag(getMARC(tag, fieldPosition, subfieldIndex));
if (newTag != currentTag)
$(this).text(newTag);
});
}
function onFieldBoxClick(box){
/*
* Handle field select boxes.
*/
// Check/uncheck all subfield boxes, add/remove selected class.
var rowGroup = $('#rowGroup_' + box.id.slice(box.id.indexOf('_')+1));
if (box.checked){
$(rowGroup).find('td[id^=content]').andSelf().addClass('bibEditSelected');
if (gReadOnlyMode === false){
$('#btnDeleteSelected').removeAttr('disabled');
}
}
else{
$(rowGroup).find('td[id^=content]').andSelf().removeClass(
'bibEditSelected');
if (!$('.bibEditSelected').length)
// Nothing is selected, disable "Delete selected"-button.
$('#btnDeleteSelected').attr('disabled', 'disabled');
}
$(rowGroup).find('input[type="checkbox"]').attr('checked', box.checked);
}
function onSubfieldBoxClick(box){
/*
* Handle subfield select boxes.
*/
var tmpArray = box.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2],
subfieldIndex = tmpArray[3];
var fieldID = tag + '_' + fieldPosition;
var subfieldID = fieldID + '_' + subfieldIndex;
// If uncheck, uncheck field box and remove selected class.
if (!box.checked){
$('#content_' + subfieldID).removeClass('bibEditSelected');
$('#boxField_' + fieldID).attr('checked', false);
$('#rowGroup_' + fieldID).removeClass('bibEditSelected');
if (!$('.bibEditSelected').length)
// Nothing is selected, disable "Delete selected"-button.
$('#btnDeleteSelected').attr('disabled', 'disabled');
}
// If check and all other subfield boxes checked, check field box, add
// selected class.
else{
$('#content_' + subfieldID).addClass('bibEditSelected');
var field = gRecord[tag][fieldPosition];
if (field[0].length == $(
'#rowGroup_' + fieldID + ' input[type=checkbox]' +
'[class=bibEditBoxSubfield]:checked').length){
$('#boxField_' + fieldID).attr('checked', true);
$('#rowGroup_' + fieldID).addClass('bibEditSelected');
}
$('#btnDeleteSelected').removeAttr('disabled');
}
}
function addFieldGatherInformations(fieldTmpNo){
/** Gathering the information about a current form
returns [template_num, data]
This funcion saves the state of a form -> saving the template name and values only would
not be enough. we want to know what has been modified in last-chosen template !
data is in the same format as teh templates data.
*/
var templateNum = $('#selectAddFieldTemplate_' + fieldTmpNo).attr("value");
var tag = $("#txtAddFieldTag_" + fieldTmpNo).attr("value");
// now checking if this is a controlfield ... controlfield if ind1 box is invisible
if ($("#txtAddFieldInd1_" + fieldTmpNo + ":visible").length == 1){
var ind1 = $("#txtAddFieldInd1_" + fieldTmpNo).attr("value");
var ind2 = $("#txtAddFieldInd2_" + fieldTmpNo).attr("value");
var subfieldTmpNo = $('#rowGroupAddField_' + fieldTmpNo).data('freeSubfieldTmpNo');
var subfields = [];
for (i=0;i<subfieldTmpNo;i++){
var subfieldCode = $('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_' + i).attr("value");
var subfieldValue = $('#txtAddFieldValue_' + fieldTmpNo + '_' + i).attr("value");
subfields.push([subfieldCode, subfieldValue]);
}
data = {
"name": "nonexisting template - values taken from the field",
"description": "The description of a template",
"tag" : tag,
"ind1" : ind1,
"ind2" : ind2,
"subfields" : subfields,
"isControlfield" : false
};
} else {
cfValue = $("#txtAddFieldValue_" + fieldTmpNo + "_0").attr("value");
data = {
"name": "nonexisting template - values taken from the field",
"description": "The description of a template",
"tag" : tag,
"value" : cfValue,
"isControlfield" : true
};
}
return [templateNum, data];
}
function addFieldAddSubfieldEditor(jQRowGroupID, fieldTmpNo, defaultCode, defaultValue){
/**
Adding a subfield input control into the editor
optional parameters:
defaultCode - the subfield code that will be displayed
defaultValue - the value that will be displayed by default in the editor
*/
var subfieldTmpNo = $(jQRowGroupID).data('freeSubfieldTmpNo');
$(jQRowGroupID).data('freeSubfieldTmpNo', subfieldTmpNo+1);
var addFieldRows = $(jQRowGroupID + ' tr');
$(addFieldRows).eq(addFieldRows.length-1).before(createAddFieldRow(
fieldTmpNo, subfieldTmpNo, defaultCode, defaultValue));
$('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_' + subfieldTmpNo).bind(
'keyup', onAddFieldChange);
$('#btnAddFieldRemove_' + fieldTmpNo + '_' + subfieldTmpNo).bind('click', function(){
$('#rowAddField_' + this.id.slice(this.id.indexOf('_')+1)).remove();
});
$('#txtAddFieldValue_' + fieldTmpNo + '_' + subfieldTmpNo).bind(
'focus', function(){
if ($(this).hasClass('bibEditVolatileSubfield')){
$(this).select();
$(this).removeClass("bibEditVolatileSubfield");
}
});
var contentEditorId = '#txtAddFieldValue_' + fieldTmpNo + '_' + subfieldTmpNo;
$(contentEditorId).bind('keyup', function(e){
onAddFieldValueKeyPressed(e, jQRowGroupID, fieldTmpNo, subfieldTmpNo);
});
}
function onAddFieldJumpToNextSubfield(jQRowGroupID, fieldTmpNo, subfieldTmpNo){
// checking, how many subfields are there and if last, submitting the form
var numberOfSubfields = $(jQRowGroupID).data('freeSubfieldTmpNo');
if (subfieldTmpNo < (numberOfSubfields - 1)){
var elementCode = "#txtAddFieldSubfieldCode_" + fieldTmpNo + "_" + (subfieldTmpNo + 1);
$(elementCode)[0].focus();
}
else{
addFieldSave(fieldTmpNo);
}
}
function applyFieldTemplate(jQRowGroupID, formData, fieldTmpNo){
/** A function that applies a template
formNo is the number of addfield form that is treated at teh moment
formData is the data of the field template
*/
// first cleaning the existing fields
$(jQRowGroupID).data('isControlfield', formData.isControlfield);
if (formData.isControlfield){
changeFieldToControlfield(fieldTmpNo);
$("#txtAddFieldTag_" + fieldTmpNo).attr("value", formData.tag);
$("#txtAddFieldInd1_" + fieldTmpNo).attr("value", '');
$("#txtAddFieldInd2_" + fieldTmpNo).attr("value", '');
$("#txtAddFieldValue_" + fieldTmpNo + "_0").attr("value", formData.value);
}
else
{
changeFieldToDatafield(fieldTmpNo);
var subfieldTmpNo = $(jQRowGroupID).data('freeSubfieldTmpNo');
$(jQRowGroupID).data('freeSubfieldTmpNo', 0);
for (i=subfieldTmpNo-1; i>=0; i--){
$('#rowAddField_' + fieldTmpNo + '_' + i).remove();
}
for (subfieldInd in formData.subfields){
subfield = formData.subfields[subfieldInd];
addFieldAddSubfieldEditor(jQRowGroupID, fieldTmpNo, subfield[0], subfield[1]);
}
// now changing the main field properties
$("#txtAddFieldTag_" + fieldTmpNo).attr("value", formData.tag);
$("#txtAddFieldInd1_" + fieldTmpNo).attr("value", formData.ind1);
$("#txtAddFieldInd2_" + fieldTmpNo).attr("value", formData.ind2);
}
}
function createAddFieldInterface(initialContent, initialTemplateNo){
/* Create form to add a new field. If only one field is selected, the
* new field will be inserted below it. Otherwise, the new field will
* be inserted in the 3rd position
*/
// Check if we are in the use case of adding in a specific position
var selected_fields = getSelectedFields();
var insert_below_selected = false;
if (selected_fields != undefined) {
var count_fields = 0;
var selected_local_field_pos;
var selected_tag;
for (var tag in selected_fields.fields) {
for (var localFieldPos in selected_fields.fields[tag]) {
count_fields++;
selected_local_field_pos = localFieldPos;
}
selected_tag = tag;
}
if (count_fields === 1)
insert_below_selected = true;
}
var fieldTmpNo = onAddFieldClick.addFieldFreeTmpNo++;
var jQRowGroupID = '#rowGroupAddField_' + fieldTmpNo;
$('#bibEditColFieldTag').css('width', '90px');
var tbodyElements = $('#bibEditTable tbody');
// If only one field selected, add below the selected field
if (insert_below_selected === true) {
$('#rowGroup' + '_' + selected_tag + '_' + selected_local_field_pos).after(
createAddFieldForm(fieldTmpNo, initialTemplateNo));
$(jQRowGroupID).data('insertionPoint', parseInt(selected_local_field_pos) + 1);
$(jQRowGroupID).data('selected_tag', selected_tag);
}
else {
var insertionPoint = (tbodyElements.length >= 4) ? 3 : tbodyElements.length-1;
$('#bibEditTable tbody').eq(insertionPoint).after(
createAddFieldForm(fieldTmpNo, initialTemplateNo));
}
$(jQRowGroupID).data('freeSubfieldTmpNo', 1);
// Bind event handlers.
$('#btnAddFieldAddSubfield_' + fieldTmpNo).bind('click', function(){
addFieldAddSubfieldEditor(jQRowGroupID, fieldTmpNo, "", "");
});
$('#txtAddFieldTag_' + fieldTmpNo).bind('keyup', onAddFieldChange);
$('#txtAddFieldInd1_' + fieldTmpNo).bind('keyup', onAddFieldChange);
$('#txtAddFieldInd2_' + fieldTmpNo).bind('keyup', onAddFieldChange);
$('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_0').bind('keyup',
onAddFieldChange);
$('#txtAddFieldValue_' + fieldTmpNo + '_0').bind('keyup', function (e){
onAddFieldValueKeyPressed(e, jQRowGroupID, fieldTmpNo, 0);
});
$('#selectAddFieldTemplate_' + fieldTmpNo).bind('change', function(e){
value = $('#selectAddFieldTemplate_' + fieldTmpNo).attr("value");
applyFieldTemplate(jQRowGroupID, fieldTemplates[value], fieldTmpNo);
});
$('#selectAddSimilarFields_' + fieldTmpNo).bind('click', function(e){
var data = addFieldGatherInformations(fieldTmpNo);
var numRepetitions = parseInt($('#selectAddFieldTemplateTimes_' +
fieldTmpNo).attr('value'), 10);
for (var i=0; i< numRepetitions; i++){
createAddFieldInterface(data[1], data[0]);
}
});
if (initialContent != undefined){
applyFieldTemplate(jQRowGroupID, initialContent , fieldTmpNo);
}else{
$(jQRowGroupID).data('isControlfield', false);
}
reColorFields();
$('#txtAddFieldTag_' + fieldTmpNo).focus();
// Color the new form for a short period.
$(jQRowGroupID).effect('highlight', {color: gNEW_ADD_FIELD_FORM_COLOR},
gNEW_ADD_FIELD_FORM_COLOR_FADE_DURATION);
}
function onAddSubfieldValueKeyPressed(e, tag, fieldPosition, subfieldPosition){
if (e.which == 13){
// enter key pressed.
var subfieldsNum = $('#rowGroup_' + tag + '_' + fieldPosition + ' .bibEditTxtSubfieldCode').length;
if (subfieldPosition < (subfieldsNum - 1)){
//jump to the next field
$('#txtAddSubfieldsCode_' + tag + '_' + fieldPosition + '_' + (subfieldPosition + 1))[0].focus();
} else {
onAddSubfieldsSave(e, tag, fieldPosition);
}
}
if (e.which == 27){
// escape key pressed
$('#rowAddSubfields_' + tag + '_' + fieldPosition + '_' + 0).nextAll().andSelf().remove();
}
}
function onAddFieldValueKeyPressed(e, jQRowGroupID, fieldTmpNo, subfieldInd){
if (e.which == 13){
// enter key pressed
onAddFieldJumpToNextSubfield(jQRowGroupID, fieldTmpNo, subfieldInd);
}
if (e.which == 27){
// escape key pressed
$(jQRowGroupID).remove();
if (!$('#bibEditTable > [id^=rowGroupAddField]').length)
$('#bibEditColFieldTag').css('width', '48px');
reColorFields();
}
}
function onAddFieldClick(){
/*
* Handle 'Add field' button.
*/
if (failInReadOnly())
return;
createAddFieldInterface();
}
// Incrementing temporary field numbers.
onAddFieldClick.addFieldFreeTmpNo = 100000;
function changeFieldToControlfield(fieldTmpNo){
/**
Switching the field to be a control field
*/
// removing additional entries
var addFieldRows = $('#rowGroupAddField_' + fieldTmpNo + ' tr');
$(addFieldRows).slice(2, addFieldRows.length-1).remove();
// Clear all fields.
var addFieldTextInput = $('#rowGroupAddField_' + fieldTmpNo +
' input[type=text]');
$(addFieldTextInput).val('').removeClass('bibEditInputError');
// Toggle hidden fields.
var elems = $('#txtAddFieldInd1_' + fieldTmpNo + ', #txtAddFieldInd2_' +
fieldTmpNo + ', #txtAddFieldSubfieldCode_' + fieldTmpNo + '_0,' +
'#btnAddFieldAddSubfield_' + fieldTmpNo).hide();
$('#txtAddFieldTag_' + fieldTmpNo).focus();
}
function changeFieldToDatafield(fieldTmpNo){
/**
Switching the field to be a datafield
*/
// making the elements visible
var elems = $('#txtAddFieldInd1_' + fieldTmpNo + ', #txtAddFieldInd2_' +
fieldTmpNo + ', #txtAddFieldSubfieldCode_' + fieldTmpNo + '_0,' +
'#btnAddFieldAddSubfield_' + fieldTmpNo).show();
$('#txtAddFieldTag_' + fieldTmpNo).focus();
}
function onAddFieldChange(event){
/*
* Validate MARC and add or remove error class.
*/
// first handling the case of escape key, which is a little different that others
var fieldTmpNo = this.id.split('_')[1];
if (event.which == 27){
// escape key pressed
var jQRowGroupID = "#rowGroupAddField_" + fieldTmpNo;
$(jQRowGroupID).remove();
if (!$('#bibEditTable > [id^=rowGroupAddField]').length)
$('#bibEditColFieldTag').css('width', '48px');
reColorFields();
}
else if (this.value.length == this.maxLength){
var fieldType;
if (this.id.indexOf('Tag') !== -1){
var jQRowGroupID = "#rowGroupAddField_" + fieldTmpNo;
fieldType = ($(jQRowGroupID).data('isControlfield')) ? 'ControlTag' : 'Tag';
}
else if (this.id.indexOf('Ind1') != -1)
fieldType = 'Indicator1';
else if (this.id.indexOf('Ind2') != -1)
fieldType = 'Indicator2';
else
fieldType = 'SubfieldCode';
var valid = (((fieldType == 'Indicator1' || fieldType == 'Indicator2') &&
(this.value == '_' || this.value == ' ')) ||
validMARC(fieldType, this.value));
if (!valid && !$(this).hasClass('bibEditInputError'))
$(this).addClass('bibEditInputError');
else if (valid){
if ($(this).hasClass('bibEditInputError'))
$(this).removeClass('bibEditInputError');
if (event.keyCode != 9 && event.keyCode != 16) {
switch(fieldType){
case 'ControlTag':
$(this).parent().nextAll().eq(3).children('input').focus();
break;
case 'Tag':
case 'Indicator1':
$(this).next().focus();
break;
case 'Indicator2':
// in case the indicator is present, we can be sure this is not a
// control field... so we can safely jump to the subfield code input
$('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_0')[0].focus();
break;
case 'SubfieldCode':
$(this).parent().next().children('input').focus();
break;
default:
break;
}
}
}
}
else {
if ($(this).hasClass('bibEditInputError')) {
$(this).removeClass('bibEditInputError');
}
}
}
function onAddFieldSave(event){
var fieldTmpNo = this.id.split('_')[1];
addFieldSave(fieldTmpNo);
}
function addFieldSave(fieldTmpNo)
{
/*
* Handle 'Save' button in add field form.
*/
updateStatus('updating');
var jQRowGroupID = "#rowGroupAddField_" + fieldTmpNo;
var controlfield = $(jQRowGroupID).data('isControlfield');
var tag = $('#txtAddFieldTag_' + fieldTmpNo).val();
var value = $('#txtAddFieldValue_' + fieldTmpNo + '_0').val();
var subfields = [], ind1 = ' ', ind2 = ' ';
// variables used when we are adding a field in a specific position
var insertPosition = $(jQRowGroupID).data('insertionPoint');
var selected_tag = $(jQRowGroupID).data('selected_tag');
if (controlfield){
// Controlfield. Validate and prepare to update.
if (fieldIsProtected(tag)){
displayAlert('alertAddProtectedField', [tag]);
updateStatus('ready');
return;
}
if (!validMARC('ControlTag', tag) || value === ''){
displayAlert('alertCriticalInput');
updateStatus('ready');
return;
}
var field = [[], ' ', ' ', value, 0];
var fieldPosition = getFieldPositionInTag(tag, field);
}
else{
// Regular field. Validate and prepare to update.
ind1 = $('#txtAddFieldInd1_' + fieldTmpNo).val();
ind1 = (ind1 === '' || ind1 == '_') ? ' ' : ind1;
ind2 = $('#txtAddFieldInd2_' + fieldTmpNo).val();
ind2 = (ind2 === '' || ind2 == '_') ? ' ' : ind2;
var MARC = tag + ind1 + ind2;
if (fieldIsProtected(MARC)){
displayAlert('alertAddProtectedField', [MARC]);
updateStatus('ready');
return;
}
var validInd1 = (ind1 == ' ' || validMARC('Indicator1', ind1));
var validInd2 = (ind2 == ' ' || validMARC('Indicator2', ind2));
if (!validMARC('Tag', tag) || !validInd1 || !validInd2){
displayAlert('alertCriticalInput');
updateStatus('ready');
return;
}
// Collect valid subfields in an array.
var invalidOrEmptySubfields = false;
$('#rowGroupAddField_' + fieldTmpNo + ' .bibEditTxtSubfieldCode').
each(function(){
var subfieldTmpNo = this.id.slice(this.id.lastIndexOf('_')+1);
var txtValue = $('#txtAddFieldValue_' + fieldTmpNo + '_' +
subfieldTmpNo);
var value = $(txtValue).val();
var isStillVolatile = txtValue.hasClass('bibEditVolatileSubfield');
if (!$(this).hasClass('bibEditInputError') &&
this.value !== '' &&
!$(txtValue).hasClass('bibEditInputError') &&
value !== ''){
if (!isStillVolatile){
subfields.push([this.value, value]);
}
}
else
invalidOrEmptySubfields = true;
});
if (invalidOrEmptySubfields){
if (!subfields.length){
// No valid subfields.
displayAlert('alertCriticalInput');
updateStatus('ready');
return;
}
else if (!displayAlert('confirmInvalidOrEmptyInput')){
updateStatus('ready');
return;
}
}
if (subfields[0] == undefined){
displayAlert('alertEmptySubfieldsList');
return;
}
var field = [subfields, ind1, ind2, '', 0];
var fieldPosition;
if ((insertPosition != undefined) && (tag == selected_tag)) {
fieldPosition = $(jQRowGroupID).data('insertionPoint');
}
else {
fieldPosition = getFieldPositionInTag(tag, field);
}
}
// adding an undo handler
var undoHandler = prepareUndoHandlerAddField(tag,
ind1,
ind2,
fieldPosition,
subfields,
controlfield,
value);
addUndoOperation(undoHandler);
// Create Ajax request.
var data = {
recID: gRecID,
requestType: 'addField',
controlfield: controlfield,
fieldPosition: fieldPosition,
tag: tag,
ind1: ind1,
ind2: ind2,
subfields: subfields,
value: value,
undoRedo: undoHandler
};
createReq(data, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
});
// Continue local updating.
var fields = gRecord[tag];
// New field?
if (!fields) {
gRecord[tag] = [field];
}
else{
fields.splice(fieldPosition, 0, field);
}
// Remove form.
$('#rowGroupAddField_' + fieldTmpNo).remove();
if (!$('#bibEditTable > [id^=rowGroupAddField]').length)
$('#bibEditColFieldTag').css('width', '48px');
// Redraw all fields with the same tag and recolor the full table.
redrawFields(tag);
reColorFields();
// Scroll and color the new field for a short period.
var rowGroup = $('#rowGroup_' + tag + '_' + fieldPosition);
$('#bibEditContent').scrollTop($(rowGroup).position().top);
$(rowGroup).effect('highlight', {color: gNEW_CONTENT_COLOR},
gNEW_CONTENT_COLOR_FADE_DURATION);
}
function onAddSubfieldsClick(img){
/*
* Handle 'Add subfield' buttons.
*/
var fieldID = img.id.slice(img.id.indexOf('_')+1);
addSubfield(fieldID);
}
function addSubfield(fieldID, defSubCode, defValue) {
/* add a subfield based on fieldID, where the first 3 digits are
* the main tag, followed by _ and the position of the field.
* defSubCode = the default value for subfield code
*/
var jQRowGroupID = '#rowGroup_' + fieldID;
var tmpArray = fieldID.split('_');
var tag = tmpArray[0];var fieldPosition = tmpArray[1];
if ($('#rowAddSubfieldsControls_' + fieldID).length === 0){
// The 'Add subfields' form does not exist for this field.
$(jQRowGroupID).append(createAddSubfieldsForm(fieldID, defSubCode, defValue));
$(jQRowGroupID).data('freeSubfieldTmpNo', 1);
$('#txtAddSubfieldsCode_' + fieldID + '_' + 0).bind('keyup',
onAddSubfieldsChange);
$('#txtAddSubfieldsValue_' + fieldID + '_0').bind('keyup', function (e){
onAddSubfieldValueKeyPressed(e, tag, fieldPosition, 0);
});
$('#txtAddSubfieldsCode_' + fieldID + '_' + 0).focus();
}
else{
// The 'Add subfields' form exist for this field. Just add another row.
var subfieldTmpNo = $(jQRowGroupID).data('freeSubfieldTmpNo');
$(jQRowGroupID).data('freeSubfieldTmpNo', subfieldTmpNo+1);
var subfieldTmpID = fieldID + '_' + subfieldTmpNo;
$('#rowAddSubfieldsControls_' + fieldID).before(
createAddSubfieldsRow(fieldID, subfieldTmpNo));
$('#txtAddSubfieldsCode_' + subfieldTmpID).bind('keyup',
onAddSubfieldsChange);
$('#btnAddSubfieldsRemove_' + subfieldTmpID).bind('click', function(){
$('#rowAddSubfields_' + subfieldTmpID).remove();
});
$('#txtAddSubfieldsValue_' + subfieldTmpID).bind('keyup', function (e){
onAddSubfieldValueKeyPressed(e, tag, fieldPosition, subfieldTmpNo);
});
}
}
function onAddSubfieldsChange(event){
/*
* Validate subfield code and add or remove error class.
*/
if (this.value.length == 1){
var valid = validMARC('SubfieldCode', this.value);
if (!valid && !$(this).hasClass('bibEditInputError'))
$(this).addClass('bibEditInputError');
else if (valid){
if ($(this).hasClass('bibEditInputError'))
$(this).removeClass('bibEditInputError');
if (event.keyCode != 9 && event.keyCode != 16){
$(this).parent().next().children('input').focus();
}
}
}
else if ($(this).hasClass('bibEditInputError'))
$(this).removeClass('bibEditInputError');
}
function onAddSubfieldsSave(event, tag, fieldPosition){
/*
* Handle 'Save' button in add subfields form.
*/
updateStatus('updating');
// var tmpArray = this.id.split('_');
// var tag = tmpArray[1], fieldPosition = tmpArray[2];
var fieldID = tag + '_' + fieldPosition;
var subfields = [];
var protectedSubfield = false, invalidOrEmptySubfields = false;
// Collect valid fields in an array.
$('#rowGroup_' + fieldID + ' .bibEditTxtSubfieldCode').
each(function(){
var MARC = getMARC(tag, fieldPosition) + this.value;
if ($.inArray(MARC, gPROTECTED_FIELDS) != -1){
protectedSubfield = MARC;
return false;
}
var subfieldTmpNo = this.id.slice(this.id.lastIndexOf('_')+1);
var txtValue = $('#txtAddSubfieldsValue_' + fieldID + '_' +
subfieldTmpNo);
var value = $(txtValue).val();
if (!$(this).hasClass('bibEditInputError') && this.value !== '' &&
!$(txtValue).hasClass('bibEditInputError') && value !== '')
subfields.push([this.value, value]);
else
invalidOrEmptySubfields = true;
return true;
});
// Report problems, like protected, empty or invalid fields.
if (protectedSubfield){
displayAlert('alertAddProtectedSubfield');
updateStatus('ready');
return;
}
if (invalidOrEmptySubfields && !displayAlert('confirmInvalidOrEmptyInput')){
updateStatus('ready');
return;
}
if (!(subfields.length === 0)){
// creating the undo/redo handler
var urHandler = prepareUndoHandlerAddSubfields(tag, fieldPosition, subfields);
addUndoOperation(urHandler);
// Create Ajax request
var data = {
recID: gRecID,
requestType: 'addSubfields',
tag: tag,
fieldPosition: fieldPosition,
subfields: subfields,
undoRedo: urHandler
};
createReq(data, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
});
// Continue local updating
var field = gRecord[tag][fieldPosition];
field[0] = field[0].concat(subfields);
var rowGroup = $('#rowGroup_' + fieldID);
var coloredRowGroup = $(rowGroup).hasClass('bibEditFieldColored');
$(rowGroup).replaceWith(createField(tag, field, fieldPosition));
if (coloredRowGroup)
$('#rowGroup_' + fieldID).addClass('bibEditFieldColored');
// Color the new fields for a short period.
var rows = $('#rowGroup_' + fieldID + ' tr');
$(rows).slice(rows.length - subfields.length).effect('highlight', {
color: gNEW_CONTENT_COLOR}, gNEW_CONTENT_COLOR_FADE_DURATION);
}
else{
// No valid fields were submitted.
$('#rowAddSubfields_' + fieldID + '_' + 0).nextAll().andSelf().remove();
updateStatus('ready');
}
}
function convertFieldIntoEditable(cell, shouldSelect){
// chacking if the clicked field is still present int the DOM structure ... if not, we have just removed the element
if ($(cell).parent().parent().parent()[0] == undefined){
return;
}
// first we have to detach all exisiting editables ... which means detaching the event
editEvent = 'click';
$(cell).unbind(editEvent);
$(cell).editable(
function(value){
newVal = onContentChange(value, this);
if (newVal.substring(0,9) == "VOLATILE:"){
$(cell).addClass("bibEditVolatileSubfield");
newVal = newVal.substring(9);
$(cell).addClass("bibEditVolatileSubfield");
if (!shouldSelect){
// the field should start selecting all the content upon the click
convertFieldIntoEditable(cell, true);
}
}
else{
$(cell).removeClass("bibEditVolatileSubfield");
if (shouldSelect){
// this is a volatile field any more - clicking should not
// select all the content inside.
convertFieldIntoEditable(cell, false);
}
}
return newVal;
}, {
type: 'autogrow',
callback: function(data, settings){
var tmpArray = this.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2],
subfieldIndex = tmpArray[3];
for (changeNum in gHoldingPenChanges){
change = gHoldingPenChanges[changeNum];
if (change.tag == tag &&
change.field_position == fieldPosition &&
change.subfield_position != undefined &&
change.subfield_position == subfieldIndex){
addChangeControl(changeNum, true);
}
}
},
event: editEvent,
data: function(){
// Get the real content from the record structure (instead of
// from the view, where HTML entities are escaped).
var tmpArray = this.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2],
subfieldIndex = tmpArray[3];
var field = gRecord[tag][fieldPosition];
var tmpResult = "";
if (tmpArray[0] == 'fieldTag'){
var ind1 = (field[1] == " ") ? "_" : field[1];
var ind2 = (field[2] == " ") ? "_" : field[2];
tmpResult = tag + ind1 + ind2;
}
else if (subfieldIndex == undefined){
// Controlfield
tmpResult = field[3];
}
else if (tmpArray[0] == 'subfieldTag'){
tmpResult = field[0][subfieldIndex][0];
}
else {
tmpResult = field[0][subfieldIndex][1];
}
if (tmpResult.substring(0,9) == "VOLATILE:"){
tmpResult = tmpResult.substring(9);
}
return tmpResult;
},
placeholder: '',
width: '100%',
onblur: 'submit',
select: shouldSelect,
autogrow: {
lineHeight: 16,
minHeight: 36
}
});
}
function onContentClick(cell){
/*
* Handle click on editable content fields.
*/
// Check if subfield is volatile subfield from a template
var shouldSelect = false;
if ( $(cell).hasClass('bibEditVolatileSubfield') ){
shouldSelect = true;
}
if (!$(cell).hasClass('edit_area')){
$(cell).addClass('edit_area').removeAttr('onclick');
convertFieldIntoEditable(cell, shouldSelect);
$(cell).trigger('click');
}
$(cell).find('TEXTAREA').bind('keydown', 'return', function(event){ $(event.target).parent().submit();} );
}
function getUpdateSubfieldValueRequestData(tag, fieldPosition, subfieldIndex,
subfieldCode, value, changeNo, undoDescriptor, modifySubfieldCode){
var requestType;
if (modifySubfieldCode == true) {
requestType = 'modifySubfieldTag';
}
else {
requestType = 'modifyContent';
}
var data = {
recID: gRecID,
requestType: requestType,
tag: tag,
fieldPosition: fieldPosition,
subfieldIndex: subfieldIndex,
subfieldCode: subfieldCode,
value: value
};
if (changeNo != undefined && changeNo != -1){
data.hpChanges = {toDisable: [changeNo]};
}
if (undoDescriptor != undefined && undoDescriptor !== null){
data.undoRedo = undoDescriptor;
}
return data;
}
function updateSubfieldValue(tag, fieldPosition, subfieldIndex, subfieldCode,
value, consumedChange, undoDescriptor,
modifySubfieldCode){
updateStatus('updating');
// Create Ajax request for simple updating the subfield value
if (consumedChange == undefined || consumedChange === null){
consumedChange = -1;
}
var data = getUpdateSubfieldValueRequestData(tag,
fieldPosition,
subfieldIndex,
subfieldCode,
value,
consumedChange,
undoDescriptor,
modifySubfieldCode);
createReq(data, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
});
}
function updateFieldTag(oldTag, newTag, oldInd1, oldInd2, ind1, ind2, fieldPosition,
consumedChange, undoDescriptor){
updateStatus('updating');
// Create Ajax request for simple updating the subfield value
if (consumedChange == undefined || consumedChange == null){
consumedChange = -1;
}
var data = getUpdateFieldTagRequestData(oldTag,
oldInd1,
oldInd2,
newTag,
ind1,
ind2,
fieldPosition,
consumedChange,
undoDescriptor);
createReq(data, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
});
}
function getUpdateFieldTagRequestData(oldTag, oldInd1, oldInd2, newTag, ind1, ind2,
fieldPosition, changeNo, undoDescriptor){
var data = {
recID: gRecID,
requestType: "modifyFieldTag",
fieldPosition: fieldPosition,
oldTag: oldTag,
newTag: newTag,
ind1: ind1,
ind2: ind2,
oldInd1: oldInd1,
oldInd2: oldInd2
};
if (changeNo != undefined && changeNo != -1){
data.hpChanges = {toDisable: [changeNo]};
}
if (undoDescriptor != undefined && undoDescriptor != null){
data.undoRedo = undoDescriptor;
}
// updating the local model
var currentField = gRecord[oldTag][fieldPosition];
currentField[1] = ind1;
currentField[2] = ind2;
gRecord[oldTag].splice(fieldPosition,1);
if (gRecord[oldTag].length == 0){
delete gRecord[oldTag];
}
var fieldNewPos;
if (gRecord[newTag] == undefined) {
fieldNewPos = 0;
gRecord[newTag] = [];
gRecord[newTag][fieldNewPos] = currentField;
}
else {
fieldNewPos = gRecord[newTag].length;
gRecord[newTag].splice(fieldNewPos, 0, currentField);
}
// changing the display .... what if being edited right now ?
redrawFields(oldTag);
redrawFields(newTag);
reColorFields();
return data;
}
/*call autosuggest, get the values, suggest them to the user*/
/*this is typically called when autosuggest key is pressed*/
function onAutosuggest(event) {
var mytarget = event.target;
if (event.srcElement) mytarget = event.srcElement;/*fix for IE*/
var myparent = mytarget.parentNode;
var mygrandparent = myparent.parentNode;
var parentid = myparent.id;
var value = mytarget.value;
var mylen = value.length;
var replacement = ""; //used by autocomplete
var tmpArray = mygrandparent.id.split('_');
/*ids for autosuggest/autocomplete html elements*/
var content_id = 'content_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
var autosuggest_id = 'autosuggest_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
var select_id = 'select_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
var maintag = tmpArray[1], fieldPosition = tmpArray[2],
subfieldIndex = tmpArray[3];
var field = gRecord[maintag][fieldPosition];
var subfieldcode = field[0][subfieldIndex][0];
var subtag1 = field[1];
var subtag2 = field[2];
//check if this an autosuggest or autocomplete field.
var fullcode = getMARC(maintag, fieldPosition, subfieldIndex);
var reqtype = ""; //autosuggest or autocomplete, according to tag..
for (var i = 0; i < gAUTOSUGGEST_TAGS.length; i++) {
if (fullcode == gAUTOSUGGEST_TAGS[i]) {
reqtype = "autosuggest";
}
}
for (var i = 0; i < gAUTOCOMPLETE_TAGS.length; i++) {
if (fullcode == gAUTOCOMPLETE_TAGS[i]) {
reqtype = "autocomplete";
}
}
if (fullcode == gKEYWORD_TAG) {
reqtype = "autokeyword";
}
if (reqtype === "") {
return;
}
// Create Ajax request.
var data = {
recID: gRecID,
maintag: maintag,
subtag1: subtag1,
subtag2: subtag2,
subfieldcode: subfieldcode,
requestType: reqtype,
value: value
}; //reqtype is autosuggest, autocomplete or autokeyword
createReq(data, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
suggestions = json[reqtype];
if (reqtype == 'autocomplete') {
if ((suggestions !== null) && (suggestions.length > 0)) {
//put the first one "here"
replacement = suggestions[0];
var myelement = document.getElementById(mygrandparent.id);
if (myelement !== null) {
//put in the the gRecord
gRecord[maintag][fieldPosition][0][subfieldIndex][1] = replacement;
mytarget.value = replacement;
}
//for the rest, create new subfields
for (var i=1;i<suggestions.length;i++) {
var valuein = suggestions[i];
var addhereID = maintag+"_"+fieldPosition;
//an id to indicate where the new subfield goes
addSubfield(addhereID, subfieldcode, valuein);
}
} else { //autocomplete, nothing found
alert("No suggestions for your search term "+value);
}
} //autocomplete
if ((reqtype == 'autosuggest') || (reqtype == 'autokeyword')) {
if ((suggestions !== null) && (suggestions.length > 0)) {
/*put the suggestions in the div autosuggest_xxxx*/
//make a nice box..
mysel = '<table width="400" border="0"><tr><td>' +
'<span class="bibeditscrollArea"><ul>';
//create the select items..
for (var i = 0; i < suggestions.length; i++) {
tmpid = select_id + "-"+suggestions[i];
mysel = mysel +'<li onClick="onAutosuggestSelect(\''+tmpid+'\');">' +
suggestions[i]+"</li>";
}
mysel = mysel + "</ul></td>";
//add a stylish close link in case the user does not find
//the value among the suggestions
mysel = mysel + "<td><form><input type='button' value='close' " +
"onClick='onAutosuggestSelect(\""+select_id+"-"+'\");></form></td>';
mysel = mysel + "</tr></table>";
autosugg_in = document.getElementById(autosuggest_id);
if (autosugg_in !== null) {
autosugg_in.innerHTML = mysel;
}
} else { //there were no suggestions
alert("No suggestions for your search term "+value);
}
} //autosuggest
}, false); /*NB! This function is called synchronously.*/
} //onAutoSuggest
/*put the content of the autosuggest select into the field where autoselect was lauched*/
function onAutosuggestSelect(selectidandselval){
/*first take the selectid. It is the string before the first hyphen*/
var tmpArray = selectidandselval.split('-');
var selectid = tmpArray[0];
var selval = tmpArray[1];
/*generate the content element id and autosuggest element id from the selectid*/
tmpArray = selectid.split('_');
var content_id = 'content_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
var autosuggest_id = 'autosuggest_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
var content_t = document.getElementById(content_id); //table
var content = null; //the actual text
//this is interesting, since if the user is browsing the list of selections by mouse,
//the autogrown form has disapperaed and there is only the table left.. so check..
if (content_t.innerHTML.indexOf("<form>") === 0) {
var content_f = null; //form
var content_ta = null; //textarea
if (content_t) {
content_f = content_t.firstChild; //form is the sub-elem of table
}
if (content_f) {
content_ta = content_f.firstChild; //textarea is the sub-elem of form
}
if (!(content_ta)) {return;}
content = content_ta;
} else {
content = content_t;
}
/*put value in place*/
if (selval) {
content.innerHTML = selval;
content.value = selval;
}
/*remove autosuggest box*/
var autosugg_in = document.getElementById(autosuggest_id);
autosugg_in.innerHTML = "";
}
function onContentChange(value, th){
/*
* Handle 'Save' button in editable content fields.
*/
if (failInReadOnly()){
return null;
}
var tmpArray = th.id.split('_');
var tag = tmpArray[1];
fieldPosition = tmpArray[2];
subfieldIndex = tmpArray[3];
var field = gRecord[tag][fieldPosition];
var tag_ind = tag + field[1] + field[2];
var oldValue = "";
value = value.replace(/\n/g, ' '); // Replace newlines with spaces.
if (subfieldIndex == undefined){
// Controlfield or modifying a field tag
if (tmpArray[0] == 'fieldTag') {
if (tag_ind == value.replace(/_/g, " "))
return escapeHTML(value);
else {
oldValue = tag_ind;
}
}
else if (field[3] == value)
return escapeHTML(value);
else{
oldValue = field[3];
field[3] = value;
subfieldIndex = null;
var subfieldCode = null;
}
}
else{
if (tmpArray[0] == 'subfieldTag') {
if (field[0][subfieldIndex][0] == value)
return escapeHTML(value);
else {
// Regular subfield
oldValue = field[0][subfieldIndex][0];
field[0][subfieldIndex][0] = value;
}
}
else {
if (field[0][subfieldIndex][1] == value)
return escapeHTML(value);
// Regular field
else {
oldValue = field[0][subfieldIndex][1];
field[0][subfieldIndex][1] = value;
}
}
var subfieldCode = field[0][subfieldIndex][0];
}
// setting the undo/redo handlers
var newValue = escapeHTML(value);
var code;
var urHandler;
var operation_type;
if (tmpArray[0] == 'subfieldTag') {
code = gRecord[tag][fieldPosition][0][subfieldIndex][0];
value = field[0][subfieldIndex][1];
operation_type = "change_subfield_code";
urHandler = prepareUndoHandlerChangeSubfield(tag,
fieldPosition,
subfieldIndex,
value,
value,
oldValue, code,
operation_type);
}
else if (tmpArray[0] == 'fieldTag') {
var oldTag = oldValue.substring(0,3);
var oldInd1 = oldValue.substring(3,4);
var oldInd2 = oldValue.substring(4,5);
var newTag = value.substring(0,3);
var newInd1 = value.substring(3,4);
var newInd2 = value.substring(4,5);
operation_type = "change_field_code";
urHandler = prepareUndoHandlerChangeFieldCode(oldTag,
oldInd1,
oldInd2,
newTag,
newInd1,
newInd2,
fieldPosition,
operation_type);
}
else {
code = gRecord[tag][fieldPosition][0][subfieldIndex][0];
operation_type = "change_content";
urHandler = prepareUndoHandlerChangeSubfield(tag,
fieldPosition,
subfieldIndex,
oldValue,
newValue,
code, code,
operation_type);
}
addUndoOperation(urHandler);
// generating the Ajax request
if (tmpArray[0] == 'subfieldTag') {
value = field[0][subfieldIndex][1];
updateSubfieldValue(tag, fieldPosition, subfieldIndex, subfieldCode, value,
null, urHandler, modifySubfieldCode=true);
}
else if (tmpArray[0] == 'fieldTag'){
updateFieldTag(oldTag, newTag, oldInd1, oldInd2, newInd1, newInd2, fieldPosition, null, urHandler);
}
else{
updateSubfieldValue(tag, fieldPosition, subfieldIndex, subfieldCode, value, null, urHandler);
}
var idPrefix;
if (tmpArray[0] == 'subfieldTag') {
idPrefix = '"#subfieldTag_';
}
else{
idPrefix = '"#content_';
}
setTimeout('$(' + idPrefix + tag + '_' + fieldPosition + '_' + subfieldIndex +
'").effect("highlight", {color: gNEW_CONTENT_COLOR}, ' +
'gNEW_CONTENT_COLOR_FADE_DURATION)', gNEW_CONTENT_HIGHLIGHT_DELAY);
// Return escaped value to display.
return newValue;
}
function onMoveSubfieldClick(type, tag, fieldPosition, subfieldIndex){
/*
* Handle subfield moving arrows.
*/
if (failInReadOnly()){
return;
}
updateStatus('updating');
// Check if moving is possible
if (type == 'up') {
if ( (parseInt(subfieldIndex, 10) - 1 )< 0) {
updateStatus('ready', '');
return;
}
}
else {
if ((parseInt(subfieldIndex, 10) + 1) >= gRecord[tag][fieldPosition][0].length) {
updateStatus('ready', '');
return;
}
}
// creating the undoRedo Hanglers
var undoHandler = prepareUndoHandlerMoveSubfields(tag,
parseInt(fieldPosition, 10), parseInt(subfieldIndex, 10), type);
addUndoOperation(undoHandler);
var ajaxData = performMoveSubfield(tag, fieldPosition, subfieldIndex, type, undoHandler);
createReq(ajaxData, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
}, false);
}
function onDeleteClick(event){
/*
* Handle 'Delete selected' button or delete hotkeys.
*/
if (failInReadOnly()){
return;
}
updateStatus('updating');
var toDelete = getSelectedFields();
// Assert that no protected fields are scheduled for deletion.
var protectedField = containsProtectedField(toDelete);
if (protectedField){
displayAlert('alertDeleteProtectedField', [protectedField]);
updateStatus('ready');
return;
}
// register the undo Handler
var urHandler = prepareUndoHandlerDeleteFields(toDelete);
addUndoOperation(urHandler);
var ajaxData = deleteFields(toDelete, urHandler);
createReq(ajaxData, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
});
}
function onMoveFieldUp(tag, fieldPosition) {
if (failInReadOnly()){
return;
}
fieldPosition = parseInt(fieldPosition, 10);
var thisField = gRecord[tag][fieldPosition];
if (fieldPosition > 0) {
var prevField = gRecord[tag][fieldPosition-1];
// check if the previous field has the same indicators
if ( cmpFields(thisField, prevField) === 0 ) {
var undoHandler = prepareUndoHandlerMoveField(tag, fieldPosition, "up");
addUndoOperation(undoHandler);
var ajaxData = performMoveField(tag, fieldPosition, "up", undoHandler);
createReq(ajaxData, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
}, false);
}
}
}
function onMoveFieldDown(tag, fieldPosition) {
if (failInReadOnly()){
return;
}
fieldPosition = parseInt(fieldPosition, 10);
var thisField = gRecord[tag][fieldPosition];
if (fieldPosition < gRecord[tag].length-1) {
var nextField = gRecord[tag][fieldPosition+1];
// check if the next field has the same indicators
if ( cmpFields(thisField, nextField) === 0 ) {
var undoHandler = prepareUndoHandlerMoveField(tag, fieldPosition, "down");
addUndoOperation(undoHandler);
var ajaxData = performMoveField(tag, fieldPosition, "down", undoHandler);
createReq(ajaxData, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
}, false);
}
}
}
function updateInterfaceAccordingToMode(){
/* updates the user interface (in particular the activity of menu buttons)
accordingly to the surrent operation mode of BibEdit.
*/
// updating the switch button caption
if (gReadOnlyMode){
deactivateRecordMenu();
$('#btnSwitchReadOnly').attr("innerHTML", "R/W");
} else {
activateRecordMenu();
$('#btnSwitchReadOnly').attr("innerHTML", "Read-only");
}
}
function switchToReadOnlyMode(){
// Moving to the read only mode with BibEdit
if (gRecordDirty === true){
alert("Please submit the record or cancel your changes before going to the read-only mode ");
return false;
}
gReadOnlyMode = true;
createReq({recID: gRecID, requestType: 'deleteRecordCache'});
gCacheMTime = 0;
updateInterfaceAccordingToMode();
return true;
}
function canSwitchToReadWriteMode(){
/*A function determining if at current moment, it is possible to switch to the read/write mode*/
// If the revision is not the newest -> return false
return true;
}
function switchToReadWriteMode(){
// swtching to a normal editing mode of BibEdit
if (!canSwitchToReadWriteMode()){
alert("It is not possible to switch to the editing mode at the moment");
return false;
}
gReadOnlyMode = false;
// reading the record as if it was just opened
getRecord(gRecID);
updateInterfaceAccordingToMode();
return true;
}
function onSwitchReadOnlyMode(){
// an event habdler being executed when user clicks on the switch to read only mode button
if (gReadOnlyMode){
switchToReadWriteMode();
} else {
switchToReadOnlyMode();
}
}
// functions handling the revisions history
function getCompareClickedHandler(revisionId){
return function(e){
- //document.location = "/record/merge/#recid1=" + gRecID + "&recid2=" + gRecID + "." + revisionId;
- var comparisonUrl = "/record/edit/compare_revisions?recid=" +
+ //document.location = "/"+ gSITE_RECORD +"/merge/#recid1=" + gRecID + "&recid2=" + gRecID + "." + revisionId;
+ var comparisonUrl = "/"+ gSITE_RECORD +"/edit/compare_revisions?recid=" +
gRecID + "&rev1=" + gRecRev + "&rev2=" + revisionId;
var newWindow = window.open(comparisonUrl);
newWindow.focus();
return false;
};
}
function onRevertClick(revisionId){
/*
* Handle 'Revert' button (submit record).
*/
updateStatus('updating');
if (displayAlert('confirmRevert')){
createReq({recID: gRecID, revId: revisionId, requestType: 'revert',
force: onSubmitClick.force}, function(json){
// Submission was successful.
changeAndSerializeHash({state: 'submit', recid: gRecID});
var resCode = json['resultCode'];
cleanUp(!gNavigatingRecordSet, '', null, true);
updateStatus('report', gRESULT_CODES[resCode]);
displayMessage(resCode);
// clear the list of record revisions
resetBibeditState();
});
onSubmitClick.force = false;
}
else
updateStatus('ready');
holdingPenPanelRemoveEntries(); // clearing the holding pen entries list
}
function getRevertClickedHandler(revisionId){
return function(e){
onRevertClick(revisionId);
return false;
};
}
function updateRevisionsHistory(){
if (gRecRevisionHistory === null){
return;
}
var result = "";
var results = [];
for (revInd in gRecRevisionHistory){
tmpResult = displayRevisionHistoryEntry(gRecID, gRecRevisionHistory[revInd]);
tmpResult["revisionID"] = gRecRevisionHistory[revInd];
results.push(tmpResult);
result += tmpResult["HTML"];
}
$("#bibEditRevisionsHistory").attr("innerHTML", result);
$(".bibEditRevHistoryEntryContent").bind("click", function(evt){
var revision = $(this)[0].id.split("_")[1];
updateStatus('updating');
getRecord(gRecID, revision);
});
/*Attaching the actions on user interface*/
for (resultInd in results){
result = results[resultInd];
$('#' + result['compareImgId']).bind("click",
getCompareClickedHandler(result["revisionID"]));
$('#' + result['revertImgId']).bind("click",
getRevertClickedHandler(result["revisionID"]));
}
}
function encodeXml(str){
var resultString = "";
for (var i=0;i<str.length;i++){
var c = str.charAt(i);
switch (c){
case '<':
resultString += "&lt;";
break;
case '>':
resultString += "&gt;";
break;
case '&':
resultString += "&amp;";
break;
case '"':
resultString += "&quot;";
break;
case "'":
resultString += "&apos;";
break;
default:
resultString += c;
break;
}
}
return resultString;
}
function getSelectionMarcXml(){
/*Gets the MARC XML of the current editor selection*/
var checkedFieldBoxes = $('input[class="bibEditBoxField"]:checked'); // interesting only for the controlfields
// where no subfields are
var checkedSubfieldBoxes = $('input[class="bibEditBoxSubfield"]:checked');
// now constructing the interesting data
var selectionNormal = {}; // a dictionary of identifiers taht have appeared already
var selectionControlFields = [];
var selectedFields = []; // a list of fields already selected
var currentField = null; // a curently edited field
// Collect subfields to be deleted in toDelete.
var normalFieldsXml = "";
var controlFieldsXml = "";
$(checkedSubfieldBoxes).each(function(){
var tmpArray = this.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2], subfieldIndex = tmpArray[3];
if (currentField === null || currentField.tag != tag ||
currentField.position != fieldPosition){
if (currentField !== null){
var newPos = selectedFields.length;
selectedFields[newPos] = currentField;
normalFieldsXml += "</datafield>";
}
// creating an empty field
currentField={};
currentField.subfields = [];
currentField.tag = tag;
currentField.position = fieldPosition;
currentField.ind1 = gRecord[tag][fieldPosition][1];
currentField.ind2 = gRecord[tag][fieldPosition][2];
currentField.isControlField = false;
selectionNormal[tag] = true;
normalFieldsXml += "<datafield tag=\"" + currentField.tag + "\" ind1=\"" +
currentField.ind1 + "\" ind2=\"" + currentField.ind2 + "\">";
}
// appending a current subfield
var newPos = currentField.subfields.length;
subfield = gRecord[tag][fieldPosition][0][subfieldIndex];
currentField.subfields[newPos] = subfield;
normalFieldsXml += "<subfield code=\"" + subfield[0] + "\">" + encodeXml(subfield[1]) + "</subfield>";
});
if (currentField !== null){
var newPos = selectedFields.length;
selectedFields[newPos] = currentField;
normalFieldsXml += "</datafield>";
}
// now extending by the control fields (they did not appear earlier)
$(checkedFieldBoxes).each(function(){
var tmpArray = this.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2];
if (selectionNormal[tag] == undefined){
// we have a control field ! otherwise, the field has been already utilised
currentField = {};
currentField.tag = tag;
currentField.value = gRecord[tag][fieldPosition][3];
var newPos = selectionControlFields.length;
selectionControlFields[newPos] = currentField;
controlFieldsXml += "<controlfield tag=\"" + currentField.tag + "\">" + currentField.value+ "</controlfield>";
}
});
return "<record>" + controlFieldsXml + normalFieldsXml + "</record>";
}
function onPerformCopy(){
/** The handler performing the copy operation
*/
if (document.activeElement.type == "textarea" || document.activeElement.type == "text"){
/*we do not want to perform this in case we are in an ordinary text area*/
return;
}
var valueToCopy = getSelectionMarcXml();
clipboardCopyValue(valueToCopy);
}
function onPerformPaste(){
/* Performing the paste operation -> retriexing the MARC XML from the clipboard,
decoding and applying the code to the
According to the default behaviour, the fields are appended as last of the same kind
*/
if (document.activeElement.type == "textarea" || document.activeElement.type == "text"){
/*we do not want to perform this in case we are in an ordinary text area*/
return;
}
var clipboardContent = clipboardPasteValue();
var record = null;
try{
record = decodeMarcXMLRecord(clipboardContent);
} catch (err){
alert("Error when parsing XML occured ... " + err.mesage);
}
var changesAdd = []; // the ajax requests for all the fields
var undoHandlers = [];
for (tag in record){
if (gRecord[tag] == undefined){
gRecord[tag] = [];
}
// now appending the fields
for (fieldInd in record[tag]){
newPos = gRecord[tag].length;
gRecord[tag][newPos] = record[tag][fieldInd];
// enqueue ajax add field request
var isControlfield = record[tag][fieldInd][0].length === 0;
var ind1 = record[tag][fieldInd][1];
var ind2 = record[tag][fieldInd][2];
var subfields = record[tag][fieldInd][0];
var fieldvalue = record[tag][fieldInd][3]; // in case of a control field
changesAdd.push({
recID: gRecID,
requestType: "addField",
controlfield : isControlfield,
fieldPosition : newPos,
tag: tag,
ind1: ind1,
ind2: ind2,
subfields: subfields,
value: fieldvalue
});
undoHandler = prepareUndoHandlerAddField(
tag, ind1, ind2, newPos, subfields, isControlfield, value);
undoHandlers.push(undoHandler);
}
}
undoHandlers.reverse();
var undoHandler = prepareUndoHandlerBulkOperation(undoHandlers, "paste");
addUndoOperation(undoHandler);
// now sending the Ajax Request
var optArgs = {
undoRedo: undoHandler
};
createBulkReq(changesAdd, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
}, optArgs);
// tags have to be redrawn in the increasing order
tags = [];
for (tag in record){
tags.push(tag);
}
tags.sort();
for (tagInd in tags){
redrawFields(tags[tagInd]);
}
reColorFields();
}
function addUndoOperation(operation){
gUndoList.push(operation);
invalidateRedo();
updateUrView();
}
function invalidateRedo(){
/** Invalidates the redo list - after some modification*/
gRedoList = [];
}
function adjustUndoRedoBtnsActivity(){
/** Making the undo/redo buttons active/inactive according to the needs
*/
if (gUndoList.length > 0){
$("#btnUndo").addAttribute("disabled", "");
}
else{
$("#btnUndo").removeAttr("disabled");
}
if (gRedoList.length > 0){
$("#btnRedo").addAttribute("disabled", "");
}
else{
$("#btnRedo").removeAttr("disabled");
}
}
function undoMany(number){
/** A function undoing many operations from the undo list
Arguments:
number: number of operations to undo
*/
var undoOperations = [];
for (i=0;i<number;i++){
undoOperations.push(getUndoOperation());
}
performUndoOperations(undoOperations);
updateUrView();
}
function prepareUndoHandlerEmpty(){
/** Creating an empty undo/redo handler - might be useful in some cases
when undo operation is required but should not be registered
*/
return {
operation_type: "no_operation"
};
}
function prepareUndoHandlerAddField(tag, ind1, ind2, fieldPosition, subfields,
isControlField, value ){
/** A function creating an undo handler for the operation of affing a new
field
Arguments:
tag: tag of the field
ind1: first indicator (a single character string)
ind2: second indicator (a single character string)
fieldPosition: a position of the field among other fields with the same
tag and possibly different indicators)
subFields: a list of fields subfields. each subfield is decribed by
a pair: [code, value]
isControlField: a boolean value indicating if the field is a control field
value: a value of a control field. (important in case of passing
iscontrolField equal true)
*/
var result = {};
result.operation_type = "add_field";
result.newSubfields = subfields;
result.tag = tag;
result.ind1 = ind1;
result.ind2 = ind2;
result.fieldPosition = fieldPosition;
result.isControlField = isControlField;
if (isControlField){
// value == false means that we are dealing with a control field
result.value = value;
} else{
result.subfields = subfields;
}
return result;
}
function prepareUndoHandlerVisualizeChangeset(changesetNumber, changesListBefore, changesListAfter){
var result = {};
result.operation_type = "visualize_hp_changeset";
result.changesetNumber = changesetNumber;
result.oldChangesList = changesListBefore;
result.newChangesList = changesListAfter;
return result;
}
function prepareUndoHandlerApplyHPChange(changeHandler, changeNo){
/** changeHandler - handler to the original undo/redo handler associated with the action
*/
var result = {};
result.operation_type = "apply_hp_change";
result.handler = changeHandler;
result.changeNo = changeNo;
result.changeType = gHoldingPenChanges[changeNo].change_type;
return result;
}
function prepareUndoHandlerApplyHPChanges(changeHandlers, changesBefore){
/** Producing the undo/redo handler associated with application of
more than one HoldingPen change
Arguments:
changeHandlers - a list od undo/redo handlers associated with subsequent changes.
changesBefore = a list of Holding Pen changes before the operation
*/
var result = {};
result.operation_type = "apply_hp_changes";
result.handlers = changeHandlers;
result.changesBefore = changesBefore;
return result;
}
function prepareUndoHandlerRemoveAllHPChanges(hpChanges){
/** A function preparing the undo handler associated with the
removal of all the Holding Pen changes present in teh interface */
var result = {};
result.operation_type = "remove_all_hp_changes";
result.old_changes_list = hpChanges;
return result;
}
function prepareUndoHandlerBulkOperation(undoHandlers, handlerTitle){
/*
Preapring an und/redo handler allowing to treat the bulk operations
( like for example in case of pasting fields )
arguments:
undoHandlers : handlers of separate operations from the bulk
handlerTitle : a message to be displayed in the undo menu
*/
var result = {};
result.operation_type = "bulk_operation";
result.handlers = undoHandlers;
result.title = handlerTitle;
return result;
}
function urPerformAddSubfields(tag, fieldPosition, subfields, isUndo){
var ajaxData = {
recID: gRecID,
requestType: 'addSubfields',
tag: tag,
fieldPosition: fieldPosition,
subfields: subfields,
undoRedo: (isUndo ? "undo": "redo")
};
gRecord[tag][fieldPosition][0] = gRecord[tag][fieldPosition][0].concat(subfields);
redrawFields(tag);
reColorFields();
return ajaxData;
}
function performModifyHPChanges(changesList, isUndo){
/** Undoing or redoing the operation of modifying the changeset
*/
// first local updates
gHoldingPenChanges = changesList;
refreshChangesControls();
var result = prepareOtherUpdateRequest(isUndo);
result.undoRedo = isUndo ? "undo" : "redo";
result.hpChanges = {toOverride: changesList};
return result;
}
function hideUndoPreview(){
$("#undoOperationVisualisationField").addClass("bibEditHiddenElement");
// clearing the selection !
$(".bibEditURDescEntrySelected").removeClass("bibEditURDescEntrySelected");
}
function getRedoOperation(){
// getting the operation to be redoed
currentElement = gRedoList[0];
gRedoList.splice(0, 1);
gUndoList.push(currentElement);
return currentElement;
}
function getUndoOperation(){
// getting the operation to be undoe
currentElement = gUndoList[gUndoList.length - 1];
gUndoList.splice(gUndoList.length - 1, 1);
gRedoList.splice(0, 0, currentElement);
return currentElement;
}
function setAllUnselected(){
// make all the fields and subfields deselected
setSelectionStatusAll(false);
}
function setSelectionStatusAll(status){
// Changing the selection status for all the fields
subfieldBoxes = $('.bibEditBoxSubfield');
subfieldBoxes.each(function(e){
if (subfieldBoxes[e].checked != status){
subfieldBoxes[e].click();
}
});
}
/*** Handlers for specific operations*/
function renderURList(list, idPrefix, isInverted){
// rendering the view of undo/redo list into a human-readible HTML
// list -> an undo or redo list
// idPrefix -> te prefix of the DOM identifier
var result = "";
var isPair = false;
var helperCnt = 0;
var iterationBeginning = list.length - 1;
var iterationJump = -1;
var iterationEnd = -1;
if (isInverted === true){
iterationBeginning = 0;
iterationJump = 1;
iterationEnd = list.length;
}
for (entryInd = iterationBeginning ; entryInd != iterationEnd ; entryInd += iterationJump){
result += "<div class=\"" + (isPair ? "bibEditURPairRow" : "bibEditUROddRow" )+ " bibEditURDescEntry\" id=\"" + idPrefix + "_" + helperCnt + "\">";
result += getHumanReadableUREntry(list[entryInd]);
result += "</div>";
isPair = ! isPair;
helperCnt += 1;
}
result += "";
return result;
}
function prepareApplyHPChangeHandler(){
// A handler for HoldingPen change application/rejection
throw 'to implement';
}
function processURUntil(entry){
// Executing the bulk undo/redo
var idParts = $(entry).attr("id").split("_");
var index = parseInt(idParts[1], 10);
if (idParts[0] == "undo"){
undoMany(index+1);
}
else{
redoMany(index+1);
}
}
function prepareUndoHandlerChangeSubfield(tag, fieldPos, subfieldPos, oldVal,
newVal, oldCode, newCode, operation_type){
var result = {};
result.operation_type = operation_type;
result.tag = tag;
result.oldVal = oldVal;
result.newVal = newVal;
result.oldCode = oldCode;
result.newCode = newCode;
result.fieldPos = fieldPos;
result.subfieldPos = subfieldPos;
return result;
}
function prepareUndoHandlerChangeFieldCode(oldTag, oldInd1, oldInd2, newTag, newInd1,
newInd2, fieldPos, operation_type){
var result = {};
result.operation_type = operation_type;
result.oldTag = oldTag;
result.oldInd1 = oldInd1;
result.oldInd2 = oldInd2;
result.newTag = newTag;
result.ind1 = newInd1;
result.ind2 = newInd2;
result.fieldPos = fieldPos;
if (gRecord[newTag] == undefined) {
result.newFieldPos = 0;
}
else {
result.newFieldPos = gRecord[newTag].length - 1;
}
return result;
}
function setAllSelected(){
// make all the fields and subfields selected
setSelectionStatusAll(true);
}
function showUndoPreview(){
$("#undoOperationVisualisationField").removeClass("bibEditHiddenElement");
}
function prepareUndoHandlerMoveSubfields(tag, fieldPosition, subfieldPosition, direction){
var result = {};
result.operation_type = "move_subfield";
result.tag = tag;
result.field_position = fieldPosition;
result.subfield_position = subfieldPosition;
result.direction = direction;
return result;
}
// Handlers to implement:
function setFieldUnselected(tag, fieldPos){
// unselect a given field
setSelectionStatusField(tag, fieldPos, false);
}
function urPerformRemoveField(tag, position, isUndo){
var toDeleteData = {};
var toDeleteTmp = {};
toDeleteTmp[position] = [];
toDeleteData[tag] = toDeleteTmp;
// first preparing the data of Ajax request
var ajaxData = {
recID: gRecID,
requestType: 'deleteFields',
toDelete: toDeleteData,
undoRedo: (isUndo ? "undo": "redo")
};
// updating the local model
gRecord[tag].splice(position,1);
if (gRecord[tag] == []){
gRecord[tag] = undefined;
}
redrawFields(tag);
reColorFields();
return ajaxData;
}
function prepareOtherUpdateRequest(isUndo){
return {
requestType : 'otherUpdateRequest',
recID : gRecID,
undoRedo: ((isUndo === true) ? "undo" : "redo"),
hpChanges: {}
};
}
function performUndoApplyHpChanges(subRequests, oldChanges){
/**
Arguemnts:
subRequests - subrequests performing the appropriate undo operations
*/
// removing all teh undo/redo informations as they should be passed globally
for (ind in subRequests){
subRequests[ind].undoRedo = undefined;
}
// var gHoldingPenChanges
return {
requestType: 'applyBulkUpdates',
undoRedo: "undo",
requestsData: subRequests,
hpChanges: {toOverride: oldChanges}
};
}
function performBulkOperation(subHandlers, isUndo){
/**
return the bulk operation
Arguments:
subReqs : requests performing the sub-operations
isUndo - is current request undo or redo ?
*/
var subReqs = [];
if (isUndo === true){
subReqs = preparePerformUndoOperations(subHandlers);
} else {
// We can not simply assign and revers as the original would be modified
var handlers = [];
for (handlerInd = subHandlers.length -1; handlerInd >= 0; handlerInd--){
handlers.push(subHandlers[handlerInd]);
}
subReqs = preparePerformRedoOperations(handlers);
}
for (ind in subReqs){
subReqs[ind].undoRedo = undefined;
}
return {
requestType: 'applyBulkUpdates',
undoRedo: (isUndo === true ? "undo" : "redo"),
requestsData: subReqs,
hpChanges: {}
};
}
function preparePerformRedoOperations(operations){
/** Redos an operation passed as an argument */
var ajaxRequestsData = [];
for (operationInd in operations){
var operation = operations[operationInd];
var ajaxData = {};
var isMultiple = false; // is the current decription a list of descriptors ?
switch (operation.operation_type){
case "no_operation":
ajaxData = prepareOtherUpdateRequest(false);
break;
case "change_content":
ajaxData = urPerformChangeSubfieldContent(operation.tag,
operation.fieldPos,
operation.subfieldPos,
operation.newCode,
operation.newVal,
false);
break;
case "change_subfield_code":
ajaxData = urPerformChangeSubfieldCode(operation.tag,
operation.fieldPos,
operation.subfieldPos,
operation.newCode,
operation.newVal,
false);
break;
case "change_field_code":
ajaxData = urPerformChangeFieldCode(operation.newTag,
operation.ind1,
operation.ind2,
operation.oldTag,
operation.oldInd1,
operation.oldInd2,
operation.fieldPos,
false);
break;
case "add_field":
ajaxData = urPerformAddField(operation.isControlField,
operation.fieldPosition,
operation.tag,
operation.ind1,
operation.ind2,
operation.subfields,
operation.value,
false);
break;
case "add_subfields":
ajaxData = urPerformAddSubfields(operation.tag,
operation.fieldPosition,
operation.newSubfields,
false);
break;
case "delete_fields":
ajaxData = urPerformDeletePositionedFieldsSubfields(operation.toDelete, false);
break;
case "move_field":
ajaxData = performMoveField(operation.tag, operation.field_position, operation.direction , false);
break;
case "move_subfield":
ajaxData = performMoveSubfield(operation.tag, operation.field_position, operation.subfield_position, operation.direction, false);
break;
case "bulk_operation":
ajaxData = performBulkOperation(operation.handlers, false);
break;
case "apply_hp_change":
removeViewedChange(operation.changeNo); // we redo the change application so the change itself gets removed
ajaxData = preparePerformRedoOperations([operation.handler]);
ajaxData[0].hpChange = {};
ajaxData[0].hpChange.toDisable = [operation.changeNo]; // reactivate this change
isMultiple = true;
break;
case "apply_hp_changes":
// in this case many changes are applied at once and the list of changes is completely overriden
ajaxData = performUndoApplyHpChanges();
break;
case "change_field":
ajaxData = urPerformChangeField(operation.tag, operation.fieldPos,
operation.newInd1, operation.newInd2,
operation.newSubfields,
operation.newIsControlField,
operation.oldValue , false);
break;
case "visualize_hp_changeset":
ajaxData = prepareVisualizeChangeset(operation.changesetNumber,
operation.newChangesList, "redo");
break;
case "remove_all_hp_changes":
ajaxData = performModifyHPChanges([], false);
break;
default:
alert("Error: wrong operation to redo");
break;
}
// now dealing with the results
if (isMultiple){
// in this case we have to merge lists rather than include inside
for (elInd in ajaxData){
ajaxRequestsData.push(ajaxData[elInd]);
}
}
else{
ajaxRequestsData.push(ajaxData);
}
}
return ajaxRequestsData;
}
function performRedoOperations(operations){
ajaxRequestsData = preparePerformRedoOperations(operations);
// now submitting the bulk request
var optArgs = {
// undoRedo: "redo"
};
createBulkReq(ajaxRequestsData, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
}, optArgs);
}
function prepareUndoHandlerDeleteFields(toDelete){
/*Creating Undo/Redo handler for the operation of removal of fields and/or subfields
Arguments: toDelete - indicates fields and subfields scheduled to be deleted.
this argument should have a following structure:
{
"fields" : { tag: {fieldsPosition: field_structure_similar_to_on_from_gRecord}}
"subfields" : {tag: { fieldPosition: { subfieldPosition: [code, value]}}}
}
*/
var result = {};
result.operation_type = "delete_fields";
result.toDelete = toDelete;
return result;
}
function setSubfieldUnselected(tag, fieldPos, subfieldPos){
// unseelcting a subfield
setSelectionStatusSubfield(tag, fieldPos, subfieldPos, false);
}
function prepareUndoHandlerAddSubfields(tag, fieldPosition, subfields){
/**
tag : tag of the field inside which the fields should be added
fieldPosition: position of the field
subfields: new subfields to be added. This argument should be a list
of lists representing a single subfield. Each subfield is represented
by a list, containing 2 elements. [subfield_code, subfield_value]
*/
var result = {};
result.operation_type = "add_subfields";
result.tag = tag;
result.fieldPosition = fieldPosition;
result.newSubfields = subfields;
return result;
}
function setFieldSelected(tag, fieldPos){
// select a given field
setSelectionStatusField(tag, fieldPos, true);
}
function redoMany(number){
// redoing an indicated number of operations
var redoOperations = [];
for (i=0;i<number;i++){
redoOperations.push(getRedoOperation());
}
performRedoOperations(redoOperations);
updateUrView();
}
function urPerformAddField(controlfield, fieldPosition, tag, ind1, ind2, subfields, value, isUndo){
var ajaxData = {
recID: gRecID,
requestType: 'addField',
controlfield: controlfield,
fieldPosition: fieldPosition,
tag: tag,
ind1: ind1,
ind2: ind2,
subfields: subfields,
value: value,
undoRedo: (isUndo? "undo": "redo")
};
// createReq(data, function(json){
// updateStatus('report', gRESULT_CODES[json['resultCode']]);
// });
// updating the local situation
if (gRecord[tag] == undefined){
gRecord[tag] = [];
}
var newField = [(controlfield ? [] : subfields), ind1, ind2,
(controlfield ? value: ""), 0];
gRecord[tag].splice(fieldPosition, 0, newField);
redrawFields(tag);
reColorFields();
return ajaxData;
}
function urPerformRemoveSubfields(tag, fieldPosition, subfields, isUndo){
var toDelete = {};
toDelete[tag] = {};
toDelete[tag][fieldPosition] = [];
var startingPosition = gRecord[tag][fieldPosition][0].length - subfields.length;
for (var i=startingPosition; i<gRecord[tag][fieldPosition][0].length ; i++){
toDelete[tag][fieldPosition].push(i);
}
var ajaxData = {
recID: gRecID,
requestType: 'deleteFields',
toDelete: toDelete,
undoRedo: (isUndo ? "undo": "redo")
};
// createReq(data, function(json){
// updateStatus('report', gRESULT_CODES[json['resultCode']]);
// });
// modifying the client-side interface
gRecord[tag][fieldPosition][0].splice( gRecord[tag][fieldPosition][0].length - subfields.length, subfields.length);
redrawFields(tag);
reColorFields();
return ajaxData;
}
function updateUrView(){
/*Updating the information box in the bibEdit menu
(What are the current undo/redo handlers*/
$('#undoOperationVisualisationFieldContent')[0].innerHTML =
(gUndoList.length === 0) ? "(empty)" :
renderURList(gUndoList, "undo");
$('#redoOperationVisualisationFieldContent')[0].innerHTML =
(gRedoList.length === 0) ? "(empty)" :
renderURList(gRedoList, "redo", true);
// now attaching the events ... the function is uniform for all the elements present inside the document
var urEntries = $('.bibEditURDescEntry');
urEntries.each(function(index){
$(urEntries[index]).bind("mouseover", function (e){
$(urEntries[index]).find(".bibEditURDescEntryDetails").removeClass("bibEditHiddenElement");
urMarkSelectedUntil(urEntries[index]);
});
$(urEntries[index]).bind("mouseout", function(e){
$(urEntries[index]).find(".bibEditURDescEntryDetails").addClass("bibEditHiddenElement");
});
$(urEntries[index]).bind("click", function(e){
processURUntil(urEntries[index]);
});
});
}
function performMoveSubfield(tag, fieldPosition, subfieldIndex,
direction, undoRedo){
var newSubfieldIndex = parseInt(subfieldIndex, 10) +
(direction == "up" ? -1 : 1);
var fieldID = tag + '_' + fieldPosition;
var field = gRecord[tag][fieldPosition];
var subfields = field[0];
// Create Ajax request.
var ajaxData = {
recID: gRecID,
requestType: 'moveSubfield',
tag: tag,
fieldPosition: fieldPosition,
subfieldIndex: subfieldIndex,
newSubfieldIndex: newSubfieldIndex,
undoRedo: (undoRedo === true) ? "undo" :
((undoRedo === false) ? "redo" : undoRedo)
};
// Continue local updating.
var subfieldToSwap = subfields[newSubfieldIndex];
subfields[newSubfieldIndex] = subfields[subfieldIndex];
subfields[subfieldIndex] = subfieldToSwap;
var rowGroup = $('#rowGroup_' + fieldID);
var coloredRowGroup = $(rowGroup).hasClass('bibEditFieldColored');
$(rowGroup).replaceWith(createField(tag, field, fieldPosition));
if (coloredRowGroup)
$('#rowGroup_' + fieldID).addClass('bibEditFieldColored');
// taking care of having only the new subfield position selected
setAllUnselected();
setSubfieldSelected(tag, fieldPosition, newSubfieldIndex);
return ajaxData;
}
function onRedo(evt){
if (gRedoList.length <= 0){
alert("No Redo operations to process");
return;
}
redoMany(1);
}
// functions related to the automatic field selection/unseletion
function hideRedoPreview(){
$("#redoOperationVisualisationField").addClass("bibEditHiddenElement");
// clearing the selection !
$(".bibEditURDescEntrySelected").removeClass("bibEditURDescEntrySelected");
}
function urPerformAddPositionedFieldsSubfields(toAdd, isUndo){
return createFields(toAdd, isUndo);
}
function setSubfieldSelected(tag, fieldPos, subfieldPos){
// selecting a subfield
setSelectionStatusSubfield(tag, fieldPos, subfieldPos, true);
}
function getHumanReadableUREntry(handler){
// rendering a human readable description of an undo/redo operation
// handler : the u/r handler to render
var operationDescription;
switch (handler.operation_type){
case "move_field":
operationDescription = "move field";
break;
case "change_field":
operationDescription = "change field";
break;
case "move_subfield":
operationDescription = "move subfield";
break;
case "change_content":
operationDescription = "edit subfield";
break;
case "change_subfield_code":
operationDescription = "edit subfield code";
break;
case "change_field_code":
operationDescription = "edit field code";
break;
case "add_field":
operationDescription = "add field";
break;
case "add_subfields":
operationDescription = "add field";
break;
case "delete_fields":
operationDescription = "delete";
break;
case "bulk_operation":
operationDescription = handler.title;
break;
case "apply_hp_change":
operationDescription = "holding pen";
break;
case "visualize_hp_changeset":
operationDescription = "show changes";
break;
case "remove_all_hp_changes":
operationDescription = "remove changes";
break;
default:
operationDescription = "unknown operation";
break;
}
// now rendering parameters of the handler
var readableDescriptors = {
'tag' : 'tag',
'operation_type' : false,
'field_position' : false,
'subfield_position' : false,
'subfieldPos' : false,
'newVal' : 'new value',
'oldVal' : 'old value',
'fieldPos' : false,
'toDelete' : false,
'handlers' : false,
'newFieldPos' : false
};
var handlerDetails = '<table>';
for (characteristic in handler){
if (readableDescriptors[characteristic] !== false){
var characteristicString = characteristic;
if (readableDescriptors[characteristic] != undefined){
characteristicString = readableDescriptors[characteristic];
}
handlerDetails += '<tr><td class="bibEditURDescChar">' +
characteristicString + ':</td><td>' + handler[characteristic] +
'</td></tr>';
}
}
handlerDetails += '</table>';
// now generating the final result
return '<div class="bibEditURDescHeader">' + operationDescription +
'</div><div class="bibEditURDescEntryDetails bibEditHiddenElement">' +
handlerDetails + '</div>';
}
function urMarkSelectedUntil(entry){
// marking all the detailed entries, until a given one as selected
// these entries have the same prefix but a smaller number
var identifierParts = $(entry).attr("id").split("_");
var position = parseInt(identifierParts[1], 10);
var potentialElements = $(".bibEditURDescEntry");
potentialElements.each(function(index){
var curIdentifierParts = $(potentialElements[index]).attr("id").split("_");
if ((curIdentifierParts[0] == identifierParts[0]) &&
(parseInt(curIdentifierParts[1], 10) <= position)){
$(potentialElements[index]).addClass("bibEditURDescEntrySelected");
}
else {
$(potentialElements[index]).removeClass("bibEditURDescEntrySelected");
}
});
}
function onUndo(evt){
if (gUndoList.length <= 0){
alert("No Undo operations to process");
return;
}
undoMany(1);
}
function preparePerformUndoOperations(operations){
/** Undos an operation passed as an argument */
var ajaxRequestsData = [];
for (operationInd in operations){
var operation = operations[operationInd];
var action = null;
var actionData = null;
var ajaxData = {};
var isMultiple = false; // is the current oepration handler a list
// of operations rather than a single op ?
switch (operation.operation_type){
case "no_operation":
ajaxData = prepareOtherUpdateRequest(true);
break;
case "change_content":
ajaxData = urPerformChangeSubfieldContent(operation.tag,
operation.fieldPos,
operation.subfieldPos,
operation.oldCode,
operation.oldVal,
true);
break;
case "change_subfield_code":
ajaxData = urPerformChangeSubfieldCode(operation.tag,
operation.fieldPos,
operation.subfieldPos,
operation.oldCode,
operation.oldVal,
true);
break;
case "change_field_code":
ajaxData = urPerformChangeFieldCode(operation.oldTag,
operation.oldInd1,
operation.oldInd2,
operation.newTag,
operation.ind1,
operation.ind2,
operation.newFieldPos,
true);
break;
case "add_field":
ajaxData = urPerformRemoveField(operation.tag,
operation.fieldPosition,
true);
break;
case "add_subfields":
ajaxData = urPerformRemoveSubfields(operation.tag,
operation.fieldPosition,
operation.newSubfields,
true);
break;
case "delete_fields":
ajaxData = urPerformAddPositionedFieldsSubfields(operation.toDelete, true);
break;
case "move_field":
var newDirection = "up";
var newPosition = operation.field_position + 1;
if (operation.direction == "up"){
newDirection = "down";
newPosition = operation.field_position - 1;
}
ajaxData = performMoveField(operation.tag, newPosition, newDirection, true);
break;
case "move_subfield":
var newDirection = "up";
var newPosition = operation.subfield_position + 1;
if (operation.direction == "up"){
newDirection = "down";
newPosition = operation.subfield_position - 1;
}
ajaxData = performMoveSubfield(operation.tag, operation.field_position,
newPosition, newDirection, true);
break;
case "bulk_operation":
ajaxData = performBulkOperation(operation.handlers, true);
break;
case "apply_hp_change":
ajaxData = preparePerformUndoOperations([operation.handler]);
ajaxData[0]["hpChange"] = {};
ajaxData[0]["hpChange"]["toEnable"] = [operation.changeNo]; // reactivate
isMultiple = true;
revertViewedChange(operation.changeNo);
break;
case "visualize_hp_changeset":
ajaxData = prepareUndoVisualizeChangeset(operation.changesetNumber,
operation.oldChangesList);
break;
case "change_field":
ajaxData = urPerformChangeField(operation.tag, operation.fieldPos,
operation.oldInd1, operation.oldInd2,
operation.oldSubfields,
operation.oldIsControlField,
operation.oldValue , true);
break;
case "remove_all_hp_changes":
ajaxData = performModifyHPChanges(operation.old_changes_list, true);
break;
default:
alert("Error: wrong operation to undo");
break;
}
if (isMultiple){
// in this case we have to merge lists rather than include inside
for (elInd in ajaxData){
ajaxRequestsData.push(ajaxData[elInd]);
}
}
else{
ajaxRequestsData.push(ajaxData);
}
}
return ajaxRequestsData;
}
function performUndoOperations(operations){
var ajaxRequestsData = preparePerformUndoOperations(operations);
// now submitting the ajax request
var optArgs={
// undoRedo: "undo"
};
createBulkReq(ajaxRequestsData, function(json){
updateStatus('report', gRESULT_CODES[json['resultCode']]);
}, optArgs);
}
function prepareUndoHandlerMoveField(tag, fieldPosition, direction){
var result = {};
result.tag = tag;
result.operation_type = "move_field";
result.field_position = fieldPosition;
result.direction = direction;
return result;
}
function prepareUndoHandlerChangeField(tag, fieldPos,
oldInd1, oldInd2, oldSubfields, oldIsControlField, oldValue,
newInd1, newInd2, newSubfields, newIsControlField, newValue){
/** Function building a handler allowing to undo the operation of
changing the field structure.
Changing can happen only if tag and position remain the same,
Otherwise we deal with removal and adding of a field
Arguments:
tag - tag of a field
fieldPos - position of a field
oldInd1, oldInd2 - indices of the old field
oldSubfields - subfields present int the old structure
oldIsControlField - a boolean value indicating if the field
is a control field
oldValue - a value before change in case of field being a control field.
if the field is normal field, this should be equal ""
newInd1, newInd2, newSubfields, newIsControlField, newValue -
Similar parameters describing new structure of a field
*/
var result = {};
result.operation_type = "change_field";
result.tag = tag;
result.fieldPos = fieldPos;
result.oldInd1 = oldInd1;
result.oldInd2 = oldInd2;
result.oldSubfields = oldSubfields;
result.oldIsControlField = oldIsControlField;
result.oldValue = oldValue;
result.newInd1 = newInd1;
result.newInd2 = newInd2;
result.newSubfields = newSubfields;
result.newIsControlField = newIsControlField;
result.newValue = newValue;
return result;
}
function showRedoPreview(){
$("#redoOperationVisualisationField").removeClass("bibEditHiddenElement");
}
function deleteFields(toDeleteStruct, undoRedo){
// a function deleting the specified fields on both client and server sides
//
// toDeleteFields : a structure describing fields and subfields to delete
// this structure is the same as for the function createFields
var toDelete = {};
// first we convert the data into a different format, loosing the informations about
// subfields of entirely removed fields
// first the entirely deleted fields
for (tag in toDeleteStruct.fields){
if (toDelete[tag] == undefined){
toDelete[tag] = {};
}
for (fieldPos in toDeleteStruct.fields[tag]){
toDelete[tag][fieldPos] = [];
}
}
for (tag in toDeleteStruct.subfields){
if (toDelete[tag] == undefined){
toDelete[tag] = {};
}
for (fieldPos in toDeleteStruct.subfields[tag]){
toDelete[tag][fieldPos] = [];
for (subfieldPos in toDeleteStruct.subfields[tag][fieldPos]){
toDelete[tag][fieldPos].push(subfieldPos);
}
}
}
var tagsToRedraw = [];
// reColorTable is true if any field are completely deleted.
var reColorTable = false;
// first we have to encode all the data in a single dictionary
// Create Ajax request.
var ajaxData = {
recID: gRecID,
requestType: 'deleteFields',
toDelete: toDelete,
undoRedo: (undoRedo === true) ? "undo" : ((undoRedo === false) ? "redo" :
undoRedo)
};
// Continue local updating.
// Parse data structure and delete accordingly in record.
var fieldsToDelete, subfieldIndexesToDelete, field, subfields, subfieldIndex;
for (var tag in toDelete) {
tagsToRedraw.push(tag);
fieldsToDelete = toDelete[tag];
// The fields should be treated in the decreasing order (during the removal, indices may change)
traversingOrder = [];
for (fieldPosition in fieldsToDelete) {
traversingOrder.push(fieldPosition);
}
// normal sorting will do this in a lexycographical order ! (problems if > 10 subfields
// function provided, allows sorting in the reversed order
var traversingOrder = traversingOrder.sort(function(a, b){
return b - a;
});
for (var fieldInd in traversingOrder) {
var fieldPosition = traversingOrder[fieldInd];
var fieldID = tag + '_' + fieldPosition;
subfieldIndexesToDelete = fieldsToDelete[fieldPosition];
if (subfieldIndexesToDelete.length === 0)
deleteFieldFromTag(tag, fieldPosition);
else {
// normal sorting will do this in a lexycographical order ! (problems if > 10 subfields
subfieldIndexesToDelete.sort(function(a, b){
return a - b;
});
field = gRecord[tag][fieldPosition];
subfields = field[0];
for (var j = subfieldIndexesToDelete.length - 1; j >= 0; j--){
subfields.splice(subfieldIndexesToDelete[j], 1);
}
}
}
}
// If entire fields has been deleted, redraw all fields with the same tag
// and recolor the full table.
for (tag in tagsToRedraw)
redrawFields(tagsToRedraw[tag]);
reColorFields();
return ajaxData;
}
function getSelectedFields(){
/** Function returning a list of selected fields
Returns all the fields and subfields that are slected.
The structure of a result is following:
{
"fields" : { tag: {fieldsPosition: field_structure_similar_to_on_from_gRecord}}
"subfields" : {tag: { fieldPosition: { subfieldPosition: [code, value]}}}
}
*/
var selectedFields = {};
var selectedSubfields = {};
var checkedFieldBoxes = $('input[class="bibEditBoxField"]:checked');
var checkedSubfieldBoxes = $('input[class="bibEditBoxSubfield"]:checked');
if (!checkedFieldBoxes.length && !checkedSubfieldBoxes.length)
// No fields selected
return null;
// Collect fields to be deleted in toDelete.
$(checkedFieldBoxes).each(function(){
var tmpArray = this.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2];
if (!selectedFields[tag]) {
selectedFields[tag] = {};
}
selectedFields[tag][fieldPosition] = gRecord[tag][fieldPosition];
});
// Collect subfields to be deleted in toDelete.
$(checkedSubfieldBoxes).each(function(){
var tmpArray = this.id.split('_');
var tag = tmpArray[1], fieldPosition = tmpArray[2], subfieldIndex = tmpArray[3];
if (selectedFields[tag] == undefined || selectedFields[tag][fieldPosition] == undefined){
// this field has not been selected entirely, we can proceed with processing subfield slection
if (!selectedSubfields[tag]) {
selectedSubfields[tag] = {};
selectedSubfields[tag][fieldPosition] = {};
selectedSubfields[tag][fieldPosition][subfieldIndex] =
gRecord[tag][fieldPosition][0][subfieldIndex];
}
else {
if (!selectedSubfields[tag][fieldPosition])
selectedSubfields[tag][fieldPosition] = {};
selectedSubfields[tag][fieldPosition][subfieldIndex] =
gRecord[tag][fieldPosition][0][subfieldIndex];
}
} // else - this subfield is a part of entirely selected field...
// we have already included the information about subfields
});
var result={};
result.fields = selectedFields;
result.subfields = selectedSubfields;
return result;
}
function urPerformChangeSubfieldContent(tag, fieldPos, subfieldPos, code, val, isUndo){
// changing the server side model
var ajaxData = {
recID: gRecID,
requestType: 'modifyContent',
tag: tag,
fieldPosition: fieldPos,
subfieldIndex: subfieldPos,
subfieldCode: code,
value: val,
undoRedo: (isUndo ? "undo": "redo")
};
// changing the model
gRecord[tag][fieldPos][0][subfieldPos][0] = code;
gRecord[tag][fieldPos][0][subfieldPos][1] = val;
// changing the display .... what if being edited right now ?
redrawFields(tag);
reColorFields();
return ajaxData;
}
function urPerformChangeSubfieldCode(tag, fieldPos, subfieldPos, code, val, isUndo){
// changing the server side model
var ajaxData = {
recID: gRecID,
requestType: 'modifySubfieldTag',
tag: tag,
fieldPosition: fieldPos,
subfieldIndex: subfieldPos,
subfieldCode: code,
value: val,
undoRedo: (isUndo ? "undo": "redo")
};
gRecord[tag][fieldPos][0][subfieldPos][0] = code;
gRecord[tag][fieldPos][0][subfieldPos][1] = val;
// changing the display .... what if being edited right now ?
redrawFields(tag);
reColorFields();
return ajaxData;
}
function urPerformChangeFieldCode(oldTag, oldInd1, oldInd2, newTag, ind1, ind2,
fieldPos, isUndo){
// changing the server side model
var ajaxData = {
recID: gRecID,
requestType: 'modifyFieldTag',
oldTag: newTag,
oldInd1: ind1,
oldInd2: ind2,
newTag: oldTag,
fieldPosition: fieldPos,
ind1: oldInd1,
ind2: oldInd2,
undoRedo: (isUndo ? "undo": "redo")
};
// updating the local model
var currentField = gRecord[newTag][fieldPos];
currentField[1] = oldInd1;
currentField[2] = oldInd2;
gRecord[newTag].splice(fieldPos,1);
if (gRecord[newTag].length == 0){
delete gRecord[newTag];
}
var fieldNewPos;
if (gRecord[oldTag] == undefined) {
fieldNewPos = 0;
gRecord[oldTag] = [];
gRecord[oldTag][fieldNewPos] = currentField;
}
else {
fieldNewPos = gRecord[oldTag].length;
gRecord[oldTag].splice(fieldNewPos, 0, currentField);
}
// changing the display .... what if being edited right now ?
redrawFields(newTag);
redrawFields(oldTag);
reColorFields();
return ajaxData;
}
function performChangeField(tag, fieldPos, ind1, ind2, subFields, isControlfield,
value, undoRedo){
/** Function changing the field structure and generating an appropriate AJAX
request handler
Arguments:
tag, fieldPos, ind1, ind2, subFields, isControlfield, value - standard
values describing a field. tag, fieldPos are used to locate the field
instance (which has to exist) and its content is modified accordingly.
undoRedo - a undoRedo Handler or one of the words "undo"/"redo"
*/
var ajaxData = {
recID: gRecID,
requestType: "modifyField",
controlfield : isControlfield,
fieldPosition : fieldPos,
ind1: ind1,
ind2: ind2,
tag: tag,
subFields: subFields,
undoRedo : undoRedo,
hpChanges: {}
};
// local changes
gRecord[tag][fieldPos][0] = subFields;
gRecord[tag][fieldPos][1] = ind1;
gRecord[tag][fieldPos][2] = ind2;
gRecord[tag][fieldPos][3] = value;
redrawFields(tag);
reColorFields();
return ajaxData;
}
function urPerformChangeField(tag, fieldPos, ind1, ind2, subFields,
isControlfield, value, isUndo){
/**
*/
return performChangeField(tag, fieldPos, ind1, ind2, subFields,
isControlfield, value, (isUndo ? "undo" : "redo"));
}
function performMoveField(tag, oldFieldPosition, direction, undoRedo){
var newFieldPosition = oldFieldPosition + (direction == "up" ? -1 : 1);
// Create Ajax request.
var ajaxData = {
recID: gRecID,
requestType: 'moveField',
tag: tag,
fieldPosition: oldFieldPosition,
direction: direction,
undoRedo: (undoRedo === true) ? "undo" : ((undoRedo === false) ? "redo" : undoRedo)
};
//continue updating locally
var currentField = gRecord[tag][oldFieldPosition];
gRecord[tag][oldFieldPosition] = gRecord[tag][newFieldPosition];
gRecord[tag][newFieldPosition] = currentField;
$('tbody#rowGroup_'+tag+'_'+(newFieldPosition)).replaceWith(
createField(tag, gRecord[tag][newFieldPosition], newFieldPosition));
$('tbody#rowGroup_'+tag+'_'+oldFieldPosition).replaceWith(
createField(tag, gRecord[tag][oldFieldPosition], oldFieldPosition));
reColorFields();
// Now taking care of having the new field selected and the rest unselected
setAllUnselected();
setFieldSelected(tag, newFieldPosition);
//$('#boxField_'+tag+'_'+(newFieldPosition)).click();
return ajaxData;
}
function setSelectionStatusField(tag, fieldPos, status){
var fieldCheckbox = $('#boxField_' + tag + '_' + fieldPos);
var subfieldCheckboxes = $('#rowGroup_' + tag + '_' + fieldPos + ' .bibEditBoxSubfield');
fieldCheckbox.each(function(ind){
if (fieldCheckbox[ind].checked != status)
{
fieldCheckbox[ind].click();
}
});
}
function urPerformDeletePositionedFieldsSubfields(toDelete, isUndo){
return deleteFields(toDelete, isUndo);
}
/** General Undo/Redo treatment lists */
function setSelectionStatusSubfield(tag, fieldPos, subfieldPos, status){
var subfieldCheckbox = $('#boxSubfield_' + tag + '_' + fieldPos + '_' + subfieldPos);
if (subfieldCheckbox[0].checked != status)
{
subfieldCheckbox[0].click();
}
}
function createFields(toCreateFields, isUndo){
// a function adding fields.
// toCreateFields : a structure describing fields and subfields to create
// this structure is the same as for the function deleteFields
// 1) Preparing the AJAX request
var tagsToRedraw = {};
var ajaxData = {
recID: gRecID,
requestType: 'addFieldsSubfieldsOnPositions',
fieldsToAdd: toCreateFields.fields,
subfieldsToAdd: toCreateFields.subfields
};
if (isUndo != undefined){
ajaxData['undoRedo'] = (isUndo ? "undo": "redo");
}
// 2) local processing -> creating the fields locally
// - first creating the missing fields so all the subsequent field indices are correcr
for (tag in toCreateFields.fields){
if (gRecord[tag] == undefined){
gRecord[tag] = [];
}
tagsToRedraw[tag] = true;
var fieldIndices = [];
for (fieldPos in toCreateFields.fields[tag]){
fieldIndices.push(fieldPos);
}
fieldIndices.sort(); // we have to add fields in the increasing order
for (indInd in fieldIndices){
var fieldIndexToAdd = fieldIndices[indInd]; // index of the field index to add in the indices array
var newField = toCreateFields.fields[tag][fieldIndexToAdd];
gRecord[tag].splice(fieldIndexToAdd, 0, newField);
}
}
// - now appending the remaining subfields
for (tag in toCreateFields.subfields){
tagsToRedraw[tag] = true;
for (fieldPos in toCreateFields.subfields[tag]){
var subfieldPositions = [];
for (subfieldPos in toCreateFields.subfields[tag][fieldPos]){
subfieldPositions.push(subfieldPos);
}
subfieldPositions.sort();
for (subfieldInd in subfieldPositions){
subfieldPosition = subfieldPositions[subfieldInd];
gRecord[tag][fieldPos][0].splice(
subfieldPosition, 0,
toCreateFields.subfields[tag][fieldPos][subfieldPosition]);
}
}
}
// - redrawint the affected tags
for (tag in tagsToRedraw){
redrawFields(tag);
}
reColorFields();
return ajaxData;
}
/* Bibcirculation Panel functions */
function isBibCirculationPanelNecessary(){
/** A function checking if the BibCirculation connectivity panel should
be displayed. This information is derieved from the state of the record.
Returns true or false
*/
if (gRecID === null){
return false;
}
// only if the record is saved and exists in the database and belongs
// to a particular colelction
return gDisplayBibCircPanel;
}
function updateBibCirculationPanel(){
/** Updates the BibCirculation panel contents and visibility
*/
if (gDisplayBibCircPanel === false){
// in case, the panel is present, should be hidden
$("#bibEditBibCircConnection").addClass("bibEditHiddenElement");
}
else {
// the panel must be present - we have to show it
$(".bibEditBibCircConnection").removeClass("bibEditHiddenElement");
}
var interfaceElement = $("#bibEditBibCircConnection");
if (isBibCirculationPanelNecessary()){
interfaceElement.removeClass("bibEditHiddenElement");
} else {
interfaceElement.addClass("bibEditHiddenElement");
}
// updating the content
var copiesCountElement = $('#bibEditBibCirculationCopies');
copiesCountElement.attr("innerHTML", gPhysCopiesNum);
}
function bibCircIntGetEditCopyUrl(recId){
/**A function returning the address under which, the edition of a
given record is possible
*/
// return "/admin/bibcirculation/bibcirculationadmin.py/get_item_details?recid=" + recId;
return gBibCircUrl;
}
function onBibCirculationBtnClicked(e){
/** A function redirecting the user to the BibCiculation web interface
*/
var link = bibCircIntGetEditCopyUrl(gRecID);
window.open(link);
}
diff --git a/modules/bibedit/lib/bibedit_engine.py b/modules/bibedit/lib/bibedit_engine.py
index 4f973b3c5..913a40611 100644
--- a/modules/bibedit/lib/bibedit_engine.py
+++ b/modules/bibedit/lib/bibedit_engine.py
@@ -1,1198 +1,1199 @@
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0103
"""Invenio BibEdit Engine."""
__revision__ = "$Id"
from invenio import bibrecord
from invenio import bibformat
from invenio.bibedit_config import CFG_BIBEDIT_AJAX_RESULT_CODES, \
CFG_BIBEDIT_JS_CHECK_SCROLL_INTERVAL, CFG_BIBEDIT_JS_HASH_CHECK_INTERVAL, \
CFG_BIBEDIT_JS_CLONED_RECORD_COLOR, \
CFG_BIBEDIT_JS_CLONED_RECORD_COLOR_FADE_DURATION, \
CFG_BIBEDIT_JS_NEW_ADD_FIELD_FORM_COLOR, \
CFG_BIBEDIT_JS_NEW_ADD_FIELD_FORM_COLOR_FADE_DURATION, \
CFG_BIBEDIT_JS_NEW_CONTENT_COLOR, \
CFG_BIBEDIT_JS_NEW_CONTENT_COLOR_FADE_DURATION, \
CFG_BIBEDIT_JS_NEW_CONTENT_HIGHLIGHT_DELAY, \
CFG_BIBEDIT_JS_STATUS_ERROR_TIME, CFG_BIBEDIT_JS_STATUS_INFO_TIME, \
CFG_BIBEDIT_JS_TICKET_REFRESH_DELAY, CFG_BIBEDIT_MAX_SEARCH_RESULTS, \
CFG_BIBEDIT_TAG_FORMAT, CFG_BIBEDIT_AJAX_RESULT_CODES_REV, \
CFG_BIBEDIT_AUTOSUGGEST_TAGS, CFG_BIBEDIT_AUTOCOMPLETE_TAGS_KBS,\
CFG_BIBEDIT_KEYWORD_TAXONOMY, CFG_BIBEDIT_KEYWORD_TAG, \
CFG_BIBEDIT_KEYWORD_RDFLABEL, CFG_BIBEDIT_LOG, CFG_BIBEDIT_LOGFILE
from invenio.config import CFG_SITE_LANG, CFG_DEVEL_SITE
from invenio.bibedit_dblayer import get_name_tags_all, reserve_record_id, \
get_related_hp_changesets, get_hp_update_xml, delete_hp_change, \
get_record_last_modification_date, get_record_revision_author, \
get_marcxml_of_record_revision, delete_related_holdingpen_changes, \
get_record_revisions
from invenio.bibedit_utils import cache_exists, cache_expired, \
create_cache_file, delete_cache_file, get_bibrecord, \
get_cache_file_contents, get_cache_mtime, get_record_templates, \
get_record_template, latest_record_revision, record_locked_by_other_user, \
record_locked_by_queue, save_xml_record, touch_cache_file, \
update_cache_file_contents, get_field_templates, get_marcxml_of_revision, \
revision_to_timestamp, timestamp_to_revision, \
get_record_revision_timestamps, record_revision_exists, \
can_record_have_physical_copies, bibedit_log
from invenio.bibrecord import create_record, print_rec, record_add_field, \
record_add_subfield_into, record_delete_field, \
record_delete_subfield_from, \
record_modify_subfield, record_move_subfield, \
create_field, record_replace_field, record_move_fields, \
record_modify_controlfield, record_get_field_values, \
record_get_subfields
from invenio.config import CFG_BIBEDIT_PROTECTED_FIELDS, CFG_CERN_SITE, \
- CFG_SITE_URL
+ CFG_SITE_URL, CFG_SITE_RECORD
from invenio.search_engine import record_exists, search_pattern
from invenio.webuser import session_param_get, session_param_set
from invenio.bibcatalog import bibcatalog_system
from invenio.webpage import page
from invenio.htmlutils import get_mathjax_header
from invenio.bibknowledge import get_kbd_values_for_bibedit, get_kbr_values, \
get_kbt_items_for_bibedit #autosuggest
from invenio.bibcirculation_dblayer import get_number_copies, has_copies
from invenio.bibcirculation_utils import create_item_details_url
from datetime import datetime
import re
import difflib
import zlib
import sys
if sys.hexversion < 0x2060000:
try:
import simplejson as json
simplejson_available = True
except ImportError:
# Okay, no Ajax app will be possible, but continue anyway,
# since this package is only recommended, not mandatory.
simplejson_available = False
else:
import json
simplejson_available = True
import invenio.template
bibedit_templates = invenio.template.load('bibedit')
re_revdate_split = re.compile('^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)')
def get_empty_fields_templates():
"""
Returning the templates of empty fields :
- an empty data field
- an empty control field
"""
return [{
"name": "Empty field",
"description": "The data field not containing any " + \
"information filled in",
"tag" : "",
"ind1" : "",
"ind2" : "",
"subfields" : [("","")],
"isControlfield" : False
},{
"name" : "Empty control field",
"description" : "The controlfield not containing any " + \
"data or tag description",
"isControlfield" : True,
"tag" : "",
"value" : ""
}]
def get_available_fields_templates():
"""
A method returning all the available field templates
Returns a list of descriptors. Each descriptor has
the same structure as a full field descriptor inside the
record
"""
templates = get_field_templates()
result = get_empty_fields_templates()
for template in templates:
tplTag = template[3].keys()[0]
field = template[3][tplTag][0]
if (field[0] == []):
# if the field is a controlField, add different structure
result.append({
"name" : template[1],
"description" : template[2],
"isControlfield" : True,
"tag" : tplTag,
"value" : field[3]
})
else:
result.append({
"name": template[1],
"description": template[2],
"tag" : tplTag,
"ind1" : field[1],
"ind2" : field[2],
"subfields" : field[0],
"isControlfield" : False
})
return result
def perform_request_init(uid, ln, req, lastupdated):
"""Handle the initial request by adding menu and JavaScript to the page."""
errors = []
warnings = []
body = ''
# Add script data.
record_templates = get_record_templates()
record_templates.sort()
tag_names = get_name_tags_all()
protected_fields = ['001']
protected_fields.extend(CFG_BIBEDIT_PROTECTED_FIELDS.split(','))
history_url = '"' + CFG_SITE_URL + '/admin/bibedit/bibeditadmin.py/history"'
cern_site = 'false'
if not simplejson_available:
title = 'Record Editor'
body = '''Sorry, the record editor cannot operate when the
`simplejson' module is not installed. Please see the INSTALL
file.'''
return page(title = title,
body = body,
errors = [],
warnings = [],
uid = uid,
language = ln,
navtrail = "",
lastupdated = lastupdated,
req = req)
if CFG_CERN_SITE:
cern_site = 'true'
data = {'gRECORD_TEMPLATES': record_templates,
'gTAG_NAMES': tag_names,
'gPROTECTED_FIELDS': protected_fields,
'gSITE_URL': '"' + CFG_SITE_URL + '"',
+ 'gSITE_RECORD': '"' + CFG_SITE_RECORD + '"',
'gHISTORY_URL': history_url,
'gCERN_SITE': cern_site,
'gHASH_CHECK_INTERVAL': CFG_BIBEDIT_JS_HASH_CHECK_INTERVAL,
'gCHECK_SCROLL_INTERVAL': CFG_BIBEDIT_JS_CHECK_SCROLL_INTERVAL,
'gSTATUS_ERROR_TIME': CFG_BIBEDIT_JS_STATUS_ERROR_TIME,
'gSTATUS_INFO_TIME': CFG_BIBEDIT_JS_STATUS_INFO_TIME,
'gCLONED_RECORD_COLOR':
'"' + CFG_BIBEDIT_JS_CLONED_RECORD_COLOR + '"',
'gCLONED_RECORD_COLOR_FADE_DURATION':
CFG_BIBEDIT_JS_CLONED_RECORD_COLOR_FADE_DURATION,
'gNEW_ADD_FIELD_FORM_COLOR':
'"' + CFG_BIBEDIT_JS_NEW_ADD_FIELD_FORM_COLOR + '"',
'gNEW_ADD_FIELD_FORM_COLOR_FADE_DURATION':
CFG_BIBEDIT_JS_NEW_ADD_FIELD_FORM_COLOR_FADE_DURATION,
'gNEW_CONTENT_COLOR': '"' + CFG_BIBEDIT_JS_NEW_CONTENT_COLOR + '"',
'gNEW_CONTENT_COLOR_FADE_DURATION':
CFG_BIBEDIT_JS_NEW_CONTENT_COLOR_FADE_DURATION,
'gNEW_CONTENT_HIGHLIGHT_DELAY':
CFG_BIBEDIT_JS_NEW_CONTENT_HIGHLIGHT_DELAY,
'gTICKET_REFRESH_DELAY': CFG_BIBEDIT_JS_TICKET_REFRESH_DELAY,
'gRESULT_CODES': CFG_BIBEDIT_AJAX_RESULT_CODES,
'gAUTOSUGGEST_TAGS' : CFG_BIBEDIT_AUTOSUGGEST_TAGS,
'gAUTOCOMPLETE_TAGS' : CFG_BIBEDIT_AUTOCOMPLETE_TAGS_KBS.keys(),
'gKEYWORD_TAG' : '"' + CFG_BIBEDIT_KEYWORD_TAG + '"'
}
fieldTemplates = get_available_fields_templates()
jquery_scripts = ['jquery.min.js', 'jquery.effects.core.min.js',
'jquery.effects.highlight.min.js',
'jquery.autogrow.js', 'jquery.jeditable.mini.js',
'jquery.hotkeys.min.js', 'json2.js']
scripts = ['bibedit_display.js', 'bibedit_engine.js', 'bibedit_keys.js',
'bibedit_menu.js', 'bibedit_holdingpen.js', 'marcxml.js',
'bibedit_clipboard.js']
stylesheets = ['bibedit.css']
body += bibedit_templates.page_headers(jquery_scripts, scripts,
stylesheets, data, fieldTemplates)
body += bibedit_templates.menu()
body += ' <div id="bibEditContent"></div>\n'
return body, errors, warnings
def get_xml_comparison(header1, header2, xml1, xml2):
"""
Return diffs of two MARCXML records.
"""
return "".join(difflib.unified_diff(xml1.splitlines(1),
xml2.splitlines(1), header1, header2))
def get_marcxml_of_revision_id(recid, revid):
"""
Return MARCXML string with corresponding to revision REVID
(=RECID.REVDATE) of a record. Return empty string if revision
does not exist.
"""
res = ""
job_date = "%s-%s-%s %s:%s:%s" % re_revdate_split.search(revid).groups()
tmp_res = get_marcxml_of_record_revision(recid, job_date)
if tmp_res:
for row in tmp_res:
res += zlib.decompress(row[0]) + "\n"
return res
def perform_request_compare(ln, recid, rev1, rev2):
"""Handle a request for comparing two records"""
body = ""
errors = []
warnings = []
if (not record_revision_exists(recid, rev1)) or \
(not record_revision_exists(recid, rev2)):
body = "The requested record revision does not exist !"
else:
xml1 = get_marcxml_of_revision_id(recid, rev1)
xml2 = get_marcxml_of_revision_id(recid, rev2)
fullrevid1 = "%i.%s" % (recid, rev1)
fullrevid2 = "%i.%s" % (recid, rev2)
comparison = bibedit_templates.clean_value(
get_xml_comparison(fullrevid1, fullrevid2, xml1, xml2),
'text').replace('\n', '<br />\n ')
job_date1 = "%s-%s-%s %s:%s:%s" % re_revdate_split.search(rev1).groups()
job_date2 = "%s-%s-%s %s:%s:%s" % re_revdate_split.search(rev2).groups()
body += bibedit_templates.history_comparebox(ln, job_date1,
job_date2, comparison)
return body, errors, warnings
def perform_request_newticket(recid, uid):
"""create a new ticket with this record's number
@param recid: record id
@param uid: user id
@return: (error_msg, url)
"""
t_id = bibcatalog_system.ticket_submit(uid, "", recid, "")
t_url = ""
errmsg = ""
if t_id:
#get the ticket's URL
t_url = bibcatalog_system.ticket_get_attribute(uid, t_id, 'url_modify')
else:
errmsg = "ticket_submit failed"
return (errmsg, t_url)
def perform_request_ajax(req, recid, uid, data, isBulk = False, \
ln = CFG_SITE_LANG):
"""Handle Ajax requests by redirecting to appropriate function."""
bibedit_log( str(data) )
response = {}
request_type = data['requestType']
undo_redo = None
if data.has_key("undoRedo"):
undo_redo = data["undoRedo"]
# Call function based on request type.
if request_type == 'searchForRecord':
# Search request.
response.update(perform_request_search(data))
elif request_type in ['changeTagFormat']:
# User related requests.
response.update(perform_request_user(req, request_type, recid, data))
elif request_type in ('getRecord', 'submit', 'cancel', 'newRecord',
'deleteRecord', 'deleteRecordCache', 'prepareRecordMerge', 'revert'):
# 'Major' record related requests.
response.update(perform_request_record(req, request_type, recid, uid,
data))
elif request_type in ('addField', 'addSubfields', \
'addFieldsSubfieldsOnPositions', 'modifyContent', \
'modifySubfieldTag', 'modifyFieldTag', \
'moveSubfield', 'deleteFields', 'moveField', \
'modifyField', 'otherUpdateRequest', \
'disableHpChange', 'deactivateHoldingPenChangeset'):
# Record updates.
cacheMTime = data['cacheMTime']
if data.has_key('hpChanges'):
hpChanges = data['hpChanges']
else:
hpChanges = {}
response.update(perform_request_update_record(request_type, recid, \
uid, cacheMTime, data, \
hpChanges, undo_redo, \
isBulk, ln))
elif request_type in ('autosuggest', 'autocomplete', 'autokeyword'):
response.update(perform_request_autocomplete(request_type, recid, uid, \
data))
elif request_type in ('getTickets', ):
# BibCatalog requests.
response.update(perform_request_bibcatalog(request_type, recid, uid))
elif request_type in ('getHoldingPenUpdates', ):
response.update(perform_request_holdingpen(request_type, recid))
elif request_type in ('getHoldingPenUpdateDetails', \
'deleteHoldingPenChangeset'):
updateId = data['changesetNumber']
response.update(perform_request_holdingpen(request_type, recid, \
updateId))
elif request_type in ('applyBulkUpdates', ):
# a general version of a bulk request
changes = data['requestsData']
cacheMTime = data['cacheMTime']
response.update(perform_bulk_request_ajax(req, recid, uid, changes, \
undo_redo, cacheMTime))
elif request_type in ('preview', ):
response.update(perform_request_preview_record(request_type, recid, uid))
return response
def perform_bulk_request_ajax(req, recid, uid, reqsData, undoRedo, cacheMTime):
""" An AJAX handler used when treating bulk updates """
lastResult = {}
lastTime = cacheMTime
isFirst = True
for data in reqsData:
assert data != None
data['cacheMTime'] = lastTime
if isFirst and undoRedo != None:
# we add the undo/redo handler to the first operation in order to
# save the handler on the server side !
data['undoRedo'] = undoRedo
isFirst = False
lastResult = perform_request_ajax(req, recid, uid, data, True)
# now we have to update the cacheMtime in next request !
# if lastResult.has_key('cacheMTime'):
try:
lastTime = lastResult['cacheMTime']
except:
raise Exception(str(lastResult))
return lastResult
def perform_request_search(data):
"""Handle search requests."""
response = {}
searchType = data['searchType']
if searchType is None:
searchType = "anywhere"
searchPattern = data['searchPattern']
if searchType == 'anywhere':
pattern = searchPattern
else:
pattern = searchType + ':' + searchPattern
result_set = list(search_pattern(p=pattern))
response['resultCode'] = 1
response['resultSet'] = result_set[0:CFG_BIBEDIT_MAX_SEARCH_RESULTS]
return response
def perform_request_user(req, request_type, recid, data):
"""Handle user related requests."""
response = {}
if request_type == 'changeTagFormat':
try:
tagformat_settings = session_param_get(req, 'bibedit_tagformat')
except KeyError:
tagformat_settings = {}
tagformat_settings[recid] = data['tagFormat']
session_param_set(req, 'bibedit_tagformat', tagformat_settings)
response['resultCode'] = 2
return response
def perform_request_holdingpen(request_type, recId, changeId=None):
"""
A method performing the holdingPen ajax request. The following types of
requests can be made:
getHoldingPenUpdates - retrieving the holding pen updates pending
for a given record
"""
response = {}
if request_type == 'getHoldingPenUpdates':
changeSet = get_related_hp_changesets(recId)
changes = []
for change in changeSet:
changes.append((str(change[0]), str(change[1])))
response["changes"] = changes
elif request_type == 'getHoldingPenUpdateDetails':
# returning the list of changes related to the holding pen update
# the format based on what the record difference xtool returns
assert(changeId != None)
hpContent = get_hp_update_xml(changeId)
holdingPenRecord = create_record(hpContent[0], "xm")[0]
# databaseRecord = get_record(hpContent[1])
response['record'] = holdingPenRecord
response['changeset_number'] = changeId
elif request_type == 'deleteHoldingPenChangeset':
assert(changeId != None)
delete_hp_change(changeId)
return response
def perform_request_record(req, request_type, recid, uid, data, ln=CFG_SITE_LANG):
"""Handle 'major' record related requests like fetching, submitting or
deleting a record, cancel editing or preparing a record for merging.
"""
response = {}
if request_type == 'newRecord':
# Create a new record.
new_recid = reserve_record_id()
new_type = data['newType']
if new_type == 'empty':
# Create a new empty record.
create_cache_file(recid, uid)
response['resultCode'], response['newRecID'] = 6, new_recid
elif new_type == 'template':
# Create a new record from XML record template.
template_filename = data['templateFilename']
template = get_record_template(template_filename)
if not template:
response['resultCode'] = 108
else:
record = create_record(template)[0]
if not record:
response['resultCode'] = 109
else:
record_add_field(record, '001',
controlfield_value=str(new_recid))
create_cache_file(new_recid, uid, record, True)
response['resultCode'], response['newRecID'] = 7, new_recid
elif new_type == 'clone':
# Clone an existing record (from the users cache).
existing_cache = cache_exists(recid, uid)
if existing_cache:
try:
record = get_cache_file_contents(recid, uid)[2]
except:
# if, for example, the cache format was wrong (outdated)
record = get_bibrecord(recid)
else:
# Cache missing. Fall back to using original version.
record = get_bibrecord(recid)
record_delete_field(record, '001')
record_add_field(record, '001', controlfield_value=str(new_recid))
create_cache_file(new_recid, uid, record, True)
response['resultCode'], response['newRecID'] = 8, new_recid
elif request_type == 'getRecord':
# Fetch the record. Possible error situations:
# - Non-existing record
# - Deleted record
# - Record locked by other user
# - Record locked by queue
# A cache file will be created if it does not exist.
# If the cache is outdated (i.e., not based on the latest DB revision),
# cacheOutdated will be set to True in the response.
record_status = record_exists(recid)
existing_cache = cache_exists(recid, uid)
read_only_mode = False
if data.has_key("inReadOnlyMode"):
read_only_mode = data['inReadOnlyMode']
if record_status == 0:
response['resultCode'] = 102
elif record_status == -1:
response['resultCode'] = 103
elif not read_only_mode and not existing_cache and \
record_locked_by_other_user(recid, uid):
response['resultCode'] = 104
elif not read_only_mode and existing_cache and \
cache_expired(recid, uid) and \
record_locked_by_other_user(recid, uid):
response['resultCode'] = 104
elif not read_only_mode and record_locked_by_queue(recid):
response['resultCode'] = 105
else:
if data.get('deleteRecordCache'):
delete_cache_file(recid, uid)
existing_cache = False
pending_changes = []
disabled_hp_changes = {}
if read_only_mode:
if data.has_key('recordRevision'):
record_revision_ts = data['recordRevision']
record_xml = get_marcxml_of_revision(recid, \
record_revision_ts)
record = create_record(record_xml)[0]
record_revision = timestamp_to_revision(record_revision_ts)
pending_changes = []
disabled_hp_changes = {}
else:
# a normal cacheless retrieval of a record
record = get_bibrecord(recid)
record_revision = get_record_last_modification_date(recid)
if record_revision == None:
record_revision = datetime.now().timetuple()
pending_changes = []
disabled_hp_changes = {}
cache_dirty = False
mtime = 0
undo_list = []
redo_list = []
elif not existing_cache:
record_revision, record = create_cache_file(recid, uid)
mtime = get_cache_mtime(recid, uid)
pending_changes = []
disabled_hp_changes = {}
undo_list = []
redo_list = []
cache_dirty = False
else:
#TODO: This try except should be replaced with something nicer,
# like an argument indicating if a new cache file is to
# be created
try:
cache_dirty, record_revision, record, pending_changes, \
disabled_hp_changes, undo_list, redo_list = \
get_cache_file_contents(recid, uid)
touch_cache_file(recid, uid)
mtime = get_cache_mtime(recid, uid)
if not latest_record_revision(recid, record_revision) and \
get_record_revisions(recid) != ():
# This sould prevent from using old cache in case of
# viewing old version. If there are no revisions,
# it means we should skip this step because this
# is a new record
response['cacheOutdated'] = True
except:
record_revision, record = create_cache_file(recid, uid)
mtime = get_cache_mtime(recid, uid)
pending_changes = []
disabled_hp_changes = {}
cache_dirty = False
undo_list = []
redo_list = []
if data.get('clonedRecord',''):
response['resultCode'] = 9
else:
response['resultCode'] = 3
revision_author = get_record_revision_author(recid, record_revision)
latest_revision = get_record_last_modification_date(recid)
if latest_revision == None:
latest_revision = datetime.now().timetuple()
last_revision_ts = revision_to_timestamp(latest_revision)
revisions_history = get_record_revision_timestamps(recid)
number_of_physical_copies = get_number_copies(recid)
bibcirc_details_URL = create_item_details_url(recid, ln)
can_have_copies = can_record_have_physical_copies(recid)
response['cacheDirty'], response['record'], \
response['cacheMTime'], response['recordRevision'], \
response['revisionAuthor'], response['lastRevision'], \
response['revisionsHistory'], response['inReadOnlyMode'], \
response['pendingHpChanges'], response['disabledHpChanges'], \
response['undoList'], response['redoList'] = cache_dirty, \
record, mtime, revision_to_timestamp(record_revision), \
revision_author, last_revision_ts, revisions_history, \
read_only_mode, pending_changes, disabled_hp_changes, \
undo_list, redo_list
response['numberOfCopies'] = number_of_physical_copies
response['bibCirculationUrl'] = bibcirc_details_URL
response['canRecordHavePhysicalCopies'] = can_have_copies
# Set tag format from user's session settings.
try:
tagformat_settings = session_param_get(req, 'bibedit_tagformat')
tagformat = tagformat_settings[recid]
except KeyError:
tagformat = CFG_BIBEDIT_TAG_FORMAT
response['tagFormat'] = tagformat
elif request_type == 'submit':
# Submit the record. Possible error situations:
# - Missing cache file
# - Cache file modified in other editor
# - Record locked by other user
# - Record locked by queue
# - Invalid XML characters
# If the cache is outdated cacheOutdated will be set to True in the
# response.
if not cache_exists(recid, uid):
response['resultCode'] = 106
elif not get_cache_mtime(recid, uid) == data['cacheMTime']:
response['resultCode'] = 107
elif cache_expired(recid, uid) and \
record_locked_by_other_user(recid, uid):
response['resultCode'] = 104
elif record_locked_by_queue(recid):
response['resultCode'] = 105
else:
try:
tmp_result = get_cache_file_contents(recid, uid)
record_revision = tmp_result[1]
record = tmp_result[2]
pending_changes = tmp_result[3]
# disabled_changes = tmp_result[4]
xml_record = print_rec(record)
record, status_code, list_of_errors = create_record(xml_record)
if status_code == 0:
response['resultCode'], response['errors'] = 110, \
list_of_errors
elif not data['force'] and \
not latest_record_revision(recid, record_revision):
response['cacheOutdated'] = True
if CFG_DEVEL_SITE:
response['record_revision'] = record_revision.__str__()
response['newest_record_revision'] = \
get_record_last_modification_date(recid).__str__()
else:
save_xml_record(recid, uid)
response['resultCode'] = 4
except Exception, e:
response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV[ \
'error_wrong_cache_file_format']
if CFG_DEVEL_SITE: # return debug information in the request
response['exception_message'] = e.__str__()
elif request_type == 'revert':
revId = data['revId']
job_date = "%s-%s-%s %s:%s:%s" % re_revdate_split.search(revId).groups()
revision_xml = get_marcxml_of_revision(recid, job_date)
save_xml_record(recid, uid, revision_xml)
if (cache_exists(recid, uid)):
delete_cache_file(recid, uid)
response['resultCode'] = 4
elif request_type == 'cancel':
# Cancel editing by deleting the cache file. Possible error situations:
# - Cache file modified in other editor
if cache_exists(recid, uid):
if get_cache_mtime(recid, uid) == data['cacheMTime']:
delete_cache_file(recid, uid)
response['resultCode'] = 5
else:
response['resultCode'] = 107
else:
response['resultCode'] = 5
elif request_type == 'deleteRecord':
# Submit the record. Possible error situations:
# - Record locked by other user
# - Record locked by queue
# As the user is requesting deletion we proceed even if the cache file
# is missing and we don't check if the cache is outdated or has
# been modified in another editor.
existing_cache = cache_exists(recid, uid)
pending_changes = []
if has_copies(recid):
response['resultCode'] = \
CFG_BIBEDIT_AJAX_RESULT_CODES_REV['error_physical_copies_exist']
elif existing_cache and cache_expired(recid, uid) and \
record_locked_by_other_user(recid, uid):
response['resultCode'] = \
CFG_BIBEDIT_AJAX_RESULT_CODES_REV['error_rec_locked_by_user']
elif record_locked_by_queue(recid):
response['resultCode'] = \
CFG_BIBEDIT_AJAX_RESULT_CODES_REV['error_rec_locked_by_queue']
else:
if not existing_cache:
record_revision, record, pending_changes, \
deactivated_hp_changes, undo_list, redo_list = \
create_cache_file(recid, uid)
else:
try:
record_revision, record, pending_changes, \
deactivated_hp_changes, undo_list, redo_list = \
get_cache_file_contents(recid, uid)[1:]
except:
record_revision, record, pending_changes, \
deactivated_hp_changes = create_cache_file(recid, uid)
record_add_field(record, '980', ' ', ' ', '', [('c', 'DELETED')])
undo_list = []
redo_list = []
update_cache_file_contents(recid, uid, record_revision, record, \
pending_changes, \
deactivated_hp_changes, undo_list, \
redo_list)
save_xml_record(recid, uid)
delete_related_holdingpen_changes(recid) # we don't need any changes
# related to a deleted record
response['resultCode'] = 10
elif request_type == 'deleteRecordCache':
# Delete the cache file. Ignore the request if the cache has been
# modified in another editor.
if cache_exists(recid, uid) and get_cache_mtime(recid, uid) == \
data['cacheMTime']:
delete_cache_file(recid, uid)
response['resultCode'] = 11
elif request_type == 'prepareRecordMerge':
# We want to merge the cache with the current DB version of the record,
# so prepare an XML file from the file cache, to be used by BibMerge.
# Possible error situations:
# - Missing cache file
# - Record locked by other user
# - Record locked by queue
# We don't check if cache is outdated (a likely scenario for this
# request) or if it has been modified in another editor.
if not cache_exists(recid, uid):
response['resultCode'] = 106
elif cache_expired(recid, uid) and \
record_locked_by_other_user(recid, uid):
response['resultCode'] = 104
elif record_locked_by_queue(recid):
response['resultCode'] = 105
else:
save_xml_record(recid, uid, to_upload=False, to_merge=True)
response['resultCode'] = 12
return response
def perform_request_update_record(request_type, recid, uid, cacheMTime, data, \
hpChanges, undoRedoOp, isBulk=False, \
ln=CFG_SITE_LANG):
"""Handle record update requests like adding, modifying, moving or deleting
of fields or subfields. Possible common error situations:
- Missing cache file
- Cache file modified in other editor
Explanation of some parameters:
undoRedoOp - Indicates in "undo"/"redo"/undo_descriptor operation is
performed by a current request.
"""
response = {}
if not cache_exists(recid, uid):
response['resultCode'] = 106
elif not get_cache_mtime(recid, uid) == cacheMTime and isBulk == False:
# In case of a bulk request, the changes are deliberately performed
# imemdiately one after another
response['resultCode'] = 107
else:
try:
record_revision, record, pending_changes, deactivated_hp_changes, \
undo_list, redo_list = get_cache_file_contents(recid, uid)[1:]
except:
response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV[ \
'error_wrong_cache_file_format']
return response
# process all the Holding Pen changes operations ... regardles the
# request type
# import rpdb2;
# rpdb2.start_embedded_debugger('password', fAllowRemote=True)
if hpChanges.has_key("toDisable"):
for changeId in hpChanges["toDisable"]:
pending_changes[changeId]["applied_change"] = True
if hpChanges.has_key("toEnable"):
for changeId in hpChanges["toEnable"]:
pending_changes[changeId]["applied_change"] = False
if hpChanges.has_key("toOverride"):
pending_changes = hpChanges["toOverride"]
if hpChanges.has_key("changesetsToDeactivate"):
for changesetId in hpChanges["changesetsToDeactivate"]:
deactivated_hp_changes[changesetId] = True
if hpChanges.has_key("changesetsToActivate"):
for changesetId in hpChanges["changesetsToActivate"]:
deactivated_hp_changes[changesetId] = False
# processing the undo/redo entries
if undoRedoOp == "undo":
try:
redo_list = [undo_list[-1]] + redo_list
undo_list = undo_list[:-1]
except:
raise Exception("An exception occured when undoing previous" + \
" operation. Undo list: " + str(undo_list) + \
" Redo list " + str(redo_list))
elif undoRedoOp == "redo":
try:
undo_list = undo_list + [redo_list[0]]
redo_list = redo_list[1:]
except:
raise Exception("An exception occured when redoing previous" + \
" operation. Undo list: " + str(undo_list) + \
" Redo list " + str(redo_list))
else:
# This is a genuine operation - we have to add a new descriptor
# to the undo list and cancel the redo unless the operation is
# a bulk operation
if undoRedoOp != None and undoRedoOp != 0:
undo_list = undo_list + [undoRedoOp]
redo_list = []
else:
assert isBulk == True
field_position_local = data.get('fieldPosition')
if field_position_local is not None:
field_position_local = int(field_position_local)
if request_type == 'otherUpdateRequest':
# An empty request. Might be useful if we want to perform
# operations that require only the actions performed globally,
# like modifying the holdingPen changes list
response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV[ \
'editor_modifications_changed']
elif request_type == 'deactivateHoldingPenChangeset':
# the changeset has been marked as processed ( user applied it in
# the editor). Marking as used in the cache file.
# CAUTION: This function has been implemented here because logically
# it fits with the modifications made to the cache file.
# No changes are made to the Holding Pen physically. The
# changesets are related to the cache because we want to
# cancel the removal every time the cache disappears for
# any reason
response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV[ \
'disabled_hp_changeset']
elif request_type == 'addField':
if data['controlfield']:
record_add_field(record, data['tag'],
controlfield_value=data['value'])
response['resultCode'] = 20
else:
record_add_field(record, data['tag'], data['ind1'],
data['ind2'], subfields=data['subfields'],
field_position_local=field_position_local)
response['resultCode'] = 21
elif request_type == 'addSubfields':
subfields = data['subfields']
for subfield in subfields:
record_add_subfield_into(record, data['tag'], subfield[0],
subfield[1], subfield_position=None,
field_position_local=field_position_local)
if len(subfields) == 1:
response['resultCode'] = 22
else:
response['resultCode'] = 23
elif request_type == 'addFieldsSubfieldsOnPositions':
#1) Sorting the fields by their identifiers
fieldsToAdd = data['fieldsToAdd']
subfieldsToAdd = data['subfieldsToAdd']
for tag in fieldsToAdd.keys():
positions = fieldsToAdd[tag].keys()
positions.sort()
for position in positions:
# now adding fields at a position
isControlfield = (len(fieldsToAdd[tag][position][0]) == 0)
# if there are n subfields, this is a control field
if isControlfield:
controlfieldValue = fieldsToAdd[tag][position][3]
record_add_field(record, tag, field_position_local = \
int(position), \
controlfield_value = \
controlfieldValue)
else:
subfields = fieldsToAdd[tag][position][0]
ind1 = fieldsToAdd[tag][position][1]
ind2 = fieldsToAdd[tag][position][2]
record_add_field(record, tag, ind1, ind2, subfields = \
subfields, field_position_local = \
int(position))
# now adding the subfields
for tag in subfieldsToAdd.keys():
for fieldPosition in subfieldsToAdd[tag].keys(): #now the fields
#order not important !
subfieldsPositions = subfieldsToAdd[tag][fieldPosition]. \
keys()
subfieldsPositions.sort()
for subfieldPosition in subfieldsPositions:
subfield = subfieldsToAdd[tag][fieldPosition]\
[subfieldPosition]
record_add_subfield_into(record, tag, subfield[0], \
subfield[1], \
subfield_position = \
int(subfieldPosition), \
field_position_local = \
int(fieldPosition))
response['resultCode'] = \
CFG_BIBEDIT_AJAX_RESULT_CODES_REV['added_positioned_subfields']
elif request_type == 'modifyField': # changing the field structure
# first remove subfields and then add new... change the indices
subfields = data['subFields'] # parse the JSON representation of
# the subfields here
new_field = create_field(subfields, data['ind1'], data['ind2'])
record_replace_field(record, data['tag'], new_field, \
field_position_local = data['fieldPosition'])
response['resultCode'] = 26
elif request_type == 'modifyContent':
if data['subfieldIndex'] != None:
record_modify_subfield(record, data['tag'],
data['subfieldCode'], data['value'],
int(data['subfieldIndex']),
field_position_local=field_position_local)
else:
record_modify_controlfield(record, data['tag'], data["value"],
field_position_local=field_position_local)
response['resultCode'] = 24
elif request_type == 'modifySubfieldTag':
record_add_subfield_into(record, data['tag'], data['subfieldCode'],
data["value"], subfield_position= int(data['subfieldIndex']),
field_position_local=field_position_local)
record_delete_subfield_from(record, data['tag'], int(data['subfieldIndex']) + 1,
field_position_local=field_position_local)
response['resultCode'] = 24
elif request_type == 'modifyFieldTag':
subfields = record_get_subfields(record, data['oldTag'],
field_position_local=field_position_local)
record_add_field(record, data['newTag'], data['ind1'],
data['ind2'] , subfields=subfields)
record_delete_field(record, data['oldTag'], ind1=data['oldInd1'], \
ind2=data['oldInd2'], field_position_local=field_position_local)
response['resultCode'] = 32
elif request_type == 'moveSubfield':
record_move_subfield(record, data['tag'],
int(data['subfieldIndex']), int(data['newSubfieldIndex']),
field_position_local=field_position_local)
response['resultCode'] = 25
elif request_type == 'moveField':
if data['direction'] == 'up':
final_position_local = field_position_local-1
else: # direction is 'down'
final_position_local = field_position_local+1
record_move_fields(record, data['tag'], [field_position_local],
final_position_local)
response['resultCode'] = 32
elif request_type == 'deleteFields':
to_delete = data['toDelete']
deleted_fields = 0
deleted_subfields = 0
for tag in to_delete:
#Sorting the fields in a edcreasing order by the local position!
fieldsOrder = to_delete[tag].keys()
fieldsOrder.sort(lambda a, b: int(b) - int(a))
for field_position_local in fieldsOrder:
if not to_delete[tag][field_position_local]:
# No subfields specified - delete entire field.
record_delete_field(record, tag,
field_position_local=int(field_position_local))
deleted_fields += 1
else:
for subfield_position in \
to_delete[tag][field_position_local][::-1]:
# Delete subfields in reverse order (to keep the
# indexing correct).
record_delete_subfield_from(record, tag,
int(subfield_position),
field_position_local=int(field_position_local))
deleted_subfields += 1
if deleted_fields == 1 and deleted_subfields == 0:
response['resultCode'] = 26
elif deleted_fields and deleted_subfields == 0:
response['resultCode'] = 27
elif deleted_subfields == 1 and deleted_fields == 0:
response['resultCode'] = 28
elif deleted_subfields and deleted_fields == 0:
response['resultCode'] = 29
else:
response['resultCode'] = 30
response['cacheMTime'], response['cacheDirty'] = \
update_cache_file_contents(recid, uid, record_revision,
record, \
pending_changes, \
deactivated_hp_changes, \
undo_list, redo_list), \
True
return response
def perform_request_autocomplete(request_type, recid, uid, data):
"""
Perfrom an AJAX request associated with the retrieval of autocomplete
data.
Arguments:
request_type: Type of the currently served request
recid: the identifer of the record
uid: The identifier of the user being currently logged in
data: The request data containing possibly important additional
arguments
"""
response = {}
# get the values based on which one needs to search
searchby = data['value']
#we check if the data is properly defined
fulltag = ''
if data.has_key('maintag') and data.has_key('subtag1') and \
data.has_key('subtag2') and data.has_key('subfieldcode'):
maintag = data['maintag']
subtag1 = data['subtag1']
subtag2 = data['subtag2']
u_subtag1 = subtag1
u_subtag2 = subtag2
if (not subtag1) or (subtag1 == ' '):
u_subtag1 = '_'
if (not subtag2) or (subtag2 == ' '):
u_subtag2 = '_'
subfieldcode = data['subfieldcode']
fulltag = maintag+u_subtag1+u_subtag2+subfieldcode
if (request_type == 'autokeyword'):
#call the keyword-form-ontology function
if fulltag and searchby:
items = get_kbt_items_for_bibedit(CFG_BIBEDIT_KEYWORD_TAXONOMY, \
CFG_BIBEDIT_KEYWORD_RDFLABEL, \
searchby)
response['autokeyword'] = items
if (request_type == 'autosuggest'):
#call knowledge base function to put the suggestions in an array..
if fulltag and searchby and len(searchby) > 3:
suggest_values = get_kbd_values_for_bibedit(fulltag, "", searchby)
#remove ..
new_suggest_vals = []
for sugg in suggest_values:
if sugg.startswith(searchby):
new_suggest_vals.append(sugg)
response['autosuggest'] = new_suggest_vals
if (request_type == 'autocomplete'):
#call the values function with the correct kb_name
if CFG_BIBEDIT_AUTOCOMPLETE_TAGS_KBS.has_key(fulltag):
kbname = CFG_BIBEDIT_AUTOCOMPLETE_TAGS_KBS[fulltag]
#check if the seachby field has semicolons. Take all
#the semicolon-separated items..
items = []
vals = []
if searchby:
if searchby.rfind(';'):
items = searchby.split(';')
else:
items = [searchby.strip()]
for item in items:
item = item.strip()
kbrvals = get_kbr_values(kbname, item, '', 'e') #we want an exact match
if kbrvals and kbrvals[0]: #add the found val into vals
vals.append(kbrvals[0])
#check that the values are not already contained in other
#instances of this field
record = get_cache_file_contents(recid, uid)[2]
xml_rec = print_rec(record)
record, status_code, dummy_errors = create_record(xml_rec)
existing_values = []
if (status_code != 0):
existing_values = record_get_field_values(record,
maintag,
subtag1,
subtag2,
subfieldcode)
#get the new values.. i.e. vals not in existing
new_vals = vals
for val in new_vals:
if val in existing_values:
new_vals.remove(val)
response['autocomplete'] = new_vals
response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV['autosuggestion_scanned']
return response
def perform_request_bibcatalog(request_type, recid, uid):
"""Handle request to BibCatalog (RT).
"""
response = {}
if request_type == 'getTickets':
# Insert the ticket data in the response, if possible
if uid:
bibcat_resp = bibcatalog_system.check_system(uid)
if bibcat_resp == "":
tickets_found = bibcatalog_system.ticket_search(uid, \
status=['new', 'open'], recordid=recid)
t_url_str = '' #put ticket urls here, formatted for HTML display
for t_id in tickets_found:
#t_url = bibcatalog_system.ticket_get_attribute(uid, \
# t_id, 'url_display')
ticket_info = bibcatalog_system.ticket_get_info( \
uid, t_id, ['url_display', 'url_close'])
t_url = ticket_info['url_display']
t_close_url = ticket_info['url_close']
#format..
t_url_str += "#" + str(t_id) + '<a href="' + t_url + \
'">[read]</a> <a href="' + t_close_url + \
'">[close]</a><br/>'
#put ticket header and tickets links in the box
t_url_str = "<strong>Tickets</strong><br/>" + t_url_str + \
"<br/>" + '<a href="new_ticket?recid=' + str(recid) + \
'>[new ticket]</a>'
response['tickets'] = t_url_str
#add a new ticket link
else:
#put something in the tickets container, for debug
response['tickets'] = "<!--"+bibcat_resp+"-->"
response['resultCode'] = 31
return response
def perform_request_preview_record(request_type, recid, uid):
""" Handle request to preview record with formatting
"""
response = {}
if request_type == "preview":
if cache_exists(recid, uid):
dummy1, dummy2, record, dummy3, dummy4, dummy5, dummy6 = get_cache_file_contents(recid, uid)
else:
record = get_bibrecord(recid)
response['html_preview'] = _get_formated_record(record)
return response
def _get_formated_record(record):
"""Returns a record in a given format
@param record: BibRecord object
"""
xml_record = bibrecord.record_xml_output(record)
result = "<html><head><title>Record preview</title></head>"
result += get_mathjax_header()
result += "<body><h2> Brief format preview </h2>"
result += bibformat.format_record(recID=None,
of="hb",
xml_record=xml_record)
result += "<h2> Detailed format preview </h2>"
result += bibformat.format_record(recID=None,
of="hd",
xml_record=xml_record)
result += "</body></html>"
return result
diff --git a/modules/bibedit/lib/bibedit_webinterface.py b/modules/bibedit/lib/bibedit_webinterface.py
index 582ca4a7f..cc76b0046 100644
--- a/modules/bibedit/lib/bibedit_webinterface.py
+++ b/modules/bibedit/lib/bibedit_webinterface.py
@@ -1,223 +1,224 @@
## This file is part of Invenio.
## Copyright (C) 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0103
"""Invenio BibEdit Administrator Interface."""
__revision__ = "$Id"
__lastupdated__ = """$Date: 2008/08/12 09:26:46 $"""
import sys
if sys.hexversion < 0x2060000:
try:
import simplejson as json
simplejson_available = True
except ImportError:
# Okay, no Ajax app will be possible, but continue anyway,
# since this package is only recommended, not mandatory.
simplejson_available = False
else:
import json
simplejson_available = True
from invenio.access_control_engine import acc_authorize_action
from invenio.bibedit_engine import perform_request_ajax, perform_request_init, \
perform_request_newticket, perform_request_compare
from invenio.bibedit_utils import json_unicode_to_utf8
-from invenio.config import CFG_SITE_LANG, CFG_SITE_URL
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, CFG_SITE_RECORD
from invenio.messages import gettext_set_language
from invenio.search_engine import guess_primary_collection_of_a_record
from invenio.urlutils import redirect_to_url
from invenio.webinterface_handler import WebInterfaceDirectory, wash_urlargd
from invenio.webpage import page
from invenio.webuser import collect_user_info, getUid, page_not_authorized
navtrail = (' <a class="navtrail" href=\"%s/help/admin\">Admin Area</a> '
) % CFG_SITE_URL
class WebInterfaceEditPages(WebInterfaceDirectory):
"""Defines the set of /edit pages."""
_exports = ['', 'new_ticket', 'compare_revisions']
def __init__(self, recid=None):
"""Initialize."""
self.recid = recid
def index(self, req, form):
"""Handle all BibEdit requests.
The responsibilities of this functions is:
* JSON decoding and encoding.
* Redirection, if necessary.
* Authorization.
* Calling the appropriate function from the engine.
"""
uid = getUid(req)
argd = wash_urlargd(form, {'ln': (str, CFG_SITE_LANG)})
# Abort if the simplejson module isn't available
if not simplejson_available:
title = 'Record Editor'
body = '''Sorry, the record editor cannot operate when the
`simplejson' module is not installed. Please see the INSTALL
file.'''
return page(title = title,
body = body,
errors = [],
warnings = [],
uid = uid,
language = argd['ln'],
navtrail = navtrail,
lastupdated = __lastupdated__,
req = req)
# If it is an Ajax request, extract any JSON data.
ajax_request, recid = False, None
if form.has_key('jsondata'):
json_data = json.loads(str(form['jsondata']))
# Deunicode all strings (Invenio doesn't have unicode
# support).
json_data = json_unicode_to_utf8(json_data)
ajax_request = True
if json_data.has_key('recID'):
recid = json_data['recID']
json_response = {'resultCode': 0, 'ID': json_data['ID']}
# Authorization.
user_info = collect_user_info(req)
if user_info['email'] == 'guest':
# User is not logged in.
if not ajax_request:
# Do not display the introductory recID selection box to guest
# users (as it used to be with v0.99.0):
auth_code, auth_message = acc_authorize_action(req,
'runbibedit')
referer = '/edit/'
if self.recid:
- referer = '/record/%s/edit/' % self.recid
+ referer = '/%s/%s/edit/' % (CFG_SITE_RECORD, self.recid)
return page_not_authorized(req=req, referer=referer,
text=auth_message, navtrail=navtrail)
else:
# Session has most likely timed out.
json_response.update({'resultCode': 100})
return json.dumps(json_response)
elif self.recid:
# Handle RESTful calls from logged in users by redirecting to
# generic URL.
- redirect_to_url(req, '%s/record/edit/#state=edit&recid=%s&recrev=%s' % (
- CFG_SITE_URL, self.recid, ""))
+ redirect_to_url(req, '%s/%s/edit/#state=edit&recid=%s&recrev=%s' % (
+ CFG_SITE_URL, CFG_SITE_RECORD, self.recid, ""))
elif recid is not None:
json_response.update({'recID': recid})
# Authorize access to record.
auth_code, auth_message = acc_authorize_action(req, 'runbibedit',
collection=guess_primary_collection_of_a_record(recid))
if auth_code != 0:
json_response.update({'resultCode': 101})
return json.dumps(json_response)
# Handle request.
if not ajax_request:
# Show BibEdit start page.
body, errors, warnings = perform_request_init(uid, argd['ln'], req, __lastupdated__)
title = 'Record Editor'
return page(title = title,
body = body,
errors = errors,
warnings = warnings,
uid = uid,
language = argd['ln'],
navtrail = navtrail,
lastupdated = __lastupdated__,
req = req)
else:
# Handle AJAX request.
json_response.update(perform_request_ajax(req, recid, uid,
json_data))
return json.dumps(json_response)
def compare_revisions(self, req, form):
"""Handle the compare revisions request"""
argd = wash_urlargd(form, { \
'ln': (str, CFG_SITE_LANG), \
'rev1' : (str, ''), \
'rev2' : (str, ''), \
'recid': (int, 0)})
ln = argd['ln']
uid = getUid(req)
_ = gettext_set_language(ln)
# Checking if currently logged user has permission to perform this request
auth_code, auth_message = acc_authorize_action(req, 'runbibedit')
if auth_code != 0:
return page_not_authorized(req=req, referer="/edit",
text=auth_message, navtrail=navtrail)
recid = argd['recid']
rev1 = argd['rev1']
rev2 = argd['rev2']
ln = argd['ln']
body, errors, warnings = perform_request_compare(ln, recid, rev1, rev2)
return page(title = _("Comparing two record revisions"),
body = body,
errors = errors,
warnings = warnings,
uid = uid,
language = ln,
navtrail = navtrail,
lastupdated = __lastupdated__,
req = req)
def new_ticket(self, req, form):
"""handle a edit/new_ticket request"""
argd = wash_urlargd(form, {'ln': (str, CFG_SITE_LANG), 'recid': (int, 0)})
ln = argd['ln']
_ = gettext_set_language(ln)
auth_code, auth_message = acc_authorize_action(req, 'runbibedit')
if auth_code != 0:
return page_not_authorized(req=req, referer="/edit",
text=auth_message, navtrail=navtrail)
uid = getUid(req)
if argd['recid']:
(errmsg, url) = perform_request_newticket(argd['recid'], uid)
if errmsg:
return page(title = _("Failed to create a ticket"),
body = _("Error")+": "+errmsg,
errors = [],
warnings = [],
uid = uid,
language = ln,
navtrail = navtrail,
lastupdated = __lastupdated__,
req = req)
else:
#redirect..
redirect_to_url(req, url)
def __call__(self, req, form):
"""Redirect calls without final slash."""
if self.recid:
- redirect_to_url(req, '%s/record/%s/edit/' % (CFG_SITE_URL,
+ redirect_to_url(req, '%s/%s/%s/edit/' % (CFG_SITE_URL,
+ CFG_SITE_RECORD,
self.recid))
else:
- redirect_to_url(req, '%s/record/edit/' % CFG_SITE_URL)
+ redirect_to_url(req, '%s/%s/edit/' % CFG_SITE_URL, CFG_SITE_RECORD)
diff --git a/modules/bibedit/lib/bibeditmulti_templates.py b/modules/bibedit/lib/bibeditmulti_templates.py
index 9ca401595..dfcf14ac1 100644
--- a/modules/bibedit/lib/bibeditmulti_templates.py
+++ b/modules/bibedit/lib/bibeditmulti_templates.py
@@ -1,796 +1,799 @@
## This file is part of Invenio.
## Copyright (C) 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio Multiple Record Editor Templates."""
__revision__ = "$Id$"
import cgi
from invenio.config import CFG_SITE_URL, CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING,\
CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING,\
CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING_TIME,\
- CFG_SITE_ADMIN_EMAIL
+ CFG_SITE_ADMIN_EMAIL, \
+ CFG_SITE_RECORD
from invenio.messages import gettext_set_language
class Template:
"""MultiEdit Templates Class."""
def __init__(self):
"""Initialize."""
pass
def styles(self):
"""Defines the local CSS styles"""
styles = """
<style type="text/css">
select[disabled] {
color: #696969;
background: #d3d3d3;
}
select {
border: solid 1px #000000;
font-family: Arial, Sans-Serif;
font-size: 15px;
}
input[type="text"]{
border: solid 1px #000000;
font-family: Arial, Sans-Serif;
font-size: 15px;
}
.actOnFieldLink{
font-family: Arial, Sans-Serif;
color: blue;
font-size: 12px;
cursor: pointer;
}
div .pagebody td{
font-family: Arial, Sans-Serif;
font-size: 16px;
}
.txtTag {
width: 34px;
}
.txtInd {
width: 14px;
}
.txtSubfieldCode {
width: 14px;
}
.txtValue {
width: 200px;
}
.textBoxConditionSubfield {
width: 14px;
}
.msg {
color:red;
background-color: #E0ECFF;
font-style: italic;
}
.tagTableRow {
background-color: #E0ECFF;
}
.subfieldTableRow {
}
.colFieldTag {
width: 80px;
}
.colSubfieldCode {
width: 28px;
}
.colDeleteButton {
width: 17px;
}
.colActionType{
width: 150px;
color: Green;
}
.colAddButton {
width: 95px;
}
.linkButton {
text-decoration: underline;
color: Blue;
cursor: pointer;
}
.buttonNewField{
cursor: pointer;
}
.buttonNewSubfield{
cursor: pointer;
}
.buttonDeleteField{
cursor: pointer;
}
.buttonDeleteSubfield{
cursor: pointer
}
.formbutton{
cursor: pointer;
}
.buttonGoToFirstPage{
cursor: pointer;
}
.buttonGoToPreviousPage{
cursor: pointer;
}
.buttonGoToNextPage{
cursor: pointer;
}
div .boxContainer{
margin-left: 20px;
text-align: left;
width: 550px;
}
div .boxleft {
float: left;
width: 150px;
padding-top: 3px;
}
div .boxleft_2 {
float: left;
width: 400px;
padding-top: 3px;
}
#actionsDisplayArea {
border: solid #C3D9FF;
}
#actionsDisplayArea .header {
background-color: #C3D9FF;
}
.clean-ok{
border:solid 1px #349534;
background:#C9FFCA;
color:#008000;
font-size:14px;
font-weight:bold;
padding:4px;
text-align:center;
}
.clean-error{
border:solid 1px #CC0000;
background:#F7CBCA;
color:#CC0000;
font-size:14px;
font-weight:bold;
padding:4px;
text-align:center;
}
.modify-list{
border:solid 1px #0033cc;
background:#e0ecff;
color:#000000;
font-size:14px;
font-weight:bold;
padding:4px;
text-align:left;
}
.inputValueGrey{
color:#000000;
}
.buttonDisabled{
background:grey;
}
</style>
"""
return styles
def page_contents(self, language, collections):
"""Returns HTML representing the MultiEdit page"""
_ = gettext_set_language(language)
page_html = """\
<div class="pagebody">
<input type="hidden" value="%(language)s" id="language">
<table width="100%%" cellspacing="6" border="0">
<tr>
<td>
<b>%(text_choose_search_criteria)s</b> %(text_search_criteria_explanation)s
</td>
</tr>
<tr>
<td>
<div class="boxContainer">
<div class="boxleft">
<b>%(text_search_criteria)s:</b>
</div>
<div class="boxleft_2">
<input type="text" id="textBoxSearchCriteria" size="40"> <br />
</div>
</div>
<div class="boxContainer">
<div class="boxleft">
<b>%(text_filter_collection)s:&nbsp;</b>
</div>
<div class="boxleft_2">
%(collections)s <br />
</div>
</div>
<div class="boxContainer">
<div class="boxleft">
<b>%(text_output_tags)s:</b>
</div>
<div class="boxleft_2">
<div><input class="inputValueGrey" type="text" id="textBoxOutputTags" value="All tags" size="28">&nbsp;&nbsp;<i>Ex. 100, 700</i><br/></div>
</div>
</div>
<div class="boxContainer">
<div class="boxleft">
<input id="buttonTestSearch" value="%(text_test_search)s" type="submit" class="formbutton"></button>
</div>
</div>
</td>
</tr>
<tr/>
<tr/>
<tr>
<td align="center">
%(actions_definition_html)s
</td>
</tr>
<tr>
<td>
<div class="boxContainer">
<div class="boxleft">
<input id="buttonPreviewResults" value="%(text_preview_results)s" type="button" class="formbutton"></button>
</div>
<div class="boxleft_2">
<input id="buttonSubmitChanges" value="%(text_submit_changes)s" type="button" class="formbutton"></button>
</div>
</div>
</td>
</tr>
</table>
<br/>
<div id="info_area"></div>
<div id="preview_area"></div>
</div>
"""% {"language" : language,
"text_test_search" : _("Search"),
"text_next_step" : _("Next Step"),
"text_search_criteria" : _("Search criteria"),
"text_output_tags" : _("Output tags"),
"text_filter_collection": _("Filter collection"),
"text_choose_search_criteria" : _("1. Choose search criteria"),
"text_search_criteria_explanation" : _("""Specify the criteria you'd like to use for filtering records that will be changed. Use "Search" to see which records would have been filtered using these criteria."""),
"actions_definition_html" : self._get_actions_definition_html(language),
"text_preview_results" : _("Preview results"),
"text_submit_changes" : _("Apply changes"),
"collections" : self._get_collections(collections),
}
return page_html
def _get_collections(self, collections):
""" Returns html select for collections"""
html = "<select id=\"collection\" onChange=\"onSelectCollectionChange(event);\">"
for collection_name in collections:
html += '<option value="%(collection_name)s"">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)}
html += "</select>"
return html
def _get_actions_definition_html(self, language):
"""Returns the html for definition of actions"""
_ = gettext_set_language(language)
html = """
<!-- Area for displaying actions to the users -->
<table id="actionsDisplayArea" width="100%%" cellspacing="0" >
<col class="colDeleteButton"/>
<col class="colFieldTag"/>
<col class="colDeleteButton"/>
<col class="colSubfieldCode"/>
<col class="colActionType"/>
<col/>
<col class="colAddButton"/>
<tbody><tr class="header"><td colspan="7">
<b>%(text_define_changes)s</b> %(text_define_changes_explanation)s
</td><tr></tbody>
<tbody class="lastRow" valign="middle"><tr><td colspan="7" align="left">
<img src="%(site_url)s/img/add.png" class="buttonNewField" alt="Add new"/>
<span class="buttonNewField linkButton">%(text_define_field)s</span>
</td></tr></tbody>
</table>
<!-- Templates for displaying of actions -->
<table id="displayTemplates" width="100%%" cellspacing="0">
<col class="colDeleteButton"/>
<col class="colFieldTag"/>
<col class="colDeleteButton"/>
<col class="colSubfieldCode"/>
<col class="colActionType"/>
<col/>
<col class="colAddButton"/>
<!-- Templates for fields -->
<tbody class="templateNewField">
<tr class="tagTableRow">
<td/>
<td>%(text_field)s</td>
<td /><td colspan="4" />
</tr>
<tr class="tagTableRow">
<td />
<td>
<input class="textBoxFieldTag txtTag" type="Text" maxlength="3" /><input class="textBoxFieldInd1 txtInd" type="Text" maxlength="1" /><input class="textBoxFieldInd2 txtInd" type="text" maxlength="1" />
</td>
<td />
<td />
<td>
<select class="fieldActionType" onchange="onFieldActionTypeChange(this);">
<option>%(text_select_action)s</option>
<option value="0">%(text_add_field)s</option>
<option value="1">%(text_delete_field)s</option>
<option value="2">%(text_update_field)s</option>
</select>
</td>
<td/>
<td/>
</tr>
<tr class="tagTableRow"><td /><td /><td /><td /><td>&nbsp;</td><td/><td/></tr>
</tbody>
<tr class="tagTableRow"><td /><td /><td>&nbsp;</td><td /><td colspan="2">
<input value="%(text_save)s" type="button" id="buttonSaveNewField" class="formbutton"/>
<input value="%(text_cancel)s" type="button" id="buttonCancelNewField" class="formbutton"/>
</td><td/></tr>
<tbody class="templateDisplayField" valign="middle">
<tr class="tagTableRow">
<td><img src="%(site_url)s/img/delete.png" class="buttonDeleteField" alt="Delete"/></td>
<td>
<strong>
<span class="tag"></span><span class="ind1"></span><span class="ind2"></span>
</strong>
</td>
<td />
<td />
<td><span class="action colActionType"></span></td>
<td class="conditionParametersDisplay">
<span class="conditionParametersDisplay"><strong> %(text_with_condition)s</strong></span>
<input id="textBoxConditionSubfieldDisplay" class="txtValue textBoxConditionSubfield conditionSubfieldParameters" type="text" maxlength="1"/>
<span class="conditionExact conditionParametersDisplay"></span>
<input id="textBoxConditionFieldDisplay" class="txtValue textBoxCondition conditionParametersDisplay" type="text"/>
</td>
<td align="center">
<img src="%(site_url)s/img/add.png" class="buttonNewSubfield" alt="Add new"/>
<span class="buttonNewSubfield linkButton">%(text_define_subfield)s</span>
</td>
<td />
</tr>
<tr class="conditionParameters">
<td /> <td /> <td /> <td /><td colspan="3">%(text_condition_subfield_delete)s
<input id="textBoxConditionSubfield" class="txtValue textBoxConditionSubfield" type="text" maxlength="1"/>
<select class="selectConditionExactMatch">
<option value="0">%(text_equal_to)s</option>
<option value="1">%(text_contains)s</option>
</select>
<input id="textBoxCondition" class="txtValue textBoxCondition" type="text" value="%(text_condition)s"/>
</td>
</tr>
<tr class="conditionActOnFields">
<td /><td /><td /><td />
<td colspan="3">
<span class="actOnFieldLink" id="actOnFieldsDelete"><u>%(text_filter_fields)s</u></span>
</td>
</tr>
<tr class="conditionActOnFieldsSave">
<td /><td /><td /><td /><td>
<input value="%(text_save)s" type="button" id="buttonSaveNewFieldCondition" class="formbutton"/>
<input value="%(text_cancel)s" type="button" id="buttonCancelFieldCondition" class="formbutton">
</td>
</tr>
</tbody>
<!-- Templates for subfields -->
<tbody class="templateDisplaySubfield">
<tr class="subfieldTableRow">
<td />
<td />
<td><img src="%(site_url)s/img/delete.png" class="buttonDeleteSubfield" alt="Delete" /></td>
<td>$$<span class="subfieldCode">a</span></td>
<td>
<span class="action colActionType">%(text_replace_text)s</span>&nbsp;
</td>
<td>
<input id="textBoxValueDisplay" class="txtValue textBoxValue valueParameters" type="text"/>&nbsp;
<span class="newValueParameters"><strong> %(text_with)s </strong></span>
<input id="textBoxNewValueDisplay" class="txtValue textBoxNewValue newValueParameters" type="text" value="%(text_new_value)s"/>
<span class="conditionParameters"><strong> %(text_with_condition)s</strong></span>
<input id="textBoxConditionSubfieldDisplay" class="txtValue textBoxConditionSubfield conditionSubfieldParameters" type="text" maxlength="1"/>
<span class="conditionExact conditionParameters"></span>
<input id="textBoxConditionDisplay" class="txtValue textBoxCondition conditionParameters" type="text"/>
</td>
<td/>
</tr>
</tbody>
<tbody class="templateNewSubfield">
<tr>
<td />
<td />
<td />
<td><input class="txtSubfieldCode textBoxSubfieldCode" type="text" maxlength="1"/></td>
<td>
<select class="subfieldActionType">
<option value="0">%(text_add_subfield)s</option>
<option value="1">%(text_delete_subfield)s</option>
<option value="2">%(text_replace_content)s</option>
<option value="3">%(text_replace_text)s</option>
</select>
</td>
</tr>
<tr class="valueParameters">
<td /><td /><td /><td />
<td colspan="3">
<input id="textBoxValue" class="txtValue textBoxValue" type="text" value="%(text_value)s"/>
</td>
</tr>
<tr class="newValueParameters">
<td /><td /><td /><td />
<td colspan="3">
<input id="textBoxNewValue" class="txtValue textBoxNewValue" type="text" value="%(text_new_value)s"/>
</td>
</tr>
<tr class="conditionParameters">
<td /> <td /> <td /> <td /><td colspan="3">%(text_condition_subfield)s
<input class="txtValue textBoxConditionSubfield" type="text" maxlength="1"/>
<select class="selectConditionExactMatch">
<option value="0">%(text_equal_to)s</option>
<option value="1">%(text_contains)s</option>
</select>
<input id="textBoxCondition" class="txtValue textBoxCondition" type="text" value="%(text_condition)s"/>
</td>
</tr>
<tr class="conditionActOnFields">
<td /><td /><td /><td />
<td colspan="3">
<span class="actOnFieldLink" id="actOnFields"><u>%(text_filter_fields)s</u></span>
</td>
</tr>
<tr>
<td /><td /><td /><td /><td>
<input value="%(text_save)s" type="button" id="buttonSaveNewSubfield" class="formbutton"/>
<input value="%(text_cancel)s" type="button" id="buttonCancelNewSubfield" class="formbutton">
</td>
</tr>
</tbody>
<tbody class="templateMsg">
<tr>
<td colspan="100"><span class="msg"></span></td>
<td/>
<td/>
<td/>
<td/>
<td/>
<td/>
</tr>
</tbody>
</table>
"""% {"site_url" : CFG_SITE_URL,
"text_define_changes" : _("2. Define changes"),
"text_define_changes_explanation" : _("Specify fields and their subfields that should be changed in every record matching the search criteria."),
"text_define_field" : _("Define new field action"),
"text_define_subfield" : _("Define new subfield action"),
"text_field" : _("Field"),
"text_select_action" : _("Select action"),
"text_add_field" : _("Add field"),
"text_delete_field" : _("Delete field"),
"text_update_field" : _("Update field"),
"text_add_subfield" : _("Add subfield"),
"text_delete_subfield" : _("Delete subfield"),
"text_save" : _("Save"),
"text_cancel" : _("Cancel"),
"text_replace_text" : _("Replace substring"),
"text_replace_content" : _("Replace full content"),
"text_with" : _("with"),
"text_with_condition": _("when subfield $$"),
"text_new_value" : _("new value"),
"text_equal_to" : _("is equal to"),
"text_contains" : _("contains"),
"text_condition" : _("condition"),
"text_condition_subfield" : _("when other subfield"),
"text_condition_subfield_delete" : _("when subfield"),
"text_filter_fields": _("Apply only to specific field instances"),
"text_value" : _("value")
}
return html
def detailed_record(self, record_content, language):
"""Returns formated content of a detailed record
@param record_content: content of the record to be displayed
@param language: language used to display the content
"""
_ = gettext_set_language(language)
result = """
<table class="searchresultsbox">
<tr>
<td class="searchresultsboxheader">
<span class="buttonBackToResults linkButton"><b><< %(text_back_to_results)s </b></span>
</td>
<td class="searchresultsboxheader" style="text-align: right;">
<span class="buttonOutputFormatMARC linkButton">MARC</span>,
<span class="buttonOutputFormatHTMLBrief linkButton">HTML Brief</span>,
<span class="buttonOutputFormatHTMLDetailed linkButton">HTML Detailed</span>
</td>
</tr>
</table>
%(record_content)s
"""% {"record_content" : record_content,
"text_back_to_results" : _("Back to Results")
}
return result
def info_box(self, language, total_modifications):
"""Returns list with a summary of the number of modifications
made to records
@param language: language used to display the content
@param total_modifications: list of modifications to records, fields
and subfields
"""
_ = gettext_set_language(language)
modifications_html = " "
if total_modifications:
modification_display_list= """<ul>
<li>Records: %(records_modified)s</li>
<li>Fields: %(fields_modified)s</li>
<li>Subfields: %(subfields_modified)s</li>
</ul>
""" % {
"records_modified": str(total_modifications[0]),
"fields_modified": str(total_modifications[1]),
"subfields_modified": str(total_modifications[2])
}
modifications_html = """<div class="modify-list"> %(modify_text)s <br /> %(modification_display)s </div>
""" % {"modify_text": "The actions defined will affect:",
"modification_display": modification_display_list}
return modifications_html
def _build_navigation_image(self, class_name, image_file_name, alt_text):
"""Creates html for image from the page navigation line """
navigation_image = '<img border="0" class="%(class)s" alt="%(alt)s" src="%(site_url)s/img/%(image_file_name)s"/>' % {
"class" : class_name,
"alt" : alt_text,
"site_url" : CFG_SITE_URL,
"image_file_name" : image_file_name
}
return navigation_image
def _search_results_header(self, number_of_records, current_page, records_per_page, language, output_format):
"""Returns header of the search results"""
_ = gettext_set_language(language)
first_page_button = self._build_navigation_image("buttonGoToFirstPage", "sb.gif", _("begin"))
previous_page_button = self._build_navigation_image("buttonGoToPreviousPage", "sp.gif", _("previous"))
next_page_button = self._build_navigation_image("buttonGoToNextPage", "sn.gif", _("next"))
#for now we don't have last page button.
last_page_button = ""#self._build_navigation_image("buttonGoToLastPage", "se.gif", _("end"))
first_record_on_page = (current_page-1)*records_per_page + 1
last_record_on_page = first_record_on_page + records_per_page - 1
if last_record_on_page > number_of_records:
last_record_on_page = number_of_records
last_page_number = number_of_records/records_per_page+1
if current_page == 1:
previous_page_button = ""
if current_page < 2:
first_page_button = ""
if current_page == last_page_number:
next_page_button = ""
last_page_button = ""
user_warning = ""
if number_of_records > CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING:
user_warning = """<div class="clean-error">%(warning_msg)s</div>
""" % {"warning_msg": "Due to the amount of records to be modified, you need 'superadmin' rights to send the modifications. If it is not the case, your changes will be saved once you click the 'Apply Changes' button and you will be able to contact the admin to apply them"
}
header = """
%(user_warning)s
<table class="searchresultsbox">
<tr><td class="searchresultsboxheader">
<strong>%(number_of_records)s</strong> %(text_records_found)s
%(first_page_button)s %(previous_page_button)s
%(first_record_on_page)s - %(last_record_on_page)s
%(next_page_button)s %(last_page_button)s
</td>
<td class="searchresultsboxheader" style="text-align: right;">
Output format: <select name="view_format" onchange="onSelectOutputFormatChange(this.value)">
<option value="Marc" %(marc_selected)s>MARC</option>
<option value="HTML Brief" %(brief_selected)s>HTML Brief</option>
</select>
</td>
</tr>
</table>
""" % {"user_warning": user_warning,
"number_of_records" : number_of_records,
"text_records_found" : _("records found"),
"first_page_button" : first_page_button,
"previous_page_button" : previous_page_button,
"next_page_button" : next_page_button,
"last_page_button" : last_page_button,
"first_record_on_page" : first_record_on_page,
"last_record_on_page" : last_record_on_page,
"marc_selected": output_format == "hm" and "SELECTED" or "",
"brief_selected": output_format == "hb" and "SELECTED" or ""
}
return header
def search_results(self, records, number_of_records, current_page, records_per_page, language, output_format):
"""Retrurns the content of search resutls.
@param records: list records to be displayed in the results.
Contains tuples (record_id, formated_record)
record_id - identifier of the record
formated_record - content for the record ready for display
@param language: language used to display the content
"""
_ = gettext_set_language(language)
result = " "
for (record_id, record) in records:
result += """
<span class="resultItem" id="recordid_%(record_id)s">
%(record)s
</span><br><hr>
""" % {"record_id" : record_id,
"record" : record
}
result_header = self._search_results_header(number_of_records = number_of_records,
current_page = current_page,
records_per_page = records_per_page,
language = language,
output_format=output_format)
result = result_header + result + result_header
return result
def scripts(self):
"""Returns the scripts that should be imported."""
scripts_jquery = ["jquery.min.js",
"json2.js"]
scripts = ["bibeditmulti.js"]
result = ""
for script in scripts_jquery:
result += '<script type="text/javascript" src="%s/js/jquery/%s">' \
'</script>\n' % (CFG_SITE_URL, script)
for script in scripts:
result += '<script type="text/javascript" src="%s/js/%s">' \
'</script>\n' % (CFG_SITE_URL, script)
return result
def changes_applied(self, status, file_path):
""" returns html message when changes sent to server """
if status == 0:
body = """
- <div class="clean-ok"><div>Changes have been sent to the server. It will take some time before they are applied. You can <a href=%s/record/multiedit>reset </a> the editor.</div>
- """ % (CFG_SITE_URL)
+ <div class="clean-ok"><div>Changes have been sent to the server. It will take some time before they are applied. You can <a href=%s/%s/multiedit>reset </a> the editor.</div>
+ """ % (CFG_SITE_URL, CFG_SITE_RECORD)
elif status in [1, 2]:
body = """
<div class="clean-ok">You are submitting a file that manipulates more than %s records. Your job will therefore be processed only during <strong>%s</strong>. <br /><br />
If you are not happy about this, please contact %s, quoting your file <strong>%s</strong> <br /><br />
- You can <a href=%s/record/multiedit>reset</a> the editor.</div>
+ You can <a href=%s/%s/multiedit>reset</a> the editor.</div>
""" % (CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING,
CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING_TIME,
CFG_SITE_ADMIN_EMAIL,
file_path,
- CFG_SITE_URL)
+ CFG_SITE_URL,
+ CFG_SITE_RECORD)
else:
body = """
<div class="clean-error">Sorry, you are submitting a file that manipulates more than %s records. You don't have enough rights for this.
<br /> <br />
If you are not happy about this, please contact %s, quoting your file %s <br /><br />
- You can <a href=%s/record/multiedit>reset</a> the editor.</div>
+ You can <a href=%s/%s/multiedit>reset</a> the editor.</div>
""" % (CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING,
CFG_SITE_ADMIN_EMAIL,
file_path,
- CFG_SITE_URL)
+ CFG_SITE_URL,
+ CFG_SITE_RECORD)
return body
diff --git a/modules/bibexport/lib/bibexport_method_sitemap.py b/modules/bibexport/lib/bibexport_method_sitemap.py
index d0d295027..6f0f03d8c 100644
--- a/modules/bibexport/lib/bibexport_method_sitemap.py
+++ b/modules/bibexport/lib/bibexport_method_sitemap.py
@@ -1,422 +1,423 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
BibExport plugin implementing 'sitemap' exporting method.
The main function is run_export_method(jobname) defined at the end.
This is what BibExport daemon calls for all the export jobs that use
this exporting method.
"""
from datetime import datetime
from urllib import quote
from ConfigParser import ConfigParser
import os
from invenio.search_engine import get_collection_reclist
from invenio.dbquery import run_sql
-from invenio.config import CFG_SITE_URL, CFG_WEBDIR, CFG_ETCDIR
+from invenio.config import CFG_SITE_URL, CFG_WEBDIR, CFG_ETCDIR, \
+ CFG_SITE_RECORD
from invenio.intbitset import intbitset
from invenio.websearch_webcoll import Collection
from invenio.messages import language_list_long
from invenio.bibtask import write_message, task_update_progress
-
+
DEFAULT_TIMEZONE = '+01:00'
DEFAULT_PRIORITY_HOME = 1
DEFAULT_CHANGEFREQ_HOME = 'hourly'
DEFAULT_PRIORITY_RECORDS = 0.8
DEFAULT_CHANGEFREQ_RECORDS = 'weekly'
DEFAULT_PRIORITY_COMMENTS = 0.4
DEFAULT_CHANGEFREQ_COMMENTS = 'weekly'
DEFAULT_PRIORITY_REVIEWS = 0.6
DEFAULT_CHANGEFREQ_REVIEWS = 'weekly'
DEFAULT_PRIORITY_FULLTEXTS = 0.9
DEFAULT_CHANGEFREQ_FULLTEXTS = 'weekly'
DEFAULT_PRIORITY_COLLECTIONS = 0.3
DEFAULT_CHANGEFREQ_COLLECTIONS = 'hourly'
MAX_RECORDS = 50000
MAX_SIZE = 10000000
def get_all_public_records(collections):
""" Get all records which exist (i.e. not suppressed ones) and are in
accessible collection.
returns list of (recid, last_modification) tuples
"""
recids = intbitset()
for collection in collections:
recids += get_collection_reclist(collection)
query = 'SELECT id, modification_date FROM bibrec'
res = run_sql(query)
return [(recid, lastmod) for (recid, lastmod) in res if recid in recids]
def get_all_public_collections(base_collections):
""" Return a list of (collection.name, last_modification) tuples for all
collections and subcollections of base_collections
"""
def get_collection_last_modification(collection):
""" last modification = modification date fo latest added record """
last_mod = None
query_last_mod = "SELECT modification_date FROM bibrec WHERE id=%s"
try:
latest_recid = collection.reclist.tolist()[-1]
except IndexError:
# this collection is empty
return last_mod
res = run_sql(query_last_mod, (latest_recid,))
if res and res[0][0]:
last_mod = res[0][0]
return last_mod
output = []
for coll_name in base_collections:
mother_collection = Collection(coll_name)
if not mother_collection.restricted_p():
last_mod = get_collection_last_modification(mother_collection)
output.append((coll_name, last_mod))
for descendant in mother_collection.get_descendants(type='r'):
if not descendant.restricted_p():
last_mod = get_collection_last_modification(descendant)
output.append((descendant.name, last_mod))
for descendant in mother_collection.get_descendants(type='v'):
if not descendant.restricted_p():
last_mod = get_collection_last_modification(descendant)
output.append((descendant.name, last_mod))
return output
def filter_fulltexts(recids, fulltext_type=None):
""" returns list of records having a fulltext of type x"""
recids = dict(recids)
if fulltext_type:
query = """SELECT id_bibrec, max(modification_date)
FROM bibrec_bibdoc
LEFT JOIN bibdoc ON bibrec_bibdoc.id_bibdoc=bibdoc.id
WHERE type=%s
GROUP BY id_bibrec"""
res = run_sql(query, (fulltext_type,))
else:
query = """SELECT id_bibrec, max(modification_date)
FROM bibrec_bibdoc
LEFT JOIN bibdoc ON bibrec_bibdoc.id_bibdoc=bibdoc.id
GROUP BY id_bibrec"""
res = run_sql(query)
return [(recid, lastmod) for (recid, lastmod) in res if recid in recids]
def filter_comments(recids):
""" Retrieve recids having a comment. return (recid, last_review_date)"""
recids = dict(recids)
query = """SELECT id_bibrec, max(date_creation)
FROM cmtRECORDCOMMENT
WHERE star_score=0
GROUP BY id_bibrec"""
res = run_sql(query)
return [(recid, lastmod) for (recid, lastmod) in res if recid in recids]
def filter_reviews(recids):
""" Retrieve recids having a review. return (recid, last_review_date)"""
recids = dict(recids)
query = """SELECT id_bibrec, max(date_creation)
FROM cmtRECORDCOMMENT
WHERE star_score>0
GROUP BY id_bibrec"""
res = run_sql(query)
return [(recid, lastmod) for (recid, lastmod) in res if recid in recids]
SITEMAP_HEADER = \
'<?xml version="1.0" encoding="UTF-8"?>\n' \
'<urlset\n' \
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' \
' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9\n' \
' http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"\n' \
' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
SITEMAP_FOOTER = '\n</urlset>\n'
class SitemapWriter(object):
""" Writer for sitemaps"""
def __init__(self, name):
""" Constructor.
name: path to the sitemap file to be created
"""
self.header = SITEMAP_HEADER
self.footer = SITEMAP_FOOTER
self.name = name
self.filedescriptor = open(self.name, 'w')
self.num_urls = 0
self.file_size = 0
self.buffer = []
self.filedescriptor.write(self.header)
self.file_size += len(self.footer)
def add_url(self, url, lastmod=datetime(1900, 1, 1), changefreq="",
priority=""):
""" create a new url node. Returns the number of url nodes in sitemap"""
self.num_urls += 1
url_node = u"""
<url>
<loc>%s</loc>%s
</url>"""
optional = ''
if lastmod:
optional += u"""
<lastmod>%s</lastmod>""" % lastmod.strftime('%Y-%m-%dT%H:%M:%S' + \
DEFAULT_TIMEZONE)
if changefreq:
optional += u"""
<changefreq>%s</changefreq>""" % changefreq
if priority:
optional += u"""
<priority>%s</priority>""" % priority
url_node %= (url, optional)
self.file_size += len(url_node)
self.buffer.append(url_node)
return self.num_urls
def get_size(self):
""" File size. Should ot be > 10MB """
return self.file_size + len(self.footer)
def get_number_of_urls(self):
""" Number of urls in the sitemap. Should not be > 50'000"""
return self.num_urls
def get_name(self):
""" Returns the filename """
return self.name
def close(self):
""" Writes the whole sitemap """
self.filedescriptor.write(''.join(self.buffer) + self.footer)
self.filedescriptor.close()
SITEMAP_INDEX_HEADER = \
'<?xml version="1.0" encoding="UTF-8"?>\n' \
'<sitemapindex\n' \
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' \
' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9\n' \
' http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd"\n' \
' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
SITEMAP_INDEX_FOOTER = '\n</sitemapindex>\n'
class SitemapIndexWriter(SitemapWriter):
"""class for writing Sitemap Index files."""
def __init__(self, name):
""" Constructor.
name: path to the sitemap index file to be created
"""
self.header = SITEMAP_INDEX_HEADER
self.footer = SITEMAP_INDEX_FOOTER
self.name = name
self.filedescriptor = open(self.name, 'w')
self.num_urls = 0
self.file_size = 0
self.buffer = []
self.filedescriptor.write(self.header)
self.file_size += len(self.footer)
def add_url(self, url, lastmod=datetime(1900, 1, 1)):
""" create a new url node. Returns the number of url nodes in sitemap"""
self.num_urls += 1
url_node = u"""
<sitemap>
<loc>%s</loc>%s
</sitemap>"""
optional = ''
if lastmod:
optional += u"""
<lastmod>%s</lastmod>""" % lastmod.strftime('%Y-%m-%dT%H:%M:%S' +\
DEFAULT_TIMEZONE)
url_node %= (url, optional)
self.file_size += len(url_node)
self.buffer.append(url_node)
return self.num_urls
def generate_sitemaps(collection_names, fulltext_filter=''):
"""
Generate sitemaps themselves. Return list of generated sitemaps files
"""
sitemap_id = 1
writer = SitemapWriter(CFG_WEBDIR + '/sitemap-%s.xml' % sitemap_id)
sitemaps = [writer.get_name()]
nb_urls = 0
for [lang, lang_name] in language_list_long():
writer.add_url(CFG_SITE_URL + '/?ln=%s' % lang,
lastmod=datetime.today(),
changefreq=DEFAULT_CHANGEFREQ_HOME,
priority=DEFAULT_PRIORITY_HOME)
nb_urls += 1
recids = get_all_public_records(collection_names)
task_update_progress("Generating urls for %s records" % len(recids))
#task_sleep_now_if_required(can_stop_too=True)
for (recid, lastmod) in recids:
if nb_urls <= MAX_RECORDS and nb_urls % 100 == 0:
#print nb_urls
#print writer.get_size()
if writer.get_size() > MAX_SIZE or nb_urls == MAX_RECORDS:
writer.close()
sitemap_id += 1
writer = SitemapWriter(CFG_WEBDIR + '/sitemap-%s.xml' % sitemap_id)
sitemaps.append(writer.get_name())
- nb_urls = writer.add_url(CFG_SITE_URL + '/record/%s' % recid,
+ nb_urls = writer.add_url(CFG_SITE_URL + '/%s/%s' % (CFG_SITE_RECORD, recid),
lastmod = lastmod,
changefreq = DEFAULT_CHANGEFREQ_RECORDS,
priority = DEFAULT_PRIORITY_RECORDS)
#task_sleep_now_if_required(can_stop_too=False)
task_update_progress("Generating urls for collections")
for (collection, lastmod) in get_all_public_collections(collection_names):
for [lang, lang_name] in language_list_long():
if nb_urls <= MAX_RECORDS and nb_urls % 100 == 0:
#print nb_urls
#print writer.get_size()
if writer.get_size() > MAX_SIZE or nb_urls == MAX_RECORDS:
writer.close()
sitemap_id += 1
writer = SitemapWriter('%s/sitemap-%s.xml' % (CFG_WEBDIR,
sitemap_id))
sitemaps.append(writer.get_name())
nb_urls = writer.add_url(
'%s/collection/%s?ln=%s' % (CFG_SITE_URL, quote(collection), lang),
lastmod = lastmod,
changefreq = DEFAULT_CHANGEFREQ_COLLECTIONS,
priority = DEFAULT_PRIORITY_COLLECTIONS)
#task_sleep_now_if_required(can_stop_too=False)
task_update_progress("Generating urls for fulltexts")
for (recid, lastmod) in filter_fulltexts(recids, fulltext_filter):
if nb_urls <= MAX_RECORDS and nb_urls % 100 == 0:
#print nb_urls
#print writer.get_size()
if writer.get_size() > MAX_SIZE or nb_urls == MAX_RECORDS:
writer.close()
sitemap_id += 1
writer = SitemapWriter(CFG_WEBDIR + '/sitemap-%s.xml' % sitemap_id)
sitemaps.append(writer.get_name())
- nb_urls = writer.add_url(CFG_SITE_URL + '/record/%s/files' % recid,
+ nb_urls = writer.add_url(CFG_SITE_URL + '/%s/%s/files' % (CFG_SITE_RECORD, recid),
lastmod = lastmod,
changefreq = DEFAULT_CHANGEFREQ_FULLTEXTS,
priority = DEFAULT_PRIORITY_FULLTEXTS)
#task_sleep_now_if_required(can_stop_too=False)
task_update_progress("Generating urls for comments")
for (recid, lastmod) in filter_comments(recids):
if nb_urls <= MAX_RECORDS and nb_urls % 100 == 0:
#print nb_urls
#print writer.get_size()
if writer.get_size() > MAX_SIZE or nb_urls == MAX_RECORDS:
writer.close()
sitemap_id += 1
writer = SitemapWriter(CFG_WEBDIR + '/sitemap-%s.xml' % sitemap_id)
sitemaps.append(writer.get_name())
- nb_urls = writer.add_url(CFG_SITE_URL + '/record/%s/comments' % recid,
+ nb_urls = writer.add_url(CFG_SITE_URL + '/%s/%s/comments' % (CFG_SITE_RECORD, recid),
lastmod = lastmod,
changefreq = DEFAULT_CHANGEFREQ_COMMENTS,
priority = DEFAULT_PRIORITY_COMMENTS)
#task_sleep_now_if_required(can_stop_too=False)
task_update_progress("Generating urls for reviews")
for (recid, lastmod) in filter_reviews(recids):
if nb_urls <= MAX_RECORDS and nb_urls % 100 == 0:
#print nb_urls
#print writer.get_size()
if writer.get_size() > MAX_SIZE or nb_urls == MAX_RECORDS:
writer.close()
sitemap_id += 1
writer = SitemapWriter(CFG_WEBDIR + '/sitemap-%s.xml' % sitemap_id)
sitemaps.append(writer.get_name())
- nb_urls = writer.add_url(CFG_SITE_URL + '/record/%s/reviews' % recid,
+ nb_urls = writer.add_url(CFG_SITE_URL + '/%s/%s/reviews' % (CFG_SITE_RECORD, recid),
lastmod = lastmod,
changefreq = DEFAULT_CHANGEFREQ_REVIEWS,
priority = DEFAULT_PRIORITY_REVIEWS)
#task_sleep_now_if_required(can_stop_too=False)
try:
writer.close()
except:
pass
return sitemaps
def generate_sitemaps_index(collection_list, fulltext_filter=None):
"""main function. Generates the sitemap index and the sitemaps
collection_list: list of collection names to add in sitemap
fulltext_filter: if provided the parser will intergrate only give fulltext
types
"""
sitemaps = generate_sitemaps(collection_list, fulltext_filter)
writer = SitemapIndexWriter(CFG_WEBDIR + '/sitemap-index.xml')
task_update_progress("Generating sitemap index for %s sitemap files" % \
len(sitemaps))
#task_sleep_now_if_required(can_stop_too=False)
for sitemap in sitemaps:
writer.add_url(CFG_SITE_URL + '/%s' % sitemap.split('/')[-1],
lastmod=datetime.today())
writer.close()
def run_export_method(jobname):
"""Main function, reading params and running the task."""
write_message("bibexport_sitemap: job %s started." % jobname)
collections = get_config_parameter(jobname = "sitemap", parameter_name = "collection", is_parameter_collection = True)
fulltext_type = get_config_parameter(jobname = "sitemap", parameter_name = "fulltext_status")
generate_sitemaps_index(collections, fulltext_type)
write_message("bibexport_sitemap: job %s finished." % jobname)
def get_config_parameter(jobname, parameter_name, is_parameter_collection = False):
"""Detect export method of JOBNAME. Basically, parse JOBNAME.cfg
and return export_method. Return None if problem found."""
jobconfig = ConfigParser()
jobconffile = CFG_ETCDIR + os.sep + 'bibexport' + os.sep + jobname + '.cfg'
if not os.path.exists(jobconffile):
write_message("ERROR: cannot find config file %s." % jobconffile)
return None
jobconfig.read(jobconffile)
if is_parameter_collection:
all_items = jobconfig.items(section = 'export_job')
parameters = []
for item_name, item_value in all_items:
if item_name.startswith(parameter_name):
parameters.append(item_value)
return parameters
else:
parameter = jobconfig.get('export_job', parameter_name)
return parameter
diff --git a/modules/bibformat/etc/format_templates/Default_HTML_actions.bft b/modules/bibformat/etc/format_templates/Default_HTML_actions.bft
index a2ea6c0a6..46917b71d 100644
--- a/modules/bibformat/etc/format_templates/Default_HTML_actions.bft
+++ b/modules/bibformat/etc/format_templates/Default_HTML_actions.bft
@@ -1,17 +1,17 @@
<name>Actions HTML detailed</name>
<description>Output the various actions available on a record</description>
<ul class="detailedrecordactions">
<li><a href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>yourbaskets/add?ln=<BFE_CLIENT_INFO var='ln'/>&amp;recid=<BFE_RECORD_ID/>">_(Add to personal basket)_</a></li>
<li>_(Export as)_
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/hx?ln=<BFE_CLIENT_INFO var='ln'/>">BibTeX</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/hm?ln=<BFE_CLIENT_INFO var='ln'/>">MARC</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xm?ln=<BFE_CLIENT_INFO var='ln'/>">MARCXML</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xd?ln=<BFE_CLIENT_INFO var='ln'/>">DC</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xe?ln=<BFE_CLIENT_INFO var='ln'/>">EndNote</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xn?ln=<BFE_CLIENT_INFO var='ln'/>">NLM</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xw?ln=<BFE_CLIENT_INFO var='ln'/>">RefWorks</a></li>
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/><BFE_SERVER_INFO var='CFG_SITE_RECORD'/>/<BFE_RECORD_ID/>/export/hx?ln=<BFE_CLIENT_INFO var='ln'/>">BibTeX</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/><BFE_SERVER_INFO var='CFG_SITE_RECORD'/>/<BFE_RECORD_ID/>/export/hm?ln=<BFE_CLIENT_INFO var='ln'/>">MARC</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/><BFE_SERVER_INFO var='CFG_SITE_RECORD'/>/<BFE_RECORD_ID/>/export/xm?ln=<BFE_CLIENT_INFO var='ln'/>">MARCXML</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/><BFE_SERVER_INFO var='CFG_SITE_RECORD'/>/<BFE_RECORD_ID/>/export/xd?ln=<BFE_CLIENT_INFO var='ln'/>">DC</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/><BFE_SERVER_INFO var='CFG_SITE_RECORD'/>/<BFE_RECORD_ID/>/export/xe?ln=<BFE_CLIENT_INFO var='ln'/>">EndNote</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/><BFE_SERVER_INFO var='CFG_SITE_RECORD'/>/<BFE_RECORD_ID/>/export/xn?ln=<BFE_CLIENT_INFO var='ln'/>">NLM</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/><BFE_SERVER_INFO var='CFG_SITE_RECORD'/>/<BFE_RECORD_ID/>/export/xw?ln=<BFE_CLIENT_INFO var='ln'/>">RefWorks</a></li>
<BFE_EDIT_RECORD prefix="<li>" suffix="</li>" />
<BFE_EDIT_FILES prefix="<li>" suffix="</li>" />
<BFE_SWORD_PUSH prefix="<li>" suffix="</li>" />
</ul>
diff --git a/modules/bibformat/etc/format_templates/Default_HTML_similarity.bft b/modules/bibformat/etc/format_templates/Default_HTML_similarity.bft
index 91b058d21..6316646fd 100644
--- a/modules/bibformat/etc/format_templates/Default_HTML_similarity.bft
+++ b/modules/bibformat/etc/format_templates/Default_HTML_similarity.bft
@@ -1,6 +1,6 @@
<name>Default HTML similarity</name>
<description>Small HTML record printed in "Similar Documents" section</description>
-<strong><a href="/record/<BFE_RECORD_ID/><BFE_CLIENT_INFO var="ln" prefix="?ln=">"><BFE_TITLE_BRIEF /></a></strong>
+<strong><a href="/<BFE_SERVER_INFO var='CFG_SITE_RECORD'/>/<BFE_RECORD_ID/><BFE_CLIENT_INFO var="ln" prefix="?ln=">"><BFE_TITLE_BRIEF /></a></strong>
<BFE_AUTHORS limit="1" prefix=" - " extension=" <em>et al</em>"/>
<BFE_PUBLI_INFO prefix=" - "/>
<BFE_PRIMARY_REPORT_NUMBER prefix=" - "/>
diff --git a/modules/bibformat/lib/bibformat.py b/modules/bibformat/lib/bibformat.py
index cf65ad233..085c97f82 100644
--- a/modules/bibformat/lib/bibformat.py
+++ b/modules/bibformat/lib/bibformat.py
@@ -1,566 +1,567 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Format records using specified format.
API functions: format_record, format_records, create_excel,
get_output_format_content_type
Used to wrap the BibFormat engine and associated functions. This is
also where special formatting functions of multiple records (that the
engine does not handle, as it works on a single record basis) should
be defined, with name C{def create_*}.
SEE: bibformat_utils.py
"""
__revision__ = "$Id$"
import zlib
from invenio import bibformat_dblayer
from invenio import bibformat_engine
from invenio import bibformat_utils
from invenio.errorlib import register_exception
from invenio.config import \
CFG_SITE_LANG, \
CFG_PATH_PHP, \
CFG_SITE_URL, \
- CFG_BIBFORMAT_HIDDEN_TAGS
+ CFG_BIBFORMAT_HIDDEN_TAGS, \
+ CFG_SITE_RECORD
from invenio.bibformat_config import \
CFG_BIBFORMAT_USE_OLD_BIBFORMAT, \
CFG_BIBFORMAT_ENABLE_I18N_BRIEF_FORMAT
from invenio.access_control_engine import acc_authorize_action
import getopt
import sys
# Functions to format a single record
##
def filter_hidden_fields(recxml, user_info=None, filter_tags=CFG_BIBFORMAT_HIDDEN_TAGS,
force_filtering=False):
"""
Filter out tags specified by filter_tags from MARCXML. If the user
is allowed to run bibedit, then filter nothing, unless
force_filtering is set to True.
@param recxml: marcxml presentation of the record
@param user_info: user information; if None, then assume invoked via CLI with all rights
@param filter_tags: list of MARC tags to be filtered
@param force_filtering: do we force filtering regardless of user rights?
@return: recxml without the hidden fields
"""
if force_filtering:
pass
else:
if user_info is None:
#by default
return recxml
else:
if (acc_authorize_action(user_info, 'runbibedit')[0] == 0):
#no need to filter
return recxml
#filter..
out = ""
omit = False
for line in recxml.splitlines(True):
#check if this block needs to be omitted
for htag in filter_tags:
if line.count('datafield tag="'+str(htag)+'"'):
omit = True
if not omit:
out += line
if omit and line.count('</datafield>'):
omit = False
return out
def format_record(recID, of, ln=CFG_SITE_LANG, verbose=0, search_pattern=None,
xml_record=None, user_info=None, on_the_fly=False):
"""
Formats a record in given output format.
Returns a formatted version of the record in the specified
language, search pattern, and with the specified output format.
The function will define which format template must be applied.
The record to be formatted can be specified with its ID (with
'recID' parameter) or given as XML representation (with
'xml_record' parameter). If 'xml_record' is specified 'recID' is
ignored (but should still be given for reference. A dummy recid 0
or -1 could be used).
'user_info' allows to grant access to some functionalities on a
page depending on the user's priviledges. The 'user_info' object
makes sense only in the case of on-the-fly formatting. 'user_info'
is the same object as the one returned by
'webuser.collect_user_info(req)'
@param recID: the ID of record to format.
@type recID: int
@param of: an output format code (or short identifier for the output format)
@type of: string
@param ln: the language to use to format the record
@type ln: string
@param verbose: the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings, stop if error in format elements
9: errors and warnings, stop if error (debug mode ))
@type verbose: int
@param search_pattern: list of strings representing the user request in web interface
@type search_pattern: list(string)
@param xml_record: an xml string represention of the record to format
@type xml_record: string or None
@param user_info: the information of the user who will view the formatted page (if applicable)
@param on_the_fly: if False, try to return an already preformatted version of the record in the database
@type on_the_fly: boolean
@return: formatted record
@rtype: string
"""
from invenio.search_engine import record_exists
if search_pattern is None:
search_pattern = []
out = ""
if verbose == 9:
out += """\n<span class="quicknote">
Formatting record %i with output format %s.
</span>""" % (recID, of)
############### FIXME: REMOVE WHEN MIGRATION IS DONE ###############
if CFG_BIBFORMAT_USE_OLD_BIBFORMAT and CFG_PATH_PHP:
return bibformat_engine.call_old_bibformat(recID, of=of, on_the_fly=on_the_fly)
############################# END ##################################
if not on_the_fly and \
(ln == CFG_SITE_LANG or \
of.lower() == 'xm' or \
CFG_BIBFORMAT_USE_OLD_BIBFORMAT or \
(CFG_BIBFORMAT_ENABLE_I18N_BRIEF_FORMAT == False and of.lower() == 'hb')) and \
record_exists(recID) != -1:
# Try to fetch preformatted record Only possible for records
# formatted in CFG_SITE_LANG language (other are never
# stored), or of='xm' which does not depend on language.
# Also, when formatting in HB, and when
# CFG_BIBFORMAT_ENABLE_I18N_BRIEF_FORMAT is set to False,
# ignore other languages and fetch the preformatted output.
# Also, do not fetch from DB when record has been deleted: we
# want to return an "empty" record in that case
res = bibformat_dblayer.get_preformatted_record(recID, of)
if res is not None:
# record 'recID' is formatted in 'of', so return it
if verbose == 9:
last_updated = bibformat_dblayer.get_preformatted_record_date(recID, of)
out += """\n<br/><span class="quicknote">
Found preformatted output for record %i (cache updated on %s).
</span><br/>""" % (recID, last_updated)
if of.lower() == 'xm':
res = filter_hidden_fields(res, user_info)
out += res
return out
else:
if verbose == 9:
out += """\n<br/><span class="quicknote">
No preformatted output found for record %s.
</span>"""% recID
# Live formatting of records in all other cases
if verbose == 9:
out += """\n<br/><span class="quicknote">
Formatting record %i on-the-fly.
</span>""" % recID
try:
out += bibformat_engine.format_record(recID=recID,
of=of,
ln=ln,
verbose=verbose,
search_pattern=search_pattern,
xml_record=xml_record,
user_info=user_info)
if of.lower() == 'xm':
out = filter_hidden_fields(out, user_info)
return out
except Exception, e:
register_exception(prefix="An error occured while formatting record %i in %s" % \
(recID, of),
alert_admin=True)
#Failsafe execution mode
import invenio.template
websearch_templates = invenio.template.load('websearch')
if verbose == 9:
out += """\n<br/><span class="quicknote">
An error occured while formatting record %i. (%s)
</span>""" % (recID, str(e))
if of.lower() == 'hd':
if verbose == 9:
out += """\n<br/><span class="quicknote">
Formatting record %i with websearch_templates.tmpl_print_record_detailed.
</span><br/>""" % recID
return out + websearch_templates.tmpl_print_record_detailed(
ln = ln,
recID = recID,
)
if verbose == 9:
out += """\n<br/><span class="quicknote">
Formatting record %i with websearch_templates.tmpl_print_record_brief.
</span><br/>""" % recID
return out + websearch_templates.tmpl_print_record_brief(ln = ln,
recID = recID,
)
def record_get_xml(recID, format='xm', decompress=zlib.decompress):
"""
Returns an XML string of the record given by recID.
The function builds the XML directly from the database,
without using the standard formatting process.
'format' allows to define the flavour of XML:
- 'xm' for standard XML
- 'marcxml' for MARC XML
- 'oai_dc' for OAI Dublin Core
- 'xd' for XML Dublin Core
If record does not exist, returns empty string.
@param recID: the id of the record to retrieve
@return: the xml string of the record
"""
return bibformat_utils.record_get_xml(recID=recID, format=format, decompress=decompress)
# Helper functions to do complex formatting of multiple records
#
# You should not modify format_records when adding a complex
# formatting of multiple records, but add a create_* method
# that relies on format_records to do the formatting.
##
def format_records(recIDs, of, ln=CFG_SITE_LANG, verbose=0, search_pattern=None,
xml_records=None, user_info=None, record_prefix=None,
record_separator=None, record_suffix=None, prologue="",
epilogue="", req=None, on_the_fly=False):
"""
Format records given by a list of record IDs or a list of records
as xml. Adds a prefix before each record, a suffix after each
record, plus a separator between records.
Also add optional prologue and epilogue to the complete formatted
list.
You can either specify a list of record IDs to format, or a list
of xml records, but not both (if both are specified recIDs is
ignored).
'record_separator' is a function that returns a string as
separator between records. The function must take an integer as
unique parameter, which is the index in recIDs (or xml_records) of
the record that has just been formatted. For example separator(i)
must return the separator between recID[i] and recID[i+1].
Alternatively separator can be a single string, which will be used
to separate all formatted records. The same applies to
'record_prefix' and 'record_suffix'.
'req' is an optional parameter on which the result of the function
are printed lively (prints records after records) if it is given.
Note that you should set 'req' content-type by yourself, and send
http header before calling this function as it will not do it.
This function takes the same parameters as 'format_record' except for:
@param recIDs: a list of record IDs
@type recIDs: list(int)
@param of: an output format code (or short identifier for the output format)
@type of: string
@param ln: the language to use to format the record
@type ln: string
@param verbose: the level of verbosity from 0 to 9 (0: silent,
5: errors,
7: errors and warnings, stop if error in format elements
9: errors and warnings, stop if error (debug mode ))
@type verbose: int
@param search_pattern: list of strings representing the user request in web interface
@type search_pattern: list(string)
@param user_info: the information of the user who will view the formatted page (if applicable)
@param xml_records: a list of xml string representions of the records to format
@type xml_records: list(string)
@param record_prefix: a string printed before B{each} formatted records (n times)
@type record_prefix: string
@param record_suffix: a string printed after B{each} formatted records (n times)
@type record_suffix: string
@param prologue: a string printed at the beginning of the complete formatted records (1x)
@type prologue: string
@param epilogue: a string printed at the end of the complete formatted output (1x)
@type epilogue: string
@param record_separator: either a string or a function that returns string to join formatted records
@param record_separator: string or function
@param req: an optional request object where to print records
@param on_the_fly: if False, try to return an already preformatted version of the record in the database
@type on_the_fly: boolean
@rtype: string
"""
if req is not None:
req.write(prologue)
formatted_records = ''
#Fill one of the lists with Nones
if xml_records is not None:
recIDs = map(lambda x:None, xml_records)
else:
xml_records = map(lambda x:None, recIDs)
total_rec = len(recIDs)
last_iteration = False
for i in range(total_rec):
if i == total_rec - 1:
last_iteration = True
#Print prefix
if record_prefix is not None:
if isinstance(record_prefix, str):
formatted_records += record_prefix
if req is not None:
req.write(record_prefix)
else:
string_prefix = record_prefix(i)
formatted_records += string_prefix
if req is not None:
req.write(string_prefix)
#Print formatted record
formatted_record = format_record(recIDs[i], of, ln, verbose, \
search_pattern, xml_records[i],\
user_info, on_the_fly)
formatted_records += formatted_record
if req is not None:
req.write(formatted_record)
#Print suffix
if record_suffix is not None:
if isinstance(record_suffix, str):
formatted_records += record_suffix
if req is not None:
req.write(record_suffix)
else:
string_suffix = record_suffix(i)
formatted_records += string_suffix
if req is not None:
req.write(string_suffix)
#Print separator if needed
if record_separator is not None and not last_iteration:
if isinstance(record_separator, str):
formatted_records += record_separator
if req is not None:
req.write(record_separator)
else:
string_separator = record_separator(i)
formatted_records += string_separator
if req is not None:
req.write(string_separator)
if req is not None:
req.write(epilogue)
return prologue + formatted_records + epilogue
def create_excel(recIDs, req=None, ln=CFG_SITE_LANG, ot=None, ot_sep="; "):
"""
Returns an Excel readable format containing the given recIDs.
If 'req' is given, also prints the output in 'req' while individual
records are being formatted.
This method shows how to create a custom formatting of multiple
records.
The excel format is a basic HTML table that most spreadsheets
applications can parse.
If 'ot' is given, the BibFormat engine is overridden and the
output is produced on the basis of the fields that 'ot' defines
(see search_engine.perform_request_search(..) 'ot' param).
@param recIDs: a list of record IDs
@param ot: a list of fields that should be included in the excel output as columns(see perform_request_search 'ot' param)
@param ot_sep: a separator used to separate values for the same record, in the same columns, if any
@return: a string in Excel format
"""
# Prepare the column headers to display in the Excel file
column_headers_list = ['Title',
'Authors',
'Addresses',
'Affiliation',
'Date',
'Publisher',
'Place',
'Abstract',
'Keywords',
'Notes']
# Prepare Content
column_headers = '</b></td><td style="border-color:black; border-style:solid; border-width:thin; background-color:black;color:white"><b>'.join(column_headers_list) + ''
column_headers = '<table style="border-collapse: collapse;">\n'+ '<td style="border-color:black; border-style:solid; border-width:thin; background-color:black;color:white"><b>' + column_headers + '</b></td>'
footer = '</table>'
# Apply content_type and print column headers
if req is not None:
req.content_type = get_output_format_content_type('excel')
req.headers_out["Content-Disposition"] = "inline; filename=%s" % 'results.xls'
req.send_http_header()
if ot is not None and len(ot) > 0:
# Skip BibFormat engine, produce our own output based on
# specified fields. Each field will be a column of the
# output. If a field has multiple values, then they are joined
# into the same cell.
out = "<table>"
if req: req.write("<table>")
for recID in recIDs:
row = '<tr>'
- row += '<td><a href="%(CFG_SITE_URL)s/record/%(recID)i">%(recID)i</a></td>' % \
- {'recID': recID, 'CFG_SITE_URL': CFG_SITE_URL}
+ row += '<td><a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recID)i">%(recID)i</a></td>' % \
+ {'recID': recID, 'CFG_SITE_RECORD': CFG_SITE_RECORD, 'CFG_SITE_URL': CFG_SITE_URL}
for field in ot:
row += '<td>' + \
ot_sep.join(bibformat_utils.get_all_fieldvalues(recID, field)) + \
'</td>'
row += '</tr>'
out += row
if req: req.write(row)
out += '</table>'
if req: req.write('</table>')
return out
#Format the records
excel_formatted_records = format_records(recIDs, 'excel', ln=CFG_SITE_LANG,
record_separator='\n',
prologue = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><table>',
epilogue = footer,
req=req)
return excel_formatted_records
# Utility functions
##
def get_output_format_content_type(of):
"""
Returns the content type (for example 'text/html' or 'application/ms-excel') \
of the given output format.
@param of: the code of output format for which we want to get the content type
"""
content_type = bibformat_dblayer.get_output_format_content_type(of)
if content_type == '':
content_type = 'text/html'
return content_type
def usage(exitcode=1, msg=""):
"""Prints usage info."""
if msg:
sys.stderr.write("Error: %s.\n" % msg)
print """BibFormat: outputs the result of the formatting of a record.
Usage: bibformat required [options]
Examples:
$ bibformat -i 10 -o HB
$ bibformat -i 10,11,13 -o HB
$ bibformat -i 10:13
$ bibformat -i 10 -o HB -v 9
Required:
-i, --id=ID[ID2,ID3:ID5] ID (or range of IDs) of the record(s) to be formatted.
Options:
-o, --output=CODE short code of the output format used for formatting (default HB).
-l, --lang=LN language used for formatting.
-y, --onthefly on-the-fly formatting, avoiding caches created by BibReformat.
General options:
-h, --help print this help and exit
-v, --verbose=LEVEL verbose level (from 0 to 9, default 0)
-V --version print the script version
"""
sys.exit(exitcode)
def main():
"""main entry point for biformat via command line"""
options = {} # will hold command-line options
options["verbose"] = 0
options["onthefly"] = False
options["lang"] = CFG_SITE_LANG
options["output"] = "HB"
options["recID"] = None
try:
opts, args = getopt.getopt(sys.argv[1:],
"hVv:yl:i:o:",
["help",
"version",
"verbose=",
"onthefly",
"lang=",
"id=",
"output="])
except getopt.GetoptError, err:
usage(1, err)
pass
try:
for opt in opts:
if opt[0] in ["-h", "--help"]:
usage(0)
elif opt[0] in ["-V", "--version"]:
print __revision__
sys.exit(0)
elif opt[0] in ["-v", "--verbose"]:
options["verbose"] = int(opt[1])
elif opt[0] in ["-y", "--onthefly"]:
options["onthefly"] = True
elif opt[0] in ["-l", "--lang"]:
options["lang"] = opt[1]
elif opt[0] in ["-i", "--id"]:
recIDs = []
for recID in opt[1].split(','):
if ":" in recID:
start = int(recID.split(':')[0])
end = int(recID.split(':')[1])
recIDs.extend(range(start, end))
else:
recIDs.append(int(recID))
options["recID"] = recIDs
elif opt[0] in ["-o", "--output"]:
options["output"] = opt[1]
if options["recID"] == None:
usage(1, "-i argument is needed")
except StandardError, e:
usage(e)
print format_records(recIDs=options["recID"],
of=options["output"],
ln=options["lang"],
verbose=options["verbose"],
on_the_fly=options["onthefly"])
return
if __name__ == "__main__":
main()
diff --git a/modules/bibformat/lib/bibformat_regression_tests.py b/modules/bibformat/lib/bibformat_regression_tests.py
index bc9873bdc..90dd76c25 100644
--- a/modules/bibformat/lib/bibformat_regression_tests.py
+++ b/modules/bibformat/lib/bibformat_regression_tests.py
@@ -1,452 +1,452 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat module regression tests."""
__revision__ = "$Id$"
import unittest
-from invenio.config import CFG_SITE_URL, CFG_SITE_LANG
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG, CFG_SITE_RECORD
from invenio.testutils import make_test_suite, \
run_test_suite, \
test_web_page_content
from invenio.bibformat import format_record
class BibFormatAPITest(unittest.TestCase):
"""Check BibFormat API"""
def test_basic_formatting(self):
"""bibformat - Checking BibFormat API"""
result = format_record(recID=73,
of='hx',
ln=CFG_SITE_LANG,
verbose=0,
search_pattern=[],
xml_record=None,
user_info=None,
on_the_fly=True)
- pageurl = CFG_SITE_URL + '/record/73?of=hx'
+ pageurl = CFG_SITE_URL + '/%s/73?of=hx' % CFG_SITE_RECORD
result = test_web_page_content(pageurl,
expected_text=result)
class BibFormatBibTeXTest(unittest.TestCase):
"""Check output produced by BibFormat for BibTeX output for
various records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_74_hx = '''<pre>
@article{Wang:74,
author = "Wang, B and Lin, C Y and Abdalla, E",
title = "Quasinormal modes of Reissner-Nordstrom Anti-de Sitter
Black Holes",
journal = "Phys. Lett., B",
number = "hep-th/0003295",
volume = "481",
pages = "79-88",
year = "2000",
}
</pre>'''
def test_bibtex_output(self):
"""bibformat - BibTeX output"""
- pageurl = CFG_SITE_URL + '/record/74?of=hx'
+ pageurl = CFG_SITE_URL + '/%s/74?of=hx' % CFG_SITE_RECORD
result = test_web_page_content(pageurl,
expected_text=self.record_74_hx)
self.assertEqual([], result)
class BibFormatDetailedHTMLTest(unittest.TestCase):
"""Check output produced by BibFormat for detailed HTML ouput for
various records"""
def setUp(self):
"""Prepare some ideal outputs"""
# Record 7 (Article)
self.record_74_hd_header = '''<table border="0" width="100%">
<tr>
<td>Published Article<small> / Particle Physics - Theory</small></td>
<td><small><strong></strong></small></td>
<td align="right"><strong>hep-th/0003295</strong></td>
</tr>
</table>'''
self.record_74_hd_title = '''<center><big><big><strong>Quasinormal modes of Reissner-Nordstrom Anti-de Sitter Black Holes</strong></big></big></center>'''
self.record_74_hd_authors = '''<a href="%(siteurl)s/search?f=author&amp;p=Wang%%2C%%20B&amp;ln=%(lang)s">Wang, B</a><small> (Fudan University)</small> ; <a href="%(siteurl)s/search?f=author&amp;p=Lin%%2C%%20C%%20Y&amp;ln=%(lang)s">Lin, C Y</a> ; <a href="%(siteurl)s/search?f=author&amp;p=Abdalla%%2C%%20E&amp;ln=%(lang)s">Abdalla, E</a><br />'''% \
{'siteurl' : CFG_SITE_URL,
'lang': CFG_SITE_LANG}
self.record_74_hd_abstract = '''<small><strong>Abstract: </strong>Complex frequencies associated with quasinormal modes for large Reissner-Nordstr$\ddot{o}$m Anti-de Sitter black holes have been computed. These frequencies have close relation to the black hole charge and do not linearly scale withthe black hole temperature as in Schwarzschild Anti-de Sitter case. In terms of AdS/CFT correspondence, we found that the bigger the black hole charge is, the quicker for the approach to thermal equilibrium in the CFT. The propertiesof quasinormal modes for $l&gt;0$ have also been studied.</small><br />'''
self.record_74_hd_pubinfo = '''<strong>Published in: </strong><a href="http://weblib.cern.ch/cgi-bin/ejournals?publication=Phys.%20Lett.%2C%20B&amp;volume=481&amp;year=2000&amp;page=79">Phys. Lett., B :481 2000 79-88</a>'''
self.record_74_hd_fulltext = '''0003295.pdf"><img style="border:none"'''
self.record_74_hd_citations = '''<strong>Cited by:</strong> try citation search for <a href="%(siteurl)s/search?f=reference&amp;p=hep-th/0003295&amp;ln=%(lang)s">hep-th/0003295</a>'''% \
{'siteurl' : CFG_SITE_URL,
'lang': CFG_SITE_LANG}
self.record_74_hd_references = '''<li><small>[17]</small> <small>A. Chamblin, R. Emparan, C. V. Johnson and R. C. Myers, Phys. Rev., D60: 104026 (1999) 5070 90 110 130 150 r+ 130 230 330 50 70 90 110 130 150 r+</small> </li>'''
# Record 7 (Picture)
self.record_7_hd_header = '''<table border="0" width="100%">
<tr>
<td>Pictures<small> / Life at CERN</small></td>
<td><small><strong></strong></small></td>
<td align="right"><strong>CERN-GE-9806033</strong></td>
</tr>
</table>'''
self.record_7_hd_title = '''<center><big><big><strong>Tim Berners-Lee</strong></big></big></center>'''
self.record_7_hd_date = '''<center>28 Jun 1998</center>'''
self.record_7_hd_abstract = '''<p><span class="blocknote">
Caption</span><br /> <small>Conference "Internet, Web, What's next?" on 26 June 1998 at CERN : Tim Berners-Lee, inventor of the World-Wide Web and Director of the W3C, explains how the Web came to be and give his views on the future.</small></p><p><span class="blocknote">
Légende</span><br /><small>Conference "Internet, Web, What's next?" le 26 juin 1998 au CERN: Tim Berners-Lee, inventeur du World-Wide Web et directeur du W3C, explique comment le Web est ne, et donne ses opinions sur l'avenir.</small></p>'''
- self.record_7_hd_resource = '''<img src="%s/record/7/files/9806033.gif?subformat=icon" alt="9806033" style="max-width:250px;_width:250px;" />''' % CFG_SITE_URL
- self.record_7_hd_resource_link = '%s/record/7/files/9806033.jpeg' % CFG_SITE_URL
+ self.record_7_hd_resource = '''<img src="%s/%s/7/files/9806033.gif?subformat=icon" alt="9806033" style="max-width:250px;_width:250px;" />''' % (CFG_SITE_URL, CFG_SITE_RECORD)
+ self.record_7_hd_resource_link = '%s/%s/7/files/9806033.jpeg' % (CFG_SITE_URL, CFG_SITE_RECORD)
def test_detailed_html_output(self):
"""bibformat - Detailed HTML output"""
# Test record 74 (Article)
- pageurl = CFG_SITE_URL + '/record/74?of=hd'
+ pageurl = CFG_SITE_URL + '/%s/74?of=hd' % CFG_SITE_RECORD
result = test_web_page_content(pageurl,
expected_text=[self.record_74_hd_header,
self.record_74_hd_title,
self.record_74_hd_authors,
self.record_74_hd_abstract,
self.record_74_hd_pubinfo,
self.record_74_hd_fulltext,
#self.record_74_hd_citations,
#self.record_74_hd_references
])
self.assertEqual([], result)
# Test record 7 (Picture)
- pageurl = CFG_SITE_URL + '/record/7?of=hd'
+ pageurl = CFG_SITE_URL + '/%s/7?of=hd' % CFG_SITE_RECORD
result = test_web_page_content(pageurl,
expected_text=[self.record_7_hd_header,
self.record_7_hd_title,
self.record_7_hd_date,
self.record_7_hd_abstract,
self.record_7_hd_resource,
self.record_7_hd_resource_link])
self.assertEqual([], result)
def test_detailed_html_edit_record(self):
"""bibformat - Detailed HTML output edit record link presence"""
- pageurl = CFG_SITE_URL + '/record/74?of=hd'
+ pageurl = CFG_SITE_URL + '/%s/74?of=hd' % CFG_SITE_RECORD
result = test_web_page_content(pageurl, username='admin',
expected_text="Edit This Record")
self.assertEqual([], result)
def test_detailed_html_no_error_message(self):
"""bibformat - Detailed HTML output without error message"""
# No error message should be displayed in the web interface, whatever happens
- pageurl = CFG_SITE_URL + '/record/74?of=hd'
+ pageurl = CFG_SITE_URL + '/%s/74?of=hd' % CFG_SITE_RECORD
result = test_web_page_content(pageurl, username='admin',
expected_text=["Exception",
"Could not"])
self.assertNotEqual([], result)
- pageurl = CFG_SITE_URL + '/record/7?of=hd'
+ pageurl = CFG_SITE_URL + '/%s/7?of=hd' % CFG_SITE_RECORD
result = test_web_page_content(pageurl, username='admin',
expected_text=["Exception",
"Could not"])
self.assertNotEqual([], result)
class BibFormatNLMTest(unittest.TestCase):
"""Check output produced by BibFormat for NLM output for various
records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_70_xn = '''<?xml version="1.0" encoding="UTF-8"?>
<articles>
<article xmlns:xlink="http://www.w3.org/1999/xlink/">
<front>
<journal-meta>
<journal-title>J. High Energy Phys.</journal-title>
<abbrev-journal-title>J. High Energy Phys.</abbrev-journal-title>
<issn>1126-6708</issn>
</journal-meta>
<article-meta>
<title-group>
<article-title>AdS/CFT For Non-Boundary Manifolds</article-title>
</title-group>
<contrib-group>
<contrib contrib-type="author">
<name>
<surname>McInnes</surname>
<given-names>B</given-names>
</name>
<aff>
<institution>National University of Singapore</institution>
</aff>
</contrib>
</contrib-group>
<pub-date pub-type="pub">
<year>2000</year>
</pub-date>
<volume>05</volume>
<fpage/>
<lpage/>
- <self-uri xlink:href="%(siteurl)s/record/70"/>
- <self-uri xlink:href="%(siteurl)s/record/70/files/0003291.pdf"/>
- <self-uri xlink:href="%(siteurl)s/record/70/files/0003291.ps.gz"/>
+ <self-uri xlink:href="%(siteurl)s/%(CFG_SITE_RECORD)s/70"/>
+ <self-uri xlink:href="%(siteurl)s/%(CFG_SITE_RECORD)s/70/files/0003291.pdf"/>
+ <self-uri xlink:href="%(siteurl)s/%(CFG_SITE_RECORD)s/70/files/0003291.ps.gz"/>
</article-meta>
<abstract>In its Euclidean formulation, the AdS/CFT correspondence begins as a study of Yang-Mills conformal field theories on the sphere, S^4. It has been successfully extended, however, to S^1 X S^3 and to the torus T^4. It is natural tohope that it can be made to work for any manifold on which it is possible to define a stable Yang-Mills conformal field theory. We consider a possible classification of such manifolds, and show how to deal with the most obviousobjection : the existence of manifolds which cannot be represented as boundaries. We confirm Witten's suggestion that this can be done with the help of a brane in the bulk.</abstract>
</front>
<article-type>research-article</article-type>
<ref/>
</article>
-</articles>''' % {'siteurl': CFG_SITE_URL}
+</articles>''' % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
def test_nlm_output(self):
"""bibformat - NLM output"""
- pageurl = CFG_SITE_URL + '/record/70?of=xn'
+ pageurl = CFG_SITE_URL + '/%s/70?of=xn' % CFG_SITE_RECORD
result = test_web_page_content(pageurl,
expected_text=self.record_70_xn)
try:
self.assertEqual([], result)
except AssertionError:
result = test_web_page_content(pageurl,
expected_text=self.record_70_xn.replace('<fpage/>', '<fpage></fpage>').replace('<lpage/>', '<lpage></lpage>'))
self.assertEqual([], result)
class BibFormatBriefHTMLTest(unittest.TestCase):
"""Check output produced by BibFormat for brief HTML ouput for
various records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_76_hb = '''<strong>Ιθάκη</strong>
/ <a href="%s/search?f=author&amp;p=%%CE%%9A%%CE%%B1%%CE%%B2%%CE%%AC%%CF%%86%%CE%%B7%%CF%%82%%2C%%20%%CE%%9A%%20%%CE%%A0&amp;ln=%s">Καβάφης, Κ Π</a>
<br /><small>
Σα βγεις στον πηγαιμό για την Ιθάκη, <br />
να εύχεσαι νάναι μακρύς ο δρόμος, <br />
γεμάτος περιπέτειες, γεμάτος γνώσεις [...] </small>''' % (CFG_SITE_URL, CFG_SITE_LANG)
def test_brief_html_output(self):
"""bibformat - Brief HTML output"""
- pageurl = CFG_SITE_URL + '/record/76?of=HB'
+ pageurl = CFG_SITE_URL + '/%s/76?of=HB' % CFG_SITE_RECORD
result = test_web_page_content(pageurl,
expected_text=self.record_76_hb)
self.assertEqual([], result)
class BibFormatMARCXMLTest(unittest.TestCase):
"""Check output produced by BibFormat for MARCXML ouput for various records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_9_xm = '''<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<controlfield tag="001">9</controlfield>
<datafield tag="041" ind1=" " ind2=" ">
<subfield code="a">eng</subfield>
</datafield>
<datafield tag="088" ind1=" " ind2=" ">
<subfield code="a">PRE-25553</subfield>
</datafield>
<datafield tag="088" ind1=" " ind2=" ">
<subfield code="a">RL-82-024</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Ellis, J</subfield>
<subfield code="u">University of Oxford</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Grand unification with large supersymmetry breaking</subfield>
</datafield>
<datafield tag="260" ind1=" " ind2=" ">
<subfield code="c">Mar 1982</subfield>
</datafield>
<datafield tag="300" ind1=" " ind2=" ">
<subfield code="a">18 p</subfield>
</datafield>
<datafield tag="650" ind1="1" ind2="7">
<subfield code="2">SzGeCERN</subfield>
<subfield code="a">General Theoretical Physics</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Ibanez, L E</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Ross, G G</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="0">
<subfield code="y">1982</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="0">
<subfield code="b">11</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="u">Oxford Univ.</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="u">Univ. Auton. Madrid</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="u">Rutherford Lab.</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="c">1990-01-28</subfield>
<subfield code="l">50</subfield>
<subfield code="m">2002-01-04</subfield>
<subfield code="o">BATCH</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="S">
<subfield code="s">h</subfield>
<subfield code="w">1982n</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">PREPRINT</subfield>
</datafield>
</record>
</collection>'''
def test_marcxml_output(self):
"""bibformat - MARCXML output"""
- pageurl = CFG_SITE_URL + '/record/9?of=xm'
+ pageurl = CFG_SITE_URL + '/%s/9?of=xm' % CFG_SITE_RECORD
result = test_web_page_content(pageurl,
expected_text=self.record_9_xm)
self.assertEqual([], result)
class BibFormatMARCTest(unittest.TestCase):
"""Check output produced by BibFormat for MARC ouput for various
records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_29_hm = '''000000029 001__ 29
000000029 020__ $$a0720421039
000000029 041__ $$aeng
000000029 080__ $$a517.11
000000029 100__ $$aKleene, Stephen Cole$$uUniversity of Wisconsin
000000029 245__ $$aIntroduction to metamathematics
000000029 260__ $$aAmsterdam$$bNorth-Holland$$c1952 (repr.1964.)
000000029 300__ $$a560 p
000000029 490__ $$aBibl. Matematica$$v1
000000029 909C0 $$y1952
000000029 909C0 $$b21
000000029 909C1 $$c1990-01-27$$l00$$m2002-04-12$$oBATCH
000000029 909CS $$sm$$w198606
000000029 980__ $$aBOOK'''
def test_marc_output(self):
"""bibformat - MARC output"""
- pageurl = CFG_SITE_URL + '/record/29?of=hm'
+ pageurl = CFG_SITE_URL + '/%s/29?of=hm' % CFG_SITE_RECORD
result = test_web_page_content(pageurl,
expected_text=self.record_29_hm)
self.assertEqual([], result)
class BibFormatTitleFormattingTest(unittest.TestCase):
"""Check title formatting produced by BibFormat."""
def test_subtitle_in_html_brief(self):
"""bibformat - title subtitle in HTML brief formats"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=statistics+computer',
expected_text="Statistics: a computer approach"))
def test_subtitle_in_html_detailed(self):
"""bibformat - title subtitle in HTML detailed formats"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=statistics+computer&of=HD',
expected_text="Statistics: a computer approach"))
def test_title_edition_in_html_brief(self):
"""bibformat - title edition in HTML brief formats"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=2nd',
expected_text="Introductory statistics: a decision map; 2nd ed"))
def test_title_edition_in_html_detailed(self):
"""bibformat - title edition in HTML detailed formats"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=2nd&of=HD',
expected_text="Introductory statistics: a decision map; 2nd ed"))
def test_title_part_in_html_brief(self):
"""bibformat - title part in HTML brief formats"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=analyse+informatique',
expected_text="Analyse informatique, t.2"))
def test_title_part_in_html_detailed(self):
"""bibformat - title part in HTML detailed formats"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=analyse+informatique&of=HD',
expected_text="Analyse informatique, t.2: L'accomplissement"))
class BibFormatISBNFormattingTest(unittest.TestCase):
"""Check ISBN formatting produced by BibFormat."""
def test_isbn_in_html_detailed(self):
"""bibformat - ISBN in HTML detailed formats"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=analyse+informatique&of=HD',
expected_text="ISBN: 2225350574"))
class BibFormatPublInfoFormattingTest(unittest.TestCase):
"""Check publication reference info formatting produced by BibFormat."""
def test_publinfo_in_html_brief(self):
"""bibformat - publication reference info in HTML brief formats"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A84',
expected_text="Nucl. Phys. B: 656 (2003) pp. 23-36"))
def test_publinfo_in_html_detailed(self):
"""bibformat - publication reference info in HTML detailed formats"""
self.assertEqual([],
- test_web_page_content(CFG_SITE_URL + '/record/84',
+ test_web_page_content(CFG_SITE_URL + '/%s/84' % CFG_SITE_RECORD,
expected_text="Nucl. Phys. B: 656 (2003) pp. 23-36"))
TEST_SUITE = make_test_suite(BibFormatBibTeXTest,
BibFormatDetailedHTMLTest,
BibFormatBriefHTMLTest,
BibFormatNLMTest,
BibFormatMARCTest,
BibFormatMARCXMLTest,
BibFormatAPITest,
BibFormatTitleFormattingTest,
BibFormatISBNFormattingTest,
BibFormatPublInfoFormattingTest)
if __name__ == "__main__":
run_test_suite(TEST_SUITE, warn_user=True)
diff --git a/modules/bibformat/lib/elements/bfe_edit_record.py b/modules/bibformat/lib/elements/bfe_edit_record.py
index e15b9e260..e6cd88953 100644
--- a/modules/bibformat/lib/elements/bfe_edit_record.py
+++ b/modules/bibformat/lib/elements/bfe_edit_record.py
@@ -1,61 +1,61 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints a link to BibEdit
"""
__revision__ = "$Id$"
from invenio.urlutils import create_html_link
from invenio.messages import gettext_set_language
-from invenio.config import CFG_SITE_URL
+from invenio.config import CFG_SITE_URL, CFG_SITE_RECORD
from invenio.access_control_engine import acc_authorize_action
from invenio.search_engine import guess_primary_collection_of_a_record
def format_element(bfo, style):
"""
Prints a link to BibEdit, if authorization is granted
@param style: the CSS style to be applied to the link.
"""
_ = gettext_set_language(bfo.lang)
out = ""
user_info = bfo.user_info
collection = guess_primary_collection_of_a_record(bfo.recID)
(auth_code, auth_message) = acc_authorize_action(user_info,
'runbibedit',
collection=collection)
if auth_code == 0:
linkattrd = {}
if style != '':
linkattrd['style'] = style
out += create_html_link(CFG_SITE_URL +
- '/record/edit/?ln=%s#state=edit&recid=%s' % (bfo.lang, str(bfo.recID)),
+ '/%s/edit/?ln=%s#state=edit&recid=%s' % (CFG_SITE_RECORD, bfo.lang, str(bfo.recID)),
{},
link_label=_("Edit This Record"),
linkattrd=linkattrd)
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_fulltext.py b/modules/bibformat/lib/elements/bfe_fulltext.py
index d051b9fa3..9d75f411c 100644
--- a/modules/bibformat/lib/elements/bfe_fulltext.py
+++ b/modules/bibformat/lib/elements/bfe_fulltext.py
@@ -1,299 +1,299 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints a links to fulltext
"""
__revision__ = "$Id$"
import re
from invenio.bibdocfile import BibRecDocs, file_strip_ext
from invenio.messages import gettext_set_language
-from invenio.config import CFG_SITE_URL, CFG_CERN_SITE
+from invenio.config import CFG_SITE_URL, CFG_CERN_SITE, CFG_SITE_RECORD
from invenio.websubmit_config import CFG_WEBSUBMIT_ICON_SUBFORMAT_RE
from cgi import escape, parse_qs
from urlparse import urlparse
from os.path import basename
import urllib
cern_arxiv_categories = ["astro-ph", "chao-dyn", "cond-mat", "gr-qc",
"hep-ex", "hep-lat", "hep-ph", "hep-th", "math-ph",
"math", "nucl-ex", "nucl-th", "out", "physics",
"quant-ph", "q-alg", "cs", "adap-org", "comp-gas",
"chem-ph", "cs", "math", "neuro-sys", "patt-sol",
"solv-int", "acc-phys", "alg-geom", "ao-sci",
"atom-ph", "cmp-lg", "dg-ga", "funct-an", "mtrl-th",
"plasm-ph", "q-alg", "supr-con"]
def format_element(bfo, style, separator='; ', show_icons='no', focus_on_main_file='no', show_subformat_icons='no'):
"""
This is the default format for formatting fulltext links.
When possible, it returns only the main file(s) (+ link to
additional files if needed). If no distinction is made at
submission time between main and additional files, returns
all the files
@param separator: the separator between urls.
@param style: CSS class of the link
@param show_icons: if 'yes', print icons for fulltexts
@param focus_on_main_file: if 'yes' and a doctype 'Main' is found,
prominently display this doctype. In that case other doctypes are
summarized with a link to the Files tab, named "Additional files"
@param show_subformat_icons: shall we display subformats considered as icons?
"""
_ = gettext_set_language(bfo.lang)
out = ''
# Retrieve files
(parsed_urls, old_versions, additionals) = get_files(bfo, \
distinguish_main_and_additional_files=focus_on_main_file.lower() == 'yes',
include_subformat_icons=show_subformat_icons == 'yes')
main_urls = parsed_urls['main_urls']
others_urls = parsed_urls['others_urls']
if parsed_urls.has_key('cern_urls'):
cern_urls = parsed_urls['cern_urls']
# Prepare style and icon
if style != "":
style = 'class="'+style+'"'
if show_icons.lower() == 'yes':
file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (CFG_SITE_URL, _("Download fulltext"))
else:
file_icon = ''
# Build urls list.
# Escape special chars for <a> tag value.
additional_str = ''
if additionals:
- additional_str = ' <small>(<a '+style+' href="'+CFG_SITE_URL+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("additional files")
+ additional_str = ' <small>(<a '+style+' href="'+CFG_SITE_URL+'/%s/' % CFG_SITE_RECORD + str(bfo.recID)+'/files/">%s</a>)</small>' % _("additional files")
versions_str = ''
#if old_versions:
- #versions_str = ' <small>(<a '+style+' href="'+CFG_SITE_URL+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("older versions")
+ #versions_str = ' <small>(<a '+style+' href="'+CFG_SITE_URL+'/CFG_SITE_RECORD/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("older versions")
if main_urls:
last_name = ""
main_urls_keys = sort_alphanumerically(main_urls.keys())
for descr in main_urls_keys:
urls = main_urls[descr]
if re.match(r'^\d+\s', descr) and urls[0][2] == 'png':
# FIXME: we have probably hit a Plot (as link
# description looks like '0001 This is Caption'), so
# do not take it. This test is not ideal, we should
# rather study doc type, and base ourselves on
# Main/Additional/Plot etc.
continue
out += "<strong>%s:</strong> " % descr
url_list = []
## FIXME: This is so ugly!
urls_dict = {}
for url, name, url_format in urls:
urls_dict[url] = (name, url_format)
urls_dict_keys = sort_alphanumerically(urls_dict.keys())
for url in urls_dict_keys:
name, url_format = urls_dict[url]
if not name == last_name and len(main_urls) > 1:
print_name = "<em>%s</em> - " % name
else:
print_name = ""
last_name = name
url_list.append(print_name + '<a '+style+' href="'+escape(url)+'">'+ \
file_icon + url_format.upper()+'</a>')
out += separator.join(url_list) + additional_str + versions_str + '<br />'
if CFG_CERN_SITE and cern_urls:
link_word = len(cern_urls) == 1 and _('%(x_sitename)s link') or _('%(x_sitename)s links')
out += '<strong>%s</strong>: ' % (link_word % {'x_sitename': 'CERN'})
url_list = []
for url, descr in cern_urls:
url_list.append('<a '+style+' href="'+escape(url)+'">'+ \
file_icon + escape(str(descr))+'</a>')
out += separator.join(url_list)
if others_urls:
external_link = len(others_urls) == 1 and _('external link') or _('external links')
out += '<strong>%s</strong>: ' % external_link.capitalize()
url_list = []
for url, descr in others_urls:
url_list.append('<a '+style+' href="'+escape(url)+'">'+ \
file_icon + escape(str(descr))+'</a>')
out += separator.join(url_list) + '<br />'
if out.endswith('<br />'):
out = out[:-len('<br />')]
# When exported to text (eg. in WebAlert emails) we do not want to
# display the link to the fulltext:
if out:
out = '<!--START_NOT_FOR_TEXT-->' + out + '<!--END_NOT_FOR_TEXT-->'
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
def get_files(bfo, distinguish_main_and_additional_files=True, include_subformat_icons=False):
"""
Returns the files available for the given record.
Returned structure is a tuple (parsed_urls, old_versions, additionals):
- parsed_urls: contains categorized URLS (see details below)
- old_versions: set to True if we can have access to old versions
- additionals: set to True if we have other documents than the 'main' document
Parameter 'include_subformat_icons' decides if subformat
considered as icons should be returned
'parsed_urls' is a dictionary in the form:
- {'main_urls' : {'Main' : [('http://CFG_SITE_URL/record/1/files/aFile.pdf', 'aFile', 'PDF'),
- ('http://CFG_SITE_URL/record/1/files/aFile.gif', 'aFile', 'GIF')],
- 'Additional': [('http://CFG_SITE_URL/record/1/files/bFile.pdf', 'bFile', 'PDF')]},
+ {'main_urls' : {'Main' : [('http://CFG_SITE_URL/CFG_SITE_RECORD/1/files/aFile.pdf', 'aFile', 'PDF'),
+ ('http://CFG_SITE_URL/CFG_SITE_RECORD/1/files/aFile.gif', 'aFile', 'GIF')],
+ 'Additional': [('http://CFG_SITE_URL/CFG_SITE_RECORD/1/files/bFile.pdf', 'bFile', 'PDF')]},
'other_urls': [('http://externalurl.com/aFile.pdf', 'Fulltext'), # url(8564_u), description(8564_z/y)
('http://externalurl.com/bFile.pdf', 'Fulltext')],
'cern_urls' : [('http://cern.ch/aFile.pdf', 'Fulltext'), # url(8564_u), description(8564_z/y)
('http://cern.ch/bFile.pdf', 'Fulltext')],
}
Some notes about returned structure:
- key 'cern_urls' is only available on CERN site
- keys in main_url dictionaries are defined by the BibDoc.
- older versions are not part of the parsed urls
- returns only main files when possible, that is when doctypes
make a distinction between 'Main' files and other
files. Otherwise returns all the files as main. This is only
enabled if distinguish_main_and_additional_files is set to True
"""
_ = gettext_set_language(bfo.lang)
urls = bfo.fields("8564_")
bibarchive = BibRecDocs(bfo.recID)
old_versions = False # We can provide link to older files. Will be
# set to True if older files are found.
additionals = False # We have additional files. Will be set to
# True if additional files are found.
# Prepare object to return
parsed_urls = {'main_urls':{}, # Urls hosted by Invenio (bibdocs)
'others_urls':[] # External urls
}
if CFG_CERN_SITE:
parsed_urls['cern_urls'] = [] # cern.ch urls
# Doctypes can of any type, but when there is one file marked as
# 'Main', we consider that there is a distinction between "main"
# and "additional" files. Otherwise they will all be considered
# equally as main files
distinct_main_and_additional_files = False
if len(bibarchive.list_bibdocs(doctype='Main')) > 0 and \
distinguish_main_and_additional_files:
distinct_main_and_additional_files = True
# Parse URLs
for complete_url in urls:
if complete_url.has_key('u'):
url = complete_url['u']
(dummy, host, path, dummy, params, dummy) = urlparse(url)
subformat = complete_url.get('x', '')
filename = urllib.unquote(basename(path))
name = file_strip_ext(filename)
url_format = filename[len(name):]
if url_format.startswith('.'):
url_format = url_format[1:]
descr = ''
if complete_url.has_key('y'):
descr = complete_url['y']
if not url.startswith(CFG_SITE_URL): # Not a bibdoc?
if not descr: # For not bibdoc let's have a description
# Display the URL in full:
descr = url
if CFG_CERN_SITE and 'cern.ch' in host and \
('/setlink?' in url or \
'cms' in host or \
'documents.cern.ch' in url or \
'doc.cern.ch' in url or \
'preprints.cern.ch' in url):
url_params_dict = dict([part.split('=') for part in params.split('&') if len(part.split('=')) == 2])
if url_params_dict.has_key('categ') and \
(url_params_dict['categ'].split('.', 1)[0] in cern_arxiv_categories) and \
url_params_dict.has_key('id'):
# Old arXiv links, used to be handled by
# setlink. Provide direct links to arXiv
for file_format, label in [('pdf', "PDF")]:#,
#('ps', "PS"),
#('e-print', "Source (generally TeX or LaTeX)"),
#('abs', "Abstract")]:
url = "http://arxiv.org/%(format)s/%(category)s/%(id)s" % \
{'format': file_format,
'category': url_params_dict['categ'],
'id': url_params_dict['id']}
parsed_urls['others_urls'].append((url, "%s/%s %s" % \
(url_params_dict['categ'],
url_params_dict['id'],
label)))
else:
parsed_urls['others_urls'].append((url, descr)) # external url
else: # It's a bibdoc!
assigned = False
for doc in bibarchive.list_bibdocs():
if int(doc.get_latest_version()) > 1:
old_versions = True
if True in [f.fullname.startswith(filename) \
for f in doc.list_all_files()]:
assigned = True
if not include_subformat_icons and \
CFG_WEBSUBMIT_ICON_SUBFORMAT_RE.match(subformat):
# This is an icon and we want to skip it
continue
if not doc.doctype == 'Main' and \
distinct_main_and_additional_files == True:
# In that case we record that there are
# additional files, but don't add them to
# returned structure.
additionals = True
else:
if not descr:
descr = _('Fulltext')
if not parsed_urls['main_urls'].has_key(descr):
parsed_urls['main_urls'][descr] = []
params = parse_qs(params)
if 'subformat' in params:
url_format += ' (%s)' % params['subformat'][0]
parsed_urls['main_urls'][descr].append((url, name, url_format))
if not assigned: # Url is not a bibdoc :-S
if not descr:
descr = filename
parsed_urls['others_urls'].append((url, descr)) # Let's put it in a general other url
return (parsed_urls, old_versions, additionals)
_RE_SPLIT = re.compile(r"\d+|\D+")
def sort_alphanumerically(elements):
elements = [([not token.isdigit() and token or int(token) for token in _RE_SPLIT.findall(element)], element) for element in elements]
elements.sort()
return [element[1] for element in elements]
diff --git a/modules/bibformat/lib/elements/bfe_fulltext_mini.py b/modules/bibformat/lib/elements/bfe_fulltext_mini.py
index 9e25a22cc..f4ba00a76 100644
--- a/modules/bibformat/lib/elements/bfe_fulltext_mini.py
+++ b/modules/bibformat/lib/elements/bfe_fulltext_mini.py
@@ -1,150 +1,150 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints a links to fulltext
"""
__revision__ = "$Id$"
from invenio.bibformat_elements.bfe_fulltext import get_files, sort_alphanumerically
from invenio.messages import gettext_set_language
-from invenio.config import CFG_SITE_URL, CFG_CERN_SITE
+from invenio.config import CFG_SITE_URL, CFG_CERN_SITE, CFG_SITE_RECORD
from cgi import escape
def format_element(bfo, style, separator='; ', show_icons='no', focus_on_main_file='yes', show_subformat_icons='no'):
"""
This is the format for formatting fulltext links in the mini panel.
@param separator: the separator between urls.
@param style: CSS class of the link
@param show_icons: if 'yes', print icons for fulltexts
@param focus_on_main_file: if 'yes' and a doctype 'Main' is found,
prominently display this doctype. In that case other doctypes are
summarized with a link to the Files tab, named"Additional files".
@param show_subformat_icons: shall we display subformats considered as icons?
"""
_ = gettext_set_language(bfo.lang)
out = ''
# Retrieve files
(parsed_urls, old_versions, additionals) = \
get_files(bfo, distinguish_main_and_additional_files=focus_on_main_file.lower() == 'yes',
include_subformat_icons=show_subformat_icons == 'yes')
main_urls = parsed_urls['main_urls']
others_urls = parsed_urls['others_urls']
if parsed_urls.has_key('cern_urls'):
cern_urls = parsed_urls['cern_urls']
# Prepare style
if style != "":
style = 'class="'+style+'"'
# Build urls list.
# Escape special chars for <a> tag value.
additional_str = ''
if additionals:
- additional_str = separator + '<small>(<a '+style+' href="'+CFG_SITE_URL+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("additional files")
+ additional_str = separator + '<small>(<a '+style+' href="'+CFG_SITE_URL+'/'+ CFG_SITE_RECORD +'/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("additional files")
versions_str = ''
#if old_versions:
- #versions_str = separator + '<small>(<a '+style+' href="'+CFG_SITE_URL+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("older versions")
+ #versions_str = separator + '<small>(<a '+style+' href="'+CFG_SITE_URL+'/CFG_SITE_RECORD/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("older versions")
if main_urls:
# Put a big file icon if only one file
if len(main_urls.keys()) == 1 and len(main_urls.items()[0][1]) == 1 and \
(not CFG_CERN_SITE or len(cern_urls) == 0) and len(others_urls) == 0 and \
show_icons.lower() == 'yes':
file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (CFG_SITE_URL, _("Download fulltext"))
elif show_icons.lower() == 'yes':
file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (CFG_SITE_URL, _("Download fulltext"))
else:
file_icon = ''
last_name = ""
main_urls_keys = sort_alphanumerically(main_urls.keys())
for descr in main_urls_keys:
urls = main_urls[descr]
out += '<div><small class="detailedRecordActions">%s:</small> ' % descr
url_list = []
## FIXME: This is so ugly!
urls_dict = {}
for url, name, format in urls:
urls_dict[url] = (name, format)
urls_dict_keys = sort_alphanumerically(urls_dict.keys())
for url in urls_dict_keys:
name, format = urls_dict[url]
if not name == last_name and len(main_urls) > 1:
print_name = "<em>%s</em> - " % name
else:
print_name = ""
last_name = name
url_list.append(print_name + '<a '+style+' href="'+escape(url)+'">'+file_icon+format.upper()+'</a>')
out += separator + separator.join(url_list) + \
additional_str + versions_str + '</div>'
if CFG_CERN_SITE and cern_urls:
# Put a big file icon if only one file
if len(main_urls.keys()) == 0 and \
len(cern_urls) == 1 and len(others_urls) == 0 and \
show_icons.lower() == 'yes':
file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (CFG_SITE_URL, _("Download fulltext"))
elif show_icons.lower() == 'yes':
file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (CFG_SITE_URL, _("Download fulltext"))
else:
file_icon = ''
link_word = len(cern_urls) == 1 and _('%(x_sitename)s link') or _('%(x_sitename)s links')
out += '<small class="detailedRecordActions">%s:</small><br />' % (link_word % {'x_sitename': 'CERN'})
url_list = []
for url, descr in cern_urls:
url_list.append('<a '+style+' href="'+escape(url)+'">'+file_icon+escape(str(descr))+'</a>')
out += '<small>' + separator.join(url_list) + '</small>'
out += "<br/>"
if others_urls:
# Put a big file icon if only one file
if len(main_urls.keys()) == 0 and \
(not CFG_CERN_SITE or len(cern_urls) == 0) and len(others_urls) == 1 and \
show_icons.lower() == 'yes':
file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (CFG_SITE_URL, _("Download fulltext"))
elif show_icons.lower() == 'yes':
file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (CFG_SITE_URL, _("Download fulltext"))
else:
file_icon = ''
external_link = len(others_urls) == 1 and _('external link') or _('external links')
out += '<small class="detailedRecordActions">%s:</small>%s' % (external_link.capitalize(), separator)
url_list = []
for url, descr in others_urls:
# we don't need to show the plot links here, and all are pngs.
if url.find('.png') > -1:
continue
url_list.append('<a '+style+' href="'+escape(url)+'">'+file_icon+escape(str(descr))+'</a>')
out += '<small>' + separator.join(url_list) + '</small>'
if out.endswith('<br />'):
out = out[:-len('<br />')]
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_photo_resources_brief.py b/modules/bibformat/lib/elements/bfe_photo_resources_brief.py
index f68bd5ad4..04d07d60a 100644
--- a/modules/bibformat/lib/elements/bfe_photo_resources_brief.py
+++ b/modules/bibformat/lib/elements/bfe_photo_resources_brief.py
@@ -1,45 +1,45 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints brief HTML picture and links to resources
"""
__revision__ = "$Id$"
def format_element(bfo):
"""
Prints html image and link to photo resources.
"""
- from invenio.config import CFG_SITE_URL
+ from invenio.config import CFG_SITE_URL, CFG_SITE_RECORD
resources = bfo.fields("8564_")
out = ""
for resource in resources:
if resource.get("x", "") == "icon":
- out += '<a href="'+CFG_SITE_URL+'/record/'+bfo.control_field("001")+ \
+ out += '<a href="'+CFG_SITE_URL+'/'+ CFG_SITE_RECORD +'/'+bfo.control_field("001")+ \
'?ln='+ bfo.lang + '"><img src="' + resource.get("u", "").replace(" ","") \
+ '" alt="" border="0"/></a>'
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_plots.py b/modules/bibformat/lib/elements/bfe_plots.py
index 5cb7f60d1..34cd591d5 100644
--- a/modules/bibformat/lib/elements/bfe_plots.py
+++ b/modules/bibformat/lib/elements/bfe_plots.py
@@ -1,92 +1,92 @@
# -*- coding: utf-8 -*-
##
## $Id: bfe_CERN_plots.py,v 1.3 2009/03/17 10:55:15 jerome Exp $
##
## This file is part of Invenio.
## Copyright (C) 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Display image of the plot if we are in selected plots collection
"""
__revision__ = "$Id: bfe_CERN_plots.py,v 1.3 2009/03/17 10:55:15 jerome Exp $"
from invenio.bibdocfile import BibRecDocs
from invenio.urlutils import create_html_link
-from invenio.config import CFG_SITE_URL
+from invenio.config import CFG_SITE_URL, CFG_SITE_RECORD
def format_element(bfo, width="", caption="yes", max="-1"):
"""
Display image of the plot if we are in selected plots collections
@param width: the width of the returned image (Eg: '100px')
@param separator: a separator between images
@param caption: display the captions or not?
@param max: the maximum number of plots to display (-1 is all plots)
"""
## To achieve this, we take the pngs associated with this document
img_files = []
max = int(max)
bibarchive = BibRecDocs(bfo.recID)
if width != "":
width = 'width="%s"' % width
for doc in bibarchive.list_bibdocs():
for _file in doc.list_latest_files():
if _file.get_type() == "Plot":
try:
caption_text = _file.get_description()[5:]
index = int(_file.get_description()[:5])
img_location = _file.get_url()
except:
# FIXME: we have hit probably a plot context file,
# so ignore this document; but it would be safer
# to check subformat type, so that we don't mask
# other eventual errors here.
continue
img = '<img src="%s" title="%s" %s/>' % \
(img_location, caption_text, width)
- link = create_html_link(urlbase='%s/record/%s/plots#%d' %
- (CFG_SITE_URL, bfo.recID,\
+ link = create_html_link(urlbase='%s/%s/%s/plots#%d' %
+ (CFG_SITE_URL, CFG_SITE_RECORD, bfo.recID,\
index),
urlargd={},
link_label=img)
img_files.append((index, link))
img_files = sorted(img_files, key=lambda x: x[0])
if max > 0:
img_files = img_files[:max]
for index in range(len(img_files)):
img_files[index] = img_files[index][1]
if len(img_files) == 0:
return ''
return '<div style="overflow-x:scroll;width=100%;white-space:nowrap">' +\
" ".join(img_files) + '</div>'
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_server_info.py b/modules/bibformat/lib/elements/bfe_server_info.py
index a4392575b..00b221cf0 100644
--- a/modules/bibformat/lib/elements/bfe_server_info.py
+++ b/modules/bibformat/lib/elements/bfe_server_info.py
@@ -1,68 +1,71 @@
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints server info
"""
__revision__ = "$Id$"
from invenio.config import CFG_SITE_URL, CFG_SITE_ADMIN_EMAIL, CFG_SITE_LANG, \
- CFG_SITE_NAME, CFG_VERSION, CFG_SITE_NAME_INTL, CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_NAME, CFG_VERSION, CFG_SITE_NAME_INTL, CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
def format_element(bfo, var=''):
'''
Print several server specific variables.
@param var: the name of the desired variable. Can be one of: CFG_SITE_NAME, CFG_SITE_NAME_INTL, CFG_SITE_LANG, CFG_VERSION, CFG_SITE_ADMIN_EMAIL, CFG_SITE_SUPPORT_EMAIL, CFG_SITE_URL, searchurl, recurl
CFG_SITE_NAME: the name of the server
CFG_SITE_NAME_INTL: internationalized name
CFG_SITE_LANG: the default language of the server
CFG_VERSION: the software version
CFG_SITE_ADMIN_EMAIL: the admin email
CFG_SITE_SUPPORT_EMAIL: the support email
CFG_SITE_URL: the base url for the server
searchurl: the search url for the server
recurl: the base url for the record
'''
recID = bfo.recID
if var == '':
out = ''
elif var in ['name', 'CFG_SITE_NAME']:
out = CFG_SITE_NAME
elif var in ['i18n_name', 'CFG_SITE_NAME_INTL']:
out = CFG_SITE_NAME_INTL.get(bfo.lang, CFG_SITE_NAME)
elif var in ['lang', 'CFG_SITE_LANG']:
out = CFG_SITE_LANG
elif var == 'CFG_VERSION':
out = 'Invenio v' + str(CFG_VERSION)
elif var in ['email', 'admin_email', 'CFG_SITE_ADMIN_EMAIL']:
out = CFG_SITE_ADMIN_EMAIL
elif var in ['support_email', 'CFG_SITE_SUPPORT_EMAIL']:
out = CFG_SITE_SUPPORT_EMAIL
+ elif var in ['CFG_SITE_RECORD']:
+ out = CFG_SITE_RECORD
elif var in ['weburl', 'CFG_SITE_URL']:
out = CFG_SITE_URL
if not out.endswith('/'):
out += '/'
elif var == 'searchurl':
out = CFG_SITE_URL + '/search'
if not out.endswith('/'):
out += '/'
elif var == 'recurl':
out = CFG_SITE_URL
if not out.endswith('/'):
out += '/'
- out += 'record/' + str(recID)
+ out += CFG_SITE_RECORD +'/' + str(recID)
else:
out = 'Unknown variable: %s' % (var)
return out
diff --git a/modules/bibharvest/lib/oai_harvest_admin.py b/modules/bibharvest/lib/oai_harvest_admin.py
index d7dcef0ce..b7ba3ede4 100644
--- a/modules/bibharvest/lib/oai_harvest_admin.py
+++ b/modules/bibharvest/lib/oai_harvest_admin.py
@@ -1,1424 +1,1425 @@
## Administrator interface for BibIndex
## This file is part of Invenio.
## Copyright (C) 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio OAI Harvest Administrator Interface."""
__revision__ = "$Id$"
import re
import os
import cgi
import time
import urllib
import tempfile
import datetime
from httplib import InvalidURL
from invenio.config import \
CFG_SITE_LANG, \
CFG_TMPDIR, \
CFG_SITE_URL, \
CFG_ETCDIR, \
CFG_BINDIR, \
- CFG_LOGDIR
+ CFG_LOGDIR, \
+ CFG_SITE_RECORD
from invenio.oai_harvest_config import CFG_OAI_POSSIBLE_POSTMODES
from invenio.bibrankadminlib import \
write_outcome, \
addadminbox, \
tupletotable, \
createhiddenform
from invenio.dbquery import run_sql
from invenio.oai_harvest_dblayer import get_history_entries, \
get_month_logs_size, get_history_entries_for_day, \
get_day_logs_size, get_entry_history, get_entry_logs_size, \
get_holdingpen_entries, delete_holdingpen_entry, \
get_holdingpen_years, get_holdingpen_month, get_holdingpen_year, \
get_holdingpen_day_fragment, get_holdingpen_entry_details
from invenio.search_engine import get_record
import invenio.template
from invenio import oai_harvest_daemon
from invenio.xmlmarc2textmarc import create_marc_record
from invenio.bibrecord import create_record
from invenio.urlutils import create_html_link
bibharvest_templates = invenio.template.load('bibharvest')
from invenio.messages import gettext_set_language
tmppath = CFG_TMPDIR + '/oaiharvestadmin.' + str(os.getpid())
guideurl = "help/admin/oai-admin-guide"
oai_harvest_admin_url = CFG_SITE_URL + \
"/admin/bibharvest/oaiharvestadmin.py"
freqs = [[0, "never"],
[24, "daily"],
[168, "weekly"],
[720, "monthly"] ]
dates = [[0, "from beginning"],
[1, "from today"]]
def getnavtrail(previous='', ln=CFG_SITE_LANG):
"""Get the navtrail"""
return bibharvest_templates.tmpl_getnavtrail(previous=previous,
ln=ln)
def generate_sources_actions_menu(oai_src_id, ln=CFG_SITE_LANG):
"""
Create the links/actions to administrate the given OAI source.
"""
_ = gettext_set_language(ln)
default_link_argd = {'ln': ln,
'oai_src_id': str(oai_src_id)}
edit_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/editsource",
urlargd=default_link_argd,
link_label=_("edit"))
del_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/delsource",
urlargd=default_link_argd,
link_label=_("delete"))
test_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/testsource",
urlargd=default_link_argd,
link_label=_("test"))
hist_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/viewhistory",
urlargd=default_link_argd,
link_label=_("history"))
harvest_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/harvest",
urlargd=default_link_argd,
link_label=_("harvest"))
return edit_link + " / " + del_link + " / " + test_link + \
" / " + hist_link + " / " + harvest_link
def perform_request_index(ln=CFG_SITE_LANG):
"""start area for administering harvesting from OAI repositories"""
_ = gettext_set_language(ln)
titlebar = bibharvest_templates.tmpl_draw_titlebar(ln=ln, title=_("Overview of sources"), guideurl=guideurl, extraname="add new OAI source" , extraurl="admin/bibharvest/oaiharvestadmin.py/addsource?ln=" + ln)
titlebar2 = bibharvest_templates.tmpl_draw_titlebar(ln=ln, title=_("Harvesting status"), guideurl=guideurl)
header = ['name', 'baseURL', 'metadataprefix', 'frequency',
'bibconvertfile', 'postprocess', 'actions']
header2 = ['name', 'last update']
oai_src = get_oai_src()
upd_status = get_update_status()
sources = []
for (oai_src_id, oai_src_name, oai_src_baseurl, oai_src_prefix, \
oai_src_frequency, oai_src_config, oai_src_post, \
oai_src_bibfilter, oai_src_setspecs) in oai_src:
default_link_argd = {'ln': ln,
'oai_src_id': str(oai_src_id)}
namelinked = create_html_link(urlbase=oai_harvest_admin_url + \
"/editsource",
urlargd=default_link_argd,
link_label=cgi.escape(oai_src_name))
freq = _("Not Set")
if oai_src_frequency == 0: freq = _("never")
elif oai_src_frequency == 24: freq = _("daily")
elif oai_src_frequency == 168: freq = _("weekly")
elif oai_src_frequency == 720: freq = _("monthly")
action = generate_sources_actions_menu(oai_src_id, ln)
sources.append([namelinked, oai_src_baseurl, oai_src_prefix,
freq, oai_src_config, oai_src_post, action])
updates = []
for (upd_name, upd_status) in upd_status:
if not upd_status:
upd_status = bibharvest_templates.tmpl_print_warning(ln, _("Never harvested"))
else: #cut away leading zeros
upd_status = re.sub(r'\.[0-9]+$', '', str(upd_status))
updates.append([upd_name, upd_status])
(schtime, schstatus) = get_next_schedule()
if schtime:
schtime = re.sub(r'\.[0-9]+$', '', str(schtime))
holdingpen_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/viewholdingpen",
urlargd={'ln': ln},
link_label=_("View Holding Pen"))
output = titlebar
output += bibharvest_templates.tmpl_output_numbersources(ln, get_tot_oai_src())
output += tupletotable(header=header, tuple=sources)
output += bibharvest_templates.tmpl_print_brs(ln, 2)
output += titlebar2
output += bibharvest_templates.tmpl_output_schedule(ln, schtime, str(schstatus))
output += holdingpen_link
output += bibharvest_templates.tmpl_print_brs(ln, 2)
output += tupletotable(header=header2, tuple=updates)
return output
def perform_request_editsource(oai_src_id=None, oai_src_name='',
oai_src_baseurl='', oai_src_prefix='',
oai_src_frequency='',
oai_src_config='',
oai_src_post='', ln=CFG_SITE_LANG,
confirm= -1, oai_src_sets=None,
oai_src_bibfilter=''):
"""creates html form to edit a OAI source. this method is calling other methods which again is calling this and sending back the output of the method.
confirm - determines the validation status of the data input into the form"""
_ = gettext_set_language(ln)
if oai_src_id is None:
return _("No OAI source ID selected.")
if oai_src_sets is None:
oai_src_sets = []
output = ""
subtitle = bibharvest_templates.tmpl_draw_subtitle(ln=ln, title="edit source", subtitle="Edit OAI source", guideurl=guideurl)
if confirm in [-1, "-1"]:
oai_src = get_oai_src(oai_src_id)
oai_src_name = oai_src[0][1]
oai_src_baseurl = oai_src[0][2]
oai_src_prefix = oai_src[0][3]
oai_src_frequency = oai_src[0][4]
oai_src_config = oai_src[0][5]
oai_src_post = oai_src[0][6]
oai_src_sets = oai_src[0][7].split()
oai_src_bibfilter = oai_src[0][8]
text = bibharvest_templates.tmpl_print_brs(ln, 1)
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln, title="Source name", name="oai_src_name", value=oai_src_name)
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln, title="Base URL", name="oai_src_baseurl", value=oai_src_baseurl)
sets = findSets(oai_src_baseurl)
if sets:
sets.sort()
# Show available sets to users
sets_specs = [set[0] for set in sets]
sets_names = [set[1] for set in sets]
sets_labels = [((set[1] and set[0] + ' (' + set[1] + ')') or set[0]) \
for set in sets]
sets_states = [ ((set[0] in oai_src_sets and 1) or 0) for set in sets]
text += bibharvest_templates.tmpl_admin_checkboxes(ln=ln,
title="Sets",
name="oai_src_sets",
values=sets_specs,
labels=sets_labels,
states=sets_states,
message="Leave all unchecked for non-selective harvesting")
else:
# Let user specify sets in free textbox
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln,
title="Sets",
name='oai_src_sets',
value=' '.join(oai_src_sets))
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln, \
title="Metadata prefix", name="oai_src_prefix", value=oai_src_prefix)
text += bibharvest_templates.tmpl_admin_w200_select(ln=ln, \
title="Frequency", name="oai_src_frequency", \
valuenil="- select frequency -" , values=freqs, \
lastval=oai_src_frequency)
post_values = [mode[0] for mode in CFG_OAI_POSSIBLE_POSTMODES]
post_labels = [mode[1] for mode in CFG_OAI_POSSIBLE_POSTMODES]
post_states = [((mode[0] in oai_src_post and 1) or 0) for mode in CFG_OAI_POSSIBLE_POSTMODES]
text += bibharvest_templates.tmpl_admin_checkboxes(ln=ln, title="Postprocess",
name="oai_src_post",
values=post_values, labels=post_labels, states=post_states,
message="Leave all unchecked for no post-processing")
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln, \
title="BibConvert configuration file (if needed by postprocess)", \
name="oai_src_config", value=oai_src_config, \
suffix="<small>Eg: </small><code>oaidc2marcxml.xsl</code>, <code>oaimarc2marcxml.xsl</code><br/>")
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln, \
title="BibFilter program (if needed by postprocess)", \
name="oai_src_bibfilter", value=oai_src_bibfilter)
text += bibharvest_templates.tmpl_print_brs(ln, 2)
output += createhiddenform(action="editsource#1",
text=text,
button="Modify",
oai_src_id=oai_src_id,
ln=ln,
confirm=1)
if confirm in [1, "1"] and not oai_src_name:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a name for the source.")
elif confirm in [1, "1"] and not oai_src_prefix:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a metadata prefix.")
elif confirm in [1, "1"] and not oai_src_baseurl:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a base url.")
elif confirm in [1, "1"] and not oai_src_frequency:
output += bibharvest_templates.tmpl_print_warning(ln, "Please choose a frequency of harvesting")
elif confirm in [1, "1"] and "c" in oai_src_post and (not oai_src_config or validatefile(oai_src_config) != 0):
output += bibharvest_templates.tmpl_print_warning(ln, "You selected a postprocess mode which involves conversion.")
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a valid name of or a full path to a BibConvert config file or change postprocess mode.")
elif confirm in [1, "1"] and "f" in oai_src_post and (not oai_src_bibfilter or validatefile(oai_src_bibfilter) != 0):
output += bibharvest_templates.tmpl_print_warning(ln, "You selected a postprocess mode which involves filtering.")
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a valid name of or a full path to a BibFilter script or change postprocess mode.")
elif oai_src_id > -1 and confirm in [1, "1"]:
if not oai_src_frequency:
oai_src_frequency = 0
if not oai_src_config:
oai_src_config = "NULL"
if not oai_src_post:
oai_src_post = []
res = modify_oai_src(oai_src_id, oai_src_name, oai_src_baseurl, oai_src_prefix, oai_src_frequency, oai_src_config, oai_src_post, oai_src_sets, oai_src_bibfilter)
output += write_outcome(res)
output += bibharvest_templates.tmpl_print_brs(ln, 2)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/index",
urlargd={'ln': ln},
link_label=_("Go back to the OAI sources overview"))
body = [output]
return addadminbox(subtitle, body)
def perform_request_addsource(oai_src_name=None, oai_src_baseurl='',
oai_src_prefix='', oai_src_frequency='',
oai_src_lastrun='', oai_src_config='',
oai_src_post=[], ln=CFG_SITE_LANG,
confirm= -1, oai_src_sets=None,
oai_src_bibfilter=''):
"""creates html form to add a new source"""
_ = gettext_set_language(ln)
if oai_src_name is None:
return "No OAI source name selected."
if oai_src_sets is None:
oai_src_sets = []
subtitle = bibharvest_templates.tmpl_draw_subtitle(ln=ln,
title="add source",
subtitle="Add new OAI source",
guideurl=guideurl)
output = ""
if confirm <= -1:
text = bibharvest_templates.tmpl_print_brs(ln, 1)
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln,
title="Enter the base url",
name="oai_src_baseurl",
value=oai_src_baseurl + 'http://')
output = createhiddenform(action="addsource",
text=text,
ln=ln,
button="Validate",
confirm=0)
if (confirm not in ["-1", -1] and validate(oai_src_baseurl)[0] == 0) or \
confirm in ["1", 1]:
output += bibharvest_templates.tmpl_output_validate_info(ln, 0, str(oai_src_baseurl))
output += bibharvest_templates.tmpl_print_brs(ln, 2)
text = bibharvest_templates.tmpl_admin_w200_text(ln=ln,
title="Source name",
name="oai_src_name",
value=oai_src_name)
metadatas = findMetadataFormats(oai_src_baseurl)
if metadatas:
# Show available metadata to user
prefixes = []
for value in metadatas:
prefixes.append([value, str(value)])
text += bibharvest_templates.tmpl_admin_w200_select(ln=ln,
title="Metadata prefix",
name="oai_src_prefix",
valuenil="- select prefix -" ,
values=prefixes,
lastval=oai_src_prefix)
else:
# Let user specify prefix in free textbox
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln,
title="Metadata prefix",
name="oai_src_prefix",
value=oai_src_prefix)
sets = findSets(oai_src_baseurl)
if sets:
sets.sort()
# Show available sets to users
sets_specs = [set[0] for set in sets]
sets_names = [set[1] for set in sets]
sets_labels = [((set[1] and set[0] + ' (' + set[1] + ')') or set[0]) \
for set in sets]
sets_states = [ ((set[0] in oai_src_sets and 1) or 0) \
for set in sets]
text += bibharvest_templates.tmpl_admin_checkboxes(ln=ln,
title="Sets",
name="oai_src_sets",
values=sets_specs,
labels=sets_labels,
states=sets_states)
else:
# Let user specify sets in free textbox
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln,
title="Sets",
name='oai_src_sets',
value=' '.join(oai_src_sets))
text += bibharvest_templates.tmpl_admin_w200_select(ln=ln, \
title="Frequency", name="oai_src_frequency", \
valuenil="- select frequency -" , values=freqs, \
lastval=oai_src_frequency)
text += bibharvest_templates.tmpl_admin_w200_select(ln=ln, \
title="Starting date", name="oai_src_lastrun", \
valuenil="- select a date -" , values=dates, \
lastval=oai_src_lastrun)
post_values = [mode[0] for mode in CFG_OAI_POSSIBLE_POSTMODES]
post_labels = [mode[1] for mode in CFG_OAI_POSSIBLE_POSTMODES]
post_states = [((mode[0] in oai_src_post and 1) or 0) for mode in CFG_OAI_POSSIBLE_POSTMODES]
text += bibharvest_templates.tmpl_admin_checkboxes(ln=ln, title="Postprocess",
name="oai_src_post",
values=post_values, labels=post_labels, states=post_states,
message="Leave all unchecked for no post-processing")
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln, \
title="BibConvert configuration file (if needed by postprocess)", \
name="oai_src_config", value=oai_src_config)
text += bibharvest_templates.tmpl_admin_w200_text(ln=ln, \
title="BibFilter program (if needed by postprocess)", \
name="oai_src_bibfilter", value=oai_src_bibfilter)
text += bibharvest_templates.tmpl_print_brs(ln, 2)
output += createhiddenform(action="addsource#1",
text=text,
button="Add OAI Source",
oai_src_baseurl=oai_src_baseurl,
ln=ln,
confirm=1)
elif confirm in ["0", 0] and validate(oai_src_baseurl)[0] > 0:
# Could not perform first url validation
output += bibharvest_templates.tmpl_output_validate_info(ln, 1, str(oai_src_baseurl))
output += bibharvest_templates.tmpl_print_brs(ln, 2)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/addsource",
urlargd={'ln': ln},
link_label=_("Try again with another url"))
output += " " + _("or") + " "
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/addsource",
urlargd={'ln': ln,
'oai_src_baseurl': oai_src_baseurl,
'confirm': '1'},
link_label=_("Continue anyway"))
output += bibharvest_templates.tmpl_print_brs(ln, 1)
output += " " + _("or") + " "
output += bibharvest_templates.tmpl_print_brs(ln, 1)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/index",
urlargd={'ln': ln},
link_label=_("Go back to the OAI sources overview"))
elif confirm not in ["-1", -1] and validate(oai_src_baseurl)[0] > 0:
output += bibharvest_templates.tmpl_output_validate_info(ln, 1, str(oai_src_baseurl))
output += bibharvest_templates.tmpl_print_brs(ln, 2)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/addsource",
urlargd={'ln': ln},
link_label=_("Try again"))
output += bibharvest_templates.tmpl_print_brs(ln, 1)
output += " " + _("or") + " "
output += bibharvest_templates.tmpl_print_brs(ln, 1)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/index",
urlargd={'ln': ln},
link_label=_("Go back to the OAI sources overview"))
elif confirm not in ["-1", -1]:
lnargs = [["ln", ln]]
output += bibharvest_templates.tmpl_output_error_info(ln, str(oai_src_baseurl), validate(oai_src_baseurl)[1])
output += bibharvest_templates.tmpl_print_brs(ln, 2)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/addsource",
urlargd={'ln': ln},
link_label=_("Try again"))
output += bibharvest_templates.tmpl_print_brs(ln, 1)
output += " " + _("or") + " "
output += bibharvest_templates.tmpl_print_brs(ln, 1)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/index",
urlargd={'ln': ln},
link_label=_("Go back to the OAI sources overview"))
if confirm in [1, "1"] and not oai_src_name:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a name for the source.")
elif confirm in [1, "1"] and not oai_src_prefix:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a metadata prefix.")
elif confirm in [1, "1"] and not oai_src_frequency:
output += bibharvest_templates.tmpl_print_warning(ln, "Please choose a frequency of harvesting")
elif confirm in [1, "1"] and not oai_src_lastrun:
output += bibharvest_templates.tmpl_print_warning(ln, "Please choose the harvesting starting date")
elif confirm in [1, "1"] and "c" in oai_src_post and (not oai_src_config or validatefile(oai_src_config) != 0):
output += bibharvest_templates.tmpl_print_warning(ln, "You selected a postprocess mode which involves conversion.")
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a valid name of or a full path to a BibConvert config file or change postprocess mode.")
elif confirm in [1, "1"] and "f" in oai_src_post and (not oai_src_bibfilter or validatefile(oai_src_bibfilter) != 0):
output += bibharvest_templates.tmpl_print_warning(ln, "You selected a postprocess mode which involves filtering.")
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a valid name of or a full path to a BibFilter script or change postprocess mode.")
elif oai_src_name and confirm in [1, "1"]:
if not oai_src_frequency:
oai_src_frequency = 0
if not oai_src_lastrun:
oai_src_lastrun = 1
if not oai_src_config:
oai_src_config = "NULL"
if not oai_src_post:
oai_src_post = []
res = add_oai_src(oai_src_name, oai_src_baseurl,
oai_src_prefix, oai_src_frequency,
oai_src_lastrun, oai_src_config,
oai_src_post, oai_src_sets,
oai_src_bibfilter)
output += write_outcome(res)
lnargs = [["ln", ln]]
output += bibharvest_templates.tmpl_print_brs(ln, 2)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/index",
urlargd={'ln': ln},
link_label=_("Go back to the OAI sources overview"))
body = [output]
return addadminbox(subtitle, body)
def perform_request_delsource(oai_src_id=None, ln=CFG_SITE_LANG,
callback='yes', confirm=0):
"""creates html form to delete a source
"""
_ = gettext_set_language(ln)
output = ""
subtitle = ""
if oai_src_id:
oai_src = get_oai_src(oai_src_id)
namesrc = (oai_src[0][1])
pagetitle = """Delete OAI source: %s""" % cgi.escape(namesrc)
subtitle = bibharvest_templates.tmpl_draw_subtitle(ln=ln, \
title="delete source", subtitle=pagetitle, guideurl=guideurl)
output = ""
if confirm in ["0", 0]:
if oai_src:
question = """Do you want to delete the OAI source '%s' and all its definitions?""" % cgi.escape(namesrc)
text = bibharvest_templates.tmpl_print_info(ln, question)
text += bibharvest_templates.tmpl_print_brs(ln, 3)
output += createhiddenform(action="delsource#5",
text=text,
button="Confirm",
oai_src_id=oai_src_id,
confirm=1)
else:
return bibharvest_templates.tmpl_print_info(ln, "Source specified does not exist.")
elif confirm in ["1", 1]:
res = delete_oai_src(oai_src_id)
if res[0] == 1:
output += bibharvest_templates.tmpl_print_info(ln, "Source removed.")
output += bibharvest_templates.tmpl_print_brs(ln, 1)
output += write_outcome(res)
else:
output += write_outcome(res)
output += bibharvest_templates.tmpl_print_brs(ln, 2)
output += create_html_link(urlbase=oai_harvest_admin_url + \
"/index",
urlargd={'ln': ln},
link_label=_("Go back to the OAI sources overview"))
body = [output]
return addadminbox(subtitle, body)
def perform_request_testsource(oai_src_id=None, ln=CFG_SITE_LANG,
callback='yes', confirm=0,
record_id=None):
"""Test OAI source page"""
_ = gettext_set_language(ln)
if oai_src_id is None:
return _("No OAI source ID selected.")
result = ""
result += bibharvest_templates.tmpl_output_menu(ln, oai_src_id, guideurl)
result += bibharvest_templates.tmpl_draw_titlebar(ln=ln, title=\
"Record ID ( Recognized by the data source )", guideurl=guideurl)
record_str = ""
if record_id != None:
record_str = str(record_id)
form_text = bibharvest_templates.tmpl_admin_w200_text(ln=ln, title=\
"Record identifier", name="record_id", value=record_str)
result += createhiddenform(action="testsource",
text=form_text,
button="Test",
oai_src_id=oai_src_id,
ln=ln,
confirm=1)
if record_id != None:
result += bibharvest_templates.tmpl_draw_titlebar(ln=ln, title=\
"OAI XML downloaded from the source" , guideurl=guideurl)
result += bibharvest_templates.tmpl_embed_document(\
"/admin/bibharvest/oaiharvestadmin.py/preview_original_xml?oai_src_id=" \
+ str(oai_src_id) + "&record_id=" \
+ str(record_id))
result += bibharvest_templates.tmpl_draw_titlebar(ln=ln, title=\
"MARC XML after all the transformations", guideurl=guideurl)
result += bibharvest_templates.tmpl_embed_document(\
"/admin/bibharvest/oaiharvestadmin.py/preview_harvested_xml?oai_src_id=" \
+ str(oai_src_id) + "&record_id=" \
+ str(record_id))
return result
######################################
### Displaying bibsched task logs ###
######################################
def does_logfile_exist(task_id):
"""
returns logfile name if exists. None otherwise
"""
name = CFG_LOGDIR + "/bibsched_task_" + str(task_id) + ".log"
if os.path.exists(name):
return name
else:
return None
def does_errfile_exist(task_id):
"""
returns logfile name if exists. None otherwise
"""
name = CFG_LOGDIR + "/bibsched_task_" + str(task_id) + ".err"
if os.path.exists(name):
return name
else:
return None
def perform_request_viewtasklogs(ln, confirm, task_id):
t_id = int(task_id) # preventing malicious user input
guideurl = "help/admin/bibharvest-admin-guide"
log_name = does_logfile_exist(t_id)
err_name = does_errfile_exist(t_id)
result = ""
result += bibharvest_templates.tmpl_output_menu(ln, None, guideurl)
if log_name != None:
file = open(log_name)
content = file.read(-1)
file.close();
result += bibharvest_templates.tmpl_draw_titlebar(ln, "Log file : " + \
log_name, guideurl)
result += bibharvest_templates.tmpl_output_scrollable_frame(\
bibharvest_templates.tmpl_output_preformatted(content))
if err_name != None:
file = open(err_name)
content = file.read(-1)
file.close();
result += bibharvest_templates.tmpl_print_brs(ln, 2)
result += bibharvest_templates.tmpl_draw_titlebar(ln, "Log file : " + \
err_name, guideurl)
result += bibharvest_templates.tmpl_output_scrollable_frame(\
bibharvest_templates.tmpl_output_preformatted(content))
return result
### Probably should be moved to some other data-connection file
def build_history_row(item, ln, show_selection, show_oai_source, show_record_ids, identifier=""):
def get_cssclass(cssclass):
if cssclass == "oddtablecolumn":
return "pairtablecolumn"
else:
return "oddtablecolumn"
cssclass = get_cssclass("pairtablecolumn")
result = bibharvest_templates.tmpl_table_row_begin()
result += bibharvest_templates.tmpl_table_output_cell(\
bibharvest_templates.format_date(item.date_harvested) + " " + \
bibharvest_templates.format_time(item.date_harvested), cssclass=cssclass)
cssclass = get_cssclass(cssclass)
result += bibharvest_templates.tmpl_table_output_cell(\
bibharvest_templates.format_date(item.date_inserted) + " " + \
bibharvest_templates.format_time(item.date_inserted), cssclass=cssclass)
if show_record_ids:
record_history_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/viewentryhistory",
urlargd={'ln': ln,
'oai_id': str(item.oai_id),
'start': "0"},
link_label=str(item.oai_id))
cssclass = get_cssclass(cssclass)
result += bibharvest_templates.tmpl_table_output_cell(record_history_link, \
cssclass=cssclass)
record_details_link = create_html_link(CFG_SITE_URL + \
- "/record/" + str(item.record_id),
+ "/"+ CFG_SITE_RECORD +"/" + str(item.record_id),
urlargd={'ln': ln},
link_label=str(item.record_id))
cssclass = get_cssclass(cssclass)
result += bibharvest_templates.tmpl_table_output_cell(record_details_link, \
cssclass=cssclass)
cssclass = get_cssclass(cssclass)
result += bibharvest_templates.tmpl_table_output_cell(item.inserted_to_db, \
cssclass=cssclass)
cssclass = get_cssclass(cssclass)
task_id = str(item.bibupload_task_id)
if does_errfile_exist(item.bibupload_task_id) or does_logfile_exist(item.bibupload_task_id):
task_id = create_html_link(urlbase=oai_harvest_admin_url + \
"/viewtasklogs",
urlargd={'ln': ln,
'task_id': str(item.bibupload_task_id)},
link_label=str(item.bibupload_task_id))
result += bibharvest_templates.tmpl_table_output_cell(task_id, cssclass=cssclass)
if show_selection:
chkbox = bibharvest_templates.tmpl_output_checkbox(item.oai_id, identifier, "1")
cssclass = get_cssclass(cssclass)
result += bibharvest_templates.tmpl_table_output_cell(chkbox, \
cssclass=cssclass)
if show_oai_source:
cssclass = get_cssclass(cssclass)
result += bibharvest_templates.tmpl_table_output_cell(str(item.oai_src_id), \
cssclass=cssclass)
result += bibharvest_templates.tmpl_table_row_end()
return result
def build_history_table_header(show_selection=True, show_oai_source=False, \
show_record_ids=True):
headers = ["Harvesting Date", "Insert date"]
if show_record_ids:
headers += ["Record ID ( OAI )", "Rec. ID <br/>(Invenio)"]
headers += ["DB", "task <br/> number"]
if show_selection:
headers.append("Reharvest")
if show_oai_source:
headers.append("Harvested from <br/> source no")
return headers
def build_month_history_table(oai_src_id, date, ln):
""" Function formats the historical data
@param oai_src_id: identifier of the harvesting source
@param date: date designing the month of interest
@return: String containing the history table
"""
day_limit = 10
orig_data = get_history_entries(oai_src_id, date)
stats = get_month_logs_size(oai_src_id, date)
headers = build_history_table_header()
result = bibharvest_templates.tmpl_table_begin(headers)
identifiers = {}
for day in stats:
result += bibharvest_templates.tmpl_table_row_begin()
d_date = datetime.datetime(date.year, date.month, day)
result += bibharvest_templates.tmpl_history_table_output_day_cell(d_date, \
stats[day], oai_src_id, ln, stats[day] > day_limit)
btn = bibharvest_templates.tmpl_output_select_day_button(day)
result += bibharvest_templates.tmpl_table_output_cell(btn)
result += bibharvest_templates.tmpl_table_row_end()
day_data = get_history_entries_for_day(oai_src_id, d_date, limit=day_limit)
for item in day_data:
identifier = bibharvest_templates.format_date(item.date_harvested) + \
bibharvest_templates.format_time(item.date_harvested) + "_" + item.oai_id
result += build_history_row(item, ln, show_selection=True, show_oai_source=\
False, show_record_ids=True, identifier=identifier)
if not identifiers.has_key(item.date_harvested.day):
identifiers[item.date_harvested.day] = []
identifiers[item.date_harvested.day].append(identifier)
if stats[day] > day_limit:
result += bibharvest_templates.tmpl_history_table_output_day_details_cell(\
ln, d_date, oai_src_id)
result += bibharvest_templates.tmpl_table_end()
result += bibharvest_templates.tmpl_output_identifiers(identifiers)
return result
def build_history_table(data, ln=CFG_SITE_LANG, show_selection=True, \
show_oai_source=False, show_record_ids=True):
headers = build_history_table_header(show_selection=show_selection, \
show_oai_source=show_oai_source, show_record_ids=show_record_ids)
result = bibharvest_templates.tmpl_table_begin(headers)
identifiers = {}
for item in data:
identifier = bibharvest_templates.format_date(item.date_harvested) + \
bibharvest_templates.format_time(item.date_harvested) + "_" + item.oai_id
result += build_history_row(item, ln, show_selection=show_selection, \
show_oai_source=show_oai_source, show_record_ids=show_record_ids, \
identifier=identifier)
if show_selection:
if not identifiers.has_key(item.date_harvested.day):
identifiers[item.date_harvested.day] = []
identifiers[item.date_harvested.day].append(identifier)
result += bibharvest_templates.tmpl_table_end()
if show_selection:
result += bibharvest_templates.tmpl_output_identifiers(identifiers)
return result
def perform_request_viewhistory(oai_src_id=None, ln=CFG_SITE_LANG, callback=\
'yes', confirm=0, month=None, year=None):
""" Creates html to view the harvesting history """
date = datetime.datetime.now()
if year != None and month != None:
year = int(year)
month = int(month)
date = datetime.datetime(year, month, 1)
result = ""
result += bibharvest_templates.tmpl_output_menu(ln, oai_src_id, guideurl)
result += bibharvest_templates.tmpl_output_history_javascript_functions()
result += bibharvest_templates.tmpl_output_month_selection_bar(oai_src_id, ln, \
current_month=month, current_year=year)
inner_text = build_month_history_table(oai_src_id, date, ln)
inner_text += bibharvest_templates.tmpl_print_brs(ln, 1)
inner_text = bibharvest_templates.tmpl_output_scrollable_frame(inner_text)
inner_text += bibharvest_templates.tmpl_output_selection_bar()
result += createhiddenform(action="/admin/bibharvest/oaiharvestadmin.py/reharvest", \
text=inner_text, button="Reharvest selected records", oai_src_id=\
oai_src_id, ln=ln)
return result
def perform_request_viewhistoryday(oai_src_id=None, ln=CFG_SITE_LANG,
callback='yes', confirm=0,
month=None, year=None, day=None,
start=0):
"""
Records history page
"""
_ = gettext_set_language(ln)
page_length = 50
result = ""
result += bibharvest_templates.tmpl_output_menu(ln, oai_src_id, guideurl)
considered_date = datetime.datetime.now()
if year != None and month != None and day != None:
considered_date = datetime.datetime(year, month, day)
number_of_records = get_day_logs_size(oai_src_id, considered_date)
return_to_month_link = create_html_link(
urlbase=oai_harvest_admin_url + \
"/viewhistory",
urlargd={'ln': ln,
'oai_src_id': str(oai_src_id),
'year': str(considered_date.year),
'month': str(considered_date.month)},
link_label="&lt;&lt; " + _("Return to the month view"))
next_page_link = ""
if number_of_records > start + page_length:
next_page_link = create_html_link(
urlbase=oai_harvest_admin_url + \
"/viewhistoryday",
urlargd={'ln': ln,
'oai_src_id': str(oai_src_id),
'year': str(considered_date.year),
'month': str(considered_date.month),
'day': str(considered_date.day),
'start': str(start + page_length)},
link_label=_("Next page") + " &gt;&gt;")
prev_page_link = ""
if start > 0:
new_start = start - page_length
if new_start < 0:
new_start = 0
prev_page_link = create_html_link(
urlbase=oai_harvest_admin_url + \
"/viewhistoryday",
urlargd={'ln': ln,
'oai_src_id': str(oai_src_id),
'year': str(considered_date.year),
'month': str(considered_date.month),
'day': str(considered_date.day),
'start': str(new_start)},
link_label="&lt;&lt; " + _("Previous page"))
last_shown = start + page_length
if last_shown > number_of_records:
last_shown = number_of_records
current_day_records = get_history_entries_for_day(oai_src_id, considered_date, limit=\
page_length, start=start)
current_range = "&nbsp;&nbsp;&nbsp;&nbsp;Viewing entries : " + str(start + 1) + "-" + \
str(last_shown) + "&nbsp;&nbsp;&nbsp;&nbsp;"
# Building the interface
result += bibharvest_templates.tmpl_draw_titlebar(ln, "Viewing history of " + str(year)\
+ "-" + str(month) + "-" + str(day) , guideurl)
result += prev_page_link + current_range + next_page_link + \
bibharvest_templates.tmpl_print_brs(ln, 1)
result += bibharvest_templates.tmpl_output_history_javascript_functions()
inner_text = bibharvest_templates.tmpl_output_scrollable_frame(build_history_table(\
current_day_records, ln=ln))
inner_text += bibharvest_templates.tmpl_output_selection_bar()
result += createhiddenform(action="/admin/bibharvest/oaiharvestadmin.py/reharvest", \
text=inner_text, button="Reharvest selected records", oai_src_id=oai_src_id, ln=ln)
result += return_to_month_link + bibharvest_templates.tmpl_print_brs(ln, 1)
return result
def perform_request_viewentryhistory(oai_id, ln, confirm, start):
"""History of an OAI record"""
_ = gettext_set_language(ln)
page_length = 50
result = ""
result += bibharvest_templates.tmpl_output_menu(ln, None, guideurl)
considered_date = datetime.datetime.now()
number_of_records = get_entry_logs_size(oai_id)
next_page_link = ""
if number_of_records > start + page_length:
prev_page_link = create_html_link(
urlbase=oai_harvest_admin_url + \
"/viewhistoryday",
urlargd={'ln': ln,
'oai_id': str(oai_id),
'start': str(start + page_length)},
link_label=_("Next page") + " &gt;&gt;")
prev_page_link = ""
if start > 0:
new_start = start - page_length
if new_start < 0:
new_start = 0
prev_page_link = create_html_link(
urlbase=oai_harvest_admin_url + \
"/viewhistoryday",
urlargd={'ln': ln,
'oai_id': str(oai_id),
'start': str(new_start)},
link_label="&lt;&lt; " + _("Previous page"))
last_shown = start + page_length
if last_shown > number_of_records:
last_shown = number_of_records
current_entry_records = get_entry_history(oai_id, limit=page_length, start=start)
current_range = "&nbsp;&nbsp;&nbsp;&nbsp;Viewing entries : " + str(start + 1) \
+ "-" + str(last_shown) + "&nbsp;&nbsp;&nbsp;&nbsp;"
# Building the interface
result += bibharvest_templates.tmpl_draw_titlebar(ln, "Viewing history of " + \
str(oai_id) , guideurl)
result += prev_page_link + current_range + next_page_link + \
bibharvest_templates.tmpl_print_brs(ln, 1)
result += bibharvest_templates.tmpl_output_history_javascript_functions()
inner_text = bibharvest_templates.tmpl_output_scrollable_frame(\
build_history_table(current_entry_records, ln, show_selection=False, \
show_oai_source=True, show_record_ids=False))
result += inner_text
result += bibharvest_templates.tmpl_print_brs(ln, 1)
return result
############################################################
### The functions allowing to preview the harvested XML ###
############################################################
def harvest_record(record_id , oai_src_baseurl, oai_src_prefix):
"""
Harvests given record and returns it's string as a result
"""
command = CFG_BINDIR + "/oaiharvest -vGetRecord -i" + record_id \
+ " -p" + oai_src_prefix + " " + oai_src_baseurl
program_output = os.popen(command)
result = program_output.read(-1)
program_output.close()
return result
def convert_record(oai_src_config, record_to_convert):
command = CFG_BINDIR + "/bibconvert -c " + oai_src_config
(s_in, s_out, s_err) = os.popen3(command)
s_in.write(record_to_convert)
s_in.close()
s_err.readlines()
result = s_out.read(-1)
s_err.close()
s_out.close()
return result
def format_record(oai_src_bibfilter, record_to_convert, treat_new=False):
"""
Formats the record using given formatting program.
Returns name of the file containing result,
program output, program error output
"""
(file_descriptor, file_name) = tempfile.mkstemp()
f = os.fdopen(file_descriptor, "w")
f.write(record_to_convert)
f.close()
command = oai_src_bibfilter
if treat_new:
command += " -n"
command += " " + file_name
(program_input, program_output, program_err) = os.popen3(command)
program_input.close()
out = program_output.read(-1)
err = program_err.read(-1)
program_output.close()
program_err.close()
if os.path.exists(file_name + ".insert.xml"):
return (file_name + ".insert.xml", out, err)
else:
return (None, out, err)
def harvest_postprocress_record(oai_src_id, record_id, treat_new=False):
"""Havest ther record and postprocess it"""
oai_src = get_oai_src(oai_src_id)
oai_src_baseurl = oai_src[0][2]
oai_src_prefix = oai_src[0][3]
oai_src_config = oai_src[0][5]
oai_src_post = oai_src[0][6]
oai_src_sets = oai_src[0][7].split()
oai_src_bibfilter = oai_src[0][8]
result = harvest_record(record_id, oai_src_baseurl, oai_src_prefix)
if result == None:
return (False, "Error during harvesting")
if oai_src_post.find("c") != -1:
result = convert_record(oai_src_config, result)
if result == None:
return (False, "Error during converting")
if oai_src_post.find("f") != -1:
fres = format_record(oai_src_bibfilter, result, treat_new=treat_new)
fname = fres[0]
if fname != None:
f = open(fname, "r")
result = f.read(-1)
f.close()
os.remove(fname)
else:
return (False, "Error during formatting: " + fres[1] + "\n\n" + fres[2])
return (True, result)
def upload_record(record=None, uploader_paremeters=None, oai_source_id=None):
"""Upload the given record"""
if record is None:
return
if uploader_paremeters is None:
uploader_paremeters = ["-r", "-i"]
(file_descriptor, file_name) = tempfile.mkstemp()
f = os.fdopen(file_descriptor, "w")
f.write(record)
f.close()
oai_harvest_daemon.call_bibupload(file_name, uploader_paremeters, oai_src_id=oai_source_id)
#command = CFG_BINDIR + "/bibupload " + uploader_paremeters + " "
#command += file_name
#out = os.popen(command)
#output_data = out.read(-1)
#out.close()
def perform_request_preview_original_xml(oai_src_id=None, record_id=None):
"""Harvest a record and return it. No side effect, useful for preview"""
oai_src = get_oai_src(oai_src_id)
oai_src_baseurl = oai_src[0][2]
oai_src_prefix = oai_src[0][3]
oai_src_config = oai_src[0][5]
oai_src_post = oai_src[0][6]
oai_src_sets = oai_src[0][7].split()
oai_src_bibfilter = oai_src[0][8]
record = harvest_record(record_id, oai_src_baseurl, oai_src_prefix)
return record
def perform_request_preview_harvested_xml(oai_src_id=None, record_id=None):
return harvest_postprocress_record(oai_src_id, record_id, treat_new=True)
############################################################
### Reharvesting of already existing records ###
############################################################
def perform_request_reharvest_records(oai_src_id=None, ln=CFG_SITE_LANG, confirm=0, record_ids=None):
for record_id in record_ids:
# 1) Run full harvesing process as in the preview scenarios
transformed = harvest_postprocress_record(oai_src_id, record_id, treat_new=True)[1]
upload_record(transformed, ["-i", "-r"], oai_src_id)
result = bibharvest_templates.tmpl_output_menu(ln, oai_src_id, guideurl)
result += bibharvest_templates.tmpl_print_info(ln, "Submitted for insertion into the database")
return result
def perform_request_harvest_record(oai_src_id=None, ln=CFG_SITE_LANG, confirm=0, record_id=None):
""" Request for harvesting a new record """
if oai_src_id is None:
return "No OAI source ID selected."
result = ""
guideurl = "help/admin/bibharvest-admin-guide"
result += bibharvest_templates.tmpl_output_menu(ln, oai_src_id, guideurl)
result += bibharvest_templates.tmpl_draw_titlebar(ln=ln, \
title="Record ID ( Recognized by the data source )", guideurl=guideurl)
record_str = ""
if record_id != None:
record_str = str(record_id)
form_text = bibharvest_templates.tmpl_admin_w200_text(ln=ln, \
title="Record identifier", name="record_id", value=record_str)
result += createhiddenform(action="harvest",
text=form_text,
button="Harvest",
oai_src_id=oai_src_id,
ln=ln,
confirm=1)
if record_id != None:
# there was a harvest-request
transformed = harvest_postprocress_record(oai_src_id, record_id)[1]
upload_record(transformed, ["-i"], oai_src_id)
result += bibharvest_templates.tmpl_print_info(ln, "Submitted for insertion into the database")
return result
############################
### Holding pen support ###
############################
def build_holdingpen_table(data, ln=CFG_SITE_LANG):
_ = gettext_set_language(ln)
result = ""
headers = ["OAI Record ID", "Insertion Date", "", ""]
result += bibharvest_templates.tmpl_table_begin(headers)
for record in data:
oai_id = record[0]
date_inserted = record[1]
hpupdate_id = record[2]
result += bibharvest_templates.tmpl_table_row_begin()
result += bibharvest_templates.tmpl_table_output_cell(str(oai_id), cssclass="oddtablecolumn")
result += bibharvest_templates.tmpl_table_output_cell(str(date_inserted), cssclass="pairtablecolumn")
details_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/viewhprecord",
urlargd={'ln': ln,
'hpupdate_id': str(hpupdate_id)},
link_label=_("Compare with original"))
result += bibharvest_templates.tmpl_table_output_cell(details_link, cssclass="oddtablecolumn")
delete_hp_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/delhprecord",
urlargd={'ln': ln,
'hpupdate_id' : str(hpupdate_id)},
link_label=_("Delete from holding pen"))
result += bibharvest_templates.tmpl_table_output_cell(delete_hp_link, cssclass="pairtablecolumn")
result += bibharvest_templates.tmpl_table_row_end()
result += bibharvest_templates.tmpl_table_end()
return result
def perform_request_viewholdingpen(ln=CFG_SITE_LANG, confirm=0, start=0, limit= -1):
data = get_holdingpen_entries(start, limit)
result = ""
result += build_holdingpen_table(data, ln)
return result
def perform_request_viewhprecord(hpupdate_id, ln=CFG_SITE_LANG, confirm=0):
_ = gettext_set_language(ln)
result = ""
try:
(oai_id, record_id, date_inserted, hprecord_content) = get_holdingpen_entry_details(hpupdate_id)
except:
return _("Error when retrieving the Holding Pen entry")
try:
db_rec = get_record(record_id)
db_MARC = create_marc_record(db_rec, record_id, {"text-marc": 1, "aleph-marc": 0})
#import rpdb2; rpdb2.start_embedded_debugger('password', fAllowRemote=True)
db_content = bibharvest_templates.tmpl_output_preformatted(db_MARC) # originally .encode("utf-8") ... does ot work
db_label = "Database version of record" + bibharvest_templates.tmpl_print_brs(ln, 1)
except:
return _("Error when retrieving the record")
try:
hp_rec = create_record(hprecord_content)[0]
hp_MARC = create_marc_record(hp_rec, record_id, {"text-marc": 1, "aleph-marc": 0})
hp_content = bibharvest_templates.tmpl_output_preformatted(hp_MARC) # originally .encode("utf-8") ... does ot work
hp_label = bibharvest_templates.tmpl_print_brs(ln, 2) + "Holdingpen version of record"\
+ bibharvest_templates.tmpl_print_brs(ln, 1)
except:
return _("Error when formatting the Holding Pen entry. Probably its content is broken")
submit_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/accepthprecord",
urlargd={'ln': ln,
'hpupdate_id': hpupdate_id},
link_label=_("Accept Holding Pen version"))
delete_link = create_html_link(urlbase=oai_harvest_admin_url + \
"/delhprecord",
urlargd={'ln': ln,
'oai_id': str(oai_id),
'date_inserted': str(date_inserted)},
link_label=_("Delete from holding pen"))
result = ""
result += db_label
result += db_content
result += hp_label
result += hp_content
result += delete_link + " "
result += submit_link
return result
def perform_request_delhprecord(hpupdate_id, ln=CFG_SITE_LANG, confirm=0):
delete_holdingpen_entry(hpupdate_id)
return "Record deleted from the holding pen"
def perform_request_accepthprecord(hpupdate_id, ln=CFG_SITE_LANG, confirm=0):
(_, _, _, record_xml) = get_holdingpen_entry_details(hpupdate_id)
delete_holdingpen_entry(hpupdate_id)
upload_record(record_xml)
return perform_request_view_holdingpen_tree("")
# new functions for the holding pen
def perform_request_gethpyears(prefix, filter):
years = get_holdingpen_years(filter)
result = ""
for year in years:
result += "<li id=\"%s_%s_%s\"><span>Year %s (%s entries)</span> <ul id=\"%s_%s_%s_ul\"></ul></li>" % (prefix, str(year[0]), filter, str(year[0]), str(year[1]), prefix, str(year[0]), filter)
return result
def perform_request_gethpyear(prefix, year, filter):
months = get_holdingpen_year(year, filter)
result = ""
for month in months:
result += "<li id=\"%s_%s_%s_%s\"><span>%s-%s (%s entries)</span> <ul id=\"%s_%s_%s_%s_ul\"></ul></li>" % (prefix, year, str(month[0]), filter, year, str(month[0]), str(month[1]), prefix, year, str(month[0]), filter)
return result
def perform_request_gethpmonth(prefix, year, month, filter):
days = get_holdingpen_month(year, month, filter)
result = ""
for day in days:
result += "<li id=\"%s_%s_%s_%s_%s\"><span>%s-%s-%s (%s entries)</span> <ul id=\"%s_%s_%s_%s_%s_ul\"></ul></li>" % (prefix, year, month, str(day[0]), filter, year, month, str(day[0]), str(day[1]), prefix, year, month, str(day[0]), filter)
return result
def perform_request_gethpdayfragment(year, month, day, limit, start, filter):
data = get_holdingpen_day_fragment(year, month, day, limit, start, filter)
return build_holdingpen_table(data, "en")
def view_holdingpen_headers():
return bibharvest_templates.tmpl_view_holdingpen_headers()
def perform_request_view_holdingpen_tree(filter):
return bibharvest_templates.tmpl_view_holdingpen_body(\
filter, perform_request_gethpyears("holdingpencontainer", filter))
##################################################################
### Here the functions to retrieve, modify, delete and add sources
##################################################################
def get_oai_src(oai_src_id=''):
"""Returns a row parameters for a given id"""
sql = "SELECT id,name,baseurl,metadataprefix,frequency,bibconvertcfgfile,postprocess,setspecs,bibfilterprogram FROM oaiHARVEST"
try:
if oai_src_id:
sql += " WHERE id=%s" % oai_src_id
sql += " ORDER BY id asc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def modify_oai_src(oai_src_id, oai_src_name, oai_src_baseurl, oai_src_prefix, oai_src_frequency, oai_src_config, oai_src_post, oai_src_sets=None, oai_src_bibfilter=''):
"""Modifies a row's parameters"""
if oai_src_sets is None:
oai_src_sets = []
if oai_src_post is None:
oai_src_post = []
try:
res = run_sql("UPDATE oaiHARVEST SET name=%s WHERE id=%s", (oai_src_name, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET baseurl=%s WHERE id=%s", (oai_src_baseurl, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET metadataprefix=%s WHERE id=%s", (oai_src_prefix, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET frequency=%s WHERE id=%s", (oai_src_frequency, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET bibconvertcfgfile=%s WHERE id=%s", (oai_src_config, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET postprocess=%s WHERE id=%s", ('-'.join(oai_src_post), oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET setspecs=%s WHERE id=%s", (' '.join(oai_src_sets), oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET bibfilterprogram=%s WHERE id=%s", (oai_src_bibfilter, oai_src_id))
return (1, "")
except StandardError, e:
return (0, e)
def add_oai_src(oai_src_name, oai_src_baseurl, oai_src_prefix, oai_src_frequency, oai_src_lastrun, oai_src_config, oai_src_post, oai_src_sets=None, oai_src_bibfilter=''):
"""Adds a new row to the database with the given parameters"""
if oai_src_sets is None:
oai_src_sets = []
try:
if oai_src_lastrun in [0, "0"]: lastrun_mode = 'NULL'
else:
lastrun_mode = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
# lastrun_mode = "'"+lastrun_mode+"'"
run_sql("INSERT INTO oaiHARVEST (id, baseurl, metadataprefix, arguments, comment, bibconvertcfgfile, name, lastrun, frequency, postprocess, bibfilterprogram, setspecs) VALUES (0, %s, %s, NULL, NULL, %s, %s, %s, %s, %s, %s, %s)", \
(oai_src_baseurl, oai_src_prefix, oai_src_config, oai_src_name, lastrun_mode, oai_src_frequency, '-'.join(oai_src_post), oai_src_bibfilter, " ".join(oai_src_sets)))
return (1, "")
except StandardError, e:
return (0, e)
def delete_oai_src(oai_src_id):
"""Deletes a row from the database according to its id"""
try:
res = run_sql("DELETE FROM oaiHARVEST WHERE id=%s" % oai_src_id)
return (1, "")
except StandardError, e:
return (0, e)
def get_tot_oai_src():
"""Returns number of rows in the database"""
try:
sql = "SELECT COUNT(*) FROM oaiHARVEST"
res = run_sql(sql)
return res[0][0]
except StandardError, e:
return ""
def get_update_status():
"""Returns a table showing a list of all rows and their LastUpdate status"""
try:
sql = "SELECT name,lastrun FROM oaiHARVEST ORDER BY lastrun desc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_next_schedule():
"""Returns the next scheduled oaiharvestrun tasks"""
try:
sql = "SELECT runtime,status FROM schTASK WHERE proc='oaiharvest' AND runtime > now() ORDER by runtime LIMIT 1"
res = run_sql(sql)
if len(res) > 0:
return res[0]
else:
return ("", "")
except StandardError, e:
return ("", "")
def validate(oai_src_baseurl):
"""This function validates a baseURL by opening its URL and 'greping' for the <OAI-PMH> and <Identify> tags:
Codes:
0 = okay
1 = baseURL not valid
2 = baseURL not found/not existing
3 = tmp directoy is not writable
4 = Unknown error
Returns tuple (code, message)
"""
try:
url = oai_src_baseurl + "?verb=Identify"
urllib.urlretrieve(url, tmppath)
# First check if we have xml oai-pmh output
grepOUT1 = os.popen('grep -iwc "<OAI-PMH" ' + tmppath).read()
if int(grepOUT1) == 0:
# No.. we have an http error
return (4, os.popen('cat ' + tmppath).read())
grepOUT2 = os.popen('grep -iwc "<identify>" ' + tmppath).read()
if int(grepOUT2) > 0:
#print "Valid!"
return (0, '')
else:
#print "Not valid!"
return (1, '')
except IOError, (errno, strerror):
# Quick error handling for frequent error codes.
if errno == 13:
return (3, "Please check permission on %s and retry" % CFG_TMPDIR)
elif errno == 2 or errno == 'socket error':
return (2, "Could not connect with URL %s. Check URL or retry when server is available." % url)
return (4, strerror)
except StandardError, e:
return (4, "An unknown error has occured")
except InvalidURL, e:
return (2, "Could not connect with URL %s. Check URL or retry when server is available." % url)
def validatefile(oai_src_config):
"""This function checks whether the given path to text file exists or not
0 = okay
1 = file non existing
"""
CFG_BIBCONVERT_XSL_PATH = "%s%sbibconvert%sconfig" % (CFG_ETCDIR,
os.sep,
os.sep)
path_to_config = (CFG_BIBCONVERT_XSL_PATH + os.sep +
oai_src_config)
if os.path.exists(path_to_config):
# Try to read in config directory
try:
ftmp = open(path_to_config, 'r')
cfgstr = ftmp.read()
ftmp.close()
if cfgstr != "":
#print "Valid!"
return 0
except StandardError, e:
pass
# Try to read as complete path
try:
ftmp = open(oai_src_config, 'r')
cfgstr = ftmp.read()
ftmp.close()
if cfgstr != "":
#print "Valid!"
return 0
else:
#print "Not valid!"
return 1
except StandardError, e:
return 1
return 1
def findMetadataFormats(oai_src_baseurl):
"""This function finds the Metadata formats offered by a OAI repository by analysing the output of verb=ListMetadataFormats"""
formats = []
url = oai_src_baseurl + "?verb=ListMetadataFormats"
try:
urllib.urlretrieve(url, tmppath)
except IOError:
return formats
ftmp = open(tmppath, 'r')
xmlstr = ftmp.read()
ftmp.close()
chunks = xmlstr.split('<metadataPrefix>')
count = 0 # first chunk is invalid
for chunk in chunks:
if count != 0:
formats.append(chunk.split('</metadataPrefix>')[0])
count = count + 1
return formats
def findSets(oai_src_baseurl):
"""This function finds the sets offered by a OAI repository
by analysing the output of verb=ListSets.
Returns list of tuples(SetSpec, SetName)"""
url = oai_src_baseurl + "?verb=ListSets"
sets = {}
try:
urllib.urlretrieve(url, tmppath)
except IOError:
return sets
ftmp = open(tmppath, 'r')
xmlstr = ftmp.read()
ftmp.close()
chunks = xmlstr.split('<set>')
count = 0 # first chunk is invalid
for chunk in chunks:
if count != 0:
chunks_set = chunk.split('<setSpec>')[1].split("</setSpec>")
set_spec = chunks_set[0]
#chunks_set[1].split('<setName>')
check_set_2 = chunks_set[1].split("<setName>")
set_name = None
if len(check_set_2) > 1:
set_name = check_set_2[1].split("</setName>")[0]
sets[set_spec] = [set_spec, set_name]
count = count + 1
return sets.values()
diff --git a/modules/bibmatch/lib/bibmatch_engine.py b/modules/bibmatch/lib/bibmatch_engine.py
index 33d842ba6..52a450c40 100644
--- a/modules/bibmatch/lib/bibmatch_engine.py
+++ b/modules/bibmatch/lib/bibmatch_engine.py
@@ -1,938 +1,939 @@
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibMatch - tool to match records with database content of an Invenio instance,
either locally or remotely through invenio_connector."""
__revision__ = "$Id$"
import sys
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set #for "&" intersection
# pylint: enable=W0622
import os
import getopt
import re
from tempfile import mkstemp
from time import sleep
from invenio.config import CFG_SITE_URL, CFG_BIBMATCH_FUZZY_WORDLIMITS, \
CFG_BIBMATCH_QUERY_TEMPLATES, \
CFG_BIBMATCH_FUZZY_EMPTY_RESULT_LIMIT, \
CFG_BIBMATCH_LOCAL_SLEEPTIME, \
- CFG_BIBMATCH_REMOTE_SLEEPTIME
+ CFG_BIBMATCH_REMOTE_SLEEPTIME, \
+ CFG_SITE_RECORD
from invenio.invenio_connector import InvenioConnector
from invenio.bibrecord import create_records, \
record_get_field_values, record_xml_output, record_modify_controlfield, \
record_has_field, record_add_field
from invenio import bibconvert
from invenio.search_engine import get_fieldcodes
from invenio.search_engine_query_parser import SearchQueryParenthesisedParser
from invenio.dbquery import run_sql
from invenio.textmarc2xmlmarc import transform_file
from invenio.xmlmarc2textmarc import get_sysno_from_record, create_marc_record
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
re_querystring = re.compile("\[(.+?)\]")
re_valid_tag = re.compile("^[0-9]{3}[a-zA-Z0-9_%]{0,3}$")
def usage():
"""Print help"""
print >> sys.stderr, \
""" BibMatch - match bibliographic data against database, either locally or remotely
Usage: %s [options] [QUERY]
Options:
Output:
-0 --print-new (default) print unmatched in stdout
-1 --print-match print matched records in stdout
-2 --print-ambiguous print records that match more than 1 existing records
-3 --print-fuzzy print records that match the longest words in existing records
-b --batch-output=(filename). filename.new will be new records, filename.matched will be matched,
filename.ambiguous will be ambiguous, filename.fuzzy will be fuzzy match
-t --text-marc-output transform the output to text-marc format instead of the default MARCXML
Simple query:
-q --query-string=(search-query/predefined-query) See "Querystring"-section below.
-f --field=(field)
General options:
-n --noprocess Do not print records in stdout.
-i, --input use a named file instead of stdin for input
-v, --verbose=LEVEL verbose level (from 0 to 9, default 1)
-r, --remote=URL match against a remote Invenio installation (Full URL, no trailing '/')
Beware: Only searches public records attached to home collection
-a, --alter-recid The recid (controlfield 001) of matched or fuzzy matched records in
output will be replaced by the 001 value of the matched record.
Note: Useful if you want to replace matched records using BibUpload.
-z, --clean clean queries before searching
-h, --help print this help and exit
-V, --version print version information and exit
Advanced options:
-c --config=filename load querystrings from a config file. Each line starting with QRYSTR will
be added as a query. i.e. QRYSTR --- [title] [author]
-x --collection only perform queries in certain collection(s).
Note: matching against restricted collections does not work.
-m --mode=(a|e|o|p|r) perform an advanced search using special search mode.
Where mode is:
"a" all of the words,
"o" any of the words,
"e" exact phrase,
"p" partial phrase,
"r" regular expression.
-o --operator(a|o) used to concatenate identical fields in search query (i.e. several report-numbers)
Where operator is:
"a" boolean AND (default)
"o" boolean OR
QUERYSTRINGS
Querystrings determine which type of query/strategy to use when searching for the
matching records in the database.
Predefined querystrings:
There are some predefined querystrings available:
title - standard title search. (i.e. "this is a title") (default)
title-author - title and author search (i.e. "this is a title AND Lastname, F")
reportnumber - reportnumber search (i.e. reportnumber:REP-NO-123).
You can also add your own predefined querystrings inside invenio.conf file.
You can structure your query in different ways:
* Old-style: fieldnames separated by '||' (conforms with earlier BibMatch versions):
-q "773__p||100__a"
* New-style: Invenio query syntax with "bracket syntax":
-q "773__p:\"[773__p]\" 100__a:[100__a]"
Depending on the structure of the query, it will fetch associated values from each record and put it into
the final search query. i.e in the above example it will put journal-title from 773__p.
When more then one value/datafield is found, i.e. when looking for 700__a (additional authors),
several queries will be put together to make sure all combinations of values are accounted for.
The queries are separated with given operator (-o, --operator) value.
Note: You can add more then one query to a search, just give more (-q, --query-string) arguments.
The results of all queries will be combined when matching.
BibConvert formats:
Another option to further improve your matching strategy is to use BibConvert formats. By using the formats
available by BibConvert you can change the values from the retrieved record-fields.
i.e. using WORDS(1,R) will only return the first (1) word from the right (R). This can be very useful when
adjusting your matching parameters to better match the content. For example only getting authors last-name
instead of full-name.
You can use these formats directly in the querystrings (indicated by '::'):
* Old-style: -q "100__a::WORDS(1,R)::DOWN()"
This query will take first word from the right from 100__a and also convert it to lower-case.
* New-style: -q "100__a:[100__a::WORDS(1,R)::DOWN()]"
See BibConvert documentation for a more detailed explanation of formats.
Predefined fields:
In addition to specifying distinct MARC fields in the querystrings you can use predefined
fields as configured in the LOCAL(!) Invenio system. These fields will then be mapped to one
or more fieldtags to be retrieved from input records.
Common predefined fields used in querystrings: (for Invenio demo site, your fields may vary!)
'abstract', 'affiliation', 'anyfield', 'author', 'coden', 'collaboration',
'collection', 'datecreated', 'datemodified', 'division', 'exactauthor',
'experiment', 'fulltext', 'isbn', 'issn', 'journal', 'keyword', 'recid',
'reference', 'reportnumber', 'subject', 'title', 'year'
Examples:
$ bibmatch [options] < input.xml > unmatched.xml
$ bibmatch -b out -n < input.xml
$ bibmatch -a -1 < input.xml > modified_match.xml
$ bibmatch --field=title < input.xml
$ bibmatch --field=245__a --mode=a < input.xml
$ bibmatch --print-ambiguous -q title-author < input.xml > ambigmatched.xml
$ bibmatch -q "980:Thesis 773__p:\"[773__p]\" 100__a:[100__a]" -r "http://inspirebeta.net" < input.xml
$ bibmatch -x 'Books,Articles' < input.xml
""" % (sys.argv[0],)
sys.exit(1)
return
class Querystring:
"""
Holds the information about a querystring.
The object contains lists of fields, formats and queries which generates search queries.
self.fields is a dict of found field-values {"tag": [list of found field-values]}
self.formats is a dict of found BibConvert formats {"tag": [list of found format-values]}
self.pattern contains the original search string
self.query contains the generated query
To populate the Querystring instance with values and search string structure,
call create_query(..) with BibRecord structure and a query-string to populate with retrieved values.
Example: The template "title:[245__a]" will retrieve the value from subfield 245__a in
given record. If any BibConvert formats are specified for this field, these will
be applied.
"""
def __init__(self, operator="and", clean=False):
"""
Creates Querystring instance.
@param operator: operator used to concatenate several queries
@type operator: str
@param clean: indicates if queries should be sanitized
@type clean: bool
"""
self.fields = {}
self.operator = " %s " % (operator,)
self.pattern = ""
self.query = ""
self.clean = clean
self.formats = {}
def create_query(self, record, qrystr="[title]"):
"""
Main method that parses and generates a search query from
given query-string structure and record data. Returns the
resulting query-string and completeness determination as a tuple.
@param record: bibrecord to retrive field-values from
@type record: dict
@param qrystr: proper query string template. (i.e. title:[245__a])
defaults to: [title]
@type qrystr: str
@return: (query-string, complete flag)
@rtype: tuple
"""
if qrystr == "":
qrystr = "[title]"
if "||" in qrystr or not "[" in qrystr:
# Assume old style query-strings
qrystr = self._convert_qrystr(qrystr)
# FIXME: Convert to lower case, we do this to account for fuzzy_parser
# which treats everything lower-case, and may cause KeyError when
# retrieving data from the self.fields dict.
# Also BibConvert formats are currently case sensitive.
self.pattern = qrystr.lower()
self.fields = {}
complete = True
fieldtags_found = []
# Find all potential references to record tag values and
# add to fields-dict as a list of values using fieldname as key
for field_reference in re_querystring.findall(qrystr):
# First we see if there is any special formats for this field_reference
# This is done before transforming to lower case, as BibConvert formats
# are case-sensitive
fieldname = self._extract_formats(field_reference)
self.pattern = self.pattern.replace("[%s]" % (field_reference.lower(),), "[%s]" % (fieldname,))
# Find proper MARC tag(s) for the fieldname
tag_list = get_field_tags_from_fieldname(fieldname)
if len(tag_list) == 0:
tag_list = [fieldname]
for field in tag_list:
# Check if it is really a reference to a tag to not confuse with e.g. regex syntax
if re_valid_tag.match(field) != None:
tag = field[0:3]
ind1 = field[3:4]
ind2 = field[4:5]
code = field[5:6]
if ind1 == "_" or ind1 == "%":
ind1 = ""
if ind2 == "_" or ind2 == "%":
ind2 = ""
value_list = record_get_field_values(record, tag, ind1, ind2, code)
for value in value_list:
if value.strip() != "":
# Apply formats if applicable
for aformat in self.formats.get(fieldname, []):
value = bibconvert.FormatField(value, aformat)
self.fields.setdefault(fieldname, []).append((fieldname, value))
# Add fieldname to found tags, so we can check completeness later
fieldtags_found.append(fieldname)
# Is the query deemed complete? i.e. did we find data for all field-name references
complete = not bool([n for n in fieldtags_found if n not in self.fields])
# Now determine the Cartesian product over all found values,
# then iterate over each combination to generate proper query
all_queries = []
query_tuples = cproduct(self.fields.values())
for query in query_tuples:
new_query = self.pattern
for fieldname, value in query:
new_query = new_query.replace("[%s]" % (fieldname,), value)
all_queries.append(new_query)
# Finally we concatenate all queries into one, delimited by chosen operator
self.query = self.operator.join(set(all_queries))
if not complete:
# Clean away field-name references not found
for fieldtag in fieldtags_found:
self.query = self.query.replace("[%s]" % (fieldtag,), "")
# Clean query?
if self.clean:
self._clean_query()
return self.query, complete
def fuzzy_queries(self):
"""
Returns a list of queries that are built more 'fuzzily' using the main query as base.
The list returned also contains the current operator in context, so each query is a tuple
of (operator, query).
@return: tuple of (operator, query)
@rtype: (str, str)
"""
fuzzy_query_list = []
parser = SearchQueryParenthesisedParser()
query_parts = parser.parse_query(self.pattern)
# Go through every expression in the query and generate fuzzy searches
for i in xrange(0, len(query_parts) - 1, 2):
current_operator = query_parts[i]
current_pattern = query_parts[i + 1]
fieldname_list = re_querystring.findall(current_pattern)
if fieldname_list == []:
# No reference to record value, add query 'as is'
fuzzy_query_list.append((current_operator, current_pattern))
else:
for fieldname in re_querystring.findall(current_pattern):
for dummy, value in self.fields.get(fieldname, []):
new_query = []
# Grab the x longest words in the string and perform boolean AND for each word
# x is determined by the configuration dict and is tag-based. Defaults to 3 words
word_list = get_longest_words(value, limit=CFG_BIBMATCH_FUZZY_WORDLIMITS.get(fieldname, 3))
for word in word_list:
# Create fuzzy query with key + word, including any surrounding elements like quotes, regexp etc.
new_query.append(current_pattern.replace("[%s]" % (fieldname,), word))
fuzzy_query_list.append((current_operator, " ".join(new_query)))
# Return a list of unique queries
return list(set(fuzzy_query_list))
def _clean_query(self):
"""
This function will remove erroneous characters and combinations from
a the generated search query that might cause problems when searching.
@return: cleaned query
@rtype: str
"""
#FIXME: Extend cleaning to account for encodings and LaTeX symbols
query = self.query.replace("''", "")
query = query.replace('""', "")
return query
def _convert_qrystr(self, qrystr):
"""
Converts old-style query-strings into new-style.
"""
fields = qrystr.split("||")
converted_query = []
for field in fields:
converted_query.append("[%s]" % (field,))
return self.operator.join(converted_query)
def _extract_formats(self, field_reference):
"""
Looks for BibConvert formats within query-strings and adds to
the instance. Formats are defined by one or more '::' followed
by a format keyword which is defined in BibConvert FormatField()
method.
Returns the field_reference reference, with formats stripped.
"""
field_parts = field_reference.split("::")
for aformat in field_parts[1:]:
self.formats.setdefault(field_parts[0], []).append(aformat)
return field_parts[0]
def get_field_tags_from_fieldname(field):
"""
Gets list of field 'field' for the record with 'sysno' system number from the database.
"""
query = "select tag.value from tag left join field_tag on tag.id=field_tag.id_tag " \
+ "left join field on field_tag.id_field=field.id where field.code='%s'" % (field,)
out = []
res = run_sql(query)
for row in res:
out.append(row[0])
return out
def cproduct(args):
"""
Returns the Cartesian product of passed arguments as a list of tuples.
'12','34' -> ('1', '3'), ('1', '4'), ('2', '3'), ('2', '4')
@param args: iterable with elements to compute
@type args: iterable
@return list containing tuples for each computed combination
@rtype list of tuples
Based on http://docs.python.org/library/itertools.html#itertools.product
"""
values = map(tuple, args)
result = [[]]
for value in values:
result = [x + [y] for x in result for y in value]
return [tuple(res) for res in result]
def bylen(word1, word2):
""" Sort comparison method that compares by length """
return len(word1) - len(word2)
def get_longest_words(wstr, limit=5):
"""
Select the longest words for matching. It selects the longest words from
the string, according to a given limit of words. By default the 5 longest word are selected
@param wstr: string to extract the longest words from
@type wstr: str
@param limit: maximum number of words extracted
@type limit: int
@return: list of long words
@rtype: list
"""
words = []
if wstr:
words = wstr.split()
words.sort(cmp=bylen)
words.reverse()
words = words[:limit]
return words
def add_recid(record, recid):
"""
Add a given record-id to the record as $$001 controlfield. If an 001 field already
exists it will be replaced.
@param record: the record to retrive field-values from
@type record: a bibrecord instance
@param recid: record-id to be added
@type recid: int
"""
if record_has_field(record, '001'):
record_modify_controlfield(record, '001', \
controlfield_value=str(recid), \
field_position_global=1)
else:
record_add_field(record, '001', controlfield_value=str(recid))
def match_result_output(recID_list, server_url, query, matchmode="no match"):
"""
Generates result as XML comments from passed record and matching parameters.
@param record: record tuple containing results
@type record: list
@param server_url: url to the server the matching has been performed
@type server_url: str
@param qrystrs: Querystrings
@type qrystrs: list of object
@param matchmode: matching type
@type matchmode: str
@rtype str
@return XML result string
"""
result = []
for recID in recID_list:
- result.append("<!-- BibMatch-Matching-Found: %s/record/%s -->" \
- % (server_url, recID))
+ result.append("<!-- BibMatch-Matching-Found: %s/%s/%s -->" \
+ % (server_url, CFG_SITE_RECORD, recID))
result.append("<!-- BibMatch-Matching-Mode: %s -->" \
% (matchmode,))
result.append("<!-- BibMatch-Matching-Criteria: %s -->\n" \
% (query,))
return "\n".join(result)
def match_records(records, qrystrs=None, search_mode=None, operator="and", verbose=1, \
server_url=CFG_SITE_URL, modify=0, sleeptime=CFG_BIBMATCH_LOCAL_SLEEPTIME, \
clean=False, collections=[]):
"""
Match passed records with existing records on a local or remote Invenio
installation. Returns which records are new (no match), which are matched,
which are ambiguous and which are fuzzy-matched. A formatted result of each
records matching are appended to each record tuple:
(record, status_code, list_of_errors, result)
@param records: records to analyze
@type records: list of records
@param qrystrs: list of tuples (field, querystring)
@type qrystrs: list
@param search_mode: if mode is given, the search will perform an advanced query using
the desired mode. Otherwise 'simple search' is used.
@type search_mode: str
@param operator: operator used to concatenate values of fields occurring more then once.
Valid types are: AND, OR. Defaults to AND.
@type operator: str
@param verbose: be loud
@type verbose: int
@param server_url: which server to search on. Local installation by default
@type server_url: str
@param modify: output modified records of matches
@type modify: int
@param sleeptime: amount of time to wait between each query
@type sleeptime: float
@param clean: should the search queries be cleaned before passed them along?
@type clean: bool
@param collections: list of collections to search, if specified
@type collections: list
@rtype: list of lists
@return an array of arrays of records, like this [newrecs,matchedrecs,
ambiguousrecs,fuzzyrecs]
"""
server = InvenioConnector(server_url)
newrecs = []
matchedrecs = []
ambiguousrecs = []
fuzzyrecs = []
## Go through each record and try to find matches using defined querystrings
record_counter = 0
querystring = Querystring(operator, clean=clean)
for rec in records:
record_counter += 1
if (verbose > 1):
sys.stderr.write("\n Processing record: #%d .." % (record_counter,))
# At least one (field, querystring) tuple is needed for default search query
if not qrystrs:
qrystrs = [("", "")]
# Temporary store result(s) for each record
matched_results = []
ambiguous_results = []
fuzzy_results = []
# Go through each querystring, trying to find a matching record
# Stops on first valid match, if no exact-match we continue with fuzzy match
for field, qrystr in qrystrs:
query, complete = querystring.create_query(rec[0], qrystr)
if query == "":
if (verbose > 1):
sys.stderr.write("\nEmpty query. Skipping...\n")
# Empty query, no point searching database
continue
if not complete:
if (verbose > 1):
sys.stderr.write("\nQuery not complete. Flagged as uncertain/ambiguous...\n")
# Determine proper search parameters
if search_mode != None:
search_params = dict(p1=query, f1=field, m1=search_mode, of='id', c=collections)
else:
search_params = dict(p=query, f=field, of='id', c=collections)
## Perform the search with retries
result_recids = server.search_with_retry(**search_params)
if (verbose > 8):
if len(result_recids) > 10:
sys.stderr.write("\nSearching with values %s result=%s\n" %
(search_params, "More then 10 results..."))
else:
sys.stderr.write("\nSearching with values %s result=%s\n" %
(search_params, result_recids))
sleep(sleeptime)
## Check results:
# Ambiguous match
if len(result_recids) > 1 and len(result_recids) < 11:
ambiguous_results.append((result_recids, query))
if (verbose > 8):
sys.stderr.write("Ambiguous\n")
# Match
elif len(result_recids) == 1:
if modify:
add_recid(rec[0], result_recids[0])
if complete:
matched_results.append((result_recids, query))
if (verbose > 8):
sys.stderr.write("Match\n")
# This was a complete match, so let's break out to avoid fuzzy search
break
else:
# We treat the result as ambiguous (uncertain) when query is not complete
ambiguous_results.append((result_recids, query))
if (verbose > 8):
sys.stderr.write("Ambiguous\n")
# No match
else:
if (verbose > 8):
sys.stderr.write("New (no matches)\n")
# No complete matches, lets try fuzzy matching of all the queries
else:
## Fuzzy matching: Analyze all queries and perform individual searches, then intersect results.
for field, qrystr in qrystrs:
query, complete = querystring.create_query(rec[0], qrystr)
if query == "":
if (verbose > 1):
sys.stderr.write("\nEmpty query. Skipping...\n")
# Empty query, no point searching database
continue
result_hitset = None
fuzzy_query_list = querystring.fuzzy_queries()
empty_results = 0
# Go through every expression in the query and generate fuzzy searches
for current_operator, qry in fuzzy_query_list:
current_resultset = None
search_params = dict(p=qry, f=field, of='id', c=collections)
current_resultset = server.search_with_retry(**search_params)
if (verbose > 8):
if len(current_resultset) > 10:
sys.stderr.write("\nSearching with values %s result=%s\n" %
(search_params, "More then 10 results..."))
else:
sys.stderr.write("\nSearching with values %s result=%s\n" %
(search_params, current_resultset))
sleep(sleeptime)
if current_resultset == None:
continue
if current_resultset == [] and empty_results < CFG_BIBMATCH_FUZZY_EMPTY_RESULT_LIMIT:
# Allows some empty results
empty_results += 1
else:
# Intersect results with previous results depending on current operator
if result_hitset == None:
result_hitset = current_resultset
if current_operator == '+':
result_hitset = list(set(result_hitset) & set(current_resultset))
elif current_operator == '-':
result_hitset = list(set(result_hitset) - set(current_resultset))
elif current_operator == '|':
result_hitset = list(set(result_hitset) | set(current_resultset))
if result_hitset and len(result_hitset) < 10:
# This was a fuzzy match
query_out = " #Fuzzy# ".join([q for dummy, q in fuzzy_query_list])
if len(result_hitset) == 1 and complete:
if modify:
add_recid(rec[0], result_hitset[0])
fuzzy_results.append((result_hitset, query_out))
if (verbose > 8):
sys.stderr.write("Fuzzy: %s\n" % (result_hitset,))
else:
# We treat the result as ambiguous (uncertain) when:
# - query is not complete
# - more then one result
ambiguous_results.append((result_hitset, query_out))
if (verbose > 8):
sys.stderr.write("Ambiguous\n")
## Evaluate final results for record
# Add matched record iff number found is equal to one, otherwise return fuzzy, ambiguous or no match
if len(matched_results) == 1:
results, query = matched_results[0]
matchedrecs.append((rec[0], "<!-- BibMatch-Matching-Results: -->\n%s" % (match_result_output(results, server_url, \
query, "exact-matched"))))
if (verbose > 1):
sys.stderr.write("Final result: match\n")
else:
if len(fuzzy_results) > 0:
# Find common record-id for all fuzzy results and grab first query as "representative" query
query = fuzzy_results[0][1]
result_lists = []
for res, dummy in fuzzy_results:
result_lists.extend(res)
results = set([res for res in result_lists])
fuzzyrecs.append((rec[0], "<!-- BibMatch-Matching-Results: -->\n%s" % (match_result_output(results, server_url, \
query, "fuzzy-matched"),)))
if (verbose > 1):
sys.stderr.write("Final result: fuzzy\n")
elif len(ambiguous_results) > 0:
# Find common record-id for all ambiguous results and grab first query as "representative" query
query = ambiguous_results[0][1]
result_lists = []
for res, dummy in ambiguous_results:
result_lists.extend(res)
results = set([res for res in result_lists])
ambiguousrecs.append((rec[0], "<!-- BibMatch-Matching-Results: -->\n%s" % (match_result_output(results, server_url, \
query, "ambiguous-matched"),)))
if (verbose > 1):
sys.stderr.write("Final result: ambiguous\n")
else:
newrecs.append((rec[0], "<!-- BibMatch-Matching-Results: -->\n%s" % (match_result_output([], server_url, str(qrystrs)),)))
if (verbose > 1):
sys.stderr.write("Final result: new\n")
return [newrecs, matchedrecs, ambiguousrecs, fuzzyrecs]
def transform_input_to_marcxml(filename=None, file_input=""):
"""
Takes the filename or input of text-marc and transforms it
to MARCXML.
"""
if not filename:
# Create temporary file to read from
tmp_fd, filename = mkstemp()
os.write(tmp_fd, file_input)
os.close(tmp_fd)
try:
# Redirect output, transform, restore old references
old_stdout = sys.stdout
new_stdout = StringIO()
sys.stdout = new_stdout
transform_file(filename)
finally:
sys.stdout = old_stdout
return new_stdout.getvalue()
def main():
"""
Record matches database content when defined search gives
exactly one record in the result set. By default the match is
done on the title field.
"""
try:
opts, args = getopt.getopt(sys.argv[1:], "0123hVm:fq:c:nv:o:b:i:r:tazx:",
[
"print-new",
"print-match",
"print-ambiguous",
"print-fuzzy",
"help",
"version",
"mode=",
"field=",
"query-string=",
"config=",
"no-process",
"verbose=",
"operator=",
"batch-output=",
"input=",
"remote=",
"text-marc-output",
"alter-recid",
"clean",
"collection="
])
except getopt.GetoptError, e:
usage()
match_results = []
qrystrs = [] # list of query strings
print_mode = 0 # default match mode to print new records
noprocess = 0 # dump result in stdout?
operator = "and"
verbose = 1 # 0..be quiet
records = []
batch_output = "" # print stuff in files
f_input = "" # read from where, if param "i"
server_url = CFG_SITE_URL # url to server performing search, local by default
modify = 0 # alter output with matched record identifiers
textmarc_output = 0 # output in MARC instead of MARCXML
field = ""
search_mode = None # activates a mode, uses advanced search instead of simple
sleeptime = CFG_BIBMATCH_LOCAL_SLEEPTIME # the amount of time to sleep between queries, changes on remote queries
clean = False # should queries be sanitized?
collections = [] # only search certain collections?
for opt, opt_value in opts:
if opt in ["-0", "--print-new"]:
print_mode = 0
if opt in ["-1", "--print-match"]:
print_mode = 1
if opt in ["-2", "--print-ambiguous"]:
print_mode = 2
if opt in ["-3", "--print-fuzzy"]:
print_mode = 3
if opt in ["-n", "--no-process"]:
noprocess = 1
if opt in ["-h", "--help"]:
usage()
sys.exit(0)
if opt in ["-V", "--version"]:
print __revision__
sys.exit(0)
if opt in ["-t", "--text-marc-output"]:
textmarc_output = 1
if opt in ["-v", "--verbose"]:
verbose = int(opt_value)
if opt in ["-f", "--field"]:
if opt_value in get_fieldcodes():
field = opt_value
if opt in ["-q", "--query-string"]:
try:
template = CFG_BIBMATCH_QUERY_TEMPLATES[opt_value]
qrystrs.append((field, template))
except KeyError:
qrystrs.append((field, opt_value))
if opt in ["-m", "--mode"]:
search_mode = opt_value
if opt in ["-o", "--operator"]:
if opt_value.lower() in ["o", "or", "|"]:
operator = "or"
elif opt_value.lower() in ["a", "and", "&"]:
operator = "and"
if opt in ["-b", "--batch-output"]:
batch_output = opt_value
if opt in ["-i", "--input"]:
f_input = opt_value
if opt in ["-r", "--remote"]:
server_url = opt_value
sleeptime = CFG_BIBMATCH_REMOTE_SLEEPTIME
if opt in ["-a", "--alter-recid"]:
modify = 1
if opt in ["-z", "--clean"]:
clean = True
if opt in ["-c", "--config"]:
config_file = opt_value
config_file_read = bibconvert.read_file(config_file, 0)
for line in config_file_read:
tmp = line.split("---")
if(tmp[0] == "QRYSTR"):
qrystrs.append((field, tmp[1]))
if opt in ["-x", "--collection"]:
colls = opt_value.split(',')
print opt_value
for collection in colls:
if collection not in collections:
collections.append(collection)
if verbose:
sys.stderr.write("\nBibMatch: Parsing input file %s..." % (f_input,))
read_list = []
if not f_input:
for line_in in sys.stdin:
read_list.append(line_in)
else:
f = open(f_input)
for line_in in f:
read_list.append(line_in)
f.close()
file_read = "".join(read_list)
# Detect input type
if not file_read.startswith('<'):
# Not xml, assume type textmarc
file_read = transform_input_to_marcxml(f_input, file_read)
records = create_records(file_read)
if len(records) == 0:
if verbose:
sys.stderr.write("\nBibMatch: Input file contains no records.\n")
sys.exit(0)
if verbose:
sys.stderr.write("read %d records" % (len(records),))
sys.stderr.write("\nBibMatch: Matching ...")
match_results = match_records(records,
qrystrs,
search_mode,
operator,
verbose,
server_url,
modify,
sleeptime,
clean,
collections)
# set the output according to print..
# 0-newrecs 1-matchedrecs 2-ambiguousrecs 3-fuzzyrecs
recs_out = match_results[print_mode]
if verbose:
sys.stderr.write("\n\n Bibmatch report\n")
sys.stderr.write("=" * 35)
sys.stderr.write("\n New records : %d" % (len(match_results[0]),))
sys.stderr.write("\n Matched records : %d" % (len(match_results[1]),))
sys.stderr.write("\n Ambiguous records : %d" % (len(match_results[2]),))
sys.stderr.write("\n Fuzzy records : %d\n" % (len(match_results[3]),))
sys.stderr.write("=" * 35)
sys.stderr.write("\n Total records : %d\n" % (len(records),))
if not noprocess:
options = {'text-marc':1, 'aleph-marc':0}
for record, results in recs_out:
if textmarc_output:
# FIXME: textmarc output does not print matching results
sysno = get_sysno_from_record(record, options)
print create_marc_record(record, sysno, options)
else:
print results
print record_xml_output(record)
if batch_output:
i = 0
options = {'text-marc':1, 'aleph-marc':0}
outputs = ['new', 'matched', 'ambiguous', 'fuzzy']
for result in match_results:
filename = "%s.%s" % (batch_output, outputs[i])
file_fd = open(filename, "w")
for record, results in result:
out = []
if textmarc_output:
# FIXME: textmarc output does not print matching results
sysno = get_sysno_from_record(record, options)
out.append(create_marc_record(record, sysno, options))
else:
out.append(results)
out.append(record_xml_output(record))
file_fd.write("".join(out) + '\n')
file_fd.close()
i += 1
diff --git a/modules/bibmatch/lib/bibmatch_regression_tests.py b/modules/bibmatch/lib/bibmatch_regression_tests.py
index 501ec610e..bbf8e66d3 100644
--- a/modules/bibmatch/lib/bibmatch_regression_tests.py
+++ b/modules/bibmatch/lib/bibmatch_regression_tests.py
@@ -1,490 +1,491 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=E1102
"""Unit tests for bibmatch."""
__revision__ = "$Id$"
+from invenio.config import CFG_SITE_RECORD
from invenio.testutils import make_test_suite, run_test_suite
from invenio.bibrecord import create_records, record_has_field
from invenio.bibmatch_engine import match_records, transform_input_to_marcxml, \
Querystring
import unittest
class BibMatchTest(unittest.TestCase):
"""Test functions to check the functionality of bibmatch."""
def setUp(self):
"""setting up helper variables for tests"""
self.textmarc = """
000000020 001__ 20
000000020 041__ $$aeng
000000020 088__ $$aJYFL-RR-82-7
000000020 100__ $$aArje, J$$uUniversity of Jyvaskyla
000000020 245__ $$aCharge creation and reset mechanisms in an ion guide isotope separator (IGIS)
000000020 260__ $$aJyvaskyla$$bFinland Univ. Dept. Phys.$$cJul 1982
000000020 300__ $$a18 p
000000020 65017 $$2SzGeCERN$$aDetectors and Experimental Techniques
000000020 909C0 $$y1982
000000020 909C0 $$b19
000000020 909C1 $$uJyväsklä Univ.
000000020 909C1 $$c1990-01-28$$l50$$m2002-01-04$$oBATCH
000000020 909CS $$sn$$w198238n
000000020 980__ $$aREPORT
000000019 001__ 19
000000019 041__ $$aeng
000000019 088__ $$aSTAN-CS-81-898-MF
000000019 100__ $$aWhang, K$$uStanford University
000000019 245__ $$aSeparability as a physical database design methodology
000000019 260__ $$aStanford, CA$$bStanford Univ. Comput. Sci. Dept.$$cOct 1981
000000019 300__ $$a60 p
000000019 65017 $$2SzGeCERN$$aComputing and Computers
000000019 700__ $$aWiederhold, G
000000019 700__ $$aSagalowicz, D
000000019 909C0 $$y1981
000000019 909C0 $$b19
000000019 909C1 $$uStanford Univ.
000000019 909C1 $$c1990-01-28$$l50$$m2002-01-04$$oBATCH
000000019 909CS $$sn$$w198238n
000000019 980__ $$aREPORT
"""
#ambig match: Changed Atlantis (Timaeus) ->Atlantis
self.recxml1 = """
<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<controlfield tag="001">101</controlfield>
<datafield tag="037" ind1=" " ind2=" ">
<subfield code="a">BUL-NEWS-2009-003</subfield>
</datafield>
<datafield tag="041" ind1=" " ind2=" ">
<subfield code="a">eng</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Plato</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Atlantis (Timaeus)</subfield>
</datafield>
<datafield tag="520" ind1=" " ind2=" ">
<subfield code="b">&lt;!--HTML-->&lt;p class="articleHeader">This great island lay over against the Pillars of Heracles, in extent greater than Libya and Asia put together, and was the passage to other islands and to a great ocean of which the Mediterranean sea was only the harbour; and within the Pillars the empire of Atlantis reached in Europe to Tyrrhenia and in Libya to Egypt.&lt;/p>&lt;p>This mighty power was arrayed against Egypt and Hellas and all the countries&lt;/p>&lt;div class="phrwithcaption">&lt;div class="imageScale">&lt;img src="http://invenio-software.org/download/invenio-demo-site-files/icon-journal_Athanasius_Kircher_Atlantis_image.gif" alt="" />&lt;/div>&lt;p>Representation of Atlantis by Athanasius Kircher (1669)&lt;/p>&lt;/div>bordering on the Mediterranean. Then your city did bravely, and won renown over the whole earth. For at the peril of her own existence, and when the other Hellenes had deserted her, she repelled the invader, and of her own accord gave liberty to all the nations within the Pillars. A little while afterwards there were great earthquakes and floods, and your warrior race all sank into the earth; and the great island of Atlantis also disappeared in the sea. This is the explanation of the shallows which are found in that part of the Atlantic ocean. &lt;p>&lt;/p>(Excerpt from TIMAEUS, By Plato, translated By Jowett, Benjamin)&lt;br /></subfield>
</datafield>
<datafield tag="590" ind1=" " ind2=" ">
<subfield code="b">&lt;!--HTML-->&lt;br /></subfield>
</datafield>
<datafield tag="773" ind1=" " ind2=" ">
<subfield code="c">1</subfield>
<subfield code="n">02/2009</subfield>
<subfield code="t">Atlantis Times</subfield>
</datafield>
<datafield tag="773" ind1=" " ind2=" ">
<subfield code="c">1</subfield>
<subfield code="n">03/2009</subfield>
<subfield code="t">Atlantis Times</subfield>
</datafield>
<datafield tag="773" ind1=" " ind2=" ">
<subfield code="c">1</subfield>
<subfield code="n">04/2009</subfield>
<subfield code="t">Atlantis Times</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="u">http://localhost/record/101/files/journal_Athanasius_Kircher_Atlantis_image.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="u">http://localhost/record/101/files/journal_Athanasius_Kircher_Atlantis_image.gif?subformat=icon</subfield>
<subfield code="x">icon</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">ATLANTISTIMESNEWS</subfield>
</datafield>
</record>
</collection>
"""
#this is not in the collection
self.recxml2 = """
<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<controlfield tag="001">9124</controlfield>
<datafield tag="970" ind1=" " ind2=" ">
<subfield code="a">SPIRES-5726484</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Schulz, Michael B.</subfield>
<subfield code="u">Caltech</subfield>
</datafield>
<datafield tag="773" ind1=" " ind2=" ">
<subfield code="w">C02/06/25.2</subfield>
<subfield code="t">Prepared for</subfield>
<subfield code="c">477-480</subfield>
</datafield>
<datafield tag="650" ind1="1" ind2="7">
<subfield code="a">Theory-HEP</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="690" ind1="C" ind2=" ">
<subfield code="a">Conference Paper</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="999" ind1="C" ind2="5">
<subfield code="s">Phys.Rev.,D61,022001</subfield>
</datafield>
<datafield tag="999" ind1="C" ind2="5">
<subfield code="r">hep-th/9601083</subfield>
<subfield code="s">Phys.Rev.,D53,4129</subfield>
</datafield>
<datafield tag="999" ind1="C" ind2="5">
<subfield code="r">hep-th/0201029</subfield>
<subfield code="s">Phys.Rev.,D65,126009</subfield>
</datafield>
<datafield tag="999" ind1="C" ind2="5">
<subfield code="r">hep-th/0105097</subfield>
<subfield code="s">Phys.Rev.,D66,106006</subfield>
</datafield>
<datafield tag="999" ind1="C" ind2="5">
<subfield code="r">hep-th/9906070</subfield>
<subfield code="s">Nucl.Phys.,B584,69</subfield>
</datafield>
<datafield tag="999" ind1="C" ind2="5">
<subfield code="r">hep-th/0211182</subfield>
<subfield code="s">JHEP,0303,061</subfield>
</datafield>
<datafield tag="520" ind1=" " ind2=" ">
<subfield code="a">A brief overview of hep-th/0201028 prepared for NATO Advanced Study Institute and EC Summer School on Progress in String, Field and Particle Theory, Cargese, Corsica, France, 25 June - 11 July 2002.</subfield>
<subfield code="9">arXiv</subfield>
</datafield>
<datafield tag="037" ind1=" " ind2=" ">
<subfield code="a">arXiv:0810.5197</subfield>
<subfield code="9">arXiv</subfield>
<subfield code="c">hep-th</subfield>
</datafield>
<datafield tag="035" ind1=" " ind2=" ">
<subfield code="z">oai:arXiv.org:0810.5197</subfield>
<subfield code="9">arXiv</subfield>
</datafield>
<datafield tag="037" ind1=" " ind2=" ">
<subfield code="a">CALT-68-2441</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Moduli stabilization from fluxes</subfield>
</datafield>
<datafield tag="300" ind1=" " ind2=" ">
<subfield code="a">5</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="a">talk: Cargese 2002/06/25</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="a">string model</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="a">compactification</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="a">moduli: stability</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="a">orientifold</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="a">membrane model: D-brane</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="a">flux</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="a">supersymmetry</subfield>
<subfield code="2">INSPIRE</subfield>
</datafield>
<datafield tag="035" ind1=" " ind2=" ">
<subfield code="z">D04-00603</subfield>
<subfield code="9">DESY</subfield>
</datafield>
<datafield tag="035" ind1=" " ind2=" ">
<subfield code="z">Schulz:2002eh</subfield>
<subfield code="9">SPIRESTeX</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">Conference</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">arXiv</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">Citeable</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">CORE</subfield>
</datafield>
<datafield tag="269" ind1=" " ind2=" ">
<subfield code="c">2008-10</subfield>
</datafield>
<datafield tag="961" ind1=" " ind2=" ">
<subfield code="x">2003-11-17</subfield>
</datafield>
<datafield tag="961" ind1=" " ind2=" ">
<subfield code="c">2009-12-11</subfield>
</datafield>
</record>
</collection>
"""
#exact match: using all titles to disambiguate
self.recxml3 = """
<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<datafield tag="020" ind1=" " ind2=" ">
<subfield code="a">2225350574</subfield>
</datafield>
<datafield tag="041" ind1=" " ind2=" ">
<subfield code="a">fre</subfield>
</datafield>
<datafield tag="080" ind1=" " ind2=" ">
<subfield code="a">518.5:62.01</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Dasse, Michel</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Analyse informatique</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="n">t.2</subfield>
<subfield code="p">L'accomplissement</subfield>
</datafield>
<datafield tag="260" ind1=" " ind2=" ">
<subfield code="a">Paris</subfield>
<subfield code="b">Masson</subfield>
<subfield code="c">1972</subfield>
</datafield>
<datafield tag="490" ind1=" " ind2=" ">
<subfield code="a">Informatique</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="0">
<subfield code="y">1972</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="0">
<subfield code="b">21</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="c">1990-01-27</subfield>
<subfield code="l">00</subfield>
<subfield code="m">2002-04-12</subfield>
<subfield code="o">BATCH</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="S">
<subfield code="s">m</subfield>
<subfield code="w">198604</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">BOOK</subfield>
</datafield>
</record>
</collection>
"""
#fuzzy matched: quasi-normal -> quasi normal + missing word in title
self.recxml4 = """
<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<controlfield tag="001">92</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<controlfield tag="005">20060616163757.0</controlfield>
<datafield tag="037" ind1=" " ind2=" ">
<subfield code="a">hep-th/0606096</subfield>
</datafield>
<datafield tag="041" ind1=" " ind2=" ">
<subfield code="a">eng</subfield>
</datafield>
<datafield tag="088" ind1=" " ind2=" ">
<subfield code="a">UTHET-2006-05-01</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Koutsoumbas, G</subfield>
<subfield code="u">National Technical University of Athens</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Quasi normal Modes of Electromagnetic Perturbations of Four-Dimensional Topological Black Holes</subfield>
</datafield>
<datafield tag="260" ind1=" " ind2=" ">
<subfield code="c">2006</subfield>
</datafield>
<datafield tag="269" ind1=" " ind2=" ">
<subfield code="c">10 Jun 2006</subfield>
</datafield>
<datafield tag="300" ind1=" " ind2=" ">
<subfield code="a">17 p</subfield>
</datafield>
<datafield tag="520" ind1=" " ind2=" ">
<subfield code="a">We study the perturbative behaviour of topological black holes with scalar hair. We calculate both analytically and numerically the quasi-normal modes of the electromagnetic perturbations. In the case of small black holes we find clear evidence of a second-order phase transition of a topological black hole to a hairy configuration. We also find evidence of a second-order phase transition of the AdS vacuum solution to a topological black hole.</subfield>
</datafield>
<datafield tag="650" ind1="1" ind2="7">
<subfield code="2">SzGeCERN</subfield>
<subfield code="a">Particle Physics - Theory</subfield>
</datafield>
<datafield tag="690" ind1="C" ind2=" ">
<subfield code="a">ARTICLE</subfield>
</datafield>
<datafield tag="695" ind1=" " ind2=" ">
<subfield code="9">LANL EDS</subfield>
<subfield code="a">High Energy Physics - Theory</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Musiri, S</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Papantonopoulos, E</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Siopsis, G</subfield>
</datafield>
<datafield tag="720" ind1=" " ind2=" ">
<subfield code="a">Koutsoumbas, George</subfield>
</datafield>
<datafield tag="720" ind1=" " ind2=" ">
<subfield code="a">Musiri, Suphot</subfield>
</datafield>
<datafield tag="720" ind1=" " ind2=" ">
<subfield code="a">Papantonopoulos, Eleftherios</subfield>
</datafield>
<datafield tag="720" ind1=" " ind2=" ">
<subfield code="a">Siopsis, George</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">http://137.138.33.172/record/92/files/0606096.pdf</subfield>
+ <subfield code="u">http://137.138.33.172/%s/92/files/0606096.pdf</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="4">
<subfield code="c">006</subfield>
<subfield code="p">J. High Energy Phys.</subfield>
<subfield code="v">10</subfield>
<subfield code="y">2006</subfield>
</datafield>
<datafield tag="916" ind1=" " ind2=" ">
<subfield code="s">n</subfield>
<subfield code="w">200624</subfield>
</datafield>
<datafield tag="960" ind1=" " ind2=" ">
<subfield code="a">13</subfield>
</datafield>
<datafield tag="961" ind1=" " ind2=" ">
<subfield code="c">20070425</subfield>
<subfield code="h">1021</subfield>
<subfield code="l">CER01</subfield>
<subfield code="x">20060613</subfield>
</datafield>
<datafield tag="963" ind1=" " ind2=" ">
<subfield code="a">PUBLIC</subfield>
</datafield>
<datafield tag="970" ind1=" " ind2=" ">
<subfield code="a">002628325CER</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">ARTICLE</subfield>
</datafield>
</record>
</collection>
-"""
+""" % CFG_SITE_RECORD
return
def test_check_existing(self):
"""bibmatch - check existing record"""
records = create_records(self.recxml3)
[dummy1, matchedrecs, dummy2, dummy3] = match_records(records)
self.assertEqual(1, len(matchedrecs))
def test_check_new(self):
"""bibmatch - check a new record"""
records = create_records(self.recxml2)
[newrecs, dummy1, dummy2, dummy3] = match_records(records)
self.assertEqual(1, len(newrecs))
def test_check_ambiguous(self):
"""bibmatch - check an ambiguous record"""
records = create_records(self.recxml1)
[dummy1, dummy2, ambigrecs, dummy3] = match_records(records, qrystrs=[("", "[100__a]")])
self.assertEqual(1, len(ambigrecs))
def test_check_fuzzy(self):
"""bibmatch - check fuzzily matched record"""
records = create_records(self.recxml4)
[dummy1, dummy2, dummy3, fuzzyrecs] = match_records(records)
self.assertEqual(1, len(fuzzyrecs))
def test_check_remote(self):
"""bibmatch - check remote match (Invenio demo site)"""
records = create_records(self.recxml3)
[dummy1, matchedrecs, dummy3, dummy4] = match_records(records, server_url="http://invenio-demo.cern.ch")
self.assertEqual(1, len(matchedrecs))
def test_check_textmarc(self):
"""bibmatch - check textmarc as input"""
marcxml = transform_input_to_marcxml("", self.textmarc)
records = create_records(marcxml)
[dummy1, matchedrecs, dummy3, dummy4] = match_records(records, server_url="http://invenio-demo.cern.ch")
self.assertEqual(2, len(matchedrecs))
def test_check_altered(self):
"""bibmatch - check altered match"""
records = create_records(self.recxml3)
self.assertTrue(not record_has_field(records[0][0], '001'))
[dummy1, matchedrecs, dummy3, dummy4] = match_records(records, modify=1)
self.assertTrue(record_has_field(matchedrecs[0][0], '001'))
def test_check_qrystr(self):
"""bibmatch - check querystrings"""
operator = "and"
qrystr_old = "title||author"
qrystr_new = "[title] %s [author]" % (operator,)
querystring = Querystring(operator)
records = create_records(self.recxml3)
old_query = querystring.create_query(records[0], qrystr_old)
new_query = querystring.create_query(records[0], qrystr_new)
self.assertEqual(old_query, new_query)
def test_check_completeness(self):
"""bibmatch - check query completeness"""
records = create_records(self.recxml4)
[dummy1, dummy2, ambigrecs, dummy3] = match_records(records, qrystrs=[("", "[088__a] [035__a]")])
self.assertEqual(1, len(ambigrecs))
def test_check_collection(self):
"""bibmatch - check collection"""
records = create_records(self.recxml3)
[nomatchrecs, dummy1, dummy2, dummy3] = match_records(records, collections=["Articles"])
self.assertEqual(1, len(nomatchrecs))
[dummy1, matchedrecs, dummy2, dummy3] = match_records(records, collections=["Books"])
self.assertEqual(1, len(matchedrecs))
TEST_SUITE = make_test_suite(BibMatchTest)
if __name__ == "__main__":
run_test_suite(TEST_SUITE, warn_user=True)
diff --git a/modules/bibmerge/lib/bibmerge_engine.js b/modules/bibmerge/lib/bibmerge_engine.js
index f82a850e0..60a077fcb 100644
--- a/modules/bibmerge/lib/bibmerge_engine.js
+++ b/modules/bibmerge/lib/bibmerge_engine.js
@@ -1,972 +1,972 @@
/*
* This file is part of Invenio.
* Copyright (C) 2009, 2010, 2011 CERN.
*
* Invenio 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.
*
* Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
var gRecID1 = null;
var gRecID2 = null;
var gRecord2Mode = 'recid'; //'recid', 'file', 'revision'
var gResultListMode = 'search'; //null, 'search', 'revisions'
var gSearchResults = [[], [], []];
var gSearchResultsIndex = -1;
var gRevisions = [[], []];
var gRevisionsIndex = -1;
var gHash;
var gHASH_CHECK_INTERVAL = 150;
var gHashCheckTimerID;
var gMsgTimeout;
$(document).ready( function(){
initAJAX();
initPanel();
initContent();
gHashCheckTimerID = setInterval(initStateFromHash, gHASH_CHECK_INTERVAL);
});
function showMessage(msgType, message, timeToHide) {
hideMessage();
clearTimeout(gMsgTimeout);
$('#bibMergeMessage').removeClass();
if (msgType == 'LoadingMsg') {
message = message + ' <img src="/img/loading.gif" />';
$('#bibMergeMessage').addClass('warning');
}
else if (msgType == 'OKMsg') {
$('#bibMergeMessage').addClass('warninggreen');
}
else if (msgType == 'ErrorMsg') {
$('#bibMergeMessage').addClass('warningred');
}
//$('#bibMergeMessage').html(message).show('drop','',500);
$('#bibMergeMessage').html(message).show();
//hide the message after some milliseconds if the parameter is set
if (typeof(timeToHide) != 'undefined')
hideMessage(timeToHide);
}
function hideMessage(timeout) {
if (typeof(timeout) != 'undefined') { //check if parameter was defined
gMsgTimeout = setTimeout(function(){
$('#bibMergeMessage').removeClass().hide().fadeOut();
}, timeout);
}
else //if parameter 'timeout' not set, then hide without delay
$('#bibMergeMessage').removeClass().hide().fadeOut();
}
function isValidRecid1(recid) {
var recnum = parseInt(recid);
if (isNaN(recnum) || recnum < 1 || recid.indexOf('.')>0)
return false;
return true;
}
function getRecid2Mode(recid) {
if (isValidRecid1(recid))
return 'recid';
else if ( isRevisionID(recid) )
return 'revision';
else if (recid=='tmp')
return 'tmpfile';
else if (recid=='none')
return 'none';
else
return false;
}
function initStateFromHash() {
if (window.location.hash == gHash)
return;
gHash = window.location.hash;
if (gHash == '') {
$('#bibMergeContent').html('Select two records to be compared from the side panel.');
return;
}
var parsedHash = deserializeHash(gHash);
if (parsedHash.recid1 && isValidRecid1(parsedHash.recid1)==true && parsedHash.recid2 && getRecid2Mode(parsedHash.recid2)!=false) {
ajaxGetRecordCompare(parsedHash.recid1, parsedHash.recid2);
return;
}
// if wrong parameters where given in the url:
$('#bibMergeContent').html('INVALID URL PARAMETERS');
}
function deserializeHash(aHash) {
var hashElements = {};
var args = aHash.slice(1).split('&');
var tmpArray;
for (var i=0, n=args.length; i<n; i++){
tmpArray = args[i].split('=');
if (tmpArray.length == 2)
hashElements[tmpArray[0]] = tmpArray[1];
}
return hashElements;
}
function changeAndSerializeHash(updateData) {
clearTimeout(gHashCheckTimerID);
gHash = '#';
for (var key in updateData)
gHash += key.toString() + '=' + updateData[key].toString() + '&';
gHash = gHash.slice(0, -1);
window.location.hash = gHash;
gHashCheckTimerID = setInterval(initStateFromHash, gHASH_CHECK_INTERVAL);
}
function initContent() {
initFieldGroupHeaders(".bibMergeHeaderFieldnum"); //initialize all of them
$('#bibMergeContent a').live('click', resetDiffs);
$('.bibMergeFieldGroupRefresh').live('click', onclickFieldGroupRefresh);
//$('.bibMergeFieldGroupDiff').live('click', onclickFieldGroupRefresh);
$('.bibMergeFieldGroupMerge').live('click', onclickFieldGroupMerge)
$('.bibMergeFieldGroupMergeNC').live('click', onclickFieldGroupMerge)
$('.bibMergeFieldReplace').live('click', onclickFieldReplace);
$('.bibMergeFieldAdd').live('click', onclickFieldAdd);
$('.bibMergeFieldDelete').live('click', onclickFieldDelete);
$('.bibMergeFieldMerge').live('click', onclickFieldMerge);
$('.bibMergeSubfieldDelete').live('click', onclickSubfieldDelete);
$('.bibMergeSubfieldReplace').live('click', onclickSubfieldReplace);
$('.bibMergeSubfieldAdd').live('click', onclickSubfieldAdd);
$('.bibMergeFieldGroupDiff').live('click', onclickSubfieldDiff);
$('#bibMergeContent a:not([class])').live('click', notImplemented);
}
function resetDiffs() {
$('#bibMergeContent td:has(span.bibMergeDiffSpanSame, span.bibMergeDiffSpanIns, span.bibMergeDiffSpanDel, span.bibMergeDiffSpanSub, )').each(function (i) {
var subfield_value = $(this).text();
$(this).html( subfield_value );
});
}
function getSubfieldInfo(subfield) {
var sfinfo = {}; //result
sfinfo.sfindex1 = -1;
sfinfo.sfindex2 = -1;
sfinfo.subfield_lines = 0;
var currTR = subfield.parents('tr');
var currSF1id = currTR.children('td').eq(1).attr('id');
while(!currSF1id) {
sfinfo.subfield_lines++;
if ( currTR.children('td').eq(1).text() != '')
sfinfo.sfindex1++;
if ( currTR.children('td').eq(3).text() != '')
sfinfo.sfindex2++;
currTR = currTR.prev('tr');
currSF1id = currTR.children('td').eq(1).attr('id');
}
sfinfo.field1id = currSF1id;
sfinfo.field2id = currTR.children('td').eq(3).attr('id');
sfinfo.numOfSubfields1 = sfinfo.sfindex1 + 1;
sfinfo.numOfSubfields2 = sfinfo.sfindex2 + 1;
currTR = subfield.parents('tr');
currTR = currTR.next('tr');
while( currTR.size() > 0 && !currTR.children('td').eq(1).attr('id')) {
sfinfo.subfield_lines++;
if ( currTR.children('td').eq(1).text() != '')
sfinfo.numOfSubfields1++;
if ( currTR.children('td').eq(3).text() != '')
sfinfo.numOfSubfields2++;
currTR = currTR.next('tr');
}
return sfinfo;
}
function onclickSubfieldDiff() {
var sfinfo = getSubfieldInfo( $(this) );
var currTR = $(this).parents('tr'); //the current row of the table
var value1 = currTR.children('td').eq(1).text();
var value2 = currTR.children('td').eq(3).text();
//if one of the subfields is empty
if (value1 == '' || value2 == '')
showMessage('ErrorMsg', 'One of the subfields is missing, no difference to show', 6000);
else if (value1 == value2)
showMessage('OKMsg', 'Subfields are identical, no difference to show', 6000);
else {
//ajax request to get the diffed row from server side
var _data = {
requestType: 'diffSubfield',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldCode1: sfinfo.field1id,
fieldCode2: sfinfo.field2id,
sfindex1: sfinfo.sfindex1,
sfindex2: sfinfo.sfindex2
};
showMessage('LoadingMsg', 'Diffing subfields...');
ajaxRequest(_data, function(html){
var prevTR = currTR.prev('tr');
currTR.remove();
prevTR.after(html);
});
}
return false;
}
function onclickSubfieldDelete() {
var sfinfo = getSubfieldInfo( $(this) );
var currTR = $(this).parents('tr');
if (currTR.children('td').eq(1).text() == '') { //if subfield1 is empty
showMessage('ErrorMsg', 'Cannot delete subfield that doesn\'t exist', 6000);
return false;
}
if (sfinfo.numOfSubfields1 == 1) //if field has one subfield, then delete field
$("td#"+ sfinfo.field1id +" a.bibMergeFieldDelete").click();
else {
//ajax request to delete subfield on the server side
var _data = {
requestType: 'deleteSubfield',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldCode1: sfinfo.field1id,
fieldCode2: sfinfo.field2id,
sfindex1: sfinfo.sfindex1,
sfindex2: sfinfo.sfindex2
};
showMessage('LoadingMsg', 'Deleting subfield...');
ajaxRequest(_data, function(html){} );
//perform deletion on the client side
if (currTR.children('td').eq(3).text() == '') //if subfield2 is empty
currTR.remove();
else
currTR.children('td').eq(1).empty();
currTR.children('td.bibMergeCellSimilarityGreen').attr('class', 'bibMergeCellSimilarityRed');
}
return false;
}
function onclickSubfieldReplace() {
var sfinfo = getSubfieldInfo( $(this) );
var currTR = $(this).parents('tr');
if (currTR.children('td').eq(3).text() == '') { //if subfield2 is empty
showMessage('ErrorMsg', 'Cannot replace subfield with one that doesn\'t exist', 6000);
return false;
}
if (currTR.children('td').eq(1).text() != '') { //if subfield1 is not empty
//ajax request to replace subfield on the server side
var _data = {
requestType: 'replaceSubfield',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldCode1: sfinfo.field1id,
fieldCode2: sfinfo.field2id,
sfindex1: sfinfo.sfindex1,
sfindex2: sfinfo.sfindex2
};
showMessage('LoadingMsg', 'Replacing subfield...');
ajaxRequest(_data, function(html){} );
//perform replacement on the client side
currTR.children('td').eq(1).text( currTR.children('td').eq(3).text() );
currTR.children('td.bibMergeCellSimilarityRed').attr('class', 'bibMergeCellSimilarityGreen');
return false;
}
else {
currTR.children('td').eq(2).children('a.bibMergeSubfieldAdd').click();
}
return false;
}
function onclickSubfieldAdd() {
var sfinfo = getSubfieldInfo( $(this) );
var currTR = $(this).parents('tr');
if (currTR.children('td').eq(3).text() == '') { //if subfield2 is empty
showMessage('ErrorMsg', 'Cannot add subfield that doesn\'t exist', 6000);
return false;
}
if (sfinfo.numOfSubfields1 == 0) {//field1 doesn't exist
showMessage('ErrorMsg', 'Field in the first record doesn\'t exist. Use Add Field instead which creates a new field', 8000);
}
else {
if (currTR.children('td').eq(1).text() == '') //if subfield1 is empty
sfinfo.sfindex1++; //insertion should be before the next subfield that exists
//ajax add subfield before sfindex1 on the server side
var _data = {
requestType: 'addSubfield',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldCode1: sfinfo.field1id,
fieldCode2: sfinfo.field2id,
sfindex1: sfinfo.sfindex1,
sfindex2: sfinfo.sfindex2
};
showMessage('LoadingMsg', 'Adding subfield...');
ajaxRequest(_data, function(html){} );
//perform addition of subfield on the client side
if (currTR.children('td').eq(1).text() != '') { //if subfield1 is not empty
//create another subfield line
currTR.after( "<tr>"+ currTR.html() +"</tr>" );
currTR.next('tr').children('td').eq(3).empty();
currTR.next('tr').children('td.bibMergeCellSimilarityGreen').attr('class', 'bibMergeCellSimilarityRed');
}
currTR.children('td').eq(1).text( currTR.children('td').eq(3).text() ); //replace value
currTR.children('td.bibMergeCellSimilarityRed').attr('class', 'bibMergeCellSimilarityGreen');
}
return false;
}
function onclickFieldMerge() {
var fieldGroupDiv = $(this).parents('.bibMergeFieldGroupDiv');
var ftag = getFieldTag(fieldGroupDiv);
var fieldID1 = $(this).parents('tr').children('td').eq(1).attr('id');
var fieldID2 = $(this).parents('tr').children('td').eq(3).attr('id');
var _data = {
requestType: 'mergeField',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldTag: ftag,
fieldCode1: fieldID1,
fieldCode2: fieldID2
};
showMessage('LoadingMsg', 'Merging Field...');
ajaxRequest(_data, function(html) {
var fnum = ftag.substring(0,3);
var fieldGroupsOfTag = $("div.bibMergeFieldGroupDiv[id*='-"+fnum+"']");
var prevDiv = fieldGroupsOfTag.eq(0).prev('.bibMergeFieldGroupDiv');
fieldGroupsOfTag.remove();
if (html != '') {
prevDiv.after(html);
initFieldGroupHeaders("div.bibMergeFieldGroupDiv[id*='-"+fnum+"'] .bibMergeHeaderFieldnum");
}
});
return false;
}
function onclickFieldDelete() {
var fieldGroupDiv = $(this).parents('.bibMergeFieldGroupDiv');
var ftag = getFieldTag(fieldGroupDiv);
var fieldID1 = $(this).parents('tr').children('td').eq(1).attr('id');
var fieldID2 = $(this).parents('tr').children('td').eq(3).attr('id');
var _data = {
requestType: 'deleteField',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldTag: ftag,
fieldCode1: fieldID1,
fieldCode2: fieldID2
};
showMessage('LoadingMsg', 'Deleting Field...');
ajaxRequest(_data, function(html) {
var fnum = ftag.substring(0,3);
var fieldGroupsOfTag = $("div.bibMergeFieldGroupDiv[id*='-"+fnum+"']");
var prevDiv = fieldGroupsOfTag.eq(0).prev('.bibMergeFieldGroupDiv');
fieldGroupsOfTag.remove();
if (html != '') {
prevDiv.after(html);
initFieldGroupHeaders("div.bibMergeFieldGroupDiv[id*='-"+fnum+"'] .bibMergeHeaderFieldnum");
}
});
return false;
}
function onclickFieldAdd() {
var fieldGroupDiv = $(this).parents('.bibMergeFieldGroupDiv');
var ftag = getFieldTag(fieldGroupDiv);
var fieldID1 = $(this).parents('tr').children('td').eq(1).attr('id');
var fieldID2 = $(this).parents('tr').children('td').eq(3).attr('id');
var _data = {
requestType: 'addField',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldTag: ftag,
fieldCode1: fieldID1,
fieldCode2: fieldID2
};
showMessage('LoadingMsg', 'Adding Field...');
ajaxRequest(_data, function(html) {
var fnum = ftag.substring(0,3);
var fieldGroupsOfTag = $("div.bibMergeFieldGroupDiv[id*='-"+fnum+"']");
//fieldGroupsOfTag.hide('drop','',500);
var prevDiv = fieldGroupsOfTag.eq(0).prev('.bibMergeFieldGroupDiv');
fieldGroupsOfTag.remove();
prevDiv.after(html);
initFieldGroupHeaders("div.bibMergeFieldGroupDiv[id*='-"+fnum+"'] .bibMergeHeaderFieldnum");
});
return false;
}
function onclickFieldReplace() {
var fieldGroupDiv = $(this).parents('.bibMergeFieldGroupDiv');
var ftag = getFieldTag(fieldGroupDiv);
var fieldID1 = $(this).parents('tr').children('td').eq(1).attr('id');
var fieldID2 = $(this).parents('tr').children('td').eq(3).attr('id');
var _data = {
requestType: 'replaceField',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldTag: ftag,
fieldCode1: fieldID1,
fieldCode2: fieldID2
};
showMessage('LoadingMsg', 'Replacing Field...');
ajaxRequest(_data, function(html) {
var fnum = ftag.substring(0,3);
var fieldGroupsOfTag = $("div.bibMergeFieldGroupDiv[id*='-"+fnum+"']");
//fieldGroupsOfTag.hide('drop','',500);
var prevDiv = fieldGroupsOfTag.eq(0).prev('.bibMergeFieldGroupDiv');
fieldGroupsOfTag.remove();
prevDiv.after(html);
initFieldGroupHeaders("div.bibMergeFieldGroupDiv[id*='-"+fnum+"'] .bibMergeHeaderFieldnum");
});
return false;
}
function onclickFieldGroupMerge() {
var mergeType; //merging mode
if ( $(this).hasClass('bibMergeFieldGroupMergeNC') )
mergeType = 'mergeNCFieldGroup';
else
mergeType = 'mergeFieldGroup';
var fieldGroupDiv = $(this).parents('.bibMergeFieldGroupDiv');
var ftag = getFieldTag(fieldGroupDiv);
var _data = {
requestType: mergeType,
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldTag: ftag
};
showMessage('LoadingMsg', 'Merging...');
ajaxRequest(_data, function(html) {
var fnum = ftag.substring(0,3);
var fieldGroupsOfTag = $("div.bibMergeFieldGroupDiv[id*='-"+fnum+"']");
//fieldGroupsOfTag.hide('drop','',500);
var prevDiv = fieldGroupsOfTag.eq(0).prev('.bibMergeFieldGroupDiv');
fieldGroupsOfTag.remove();
prevDiv.after(html);
initFieldGroupHeaders("div.bibMergeFieldGroupDiv[id*='-"+fnum+"'] .bibMergeHeaderFieldnum");
});
return false;
}
function onclickFieldGroupRefresh() {
var refType;
if ( $(this).hasClass('bibMergeFieldGroupDiff') )
refType = 'getFieldGroupDiff';
else
refType = 'getFieldGroup';
var fieldGroupDiv = $(this).parents('.bibMergeFieldGroupDiv');
var ftag = getFieldTag(fieldGroupDiv);
var _data = {
requestType: refType,
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode,
fieldTag: ftag
};
showMessage('LoadingMsg', 'Please wait...');
ajaxRequest(_data, function(html) {
var fnum = ftag.substring(0,3);
var fieldGroupsOfTag = $("div.bibMergeFieldGroupDiv[id*='-"+fnum+"']");
//fieldGroupsOfTag.hide('drop','',500);
var prevDiv = fieldGroupsOfTag.eq(0).prev('.bibMergeFieldGroupDiv');
fieldGroupsOfTag.remove();
prevDiv.after(html);
initFieldGroupHeaders("div.bibMergeFieldGroupDiv[id*='-"+fnum+"'] .bibMergeHeaderFieldnum");
});
return false;
}
function initFieldGroupHeaders(selector) {
$(selector).toggle(
function() {
$(this).parents(".bibMergeFieldGroupDiv").find(".bibMergeFieldTable").hide();
return false; },
function() {
$(this).parents(".bibMergeFieldGroupDiv").find(".bibMergeFieldTable").show();
return false;
}
);
}
function initPanel() {
$('#bibMergeRecInput1').focus();
$('#bibMergeMessage').hide();
onclickSelectSearch();
$('#bibMergeSelectSearch').click(onclickSelectSearch);
$('#bibMergeSelectDedupe').click(onclickSelectDedupe);
$('#bibMergeSelectRevisions').click(onclickSelectRevisions);
$('#bibMergeGetPrev').click(onclickGetPrev);
$('#bibMergeGetNext').click(onclickGetNext);
$('#bibMergeBtnCompare').click(compareRecords);
$('#bibMergeBtnSubmit').click(onclickSubmitButton);
$('#bibMergeBtnCancel').click(onclickCancelButton);
$('#bibMergeBtnSearch').click(onclickSearchButton);
$('#bibMergeRecCopy').click(onclickRecCopy);
$('#bibMergeRecMerge').click(onclickRecMerge);
$('#bibMergeRecMergeNC').click(onclickRecMergeNC);
$('#bibMergeLinkToBibEdit1').click(onclickLinkToBibEdit1);
$('#bibMergeLinkToBibEdit2').click(onclickLinkToBibEdit2);
$('.bibMergeMenuSectionHeader').toggle(menuSectionHeaderClose, menuSectionHeaderOpen);
// $('#bibMergeMethodSelect').change(onchangeMethodSelect);
onchangeMethodSelect();
$('#bibMergeSelectListRow select option').live('click', onclickSearchResult);
// Initialize menu positioning (poll for scrolling).
setInterval(positionMenu, 250);
$('#bibMergeRecInput1').keypress( function(event) {
if (event.keyCode == 13) //on press 'enter'
$('#bibMergeRecInput2').focus();
});
$('#bibMergeRecInput2').keypress( function(event) {
if (event.keyCode == 13) //on press 'enter'
compareRecords();
});
$('#bibMergeSearchInput').keypress( function(event) {
if (event.keyCode == 13) //on press 'enter'
$('#bibMergeBtnSearch').click();
});
$('#bibMergeMenuSectionCandidates div.bibMergeMenuSectionHeader').click();
$('#bibMergeMenuSectionActions div.bibMergeMenuSectionHeader').click();
}
function menuSectionHeaderOpen() {
$(this).parents('div.bibMergeMenuSection').find('table').show();
$(this).find('img').attr('src', "/img/bullet_toggle_minus.png");
}
function menuSectionHeaderClose() {
$(this).parents('div.bibMergeMenuSection').find('table').hide();
$(this).find('img').attr('src', "/img/bullet_toggle_plus.png");
}
function onclickRecCopy() {
var answer = confirm("Do you want to replace all fields of record1 with those of record2?");
if (!answer)
return false;
var _data = {
requestType: 'recCopy',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode
};
showMessage('LoadingMsg', 'Copying record2 to record1');
ajaxRequest(_data, function(html){
$('#bibMergeContent').html(html);
initFieldGroupHeaders('.bibMergeHeaderFieldnum'); //initialize all of them
});
return false;
}
function onclickRecMerge() {
var answer = confirm("Do you want to merge fields of record1 and record2 into record1?");
if (!answer)
return false;
var _data = {
requestType: 'recMerge',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode
};
showMessage('LoadingMsg', 'Merging record2 with record1');
ajaxRequest(_data, function(html){
$('#bibMergeContent').html(html);
initFieldGroupHeaders('.bibMergeHeaderFieldnum'); //initialize all of them
});
return false;
}
function onclickRecMergeNC() {
var answer = confirm("Do you want to merge non-conflicting fields of record1 and record2 into record1?");
if (!answer)
return false;
var _data = {
requestType: 'recMergeNC',
recID1: gRecID1,
recID2: gRecID2,
record2Mode: gRecord2Mode
};
showMessage('LoadingMsg', 'Merging record2 with record1');
ajaxRequest(_data, function(html){
$('#bibMergeContent').html(html);
initFieldGroupHeaders('.bibMergeHeaderFieldnum'); //initialize all of them
});
return false;
}
function buildSelectList(mode) {
var _list = null;
var _tmp;
var _optionsHtml = "";
if (mode=='forSearch') {
_list = gSearchResults;
for (var i=0, n=_list[0].length; i<n; i++)
_optionsHtml += "<option title=\""+ _list[2][i] +"\">"+ _list[1][i] +"</option>\n";
}
else {
_list = gRevisions;
for (var i=0, n=_list[0].length; i<n; i++)
_optionsHtml += "<option>"+ _list[1][i] +"</option>\n";
}
$('#bibMergeSelectListRow select').html(_optionsHtml);
}
function managePrevNextButtons(action) {
var _index;
var _list;
// take values from the correct global variable
if (gResultListMode == 'search') {
_index = gSearchResultsIndex;
_list = gSearchResults[0];
}
else if (gResultListMode == 'revisions') {
_index = gRevisionsIndex;
_list = gRevisions[0];
}
// perform requested action
if (action == 'hide') {
$('#bibMergeGetPrev').hide();
$('#bibMergeResultIndex').hide();
$('#bibMergeGetNext').hide();
}
else if (action == 'show') {
var _indexstr = '-';
if (_index >= 0)
_indexstr = _index+1;
$('#bibMergeResultIndex').html( _indexstr+'/'+_list.length);
$('#bibMergeGetPrev').show();
$('#bibMergeResultIndex').show();
$('#bibMergeGetNext').show();
}
else if (action == 'next') {
if (_index+1 < _list.length) {
_index++;
$('#bibMergeResultIndex').html( _index+1+'/'+_list.length);
$('#bibMergeRecInput2').val( _list[_index] );
$('#bibMergeSelectList option:eq('+ _index +')').attr('selected', 'selected');
compareRecords();
}
else
showMessage('ErrorMsg', 'Reached the end of list of candidate items', 6000);
}
else if (action == 'prev') {
if (_index-1 >= 0) {
_index--;
$('#bibMergeResultIndex').html( _index+1+'/'+_list.length);
$('#bibMergeRecInput2').val( _list[_index] );
$('#bibMergeSelectList option:eq('+ _index +')').attr('selected', 'selected');
compareRecords();
}
else
showMessage('ErrorMsg', 'Reached the end of list of candidate items', 6000);
}
//update the correct global variable
if (gResultListMode == 'search')
gSearchResultsIndex = _index;
else if (gResultListMode == 'revisions')
gRevisionsIndex = _index;
}
function ajaxRequestRevisions() {
var recid = gRecID1;
if (recid == null)
if ($('#bibMergeRecInput1').attr('value') != "")
recid = $('#bibMergeRecInput1').attr('value');
else {
showMessage('ErrorMsg', 'Select a record to get the revisions from', 6000);
return;
}
var _data = {
requestType: 'searchRevisions',
recID1: recid
};
showMessage('LoadingMsg', 'Retrieving revisions...');
$.ajax({
data: { jsondata: JSON.stringify(_data) },
success: function(json){
if (json['resultCode'] != 0){
showMessage('ErrorMsg', json['resultText'], 6000);
}
else {
gRevisions = json['results'];
gRevisionsIndex = -1;
buildSelectList('forRevisions');
managePrevNextButtons('show');
if (gRevisions.length == 0)
showMessage('ErrorMsg', 'No revisions found', 6000);
else
showMessage('OKMsg', json['resultText'], 6000);
}
}
});
}
function onclickSearchButton() {
var _query = $('#bibMergeSearchInput').attr('value');
var _data = {
requestType: 'searchCandidates',
query: _query
};
$.ajax({
data: {
jsondata: JSON.stringify(_data)
},
dataType: 'json',
success: function(json) {
if (json['resultCode'] != 0){
showMessage('ErrorMsg', json['resultText'], 6000);
}
else {
gSearchResults = json['results'];
gSearchResultsIndex = -1;
buildSelectList('forSearch');
managePrevNextButtons('show');
if (gSearchResults.length == 0)
showMessage('ErrorMsg', 'No results found', 6000);
else
showMessage('OKMsg', json['resultText'], 6000);
}
}
});
return false; //for the link not to be followed
}
function onclickGetPrev() {
managePrevNextButtons('prev');
return false;
}
function onclickGetNext() {
managePrevNextButtons('next');
return false;
}
function onclickSelectSearch() {
$('.bibMergeSelectListSelected').removeClass();
$('#bibMergeSelectSearch').addClass('bibMergeSelectListSelected');
$('#bibMergeSearchRow').show();
$('#bibMergeSelectListRow').show();
buildSelectList('forSearch');
gResultListMode = 'search';
managePrevNextButtons('show');
return false;
}
function onclickSelectDedupe() {
onclickSelectClose();
notImplemented();
return false;
}
function onclickSelectRevisions() {
$('.bibMergeSelectListSelected').removeClass();
$('#bibMergeSelectRevisions').addClass('bibMergeSelectListSelected');
$('#bibMergeSearchRow').hide();
$('#bibMergeSelectListRow').show();
//if list is empty or first result doesn't start with recid1
if (gRevisions.length==0 || gRevisions[0].indexOf(gRecID1+'.')==-1) {
ajaxRequestRevisions();
}
buildSelectList('forRevisions');
gResultListMode = 'revisions';
managePrevNextButtons('show');
return false;
}
function onclickSelectClose() {
$('.bibMergeSelectListSelected').removeClass();
$('#bibMergeSearchRow').hide();
$('#bibMergeSelectListRow').hide();
gResultListMode = null;
managePrevNextButtons('hide');
return false;
}
function onclickSearchResult() {
//find index of selected option
var _index = 0;
var currOpt = $(this).prev('option');
while(currOpt.html()!=null) {
_index++;
currOpt = currOpt.prev('option');
}
//set the value of record2 input field
if (gResultListMode == 'search') {
$('#bibMergeRecInput2').val( gSearchResults[0][_index] );
gSearchResultsIndex = _index;
}
else if (gResultListMode == 'revisions') {
$('#bibMergeRecInput2').val( gRevisions[0][_index] );
gRevisionsIndex = _index;
}
managePrevNextButtons('show');
compareRecords();
}
function onchangeMethodSelect() {
option = $("#bibMergeMethodSelect :selected").val();
switch(option) {
case "(none)":
$("#bibMergeSearchPanel").hide();
break;
case "Search":
$("#bibMergeSearchPanel").show();
break;
case "Revisions":
break;
}
}
function compareRecords() {
var recid1 = $('#bibMergeRecInput1').attr('value');
var recid2 = $('#bibMergeRecInput2').attr('value');
if (recid2 == "")
recid2 = 'none';
ajaxGetRecordCompare(recid1, recid2);
}
function ajaxGetRecordCompare(_recid1, _recid2) {
// validity check
if (!isValidRecid1(_recid1)) {
showMessage('ErrorMsg', 'Invalid record1', 6000);
return;
}
var _mode = getRecid2Mode(_recid2);
if (_mode == false) {
showMessage('ErrorMsg', 'Invalid record2', 6000);
return;
}
//ajax request
var _data = {
requestType: 'getRecordCompare',
recID1: _recid1,
recID2: _recid2,
record2Mode: _mode
};
showMessage('LoadingMsg', 'Please wait...');
panelDisabled(true);
ajaxRequest(_data, function(html) {
$('#bibMergeContent').html(html);
$('#bibMergeRecInput1').val(_recid1);
$('#bibMergeRecInput2').val(_recid2);
changeAndSerializeHash({recid1: _recid1, recid2: _recid2});
gRecID1 = _recid1;
gRecID2 = _recid2;
initFieldGroupHeaders('.bibMergeHeaderFieldnum'); //initialize all of them
panelDisabled(false);
});
}
function isRevisionID(str) {
if (str.indexOf('.') > -1) {
var _array = str.split('.');
if (_array.length==2 && !isNaN(_array[0]) && !isNaN(_array[1]) && _array[1].length==14)
return true;
}
return false;
}
function onclickSubmitButton() {
var checkbox = $('#bibMergeDupeCheckbox').attr('checked');
var _data = {
requestType: 'submit',
recID1: gRecID1
};
if (checkbox == true)
_data['duplicate'] = gRecID2;
showMessage('LoadingMsg', 'Submitting...');
ajaxRequest(_data, function(html){
window.location.hash = '';
});
if (checkbox == true)
$('#bibMergeDupeCheckbox').attr('checked', false);
}
function onclickCancelButton() {
var _data = {
requestType: 'cancel',
recID1: gRecID1
};
showMessage('LoadingMsg', 'Cancelling...');
ajaxRequest(_data, function(html){
window.location.hash = '';
});
}
function ajaxRequest(data, onSuccessFunc){
/* Create Ajax request. */
$.ajax({
data: { jsondata: JSON.stringify(data) },
success: function(json){
if (json['resultCode'] != 0){
showMessage('ErrorMsg', json['resultText'], 6000);
}
else {
onSuccessFunc(json['resultHtml']);
showMessage('OKMsg', json['resultText'], 6000);
}
}
});
}
function initAJAX() {
$.ajaxSetup(
{ cache: false,
dataType: 'json',
error: onError,
type: 'POST'
}
);
}
function positionMenu(){
/*
* Dynamically position menu based on vertical scroll distance.
*/
var newYscroll = $(document).scrollTop();
// Only care if there has been some major scrolling.
if (Math.abs(newYscroll - positionMenu.yScroll) > 10){
// If scroll distance is less then 200px, position menu in sufficient
// distance from header.
if (newYscroll < 200)
$('#bibMergePanel').animate({
'top': 220 - newYscroll}, 'fast');
// If scroll distance has crossed 200px, fix menu 50px from top.
else if (positionMenu.yScroll < 200 && newYscroll > 200)
$('#bibMergePanel').animate({
'top': 50}, 'fast');
positionMenu.yScroll = newYscroll;
}
}
// Last Y-scroll value
positionMenu.yScroll = 0;
function notImplemented() {
showMessage('ErrorMsg', 'Please ask the developer to hurry up and implement this feature!', 6000);
return false;
}
function onError(XHR, textStatus, errorThrown) {
panelDisabled(false);
$('#bibMergeContent').html('Request completed with status ' + textStatus
+ '\nResult: ' + XHR.responseText
+ '\nError: ' + errorThrown);
}
function getFieldTag(fieldGroupDiv) {
return fieldGroupDiv.children(":first-child").children(":first-child").text();
}
function onclickLinkToBibEdit1() {
if (gHash!='' && gRecID1!=null)
- window.location = '/record/edit/#state=edit&recid='+gRecID1;
+ window.location = '/'+ gSITE_RECORD +'/edit/#state=edit&recid='+gRecID1;
else
showMessage('ErrorMsg', 'A valid record id must be selected', 6000);
return false;
}
function onclickLinkToBibEdit2() {
if (gHash!='' && gRecord2Mode=='recid' && gRecID2!=null)
- window.location = '/record/edit/#state=edit&recid='+gRecID2;
+ window.location = '/'+ gSITE_RECORD +'/edit/#state=edit&recid='+gRecID2;
else
showMessage('ErrorMsg', 'A valid record id must be selected', 6000);
return false;
}
function panelDisabled(disabled) {
if (disabled == true)
$('#bibMergePanel').find('button, input, optgroup, option, select, textarea').attr('disabled', true);
else
$('#bibMergePanel').find('button, input, optgroup, option, select, textarea').removeAttr('disabled');
}
diff --git a/modules/bibmerge/lib/bibmerge_webinterface.py b/modules/bibmerge/lib/bibmerge_webinterface.py
index 4993dee39..1a524d0ed 100644
--- a/modules/bibmerge/lib/bibmerge_webinterface.py
+++ b/modules/bibmerge/lib/bibmerge_webinterface.py
@@ -1,148 +1,148 @@
## This file is part of Invenio.
## Copyright (C) 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0103
"""Invenio BibMerge Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import sys
if sys.hexversion < 0x2060000:
try:
import simplejson as json
except ImportError:
# Okay, no Ajax app will be possible, but continue anyway,
# since this package is only recommended, not mandatory.
pass
else:
import json
from invenio.access_control_engine import acc_authorize_action
-from invenio.config import CFG_SITE_LANG, CFG_SITE_URL
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, CFG_SITE_RECORD
from invenio.search_engine import guess_primary_collection_of_a_record
from invenio.webpage import page
from invenio.webuser import getUid, page_not_authorized, collect_user_info
from invenio.urlutils import redirect_to_url
from invenio.webinterface_handler import WebInterfaceDirectory, wash_urlargd
from invenio.bibedit_utils import json_unicode_to_utf8
from invenio.bibmerge_engine import perform_request_init, \
perform_request_ajax
navtrail = (' <a class="navtrail" href=\"%s/help/admin\">Admin Area</a> '
) % CFG_SITE_URL
class WebInterfaceMergePages(WebInterfaceDirectory):
"""Defines the set of /merge pages."""
_exports = ['']
def __init__(self, recid=None):
"""Initialize."""
self.recid = recid
def index(self, req, form):
"""Handle all BibMerge requests.
The responsibilities of this functions are:
* JSON decoding and encoding.
* Redirection, if necessary.
* Authorization.
* Calling the appropriate function from the engine.
"""
# If it is an Ajax request, extract any JSON data.
ajax_request, recid1, recid2 = False, None, None
argd = wash_urlargd(form, {'ln': (str, CFG_SITE_LANG)})
if form.has_key('jsondata'):
json_data = json.loads(str(form['jsondata']))
# Deunicode all strings (Invenio doesn't have unicode
# support).
json_data = json_unicode_to_utf8(json_data)
ajax_request = True
json_response = {}
if json_data.has_key('recID1'):
recid1 = json_data['recID1']
if json_data.has_key('recID2'):
recid2 = json_data['recID2']
# Authorization.
user_info = collect_user_info(req)
if user_info['email'] == 'guest':
# User is not logged in.
if not ajax_request:
# Do not display the introductory recID selection box to guest
# users (as it used to be with v0.99.0):
auth_code, auth_message = acc_authorize_action(req, 'runbibmerge')
referer = '/merge/'
return page_not_authorized(req=req, referer=referer,
text=auth_message, navtrail=navtrail)
else:
# Session has most likely timed out.
json_response.update({'resultCode': 1,
'resultText': 'Error: Not logged in'})
return json.dumps(json_response)
elif self.recid:
# Handle RESTful call by storing recid and redirecting to
# generic URL.
- redirect_to_url(req, '%s/record/merge/' % CFG_SITE_URL )
+ redirect_to_url(req, '%s/%s/merge/' % (CFG_SITE_URL, CFG_SITE_RECORD) )
if recid1 is not None:
# Authorize access to record 1.
auth_code, auth_message = acc_authorize_action(req, 'runbibmerge',
collection=guess_primary_collection_of_a_record(recid1))
if auth_code != 0:
json_response.update({'resultCode': 1, 'resultText': 'No access to record %s' % recid1})
return json.dumps(json_response)
if recid2 is not None:
# Authorize access to record 2.
auth_code, auth_message = acc_authorize_action(req, 'runbibmerge',
collection=guess_primary_collection_of_a_record(recid2))
if auth_code != 0:
json_response.update({'resultCode': 1, 'resultText': 'No access to record %s' % recid2})
return json.dumps(json_response)
# Handle request.
uid = getUid(req)
if not ajax_request:
# Show BibEdit start page.
body, errors, warnings = perform_request_init()
metaheaderadd = """<script type="text/javascript" src="%(site)s/js/jquery/jquery.min.js"></script>
<script type="text/javascript" src="%(site)s/js/jquery/json2.js"></script>
<script type="text/javascript" src="%(site)s/js/bibmerge_engine.js"></script>""" % {'site': CFG_SITE_URL}
title = 'Record Merger'
return page(title = title,
metaheaderadd = metaheaderadd,
body = body,
errors = errors,
warnings = warnings,
uid = uid,
language = argd['ln'],
navtrail = navtrail,
lastupdated = __lastupdated__,
req = req)
else:
# Handle AJAX request.
json_response = perform_request_ajax(req, uid, json_data)
return json.dumps(json_response)
def __call__(self, req, form):
"""Redirect calls without final slash."""
- redirect_to_url(req, '%s/record/merge/' % CFG_SITE_URL)
+ redirect_to_url(req, '%s/%s/merge/' % (CFG_SITE_URL, CFG_SITE_RECORD))
diff --git a/modules/bibrank/lib/bibrank_regression_tests.py b/modules/bibrank/lib/bibrank_regression_tests.py
index caa7234bc..7527ffea1 100644
--- a/modules/bibrank/lib/bibrank_regression_tests.py
+++ b/modules/bibrank/lib/bibrank_regression_tests.py
@@ -1,173 +1,173 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibRank Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import CFG_SITE_URL
+from invenio.config import CFG_SITE_URL, CFG_SITE_RECORD
from invenio.dbquery import run_sql
from invenio.testutils import make_test_suite, run_test_suite, \
test_web_page_content, merge_error_messages
class BibRankWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibRank web pages whether they are up or not."""
def test_rank_by_word_similarity_pages_availability(self):
"""bibrank - availability of ranking search results pages"""
baseurl = CFG_SITE_URL + '/search'
_exports = ['?p=ellis&r=wrd']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_similar_records_pages_availability(self):
"""bibrank - availability of similar records results pages"""
baseurl = CFG_SITE_URL + '/search'
_exports = ['?p=recid%3A18&rm=wrd']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
class BibRankIntlMethodNames(unittest.TestCase):
"""Check BibRank I18N ranking method names."""
def test_i18n_ranking_method_names(self):
"""bibrank - I18N ranking method names"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/collection/Articles%20%26%20Preprints?as=1',
expected_text="times cited"))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/collection/Articles%20%26%20Preprints?as=1',
expected_text="journal impact factor"))
class BibRankWordSimilarityRankingTest(unittest.TestCase):
"""Check BibRank word similarity ranking tools."""
def test_search_results_ranked_by_similarity(self):
"""bibrank - search results ranked by word similarity"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis&rm=wrd&of=id',
expected_text="[8, 10, 11, 12, 47, 17, 13, 16, 9, 14, 18, 15]"))
def test_similar_records_link(self):
"""bibrank - 'Similar records' link"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A77&rm=wrd&of=id',
expected_text="[84, 96, 95, 85, 77]"))
class BibRankCitationRankingTest(unittest.TestCase):
"""Check BibRank citation ranking tools."""
def test_search_results_ranked_by_citations(self):
"""bibrank - search results ranked by number of citations"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?cc=Articles+%26+Preprints&p=Klebanov&rm=citation&of=id',
expected_text="[85, 77, 84]"))
def test_search_results_ranked_by_citations_verbose(self):
"""bibrank - search results ranked by number of citations, verbose output"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?cc=Articles+%26+Preprints&p=Klebanov&rm=citation&verbose=2',
expected_text="find_citations retlist [[85, 0], [77, 2], [84, 3]]"))
def test_detailed_record_citations_tab(self):
"""bibrank - detailed record, citations tab"""
self.assertEqual([],
- test_web_page_content(CFG_SITE_URL + '/record/79/citations',
+ test_web_page_content(CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/79/citations',
expected_text=["Cited by: 1 records",
"Co-cited with: 2 records"]))
class BibRankExtCitesTest(unittest.TestCase):
"""Check BibRank citation ranking tools with respect to the external cites."""
def _detect_extcite_info(self, extcitepubinfo):
"""
Helper function to return list of recIDs citing given
extcitepubinfo. Could be move to the business logic, if
interesting for other callers.
"""
res = run_sql("""SELECT id_bibrec FROM rnkCITATIONDATAEXT
WHERE extcitepubinfo=%s""",
(extcitepubinfo,))
return [int(x[0]) for x in res]
def test_extcite_via_report_number(self):
"""bibrank - external cites, via report number"""
# The external paper hep-th/0112258 is cited by 9 demo
# records: you can find out via 999:"hep-th/0112258", and we
# could eventually automatize this query, but it is maybe
# safer to leave it manual in case queries fail for some
# reason.
test_case_repno = "hep-th/0112258"
test_case_repno_cited_by = [77, 78, 81, 82, 85, 86, 88, 90, 91]
self.assertEqual(self._detect_extcite_info(test_case_repno),
test_case_repno_cited_by)
def test_extcite_via_publication_reference(self):
"""bibrank - external cites, via publication reference"""
# The external paper "J. Math. Phys. 4 (1963) 915" does not
# have any report number, and is cited by 1 demo record.
test_case_pubinfo = "J. Math. Phys. 4 (1963) 915"
test_case_pubinfo_cited_by = [90]
self.assertEqual(self._detect_extcite_info(test_case_pubinfo),
test_case_pubinfo_cited_by)
def test_intcite_via_report_number(self):
"""bibrank - external cites, no internal papers via report number"""
# The internal paper hep-th/9809057 is cited by 2 demo
# records, but it also exists as a demo record, so it should
# not be found in the extcite table.
test_case_repno = "hep-th/9809057"
test_case_repno_cited_by = []
self.assertEqual(self._detect_extcite_info(test_case_repno),
test_case_repno_cited_by)
def test_intcite_via_publication_reference(self):
"""bibrank - external cites, no internal papers via publication reference"""
# The internal paper #18 has only pubinfo, no repno, and is
# cited by internal paper #96 via its pubinfo, so should not
# be present in the extcite list:
test_case_repno = "Phys. Lett., B 151 (1985) 357"
test_case_repno_cited_by = []
self.assertEqual(self._detect_extcite_info(test_case_repno),
test_case_repno_cited_by)
TEST_SUITE = make_test_suite(BibRankWebPagesAvailabilityTest,
BibRankIntlMethodNames,
BibRankWordSimilarityRankingTest,
BibRankCitationRankingTest,
BibRankExtCitesTest)
if __name__ == "__main__":
run_test_suite(TEST_SUITE, warn_user=True)
diff --git a/modules/bibsword/lib/bibsword_client_templates.py b/modules/bibsword/lib/bibsword_client_templates.py
index bceac966b..15356bc75 100644
--- a/modules/bibsword/lib/bibsword_client_templates.py
+++ b/modules/bibsword/lib/bibsword_client_templates.py
@@ -1,1105 +1,1106 @@
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
'''
BibSWORD Client Templates
'''
-from invenio.config import CFG_SITE_URL, CFG_SITE_NAME
+from invenio.config import CFG_SITE_URL, CFG_SITE_NAME, CFG_SITE_RECORD
class BibSwordTemplate:
'''
This class contains attributes and methods that allows to display all
information used by the BibSword web user interface. Theses informations
are form, validation or error messages
'''
def __init__(self):
''' No init necessary for this class '''
#---------------------------------------------------------------------------
# BibSword WebSubmit Interface
#---------------------------------------------------------------------------
def tmpl_display_submit_ack(self, remote_id, link):
'''
This method generate the html code that displays the acknoledgement
message after the submission of a record.
@param remote_id: id of the record given by arXiv
@param link: links to modify or consult submission
@return: string containing the html code
'''
html = ''
html += '''<h1>Success !</h1>'''
html += '''<p>The record has been successfully pushed to arXiv ! <br />''' \
'''You will get an email once it will be accepted by ''' \
'''arXiv moderator.</p>'''
html += '''<p>The arXiv id of the submission is: <b>%s</b></p>''' % \
remote_id
html += '''<p><a href="www.arxiv.org/user">Manage your submission</a></p>'''
return html
#---------------------------------------------------------------------------
# BibSword Administrator Interface
#---------------------------------------------------------------------------
def tmpl_display_admin_page(self, submissions, first_row, last_row,
total_rows, is_prev, is_last, offset,
error_messages=None):
'''
format the html code that display the submission table
@param submissions: list of all submissions and their status
@return: html code to be displayed
'''
if error_messages == None:
error_messages = []
body = '''
<form method="post" enctype="multipart/form-data" accept-charset="UTF-8" action="/bibsword">
%(error_message)s
<input type="hidden" name="status" value="display_submission"/>
<input type="hidden" name="first_row" value="%(first_row)s"/>
<input type="hidden" name="last_row" value="%(last_row)s"/>
<input type="hidden" name="total_rows" value="%(total_rows)s" />
<input type="submit" name="submit" value="New submission"/><br/>
<br />
<input type="submit" name="submit" value="Refresh all"/><br/>
<br />
Display
<select name="offset">
<option value="5" %(selected_1)s>5</option>
<option value="10" %(selected_2)s>10</option>
<option value="25" %(selected_3)s>25</option>
<option value="50" %(selected_4)s>50</option>
<option value=%(total_rows)s %(selected_5)s>all</option>
</select>
rows per page <input type="submit" name="submit" value="Select" /><br />
<br />
<input type="submit" name="submit" value="First" %(is_prev)s/>
<input type="submit" name="submit" value="Prev" %(is_prev)s/>
Pages %(first_row)s - %(last_row)s / %(total_rows)s
<input type="submit" name="submit" value="Next" %(is_last)s/>
<input type="submit" name="submit" value="Last" %(is_last)s/><br/>
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="7" bgcolor="#e6e6fa">
<h2>Submission state</h2>
</td>
</tr>
<tr>
<td align="center" bgcolor="#e6e6fa"><b>Remote server</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Submitter</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Record number</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Remote id</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Status</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Dates</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Links</b></td>
</tr>
%(submissions)s
</table>
</form>''' % {
'error_message': \
self.display_error_message_row(error_messages),
'table_width' : '100%',
'first_row' : first_row,
'last_row' : last_row,
'total_rows' : total_rows,
'is_prev' : is_prev,
'is_last' : is_last,
'selected_1' : offset[0],
'selected_2' : offset[1],
'selected_3' : offset[2],
'selected_4' : offset[3],
'selected_5' : offset[4],
'submissions' : self.fill_submission_table(submissions)
}
return body
def tmpl_display_remote_server_info(self, server_info):
'''
Display a table containing all server informations
@param server_info: tuple containing all server infos
@return: html code for the table containing infos
'''
body = '''<table width="%(table_width)s">\n''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">ID</td>\n''' \
''' <td>%(server_id)s</td>\n''' \
''' </tr>\n ''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">Name</td>\n''' \
''' <td>%(server_name)s</td>\n''' \
''' </tr>\n ''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">Host</td>\n''' \
''' <td>%(server_host)s</td>\n''' \
''' </tr>\n ''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">Username</td>\n''' \
''' <td>%(username)s</td>\n''' \
''' </tr>\n ''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">Password</td>\n''' \
''' <td>%(password)s</td>\n''' \
''' </tr>\n ''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">Email</td>\n''' \
''' <td>%(email)s</td>\n''' \
''' </tr>\n ''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">Realm</td>\n''' \
''' <td>%(realm)s</td>\n''' \
''' </tr>\n ''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">Record URL</td>\n''' \
''' <td>%(url_base_record)s</td>\n''' \
''' </tr>\n ''' \
''' <tr>\n''' \
''' <td bgcolor="#e6e6fa">URL Servicedocument</td>\n'''\
''' <td>%(url_servicedocument)s</td>\n''' \
''' </tr>\n ''' \
'''</table>''' % {
'table_width' : '50%',
'server_id' : server_info['server_id'],
'server_name' : server_info['server_name'],
'server_host' : server_info['server_host'],
'username' : server_info['username'],
'password' : server_info['password'],
'email' : server_info['email'],
'realm' : server_info['realm'],
'url_base_record' : server_info['url_base_record'],
'url_servicedocument': server_info['url_servicedocument']
}
return body
def tmpl_display_remote_servers(self, remote_servers, id_record,
error_messages):
'''
format the html code that display a dropdown list containing the
servers
@param self: reference to the current instance of the class
@param remote_servers: list of tuple containing server's infos
@return: string containing html code
'''
body = '''
<form method="post" enctype="multipart/form-data" accept-charset="UTF-8" action="/bibsword">
<input type="hidden" name="status" value="select_server"/>
%(error_message)s
<input type="submit" name="submit" value="Cancel" />
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="2" bgcolor="#e6e6fa">
<h2>Forward a record</h2>
</td>
</tr>
<tr>
<td align="right" width="%(row_width)s">
<p>Enter the number of the report to submit: </p>
</td>
<td align="left" width="%(row_width)s">
<input type="text" name="id_record" size="20"
value="%(id_record)s"/>
</td>
</tr>
<tr>
<td align="right" width="%(row_width)s">
<p>Select a remote server: </p>
</td>
<td align="left" width="%(row_width)s">
<select name="id_remote_server" size="1">
<option value="0">-- select a remote server --</option>
%(remote_server)s
</select>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="Select" name="submit"/>
</td>
</tr>
</table>
</form>''' % {
'error_message': \
self.display_error_message_row(error_messages),
'table_width' : '100%',
'row_width' : '50%',
'id_record' : id_record,
'remote_server': \
self.fill_dropdown_remote_servers(remote_servers)
}
return body
def tmpl_display_collections(self, selected_server, server_infos,
collections, id_record, recid, error_messages):
'''
format the html code that display the selected server, the informations
about the selected server and a dropdown list containing the server's
collections
@param self: reference to the current instance of the class
@param selected_server: tuple containing selected server name and id
@param server_infos: tuple containing infos about selected server
@param collections: list contianing server's collections
@return: string containing html code
'''
body = '''
<form method="post" enctype="multipart/form-data" accept-charset="UTF-8" action="/bibsword">
<input type="hidden" name="status" value="select_collection"/>
<input type="hidden" name="id_remote_server" value="%(id_server)s"/>
<input type="hidden" name="id_record" value="%(id_record)s"/>
<input type="hidden" name="recid" value="%(recid)s"/>
%(error_message)s
<input type="submit" name="submit" value="Cancel" />
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="2" bgcolor="#e6e6fa">
<h2>Remote server</h2></td>
</tr>
<tr>
<td align="center" rowspan="2" valign="center">
<h2>%(server_name)s</h2>
</td>
<td align="left">
SWORD version: %(server_version)s
</td>
</tr>
<tr>
<td align="left">
Max upload size [Kb]: %(server_maxUpload)s
</td>
</tr>
<tr>
<td align="left" colspan="2">
<input type="submit" value="Modify server" name="submit"/>
</td>
</tr>
</table>
<p> </p>
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="2" bgcolor="#e6e6fa"><h2>Collection</h2>
</td>
</tr>
<tr>
<td align="right" width="%(row_width)s">Select a collection: </td>
<td align="left" width="%(row_width)s">
<select name="id_collection" size="1">
<option value="0">-- select a collection --</option>
%(collection)s
</select>
</td>
</tr>
<tr>
<td align="center" colspan="2">
<input type="submit" value="Select" name="submit"/>
</td>
</tr>
</table>
</form>''' % {
'table_width' : '100%',
'row_width' : '50%',
'error_message' : \
self.display_error_message_row(error_messages),
'id_server' : selected_server['id'],
'server_name' : selected_server['name'],
'server_version' : server_infos['version'],
'server_maxUpload': server_infos['maxUploadSize'],
'collection' : \
self.fill_dropdown_collections(collections),
'id_record' : id_record,
'recid' : recid
}
return body
def tmpl_display_categories(self, selected_server, server_infos,
selected_collection, collection_infos,
primary_categories, secondary_categories,
id_record, recid, error_messages):
'''
format the html code that display the selected server, the informations
about the selected server, the selected collections, the informations
about the collection and a dropdown list containing the server's
primary and secondary categories
@param self: reference to the current instance of the class
@param selected_server: tuple containing selected server name and id
@param server_infos: tuple containing infos about selected server
@param selected_collection: selected collection
@param collection_infos: tuple containing infos about selected col
@param primary_categories: list of mandated categories for the col
@return: string containing html code
'''
body = '''
<form method="post" enctype="multipart/form-data" accept-charset="UTF-8" action="/bibsword">
<input type="hidden" name="status" value="select_primary_category"/>
<input type="hidden" name="id_remote_server" value="%(id_server)s"/>
<input type="hidden" name="id_collection" value="%(id_collection)s"/>
<input type="hidden" name="id_record" value="%(id_record)s"/>
<input type="hidden" name="recid" value="%(recid)s"/>
%(error_message)s
<input type="submit" name="submit" value="Cancel" />
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="2" bgcolor="#e6e6fa">
<h2>Remote server</h2>
</td>
</tr>
<tr>
<td align="center" rowspan="2" valign="center">
<h2>%(server_name)s</h2>
</td>
<td align="left">
SWORD version: %(server_version)s
</td>
</tr>
<tr>
<td align="left">
Max upload size [Kb]: %(server_maxUpload)s
</td>
</tr>
<tr>
<td align="left" colspan="2">
<input type="submit" value="Modify server" name="submit"/>
</td>
</tr>
</table>
<p> </p>
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="2" bgcolor="#e6e6fa">
<h2>Collection</h2>
</td>
</tr>
<tr>
<td align="center" rowspan="2" valign="center">
<h2>%(collection_name)s</h2>
</td>
<td align="left">
URL: %(collection_url)s
</td>
</tr>
<tr>
<td align="left">
Accepted media types:
<ul>%(collection_accept)s</ul>
</td>
</tr>
<tr>
<td align="left" colspan=2>
<input type="submit" value="Modify collection" name="submit"/>
</td>
</tr>
</table>
<p> </p>
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="2" bgcolor="#e6e6fa">
<h2>Mandatory category</h2>
</td>
</tr>
<tr>
<td align="right" width="%(row_width)s">
<p>Select a mandated category: </p>
</td>
<td align="left" width="%(row_width)s">
<select name="id_primary" size="1">
<option value="0">-- select a category --</option>
%(primary_categories)s
</select>
</td>
</tr>
</table>
<p></p>
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="2" bgcolor="#e6e6fa">
<h2>Optional categories</h2>
</td>
</tr>
<td align="right" width="%(row_width)s">
<p>Select optional categories: </p>
</td>
<td align="left" width="%(row_width)s">
<select name="id_categories" size="10" multiple>
%(secondary_categories)s
</select>
</td>
</tr>
</table>
<p> </p>
<center>
<input type="submit" value="Select" name="submit"/>
</center>
</form>''' % {
'table_width' : '100%',
'row_width' : '50%',
'error_message' : self.display_error_message_row(
error_messages),
# hidden input
'id_server' : selected_server['id'],
'id_collection' : selected_collection['id'],
'id_record' : id_record,
'recid' : recid,
# variables values
'server_name' : selected_server['name'],
'server_version' : server_infos['version'],
'server_maxUpload' : server_infos['maxUploadSize'],
'collection_name' : selected_collection['label'],
'collection_accept': ''.join([
'''<li>%(name)s </li>''' % {
'name': accept
} for accept in collection_infos['accept'] ]),
'collection_url' : selected_collection['url'],
'primary_categories' : self.fill_dropdown_primary(
primary_categories),
'secondary_categories': self.fill_dropdown_secondary(
secondary_categories)
}
return body
def tmpl_display_metadata(self, user, server, collection, primary,
categories, medias, metadata, id_record, recid,
error_messages):
'''
format a string containing every informations before a submission
'''
body = '''
<form method="post" enctype="multipart/form-data" accept-charset="UTF-8" action="/bibsword">
<input type="hidden" name="status" value="check_submission"/>
<input type="hidden" name="id_remote_server" value="%(id_server)s"/>
<input type="hidden" name="id_collection" value="%(id_collection)s"/>
<input type="hidden" name="id_primary" value="%(id_primary)s"/>
<input type="hidden" name="id_categories" value="%(id_categories)s"/>
<input type="hidden" name="id_record" value="%(id_record)s"/>
<input type="hidden" name="recid" value="%(recid)s"/>
%(error_message)s
<input type="submit" name="submit" value="Cancel" />
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="2" bgcolor="#e6e6fa">
<h2>Destination</h2>
</td>
</tr>
<tr>
<td align="center" rowspan="3" valign="center">
<h2>%(server_name)s</h2>
</td>
<td align="left">
Collection: %(collection_name)s ( %(collection_url)s )
</td>
</tr>
<tr>
<td align="left">
Primary category: %(primary_name)s ( %(primary_url)s )
</td>
</tr>
%(categories)s
<tr>
<td align="left" colspan="2">
<input type="submit" value="Modify destination" name="submit"/>
</td>
</tr>
</table>
<p> </p>
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="4" bgcolor="#e6e6fa">
<h2>Submitter</h2>
</td>
</tr>
<tr>
<td width="%(row_width)s">Name:</td>
<td><input type="text" name="author_name" size="100"
value="%(user_name)s"/></td>
</tr>
<tr>
<td>Email:</td>
<td><input type="text" name="author_email" size="100"
value="%(user_email)s"/></td>
</tr>
</table>
<p></p>
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="4" bgcolor="#e6e6fa"><h2>Media</h2></td>
</tr>
<tr><td colspan="4">%(medias)s%(media_help)s</td></tr>
</table>
<p></p>
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="3" bgcolor="#e6e6fa"><h2>Metadata</h2> <font color="red"><b>Warning:</b> modification(s) will not be saved on the %(CFG_SITE_NAME)s</font>
</td>
</tr>
<tr>
<td align="left" width="%(row_width)s"><p>Report Number<span style="color:#f00">*</span>:</p></td>
<td><input type="text" name="id" size="100" value="%(id)s"/></td>
</tr>
<tr>
<td align="left" width="%(row_width)s"><p>Title<span style="color:#f00">*</span>:</p></td>
<td><input type="text" name="title" size="100" value="%(title)s"/>
</td>
</tr>
<tr>
<td align="left" width="%(row_width)s"><p>Summary<span style="color:#f00">*</span>:</p></td>
<td>
<textarea name="summary" rows="4" cols="100">%(summary)s
</textarea>
</td>
</tr>
%(contributors)s
%(journal_refs)s
%(report_nos)s
</table>
<p><font color="red">The fields having a * are mandatory</font></p>
<center>
<input type="submit" value="Submit" name="submit"/>
</center>
<form>''' % {
'table_width' : '100%',
'row_width' : '25%',
'error_message' : \
self.display_error_message_row(error_messages),
'CFG_SITE_NAME': CFG_SITE_NAME,
# hidden input
'id_server' : server['id'],
'id_collection' : collection['id'],
'id_primary' : primary['id'],
'id_categories' : self.get_list_id_categories(categories),
'id_record' : id_record,
'recid' : recid,
# variables values
'server_name' : server['name'],
'collection_name' : collection['label'],
'collection_url' : collection['url'],
'primary_name' : primary['label'],
'primary_url' : primary['url'],
'categories' : self.fill_optional_category_list(categories),
#user
'user_name' : user['nickname'],
'user_email' : user['email'],
# media
'medias' : self.fill_media_list(medias, server['id']),
'media_help' : self.fill_arxiv_help_message(),
# metadata
'id' : metadata['id'],
'title' : metadata['title'],
'summary' : metadata['summary'],
'contributors' : self.fill_contributors_list(
metadata['contributors']),
'journal_refs' : self.fill_journal_refs_list(
metadata['journal_refs']),
'report_nos' : self.fill_report_nos_list(
metadata['report_nos'])
}
return body
def tmpl_display_list_submission(self, submissions):
'''
Display the data of submitted recods
'''
body = '''
<form method="post" enctype="multipart/form-data" accept-charset="UTF-8" action="/bibsword">
<table border="1" valign="top" width="%(table_width)s">
<tr>
<td align="left" colspan="7" bgcolor="#e6e6fa">
<h2>Document successfully submitted !</h2>
</td>
</tr>
<tr>
<td align="center" bgcolor="#e6e6fa"><b>Remote server</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Submitter</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Record id</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Remote id</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Status</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Dates</b></td>
<td align="center" bgcolor="#e6e6fa"><b>Links</b></td>
</tr>
%(submissions)s
</table>
<a href=%(CFG_SITE_URL)s/bibsword>Return</a>
</form>''' % {
'table_width' : '100%',
'submissions' : self.fill_submission_table(submissions),
'CFG_SITE_URL' : CFG_SITE_URL
}
return body
#***************************************************************************
# Private functions
#***************************************************************************
def display_error_message_row(self, error_messages):
'''
return a list of error_message in form of a bullet list
@param error_messages: list of error_messages to display
@return: html code that display list of errors
'''
# if no errors, return nothing
if len(error_messages) == 0:
return ''
if len(error_messages) == 1:
# display a generic header message
body = '''
<tr>
<td align="left" colspan=2>
<font color='red'>
<p> The following error was found: </p>
<ul>
'''
else:
# display a generic header message
body = '''
<tr>
<td align="left" colspan=2>
<font color='red'>
<p> Following errors were found: </p>
<ul>
'''
# insert each error lines
for error_message in error_messages:
body = body + '''
<li>%(error)s</li>''' % {
'error': error_message
}
body = body + '''
</ul>
</font>
</td>
</tr>'''
return body
def fill_submission_table(self, submissions):
'''
This method return the body of the submission state table. each
submissions given in parameters has one row
@param submissions: submission status list
@return: html table body
'''
return ''.join([
''' <tr>
<td>%(id_server)s: <a href="%(server_infos)s">
%(server_name)s</a></td>
<td>%(user_name)s <br/> %(user_email)s</td
- <td>%(id_bibrec)s: <a href="%(cfg_site_url)s/record/%(id_bibrec)s"
+ <td>%(id_bibrec)s: <a href="%(cfg_site_url)s/%(CFG_SITE_RECORD)s/%(id_bibrec)s"
target="_blank">%(no_bibrec)s</a></td>
<td><a href="%(url_base_remote)s/%(id_remote)s" target="_blank">
%(id_remote)s</a></td>
<td>%(status)s</td>
<td><b>submission: </b> %(submission_date)s <br/>
<b>publication: </b> %(publication_date)s <br/>
<b>removal: </b> %(removal_date)s </td>
<td><b>media: </b> <a href="%(media_link)s" target="_blank">
%(media_link)s</a> <br/>
<b>metadata: </b> <a href="%(metadata_link)s" target="_blank">
%(metadata_link)s</a> <br />
<b>status: </b> <a href="%(status_link)s" target="_blank">
%(status_link)s</a></td>
</tr>''' % {
'id_server' : str(submission['id_server']),
'server_infos' : "%s/bibsword/remoteserverinfos?id=%s" % \
(CFG_SITE_URL, submission['id_server']),
'server_name' : str(submission['server_name']),
'user_name' : str(submission['user_name']),
'user_email' : str(submission['user_email']),
'id_bibrec' : str(submission['id_record']),
'no_bibrec' : str(submission['report_no']),
'id_remote' : str(submission['id_remote']),
'status' : str(submission['status']),
'submission_date' : str(submission['submission_date']),
'publication_date' : str(submission['publication_date']),
'removal_date' : str(submission['removal_date']),
'media_link' : str(submission['link_medias']),
'metadata_link' : str(submission['link_metadata']),
'status_link' : str(submission['link_status']),
'url_base_remote' : str(submission['url_base_remote']),
- 'cfg_site_url' : CFG_SITE_URL
+ 'cfg_site_url' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD
} for submission in submissions])
def fill_dropdown_remote_servers(self, remote_servers):
'''
This method fill a dropdown list of remote servers.
@return: html code to display
'''
return ''.join([
'''<option value="%(id)s">%(name)s - %(host)s</option>''' % {
'id': str(remote_server['id']),
'name': remote_server['name'],
'host': remote_server['host']
} for remote_server in remote_servers])
def fill_dropdown_collections(self, collections):
'''
This method fill a dropdown list of collection.
@param collections: list of all collections with name - url
@return: html code to display
'''
return ''.join([
'''<option value="%(id)s">%(name)s</option>''' % {
'id': str(collection['id']),
'name': collection['label']
} for collection in collections])
def fill_dropdown_primary(self, primary_categories):
'''
This method fill the primary dropdown list with the data given in
parameter
@param primary_categories: list of 'url' 'name' tuples
@return: html code generated to display the list
'''
return ''.join([
'''<option value="%(id)s">%(name)s</option>''' % {
'id': primary_categorie['id'],
'name': primary_categorie['label']
} for primary_categorie in primary_categories])
def fill_dropdown_secondary(self, categories):
'''
This method fill a category list. This list is allows the multi-selection
or items. To proced to select more than one categorie through a browser
ctrl + clic
@param categories: list of all categories in the format name - url
@return: the html code that display each dropdown list
'''
if len(categories) == '':
return ''
return ''.join([
'''<option value="%(id)s">%(name)s</option>''' % {
'id': category['id'],
'name': category['label']
} for category in categories])
def fill_optional_category_list(self, categories):
'''
This method fill a table row that contains name and url of the selected
optional categories
@param self: reference to the current instance of the class
@param categories: list of tuples containing selected categories
@return: html code generated to display the list
'''
if len(categories) == 0:
return ''
else:
body = '<tr><td>'
body = body + ''.join([
'''<p>Category: %(category_name)s ( %(category_url)s )</p>'''%{
'category_name' : category['label'],
'category_url' : category['url']
} for category in categories
])
body = body + '</td></tr>'
return body
def fill_media_list(self, medias, id_server, from_websubmit=False):
'''
Concatenate the string that contains all informations about the medias
'''
text = ''
if id_server == 1:
media_type = self.format_media_list_by_type(medias)
text = '''<h2>Please select files you would like to push to arXiv:</h2>'''
for mtype in media_type:
text += '''<h3><b>%s: </b></h3>''' % mtype['media_type']
text += '''<blockquote>'''
for media in mtype['media_list']:
text += '''<input type='checkbox' name="media" value="%s" %s>%s</input><br />''' % (media['path'], media['selected'], media['name'])
text += "</blockquote>"
text += '''<h3>Upload</h3>'''
text += '''<blockquote>'''
text += '''<p>In addition, you can submit a new file (that will be added to the record as well):</p>'''
if from_websubmit == False:
text += '''<input type="file" name="new_media" size="60"/>'''
return text
def fill_arxiv_help_message(self):
text = '''</blockquote><h3>Help</h3>'''
text += '''<blockquote><p>For more help on which formats are supported by arXiv, please see:'''\
'''<ul>'''\
'''<li><a href="http://arxiv.org/help/submit" target="_blank">'''\
'''arXiv submission process</a></li>'''\
'''<li><a href="http://arxiv.org/help/submit_tex" target="_blank">'''\
'''arXiv TeX submission</a></li>'''\
'''<li><a href="http://arxiv.org/help/submit_docx" target="_blank">'''\
'''arXiv Docx submission</a></li>'''\
'''<li><a href="http://arxiv.org/help/submit_pdf" target="_blank">'''\
'''arXiv PDF submission</a></li>'''\
'''</ul></blockquote>'''
return text
def fill_contributors_list(self, contributors):
'''
This method display each contributors in the format of an editable input
text. This allows the user to modifie it.
@param contributors: The list of all contributors of the document
@return: the html code that display each dropdown list
'''
output = ''
is_author = True
for author in contributors:
nb_rows = 2
author_name = \
'''<LABEL for="name">Name: </LABEL><input type = "text" ''' \
'''name = "contributor_name" size = "100" value = "%s" ''' \
'''id="name"/>''' % author['name']
author_email = \
'''<LABEL for = "email">Email: </LABEL>''' \
'''<input type = "text" name = "contributor_email" ''' \
'''size = "100" value = "%s" id = "email"/>''' % author['email']
author_affiliations = []
for affiliation in author['affiliation']:
affiliation_row = \
'''<LABEL for = "affiliation">Affiliation: </LABEL> ''' \
'''<input type="text" name = "contributor_affiliation" ''' \
'''size = "100" value = "%s" id = "affiliation"/>''' % \
affiliation
author_affiliations.append(affiliation_row)
nb_rows = nb_rows + 1
affiliation_row = \
'''<LABEL for = "affiliation">Affiliation: </LABEL>''' \
'''<input type = "text" name = "contributor_affiliation" ''' \
'''size = "100" id = "affiliation"/>'''
author_affiliations.append(affiliation_row)
nb_rows = nb_rows + 1
if is_author:
output += '''<tr><td rowspan = "%s">Author: </td>''' % nb_rows
is_author = False
else:
output += '''<tr><td rowspan = "%s">Contributor: </td>''' % \
nb_rows
output += '''<td>%s</td></tr>''' % author_name
if author_email != '':
output += '''<tr><td>%s</td></tr>''' % author_email
for affiliation in author_affiliations:
output += '''<tr><td>%s</td></tr>''' % affiliation
output += \
'''<input type = "hidden" name = "contributor_affiliation" ''' \
'''value = "next"/>'''
return output
def fill_journal_refs_list(self, journal_refs):
'''
This method display each journal references in the format of an editable
input text. This allows the user to modifie it.
@param journal_refs: The list of all journal references of the document
@return: the html code that display each dropdown list
'''
html = ''
if len(journal_refs) > 0:
html += '''
<tr>
<td align="left"><p>Journal references: </p></td><td>
'''
html = html + ''.join([
'''
<p><input type="text" name="journal_refs" size="100" ''' \
'''value="%(journal_ref)s"/></p>
''' % {
'journal_ref': journal_ref
} for journal_ref in journal_refs
])
html = html + '''
</td>
</tr>
'''
return html
def fill_report_nos_list(self, report_nos):
'''
Concatate a string containing the report number html table rows
'''
html = ''
if len(report_nos) > 0:
html = '''
<tr>
<td align="left"><p>Report numbers: </p></td><td>
'''
html = html + ''.join([
'''
<p><input type="text" name="report_nos" size="100" ''' \
'''value="%(report_no)s"/></p>''' % {
'report_no': report_no
} for report_no in report_nos
])
html = html + '''
</td>
</tr>
'''
return html
def get_list_id_categories(self, categories):
'''
gives the id of the categores tuple
'''
id_categories = []
for category in categories:
id_categories.append(category['id'])
return id_categories
def format_media_list_by_type(self, medias):
'''
This function format the media by type (Main, Uploaded, ...)
'''
#format media list by type of document
media_type = []
for media in medias:
# if it is the first media of this type, create a new type
is_type_in_media_type = False
for type in media_type:
if media['collection'] == type['media_type']:
is_type_in_media_type = True
if is_type_in_media_type == False:
type = {}
type['media_type'] = media['collection']
type['media_list'] = []
media_type.append(type)
# insert the media in the good media_type element
for type in media_type:
if type['media_type'] == media['collection']:
type['media_list'].append(media)
return media_type
diff --git a/modules/bibupload/lib/bibupload_regression_tests.py b/modules/bibupload/lib/bibupload_regression_tests.py
index c309c6171..7ca486eda 100644
--- a/modules/bibupload/lib/bibupload_regression_tests.py
+++ b/modules/bibupload/lib/bibupload_regression_tests.py
@@ -1,3864 +1,3874 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0301
"""Regression tests for the BibUpload."""
__revision__ = "$Id$"
import re
import unittest
import datetime
import os
import time
import sys
from urllib2 import urlopen
import pprint
if sys.hexversion < 0x2060000:
from md5 import md5
else:
from hashlib import md5
from invenio.config import CFG_OAI_ID_FIELD, CFG_PREFIX, CFG_SITE_URL, CFG_TMPDIR, \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG, \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG, \
CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG, \
- CFG_WEBDIR
+ CFG_WEBDIR, \
+ CFG_SITE_RECORD
from invenio.access_control_config import CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS
from invenio import bibupload
from invenio.search_engine import print_record
from invenio.dbquery import run_sql, get_table_status_info
from invenio.dateutils import convert_datestruct_to_datetext
from invenio.testutils import make_test_suite, run_test_suite, test_web_page_content
from invenio.bibdocfile import BibRecDocs
from invenio.bibtask import task_set_task_param, setup_loggers
# helper functions:
def remove_tag_001_from_xmbuffer(xmbuffer):
"""Remove tag 001 from MARCXML buffer. Useful for testing two
MARCXML buffers without paying attention to recIDs attributed
during the bibupload.
"""
return re.sub(r'<controlfield tag="001">.*</controlfield>', '', xmbuffer)
def compare_xmbuffers(xmbuffer1, xmbuffer2):
"""Compare two XM (XML MARC) buffers by removing whitespaces
before testing.
"""
def remove_blanks_from_xmbuffer(xmbuffer):
"""Remove \n and blanks from XMBUFFER."""
out = xmbuffer.replace("\n", "")
out = out.replace(" ", "")
return out
# remove whitespace:
xmbuffer1 = remove_blanks_from_xmbuffer(xmbuffer1)
xmbuffer2 = remove_blanks_from_xmbuffer(xmbuffer2)
if xmbuffer1 != xmbuffer2:
return "\n=" + xmbuffer1 + "=\n" + '!=' + "\n=" + xmbuffer2 + "=\n"
return ''
def remove_tag_001_from_hmbuffer(hmbuffer):
"""Remove tag 001 from HTML MARC buffer. Useful for testing two
HTML MARC buffers without paying attention to recIDs attributed
during the bibupload.
"""
return re.sub(r'(^|\n)(<pre>)?[0-9]{9}\s001__\s\d+($|\n)', '', hmbuffer)
def compare_hmbuffers(hmbuffer1, hmbuffer2):
"""Compare two HM (HTML MARC) buffers by removing whitespaces
before testing.
"""
hmbuffer1 = hmbuffer1.strip()
hmbuffer2 = hmbuffer2.strip()
# remove eventual <pre>...</pre> formatting:
hmbuffer1 = re.sub(r'^<pre>', '', hmbuffer1)
hmbuffer2 = re.sub(r'^<pre>', '', hmbuffer2)
hmbuffer1 = re.sub(r'</pre>$', '', hmbuffer1)
hmbuffer2 = re.sub(r'</pre>$', '', hmbuffer2)
# remove leading recid, leaving only field values:
hmbuffer1 = re.sub(r'(^|\n)[0-9]{9}\s', '', hmbuffer1)
hmbuffer2 = re.sub(r'(^|\n)[0-9]{9}\s', '', hmbuffer2)
# remove leading whitespace:
hmbuffer1 = re.sub(r'(^|\n)\s+', '', hmbuffer1)
hmbuffer2 = re.sub(r'(^|\n)\s+', '', hmbuffer2)
compare_hmbuffers = hmbuffer1 == hmbuffer2
if not compare_hmbuffers:
return "\n=" + hmbuffer1 + "=\n" + '!=' + "\n=" + hmbuffer2 + "=\n"
return ''
def wipe_out_record_from_all_tables(recid):
"""
Wipe out completely the record and all its traces of RECID from
the database (bibrec, bibrec_bibxxx, bibxxx, bibfmt). Useful for
the time being for test cases.
"""
# delete all the linked bibdocs
for bibdoc in BibRecDocs(recid).list_bibdocs():
bibdoc.expunge()
# delete from bibrec:
run_sql("DELETE FROM bibrec WHERE id=%s", (recid,))
# delete from bibrec_bibxxx:
for i in range(0, 10):
for j in range(0, 10):
run_sql("DELETE FROM %(bibrec_bibxxx)s WHERE id_bibrec=%%s" % \
{'bibrec_bibxxx': "bibrec_bib%i%ix" % (i, j)},
(recid,))
# delete all unused bibxxx values:
for i in range(0, 10):
for j in range(0, 10):
run_sql("DELETE %(bibxxx)s FROM %(bibxxx)s " \
" LEFT JOIN %(bibrec_bibxxx)s " \
" ON %(bibxxx)s.id=%(bibrec_bibxxx)s.id_bibxxx " \
" WHERE %(bibrec_bibxxx)s.id_bibrec IS NULL" % \
{'bibxxx': "bib%i%ix" % (i, j),
'bibrec_bibxxx': "bibrec_bib%i%ix" % (i, j)})
# delete from bibfmt:
run_sql("DELETE FROM bibfmt WHERE id_bibrec=%s", (recid,))
# delete from bibrec_bibdoc:
run_sql("DELETE FROM bibrec_bibdoc WHERE id_bibrec=%s", (recid,))
def try_url_download(url):
"""Try to download a given URL"""
try:
open_url = urlopen(url)
open_url.read()
except Exception, e:
raise StandardError("Downloading %s is impossible because of %s"
% (url, str(e)))
return True
def force_webcoll(recid):
from invenio import bibindex_engine
reload(bibindex_engine)
from invenio import websearch_webcoll
reload(websearch_webcoll)
index_id, index_name, index_tags = bibindex_engine.get_word_tables("collection")[0]
bibindex_engine.WordTable(index_name, index_id, index_tags, "idxWORD%02dF", default_get_words_fnc=bibindex_engine.get_words_from_phrase, tag_to_words_fnc_map={'8564_u': bibindex_engine.get_words_from_fulltext}).add_recIDs([[recid, recid]], 1)
c = websearch_webcoll.Collection()
c.calculate_reclist()
c.update_reclist()
class GenericBibUploadTest(unittest.TestCase):
"""Generic BibUpload testing class with predefined
setUp and tearDown methods.
"""
def setUp(self):
self.verbose = 0
setup_loggers()
task_set_task_param('verbose', self.verbose)
self.last_recid = run_sql("SELECT MAX(id) FROM bibrec")[0][0]
def tearDown(self):
for recid in run_sql("SELECT id FROM bibrec WHERE id>%s", (self.last_recid,)):
wipe_out_record_from_all_tables(recid[0])
class BibUploadInsertModeTest(GenericBibUploadTest):
"""Testing insert mode."""
def setUp(self):
# pylint: disable=C0103
"""Initialise the MARCXML variable"""
GenericBibUploadTest.setUp(self)
self.test = """<record>
<datafield tag ="245" ind1=" " ind2=" ">
<subfield code="a">something</subfield>
</datafield>
<datafield tag ="700" ind1=" " ind2=" ">
<subfield code="a">Tester, J Y</subfield>
<subfield code="u">MIT</subfield>
</datafield>
<datafield tag ="700" ind1=" " ind2=" ">
<subfield code="a">Tester, K J</subfield>
<subfield code="u">CERN2</subfield>
</datafield>
<datafield tag ="700" ind1=" " ind2=" ">
<subfield code="a">Tester, G</subfield>
<subfield code="u">CERN3</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="a">test11</subfield>
<subfield code="c">test31</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="a">test12</subfield>
<subfield code="c">test32</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="a">test13</subfield>
<subfield code="c">test33</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="b">test21</subfield>
<subfield code="d">test41</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="b">test22</subfield>
<subfield code="d">test42</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="a">test14</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="e">test51</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="e">test52</subfield>
</datafield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">CERN</subfield>
</datafield>
</record>"""
self.test_hm = """
100__ $$aTester, T$$uCERN
111__ $$atest11$$ctest31
111__ $$atest12$$ctest32
111__ $$atest13$$ctest33
111__ $$btest21$$dtest41
111__ $$btest22$$dtest42
111__ $$atest14
111__ $$etest51
111__ $$etest52
245__ $$asomething
700__ $$aTester, J Y$$uMIT
700__ $$aTester, K J$$uCERN2
700__ $$aTester, G$$uCERN3
"""
def test_create_record_id(self):
"""bibupload - insert mode, trying to create a new record ID in the database"""
rec_id = bibupload.create_new_record()
self.assertNotEqual(-1, rec_id)
def test_no_retrieve_record_id(self):
"""bibupload - insert mode, detection of record ID in the input file"""
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test)
# We call the function which should retrieve the record id
rec_id = bibupload.retrieve_rec_id(recs[0], 'insert')
# We compare the value found with None
self.assertEqual(None, rec_id)
def test_insert_complete_xmlmarc(self):
"""bibupload - insert mode, trying to insert complete MARCXML file"""
# Initialize the global variable
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test)
# We call the main function with the record as a parameter
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# We retrieve the inserted xml
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
# Compare if the two MARCXML are the same
self.assertEqual(compare_xmbuffers(remove_tag_001_from_xmbuffer(inserted_xm),
self.test), '')
self.assertEqual(compare_hmbuffers(remove_tag_001_from_hmbuffer(inserted_hm),
self.test_hm), '')
class BibUploadAppendModeTest(GenericBibUploadTest):
"""Testing append mode."""
def setUp(self):
# pylint: disable=C0103
"""Initialize the MARCXML variable"""
GenericBibUploadTest.setUp(self)
self.test_existing = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">DESY</subfield>
</datafield>
<datafield tag ="970" ind1=" " ind2=" ">
<subfield code="a">0003719PHOPHO</subfield>
</datafield>
</record>"""
self.test_to_append = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, U</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag ="970" ind1=" " ind2=" ">
<subfield code="a">0003719PHOPHO</subfield>
</datafield>
</record>"""
self.test_expected_xm = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">DESY</subfield>
</datafield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, U</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag ="970" ind1=" " ind2=" ">
<subfield code="a">0003719PHOPHO</subfield>
</datafield>
</record>"""
self.test_expected_hm = """
001__ 123456789
100__ $$aTester, T$$uDESY
100__ $$aTester, U$$uCERN
970__ $$a0003719PHOPHO
"""
# insert test record:
test_to_upload = self.test_existing.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
self.test_recid = recid
# replace test buffers with real recid of inserted test record:
self.test_existing = self.test_existing.replace('123456789',
str(self.test_recid))
self.test_to_append = self.test_to_append.replace('123456789',
str(self.test_recid))
self.test_expected_xm = self.test_expected_xm.replace('123456789',
str(self.test_recid))
self.test_expected_hm = self.test_expected_hm.replace('123456789',
str(self.test_recid))
def test_retrieve_record_id(self):
"""bibupload - append mode, the input file should contain a record ID"""
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test_to_append)
# We call the function which should retrieve the record id
rec_id = bibupload.retrieve_rec_id(recs[0], 'append')
# We compare the value found with None
self.assertEqual(self.test_recid, rec_id)
# clean up after ourselves:
def test_update_modification_record_date(self):
"""bibupload - append mode, checking the update of the modification date"""
# Initialize the global variable
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test_existing)
# We call the function which should retrieve the record id
rec_id = bibupload.retrieve_rec_id(recs[0], opt_mode='append')
# Retrieve current localtime
now = time.localtime()
# We update the modification date
bibupload.update_bibrec_modif_date(convert_datestruct_to_datetext(now), rec_id)
# We retrieve the modification date from the database
query = """SELECT DATE_FORMAT(modification_date,'%%Y-%%m-%%d %%H:%%i:%%s') FROM bibrec where id = %s"""
res = run_sql(query % rec_id)
# We compare the two results
self.assertEqual(res[0][0], convert_datestruct_to_datetext(now))
# clean up after ourselves:
def test_append_complete_xml_marc(self):
"""bibupload - append mode, appending complete MARCXML file"""
# Now we append a datafield
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test_to_append)
# We call the main function with the record as a parameter
err, recid = bibupload.bibupload(recs[0], opt_mode='append')
# We retrieve the inserted xm
after_append_xm = print_record(recid, 'xm')
after_append_hm = print_record(recid, 'hm')
# Compare if the two MARCXML are the same
self.assertEqual(compare_xmbuffers(after_append_xm, self.test_expected_xm), '')
self.assertEqual(compare_hmbuffers(after_append_hm, self.test_expected_hm), '')
# clean up after ourselves:
class BibUploadCorrectModeTest(GenericBibUploadTest):
"""
Testing correcting a record containing similar tags (identical
tag, different indicators). Currently Invenio replaces only
those tags that have matching indicators too, unlike ALEPH500 that
does not pay attention to indicators, it corrects all fields with
the same tag, regardless of the indicator values.
"""
def setUp(self):
"""Initialize the MARCXML test record."""
GenericBibUploadTest.setUp(self)
self.testrec1_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="8">
<subfield code="a">Cool</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Jim</subfield>
<subfield code="u">Test Laboratory</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
10047 $$aTest, John$$uTest University
10048 $$aCool
10047 $$aTest, Jim$$uTest Laboratory
"""
self.testrec1_xm_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test2, Joseph</subfield>
<subfield code="u">Test2 Academy</subfield>
</datafield>
</record>
"""
self.testrec1_corrected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="8">
<subfield code="a">Cool</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test2, Joseph</subfield>
<subfield code="u">Test2 Academy</subfield>
</datafield>
</record>
"""
self.testrec1_corrected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
10048 $$aCool
10047 $$aTest, Joseph$$uTest Academy
10047 $$aTest2, Joseph$$uTest2 Academy
"""
# insert test record:
test_record_xm = self.testrec1_xm.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_record_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.testrec1_xm = self.testrec1_xm.replace('123456789', str(recid))
self.testrec1_hm = self.testrec1_hm.replace('123456789', str(recid))
self.testrec1_xm_to_correct = self.testrec1_xm_to_correct.replace('123456789', str(recid))
self.testrec1_corrected_xm = self.testrec1_corrected_xm.replace('123456789', str(recid))
self.testrec1_corrected_hm = self.testrec1_corrected_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm, self.testrec1_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm, self.testrec1_hm), '')
def test_record_correction(self):
"""bibupload - correct mode, similar MARCXML tags/indicators"""
# correct some tags:
recs = bibupload.xml_marc_to_records(self.testrec1_xm_to_correct)
err, self.recid = bibupload.bibupload(recs[0], opt_mode='correct')
corrected_xm = print_record(self.recid, 'xm')
corrected_hm = print_record(self.recid, 'hm')
# did it work?
self.assertEqual(compare_xmbuffers(corrected_xm, self.testrec1_corrected_xm), '')
self.assertEqual(compare_hmbuffers(corrected_hm, self.testrec1_corrected_hm), '')
# clean up after ourselves:
return
class BibUploadDeleteModeTest(GenericBibUploadTest):
"""
Testing deleting specific tags from a record while keeping anything else
untouched. Currently Invenio deletes only those tags that have
matching indicators too, unlike ALEPH500 that does not pay attention to
indicators, it corrects all fields with the same tag, regardless of the
indicator values.
"""
def setUp(self):
"""Initialize the MARCXML test record."""
GenericBibUploadTest.setUp(self)
self.testrec1_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="8">
<subfield code="a">Cool</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Jim</subfield>
<subfield code="u">Test Laboratory</subfield>
</datafield>
<datafield tag="888" ind1=" " ind2=" ">
<subfield code="a">dumb text</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
10047 $$aTest, John$$uTest University
10048 $$aCool
10047 $$aTest, Jim$$uTest Laboratory
888__ $$adumb text
"""
self.testrec1_xm_to_delete = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Johnson</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="8">
<subfield code="a">Cool</subfield>
</datafield>
<datafield tag="888" ind1=" " ind2=" ">
<subfield code="a">dumb text</subfield>
</datafield>
</record>
"""
self.testrec1_corrected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Jim</subfield>
<subfield code="u">Test Laboratory</subfield>
</datafield>
</record>
"""
self.testrec1_corrected_hm = """
001__ 123456789
003__ SzGeCERN
10047 $$aTest, John$$uTest University
10047 $$aTest, Jim$$uTest Laboratory
"""
# insert test record:
test_record_xm = self.testrec1_xm.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_record_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.testrec1_xm = self.testrec1_xm.replace('123456789', str(recid))
self.testrec1_hm = self.testrec1_hm.replace('123456789', str(recid))
self.testrec1_xm_to_delete = self.testrec1_xm_to_delete.replace('123456789', str(recid))
self.testrec1_corrected_xm = self.testrec1_corrected_xm.replace('123456789', str(recid))
self.testrec1_corrected_hm = self.testrec1_corrected_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm, self.testrec1_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm, self.testrec1_hm), '')
# Checking dumb text is in bibxxx
self.failUnless(run_sql("SELECT id_bibrec from bibrec_bib88x WHERE id_bibrec=%s", (recid, )))
def test_record_tags_deletion(self):
"""bibupload - delete mode, deleting specific tags"""
# correct some tags:
recs = bibupload.xml_marc_to_records(self.testrec1_xm_to_delete)
err, recid = bibupload.bibupload(recs[0], opt_mode='delete')
corrected_xm = print_record(recid, 'xm')
corrected_hm = print_record(recid, 'hm')
# did it work?
self.assertEqual(compare_xmbuffers(corrected_xm, self.testrec1_corrected_xm), '')
self.assertEqual(compare_hmbuffers(corrected_hm, self.testrec1_corrected_hm), '')
# Checking dumb text is no more in bibxxx
self.failIf(run_sql("SELECT id_bibrec from bibrec_bib88x WHERE id_bibrec=%s", (recid, )))
# clean up after ourselves:
class BibUploadReplaceModeTest(GenericBibUploadTest):
"""Testing replace mode."""
def setUp(self):
"""Initialize the MARCXML test record."""
GenericBibUploadTest.setUp(self)
self.testrec1_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="8">
<subfield code="a">Cool</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Jim</subfield>
<subfield code="u">Test Laboratory</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
10047 $$aTest, John$$uTest University
10048 $$aCool
10047 $$aTest, Jim$$uTest Laboratory
"""
self.testrec1_xm_to_replace = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test2, Joseph</subfield>
<subfield code="u">Test2 Academy</subfield>
</datafield>
</record>
"""
self.testrec1_replaced_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test2, Joseph</subfield>
<subfield code="u">Test2 Academy</subfield>
</datafield>
</record>
"""
self.testrec1_replaced_hm = """
001__ 123456789
10047 $$aTest, Joseph$$uTest Academy
10047 $$aTest2, Joseph$$uTest2 Academy
"""
# insert test record:
test_record_xm = self.testrec1_xm.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_record_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.testrec1_xm = self.testrec1_xm.replace('123456789', str(recid))
self.testrec1_hm = self.testrec1_hm.replace('123456789', str(recid))
self.testrec1_xm_to_replace = self.testrec1_xm_to_replace.replace('123456789', str(recid))
self.testrec1_replaced_xm = self.testrec1_replaced_xm.replace('123456789', str(recid))
self.testrec1_replaced_hm = self.testrec1_replaced_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm, self.testrec1_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm, self.testrec1_hm), '')
def test_record_replace(self):
"""bibupload - replace mode, similar MARCXML tags/indicators"""
# replace some tags:
recs = bibupload.xml_marc_to_records(self.testrec1_xm_to_replace)
err, self.recid = bibupload.bibupload(recs[0], opt_mode='replace')
replaced_xm = print_record(self.recid, 'xm')
replaced_hm = print_record(self.recid, 'hm')
# did it work?
self.assertEqual(compare_xmbuffers(replaced_xm, self.testrec1_replaced_xm), '')
self.assertEqual(compare_hmbuffers(replaced_hm, self.testrec1_replaced_hm), '')
# clean up after ourselves:
class BibUploadReferencesModeTest(GenericBibUploadTest):
"""Testing references mode."""
def setUp(self):
"""Initialize the MARCXML variable"""
GenericBibUploadTest.setUp(self)
self.test_insert = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">CERN</subfield>
</datafield>
</record>"""
self.test_reference = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag =\"""" + bibupload.CFG_BIBUPLOAD_REFERENCE_TAG + """\" ind1="C" ind2="5">
<subfield code="m">M. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,</subfield>
<subfield code="s">J. High Energy Phys. 07 (2004) 014</subfield>
</datafield>
</record>"""
self.test_reference_expected_xm = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag =\"""" + bibupload.CFG_BIBUPLOAD_REFERENCE_TAG + """\" ind1="C" ind2="5">
<subfield code="m">M. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,</subfield>
<subfield code="s">J. High Energy Phys. 07 (2004) 014</subfield>
</datafield>
</record>"""
self.test_insert_hm = """
001__ 123456789
100__ $$aTester, T$$uCERN
"""
self.test_reference_expected_hm = """
001__ 123456789
100__ $$aTester, T$$uCERN
%(reference_tag)sC5 $$mM. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,$$sJ. High Energy Phys. 07 (2004) 014
""" % {'reference_tag': bibupload.CFG_BIBUPLOAD_REFERENCE_TAG}
# insert test record:
test_insert = self.test_insert.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_insert)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.test_insert = self.test_insert.replace('123456789', str(recid))
self.test_insert_hm = self.test_insert_hm.replace('123456789', str(recid))
self.test_reference = self.test_reference.replace('123456789', str(recid))
self.test_reference_expected_xm = self.test_reference_expected_xm.replace('123456789', str(recid))
self.test_reference_expected_hm = self.test_reference_expected_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm, self.test_insert), '')
self.assertEqual(compare_hmbuffers(inserted_hm, self.test_insert_hm), '')
self.test_recid = recid
def test_reference_complete_xml_marc(self):
"""bibupload - reference mode, inserting references MARCXML file"""
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test_reference)
# We call the main function with the record as a parameter
err, recid = bibupload.bibupload(recs[0], opt_mode='reference')
# We retrieve the inserted xml
reference_xm = print_record(recid, 'xm')
reference_hm = print_record(recid, 'hm')
# Compare if the two MARCXML are the same
self.assertEqual(compare_xmbuffers(reference_xm, self.test_reference_expected_xm), '')
self.assertEqual(compare_hmbuffers(reference_hm, self.test_reference_expected_hm), '')
class BibUploadFMTModeTest(GenericBibUploadTest):
"""Testing FMT mode."""
def setUp(self):
"""Initialize the MARCXML variable"""
GenericBibUploadTest.setUp(self)
self.new_xm_with_fmt = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HB</subfield>
<subfield code="g">Test. Okay.</subfield>
<subfield code="d">2008-03-14 15:14:00</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux</subfield>
</datafield>
</record>
"""
self.expected_xm_after_inserting_new_xm_with_fmt = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux</subfield>
</datafield>
</record>
"""
self.expected_hm_after_inserting_new_xm_with_fmt = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux
"""
self.recid76_xm_before_all_the_tests = print_record(76, 'xm')
self.recid76_hm_before_all_the_tests = print_record(76, 'hm')
self.recid76_fmts = run_sql("""SELECT last_updated, value, format FROM bibfmt WHERE id_bibrec=76""")
self.recid76_xm_with_fmt = """
<record>
<controlfield tag="001">76</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HB</subfield>
<subfield code="g">Test. Here is some format value.</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Doe, John</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the foos and bars</subfield>
</datafield>
</record>
"""
self.recid76_xm_with_fmt_only_first = """
<record>
<controlfield tag="001">76</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HB</subfield>
<subfield code="g">Test. Let us see if this gets inserted well.</subfield>
</datafield>
</record>
"""
self.recid76_xm_with_fmt_only_second = """
<record>
<controlfield tag="001">76</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HB</subfield>
<subfield code="g">Test. Yet another test, to be run after the first one.</subfield>
</datafield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HD</subfield>
<subfield code="g">Test. Let's see what will be stored in the detailed format field.</subfield>
</datafield>
</record>
"""
def tearDown(self):
"""Helper function that restores recID 76 MARCXML, using the
value saved before all the tests started to execute.
(see self.recid76_xm_before_all_the_tests).
Does not restore HB and HD formats.
"""
recs = bibupload.xml_marc_to_records(self.recid76_xm_before_all_the_tests)
err, recid = bibupload.bibupload(recs[0], opt_mode='replace')
for (last_updated, value, format) in self.recid76_fmts:
run_sql("""UPDATE bibfmt SET last_updated=%s, value=%s WHERE id_bibrec=76 AND format=%s""", (last_updated, value, format))
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm, self.recid76_xm_before_all_the_tests), '')
self.assertEqual(compare_hmbuffers(inserted_hm, self.recid76_hm_before_all_the_tests), '')
def test_inserting_new_record_containing_fmt_tag(self):
"""bibupload - FMT tag, inserting new record containing FMT tag"""
recs = bibupload.xml_marc_to_records(self.new_xm_with_fmt)
(dummy, new_recid) = bibupload.bibupload(recs[0], opt_mode='insert')
xm_after = print_record(new_recid, 'xm')
hm_after = print_record(new_recid, 'hm')
hb_after = print_record(new_recid, 'hb')
self.assertEqual(compare_xmbuffers(xm_after,
self.expected_xm_after_inserting_new_xm_with_fmt.replace('123456789', str(new_recid))), '')
self.assertEqual(compare_hmbuffers(hm_after,
self.expected_hm_after_inserting_new_xm_with_fmt.replace('123456789', str(new_recid))), '')
self.assertEqual(run_sql('SELECT last_updated from bibfmt WHERE id_bibrec=%s', (new_recid, ))[0][0], datetime.datetime(2008, 3, 14, 15, 14))
self.failUnless(hb_after.startswith("Test. Okay."))
def test_updating_existing_record_formats_in_format_mode(self):
"""bibupload - FMT tag, updating existing record via format mode"""
xm_before = print_record(76, 'xm')
hm_before = print_record(76, 'hm')
# insert first format value:
recs = bibupload.xml_marc_to_records(self.recid76_xm_with_fmt_only_first)
bibupload.bibupload(recs[0], opt_mode='format')
xm_after = print_record(76, 'xm')
hm_after = print_record(76, 'hm')
hb_after = print_record(76, 'hb')
self.assertEqual(xm_after, xm_before)
self.assertEqual(hm_after, hm_before)
self.failUnless(hb_after.startswith("Test. Let us see if this gets inserted well."))
# now insert another format value and recheck:
recs = bibupload.xml_marc_to_records(self.recid76_xm_with_fmt_only_second)
bibupload.bibupload(recs[0], opt_mode='format')
xm_after = print_record(76, 'xm')
hm_after = print_record(76, 'hm')
hb_after = print_record(76, 'hb')
hd_after = print_record(76, 'hd')
self.assertEqual(xm_after, xm_before)
self.assertEqual(hm_after, hm_before)
self.failUnless(hb_after.startswith("Test. Yet another test, to be run after the first one."))
self.failUnless(hd_after.startswith("Test. Let's see what will be stored in the detailed format field."))
def test_updating_existing_record_formats_in_correct_mode(self):
"""bibupload - FMT tag, updating existing record via correct mode"""
xm_before = print_record(76, 'xm')
hm_before = print_record(76, 'hm')
# insert first format value:
recs = bibupload.xml_marc_to_records(self.recid76_xm_with_fmt_only_first)
bibupload.bibupload(recs[0], opt_mode='correct')
xm_after = print_record(76, 'xm')
hm_after = print_record(76, 'hm')
hb_after = print_record(76, 'hb')
self.assertEqual(xm_after, xm_before)
self.assertEqual(hm_after, hm_before)
self.failUnless(hb_after.startswith("Test. Let us see if this gets inserted well."))
# now insert another format value and recheck:
recs = bibupload.xml_marc_to_records(self.recid76_xm_with_fmt_only_second)
bibupload.bibupload(recs[0], opt_mode='correct')
xm_after = print_record(76, 'xm')
hm_after = print_record(76, 'hm')
hb_after = print_record(76, 'hb')
hd_after = print_record(76, 'hd')
self.assertEqual(xm_after, xm_before)
self.assertEqual(hm_after, hm_before)
self.failUnless(hb_after.startswith("Test. Yet another test, to be run after the first one."))
self.failUnless(hd_after.startswith("Test. Let's see what will be stored in the detailed format field."))
def test_updating_existing_record_formats_in_replace_mode(self):
"""bibupload - FMT tag, updating existing record via replace mode"""
# insert first format value:
recs = bibupload.xml_marc_to_records(self.recid76_xm_with_fmt_only_first)
bibupload.bibupload(recs[0], opt_mode='replace')
xm_after = print_record(76, 'xm')
hm_after = print_record(76, 'hm')
hb_after = print_record(76, 'hb')
self.assertEqual(compare_xmbuffers(xm_after,
'<record><controlfield tag="001">76</controlfield></record>'), '')
self.assertEqual(compare_hmbuffers(hm_after,
'000000076 001__ 76'), '')
self.failUnless(hb_after.startswith("Test. Let us see if this gets inserted well."))
# now insert another format value and recheck:
recs = bibupload.xml_marc_to_records(self.recid76_xm_with_fmt_only_second)
bibupload.bibupload(recs[0], opt_mode='replace')
xm_after = print_record(76, 'xm')
hm_after = print_record(76, 'hm')
hb_after = print_record(76, 'hb')
hd_after = print_record(76, 'hd')
self.assertEqual(compare_xmbuffers(xm_after, """
<record>
<controlfield tag="001">76</controlfield>
</record>"""), '')
self.assertEqual(compare_hmbuffers(hm_after, '000000076 001__ 76'), '')
self.failUnless(hb_after.startswith("Test. Yet another test, to be run after the first one."))
self.failUnless(hd_after.startswith("Test. Let's see what will be stored in the detailed format field."))
# final insertion and recheck:
recs = bibupload.xml_marc_to_records(self.recid76_xm_with_fmt)
bibupload.bibupload(recs[0], opt_mode='replace')
xm_after = print_record(76, 'xm')
hm_after = print_record(76, 'hm')
hb_after = print_record(76, 'hb')
hd_after = print_record(76, 'hd')
self.assertEqual(compare_xmbuffers(xm_after, """
<record>
<controlfield tag="001">76</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Doe, John</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the foos and bars</subfield>
</datafield>
</record>
"""), '')
self.assertEqual(compare_hmbuffers(hm_after, """
001__ 76
003__ SzGeCERN
100__ $$aDoe, John$$uCERN
245__ $$aOn the foos and bars
"""), '')
self.failUnless(hb_after.startswith("Test. Here is some format value."))
self.failUnless(hd_after.startswith("Test. Let's see what will be stored in the detailed format field."))
class BibUploadRecordsWithSYSNOTest(GenericBibUploadTest):
"""Testing uploading of records that have external SYSNO present."""
def setUp(self):
# pylint: disable=C0103
"""Initialize the MARCXML test records."""
GenericBibUploadTest.setUp(self)
# Note that SYSNO fields are repeated but with different
# subfields, this is to test whether bibupload would not
# mistakenly pick up wrong values.
self.xm_testrec1 = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="%(sysnosubfieldcode)s">sysno1</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="0">sysno2</subfield>
</datafield>
</record>
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or " ",
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or " ",
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.hm_testrec1 = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$%(sysnosubfieldcode)ssysno1
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$0sysno2
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4],
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5],
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.xm_testrec1_to_update = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="%(sysnosubfieldcode)s">sysno1</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="0">sysno2</subfield>
</datafield>
</record>
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or " ",
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or " ",
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.xm_testrec1_updated = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="%(sysnosubfieldcode)s">sysno1</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="0">sysno2</subfield>
</datafield>
</record>
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or " ",
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or " ",
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.hm_testrec1_updated = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1 Updated
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$%(sysnosubfieldcode)ssysno1
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$0sysno2
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4],
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5],
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.xm_testrec2 = """
<record>
<controlfield tag="001">987654321</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 2</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="%(sysnosubfieldcode)s">sysno2</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="0">sysno1</subfield>
</datafield>
</record>
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or " ",
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or " ",
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.hm_testrec2 = """
001__ 987654321
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 2
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$%(sysnosubfieldcode)ssysno2
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$0sysno1
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4],
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5],
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
def test_insert_the_same_sysno_record(self):
"""bibupload - SYSNO tag, refuse to insert the same SYSNO record"""
# initialize bibupload mode:
if self.verbose:
print "test_insert_the_same_sysno_record() started"
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# insert record 2 first time:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid2, 'xm')
inserted_hm = print_record(recid2, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec2 = self.xm_testrec2.replace('987654321', str(recid2))
self.hm_testrec2 = self.hm_testrec2.replace('987654321', str(recid2))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec2), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec2), '')
# try to insert updated record 1, it should fail:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='insert')
self.assertEqual(-1, recid1_updated)
if self.verbose:
print "test_insert_the_same_sysno_record() finished"
def test_insert_or_replace_the_same_sysno_record(self):
"""bibupload - SYSNO tag, allow to insert or replace the same SYSNO record"""
# initialize bibupload mode:
if self.verbose:
print "test_insert_or_replace_the_same_sysno_record() started"
# insert/replace record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# try to insert/replace updated record 1, it should be okay:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0],
opt_mode='replace_or_insert')
inserted_xm = print_record(recid1_updated, 'xm')
inserted_hm = print_record(recid1_updated, 'hm')
self.assertEqual(recid1, recid1_updated)
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1_updated = self.xm_testrec1_updated.replace('123456789', str(recid1))
self.hm_testrec1_updated = self.hm_testrec1_updated.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1_updated), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1_updated), '')
if self.verbose:
print "test_insert_or_replace_the_same_sysno_record() finished"
def test_replace_nonexisting_sysno_record(self):
"""bibupload - SYSNO tag, refuse to replace non-existing SYSNO record"""
# initialize bibupload mode:
if self.verbose:
print "test_replace_nonexisting_sysno_record() started"
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# try to replace record 2 it should fail:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='replace')
self.assertEqual(-1, recid2)
if self.verbose:
print "test_replace_nonexisting_sysno_record() finished"
class BibUploadRecordsWithEXTOAIIDTest(GenericBibUploadTest):
"""Testing uploading of records that have external EXTOAIID present."""
def setUp(self):
# pylint: disable=C0103
"""Initialize the MARCXML test records."""
GenericBibUploadTest.setUp(self)
# Note that EXTOAIID fields are repeated but with different
# subfields, this is to test whether bibupload would not
# mistakenly pick up wrong values.
self.xm_testrec1 = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">extoaiid1</subfield>
<subfield code="%(extoaisrcsubfieldcode)s">extoaisrc1</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="0">extoaiid2</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1</subfield>
</datafield>
</record>
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
'extoaisrcsubfieldcode' : CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG[5:6],
}
self.hm_testrec1 = """
001__ 123456789
003__ SzGeCERN
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$%(extoaisrcsubfieldcode)sextoaisrc1$$%(extoaiidsubfieldcode)sextoaiid1
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$0extoaiid2
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4],
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5],
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
'extoaisrcsubfieldcode' : CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG[5:6],
}
self.xm_testrec1_to_update = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">extoaiid1</subfield>
<subfield code="%(extoaisrcsubfieldcode)s">extoaisrc1</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="0">extoaiid2</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
</record>
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
'extoaisrcsubfieldcode' : CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG[5:6],
}
self.xm_testrec1_updated = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">extoaiid1</subfield>
<subfield code="%(extoaisrcsubfieldcode)s">extoaisrc1</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="0">extoaiid2</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
</record>
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
'extoaisrcsubfieldcode' : CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG[5:6],
}
self.hm_testrec1_updated = """
001__ 123456789
003__ SzGeCERN
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$%(extoaisrcsubfieldcode)sextoaisrc1$$%(extoaiidsubfieldcode)sextoaiid1
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$0extoaiid2
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1 Updated
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4],
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5],
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
'extoaisrcsubfieldcode' : CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG[5:6],
}
self.xm_testrec2 = """
<record>
<controlfield tag="001">987654321</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">extoaiid2</subfield>
<subfield code="%(extoaisrcsubfieldcode)s">extoaisrc1</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="0">extoaiid1</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 2</subfield>
</datafield>
</record>
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
'extoaisrcsubfieldcode' : CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG[5:6],
}
self.hm_testrec2 = """
001__ 987654321
003__ SzGeCERN
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$%(extoaisrcsubfieldcode)sextoaisrc1$$%(extoaiidsubfieldcode)sextoaiid2
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$0extoaiid1
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 2
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4],
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5],
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
'extoaisrcsubfieldcode' : CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG[5:6],
}
def test_insert_the_same_extoaiid_record(self):
"""bibupload - EXTOAIID tag, refuse to insert the same EXTOAIID record"""
# initialize bibupload mode:
if self.verbose:
print "test_insert_the_same_extoaiid_record() started"
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# insert record 2 first time:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid2, 'xm')
inserted_hm = print_record(recid2, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec2 = self.xm_testrec2.replace('987654321', str(recid2))
self.hm_testrec2 = self.hm_testrec2.replace('987654321', str(recid2))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec2), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec2), '')
# try to insert updated record 1, it should fail:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='insert')
self.assertEqual(-1, recid1_updated)
if self.verbose:
print "test_insert_the_same_extoaiid_record() finished"
def test_insert_or_replace_the_same_extoaiid_record(self):
"""bibupload - EXTOAIID tag, allow to insert or replace the same EXTOAIID record"""
# initialize bibupload mode:
if self.verbose:
print "test_insert_or_replace_the_same_extoaiid_record() started"
# insert/replace record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# try to insert/replace updated record 1, it should be okay:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1_updated, 'xm')
inserted_hm = print_record(recid1_updated, 'hm')
self.assertEqual(recid1, recid1_updated)
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1_updated = self.xm_testrec1_updated.replace('123456789', str(recid1))
self.hm_testrec1_updated = self.hm_testrec1_updated.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1_updated), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1_updated), '')
if self.verbose:
print "test_insert_or_replace_the_same_extoaiid_record() finished"
def test_replace_nonexisting_extoaiid_record(self):
"""bibupload - EXTOAIID tag, refuse to replace non-existing EXTOAIID record"""
# initialize bibupload mode:
if self.verbose:
print "test_replace_nonexisting_extoaiid_record() started"
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# try to replace record 2 it should fail:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='replace')
self.assertEqual(-1, recid2)
if self.verbose:
print "test_replace_nonexisting_extoaiid_record() finished"
class BibUploadRecordsWithOAIIDTest(GenericBibUploadTest):
"""Testing uploading of records that have OAI ID present."""
def setUp(self):
"""Initialize the MARCXML test records."""
GenericBibUploadTest.setUp(self)
# Note that OAI fields are repeated but with different
# subfields, this is to test whether bibupload would not
# mistakenly pick up wrong values.
GenericBibUploadTest.setUp(self)
self.xm_testrec1 = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="%(oaisubfieldcode)s">oai:foo:1</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="0">oai:foo:2</subfield>
</datafield>
</record>
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or " ",
'oaiind2': CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or " ",
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.hm_testrec1 = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1
%(oaitag)s%(oaiind1)s%(oaiind2)s $$%(oaisubfieldcode)soai:foo:1
%(oaitag)s%(oaiind1)s%(oaiind2)s $$0oai:foo:2
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4],
'oaiind2': CFG_OAI_ID_FIELD[4:5],
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.xm_testrec1_to_update = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="%(oaisubfieldcode)s">oai:foo:1</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="0">oai:foo:2</subfield>
</datafield>
</record>
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or " ",
'oaiind2': CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or " ",
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.xm_testrec1_updated = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="%(oaisubfieldcode)s">oai:foo:1</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="0">oai:foo:2</subfield>
</datafield>
</record>
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or " ",
'oaiind2': CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or " ",
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.hm_testrec1_updated = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1 Updated
%(oaitag)s%(oaiind1)s%(oaiind2)s $$%(oaisubfieldcode)soai:foo:1
%(oaitag)s%(oaiind1)s%(oaiind2)s $$0oai:foo:2
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4],
'oaiind2': CFG_OAI_ID_FIELD[4:5],
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.xm_testrec2 = """
<record>
<controlfield tag="001">987654321</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 2</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="%(oaisubfieldcode)s">oai:foo:2</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="0">oai:foo:1</subfield>
</datafield>
</record>
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or " ",
'oaiind2': CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or " ",
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.hm_testrec2 = """
001__ 987654321
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 2
%(oaitag)s%(oaiind1)s%(oaiind2)s $$%(oaisubfieldcode)soai:foo:2
%(oaitag)s%(oaiind1)s%(oaiind2)s $$0oai:foo:1
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4],
'oaiind2': CFG_OAI_ID_FIELD[4:5],
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
def test_insert_the_same_oai_record(self):
"""bibupload - OAIID tag, refuse to insert the same OAI record"""
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# insert record 2 first time:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid2, 'xm')
inserted_hm = print_record(recid2, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec2 = self.xm_testrec2.replace('987654321', str(recid2))
self.hm_testrec2 = self.hm_testrec2.replace('987654321', str(recid2))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec2), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec2), '')
# try to insert updated record 1, it should fail:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='insert')
self.assertEqual(-1, recid1_updated)
def test_insert_or_replace_the_same_oai_record(self):
"""bibupload - OAIID tag, allow to insert or replace the same OAI record"""
# initialize bibupload mode:
# insert/replace record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# try to insert/replace updated record 1, it should be okay:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1_updated, 'xm')
inserted_hm = print_record(recid1_updated, 'hm')
self.assertEqual(recid1, recid1_updated)
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1_updated = self.xm_testrec1_updated.replace('123456789', str(recid1))
self.hm_testrec1_updated = self.hm_testrec1_updated.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1_updated), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1_updated), '')
def test_replace_nonexisting_oai_record(self):
"""bibupload - OAIID tag, refuse to replace non-existing OAI record"""
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.assertEqual(compare_xmbuffers(inserted_xm,
self.xm_testrec1), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
self.hm_testrec1), '')
# try to replace record 2 it should fail:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='replace')
self.assertEqual(-1, recid2)
class BibUploadIndicatorsTest(GenericBibUploadTest):
"""
Testing uploading of a MARCXML record with indicators having
either blank space (as per MARC schema) or empty string value (old
behaviour).
"""
def setUp(self):
"""Initialize the MARCXML test record."""
GenericBibUploadTest.setUp(self)
self.testrec1_xm = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
003__ SzGeCERN
100__ $$aTest, John$$uTest University
"""
self.testrec2_xm = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1="" ind2="">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
self.testrec2_hm = """
003__ SzGeCERN
100__ $$aTest, John$$uTest University
"""
def test_record_with_spaces_in_indicators(self):
"""bibupload - inserting MARCXML with spaces in indicators"""
recs = bibupload.xml_marc_to_records(self.testrec1_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(remove_tag_001_from_xmbuffer(inserted_xm),
self.testrec1_xm), '')
self.assertEqual(compare_hmbuffers(remove_tag_001_from_hmbuffer(inserted_hm),
self.testrec1_hm), '')
def test_record_with_no_spaces_in_indicators(self):
"""bibupload - inserting MARCXML with no spaces in indicators"""
recs = bibupload.xml_marc_to_records(self.testrec2_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(remove_tag_001_from_xmbuffer(inserted_xm),
self.testrec2_xm), '')
self.assertEqual(compare_hmbuffers(remove_tag_001_from_hmbuffer(inserted_hm),
self.testrec2_hm), '')
class BibUploadUpperLowerCaseTest(GenericBibUploadTest):
"""
Testing treatment of similar records with only upper and lower
case value differences in the bibxxx table.
"""
def setUp(self):
"""Initialize the MARCXML test records."""
GenericBibUploadTest.setUp(self)
self.testrec1_xm = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
003__ SzGeCERN
100__ $$aTest, John$$uTest University
"""
self.testrec2_xm = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1="" ind2="">
<subfield code="a">TeSt, JoHn</subfield>
<subfield code="u">Test UniVeRsity</subfield>
</datafield>
</record>
"""
self.testrec2_hm = """
003__ SzGeCERN
100__ $$aTeSt, JoHn$$uTest UniVeRsity
"""
def test_record_with_upper_lower_case_letters(self):
"""bibupload - inserting similar MARCXML records with upper/lower case"""
# insert test record #1:
recs = bibupload.xml_marc_to_records(self.testrec1_xm)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='insert')
recid1_inserted_xm = print_record(recid1, 'xm')
recid1_inserted_hm = print_record(recid1, 'hm')
# insert test record #2:
recs = bibupload.xml_marc_to_records(self.testrec2_xm)
err1, recid2 = bibupload.bibupload(recs[0], opt_mode='insert')
recid2_inserted_xm = print_record(recid2, 'xm')
recid2_inserted_hm = print_record(recid2, 'hm')
# let us compare stuff now:
self.assertEqual(compare_xmbuffers(remove_tag_001_from_xmbuffer(recid1_inserted_xm),
self.testrec1_xm), '')
self.assertEqual(compare_hmbuffers(remove_tag_001_from_hmbuffer(recid1_inserted_hm),
self.testrec1_hm), '')
self.assertEqual(compare_xmbuffers(remove_tag_001_from_xmbuffer(recid2_inserted_xm),
self.testrec2_xm), '')
self.assertEqual(compare_hmbuffers(remove_tag_001_from_hmbuffer(recid2_inserted_hm),
self.testrec2_hm), '')
class BibUploadControlledProvenanceTest(GenericBibUploadTest):
"""Testing treatment of tags under controlled provenance in the correct mode."""
def setUp(self):
"""Initialize the MARCXML test record."""
GenericBibUploadTest.setUp(self)
self.testrec1_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Test title</subfield>
</datafield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">blabla</subfield>
<subfield code="9">sam</subfield>
</datafield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">blublu</subfield>
<subfield code="9">sim</subfield>
</datafield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">human</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
245__ $$aTest title
6531_ $$9sam$$ablabla
6531_ $$9sim$$ablublu
6531_ $$ahuman
"""
self.testrec1_xm_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">bleble</subfield>
<subfield code="9">sim</subfield>
</datafield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">bloblo</subfield>
<subfield code="9">som</subfield>
</datafield>
</record>
"""
self.testrec1_corrected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Test title</subfield>
</datafield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">blabla</subfield>
<subfield code="9">sam</subfield>
</datafield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">human</subfield>
</datafield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">bleble</subfield>
<subfield code="9">sim</subfield>
</datafield>
<datafield tag="653" ind1="1" ind2=" ">
<subfield code="a">bloblo</subfield>
<subfield code="9">som</subfield>
</datafield>
</record>
"""
self.testrec1_corrected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
245__ $$aTest title
6531_ $$9sam$$ablabla
6531_ $$ahuman
6531_ $$9sim$$ableble
6531_ $$9som$$abloblo
"""
# insert test record:
test_record_xm = self.testrec1_xm.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_record_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.testrec1_xm = self.testrec1_xm.replace('123456789', str(recid))
self.testrec1_hm = self.testrec1_hm.replace('123456789', str(recid))
self.testrec1_xm_to_correct = self.testrec1_xm_to_correct.replace('123456789', str(recid))
self.testrec1_corrected_xm = self.testrec1_corrected_xm.replace('123456789', str(recid))
self.testrec1_corrected_hm = self.testrec1_corrected_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm, self.testrec1_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm, self.testrec1_hm), '')
def test_controlled_provenance_persistence(self):
"""bibupload - correct mode, tags with controlled provenance"""
# correct metadata tags; will the protected tags be kept?
recs = bibupload.xml_marc_to_records(self.testrec1_xm_to_correct)
err, recid = bibupload.bibupload(recs[0], opt_mode='correct')
corrected_xm = print_record(recid, 'xm')
corrected_hm = print_record(recid, 'hm')
# did it work?
self.assertEqual(compare_xmbuffers(corrected_xm, self.testrec1_corrected_xm), '')
self.assertEqual(compare_hmbuffers(corrected_hm, self.testrec1_corrected_hm), '')
class BibUploadStrongTagsTest(GenericBibUploadTest):
"""Testing treatment of strong tags and the replace mode."""
def setUp(self):
"""Initialize the MARCXML test record."""
GenericBibUploadTest.setUp(self)
self.testrec1_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Test title</subfield>
</datafield>
<datafield tag="%(strong_tag)s" ind1=" " ind2=" ">
<subfield code="a">A value</subfield>
<subfield code="b">Another value</subfield>
</datafield>
</record>
""" % {'strong_tag': bibupload.CFG_BIBUPLOAD_STRONG_TAGS[0]}
self.testrec1_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
245__ $$aTest title
%(strong_tag)s__ $$aA value$$bAnother value
""" % {'strong_tag': bibupload.CFG_BIBUPLOAD_STRONG_TAGS[0]}
self.testrec1_xm_to_replace = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
</record>
"""
self.testrec1_replaced_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="%(strong_tag)s" ind1=" " ind2=" ">
<subfield code="a">A value</subfield>
<subfield code="b">Another value</subfield>
</datafield>
</record>
""" % {'strong_tag': bibupload.CFG_BIBUPLOAD_STRONG_TAGS[0]}
self.testrec1_replaced_hm = """
001__ 123456789
100__ $$aTest, Joseph$$uTest Academy
%(strong_tag)s__ $$aA value$$bAnother value
""" % {'strong_tag': bibupload.CFG_BIBUPLOAD_STRONG_TAGS[0]}
# insert test record:
test_record_xm = self.testrec1_xm.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_record_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.testrec1_xm = self.testrec1_xm.replace('123456789', str(recid))
self.testrec1_hm = self.testrec1_hm.replace('123456789', str(recid))
self.testrec1_xm_to_replace = self.testrec1_xm_to_replace.replace('123456789', str(recid))
self.testrec1_replaced_xm = self.testrec1_replaced_xm.replace('123456789', str(recid))
self.testrec1_replaced_hm = self.testrec1_replaced_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm, self.testrec1_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm, self.testrec1_hm), '')
def test_strong_tags_persistence(self):
"""bibupload - strong tags, persistence in replace mode"""
# replace all metadata tags; will the strong tags be kept?
recs = bibupload.xml_marc_to_records(self.testrec1_xm_to_replace)
err, recid = bibupload.bibupload(recs[0], opt_mode='replace')
replaced_xm = print_record(recid, 'xm')
replaced_hm = print_record(recid, 'hm')
# did it work?
self.assertEqual(compare_xmbuffers(replaced_xm, self.testrec1_replaced_xm), '')
self.assertEqual(compare_hmbuffers(replaced_hm, self.testrec1_replaced_hm), '')
class BibUploadPretendTest(GenericBibUploadTest):
"""
Testing bibupload --pretend correctness.
"""
def setUp(self):
GenericBibUploadTest.setUp(self)
self.demo_data = bibupload.xml_marc_to_records(open(os.path.join(CFG_TMPDIR, 'demobibdata.xml')).read())[0]
self.before = self._get_tables_fingerprint()
task_set_task_param('pretend', True)
def tearDown(self):
task_set_task_param('pretend', False)
def _get_tables_fingerprint():
"""
Take lenght and last modification time of all the tables that
might be touched by bibupload and return them in a nice structure.
"""
fingerprint = {}
tables = ['bibrec', 'bibdoc', 'bibrec_bibdoc', 'bibdoc_bibdoc', 'bibfmt', 'hstDOCUMENT', 'hstRECORD']
for i in xrange(100):
tables.append('bib%02dx' % i)
tables.append('bibrec_bib%02dx' % i)
for table in tables:
fingerprint[table] = get_table_status_info(table)
return fingerprint
_get_tables_fingerprint = staticmethod(_get_tables_fingerprint)
def _checks_tables_fingerprints(before, after):
"""
Checks differences in table_fingerprints.
"""
err = True
for table in before.keys():
if before[table] != after[table]:
print >> sys.stderr, "Table %s has been modified: before was [%s], after was [%s]" % (table, pprint.pformat(before[table]), pprint.pformat(after[table]))
err = False
return err
_checks_tables_fingerprints = staticmethod(_checks_tables_fingerprints)
def test_pretend_insert(self):
"""bibupload - pretend insert"""
bibupload.bibupload(self.demo_data, opt_mode='insert', pretend=True)
self.failUnless(self._checks_tables_fingerprints(self.before, self._get_tables_fingerprint()))
def test_pretend_correct(self):
"""bibupload - pretend correct"""
bibupload.bibupload(self.demo_data, opt_mode='correct', pretend=True)
self.failUnless(self._checks_tables_fingerprints(self.before, self._get_tables_fingerprint()))
def test_pretend_replace(self):
"""bibupload - pretend replace"""
bibupload.bibupload(self.demo_data, opt_mode='replace', pretend=True)
self.failUnless(self._checks_tables_fingerprints(self.before, self._get_tables_fingerprint()))
def test_pretend_append(self):
"""bibupload - pretend append"""
bibupload.bibupload(self.demo_data, opt_mode='append', pretend=True)
self.failUnless(self._checks_tables_fingerprints(self.before, self._get_tables_fingerprint()))
def test_pretend_replace_or_insert(self):
"""bibupload - pretend replace or insert"""
bibupload.bibupload(self.demo_data, opt_mode='replace_or_insert', pretend=True)
self.failUnless(self._checks_tables_fingerprints(self.before, self._get_tables_fingerprint()))
def test_pretend_holdingpen(self):
"""bibupload - pretend holdingpen"""
bibupload.bibupload(self.demo_data, opt_mode='holdingpen', pretend=True)
self.failUnless(self._checks_tables_fingerprints(self.before, self._get_tables_fingerprint()))
def test_pretend_delete(self):
"""bibupload - pretend delete"""
bibupload.bibupload(self.demo_data, opt_mode='delete', pretend=True)
self.failUnless(self._checks_tables_fingerprints(self.before, self._get_tables_fingerprint()))
def test_pretend_reference(self):
"""bibupload - pretend reference"""
bibupload.bibupload(self.demo_data, opt_mode='reference', pretend=True)
self.failUnless(self._checks_tables_fingerprints(self.before, self._get_tables_fingerprint()))
class BibUploadHoldingPenTest(GenericBibUploadTest):
"""
Testing the Holding Pen usage.
"""
def setUp(self):
GenericBibUploadTest.setUp(self)
self.verbose = 9
setup_loggers()
task_set_task_param('verbose', self.verbose)
self.recid = 10
self.oai_id = "oai:cds.cern.ch:CERN-EP-2001-094"
def test_holding_pen_upload_with_recid(self):
"""bibupload - holding pen upload with recid"""
test_to_upload = """<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<controlfield tag="001">%s</controlfield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Kleefeld, F</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Newcomer, Y</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Rupp, G</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Scadron, M D</subfield>
</datafield>
</record>
</collection>""" % self.recid
recs = bibupload.xml_marc_to_records(test_to_upload)
bibupload.insert_record_into_holding_pen(recs[0], "")
res = run_sql("SELECT changeset_xml FROM bibHOLDINGPEN WHERE id_bibrec=%s", (self.recid, ))
self.failUnless("Rupp, G" in res[0][0])
def test_holding_pen_upload_with_oai_id(self):
"""bibupload - holding pen upload with oai_id"""
test_to_upload = """<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Kleefeld, F</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Newcomer, Y</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Rupp, G</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Scadron, M D</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">%(value)s</subfield>
</datafield>
</record>
</collection>""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
'value': self.oai_id
}
recs = bibupload.xml_marc_to_records(test_to_upload)
bibupload.insert_record_into_holding_pen(recs[0], self.oai_id)
res = run_sql("SELECT changeset_xml FROM bibHOLDINGPEN WHERE id_bibrec=%s AND oai_id=%s", (self.recid, self.oai_id))
self.failUnless("Rupp, G" in res[0][0])
def tearDown(self):
GenericBibUploadTest.tearDown(self)
run_sql("DELETE FROM bibHOLDINGPEN WHERE id_bibrec=%s", (self.recid, ))
class BibUploadFFTModeTest(GenericBibUploadTest):
"""
Testing treatment of fulltext file transfer import mode.
"""
def _test_bibdoc_status(self, recid, docname, status):
res = run_sql('SELECT bd.status FROM bibrec_bibdoc as bb JOIN bibdoc as bd ON bb.id_bibdoc = bd.id WHERE bb.id_bibrec = %s AND bd.docname = %s', (recid, docname))
self.failUnless(res)
self.assertEqual(status, res[0][0])
def test_writing_rights(self):
"""bibupload - FFT has writing rights"""
self.failUnless(bibupload.writing_rights_p())
def test_simple_fft_insert(self):
"""bibupload - simple FFT insert"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
</record>
- """ % {'siteurl': CFG_SITE_URL}
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
- """ % {'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" \
- % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self.failUnless(try_url_download(testrec_expected_url))
def test_fft_insert_with_valid_embargo(self):
"""bibupload - FFT insert with valid embargo"""
# define the test case:
import time
future_date = time.strftime('%Y-%m-%d', time.gmtime(time.time() + 24 * 3600 * 2))
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="r">firerole: deny until '%(future_date)s'
allow any</subfield>
</datafield>
</record>
""" % {
'future_date': future_date,
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
</record>
- """ % {'siteurl': CFG_SITE_URL}
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
- """ % {'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" \
- % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
result = urlopen(testrec_expected_url).read()
self.failUnless("This file is restricted." in result, result)
def test_fft_insert_with_expired_embargo(self):
"""bibupload - FFT insert with expired embargo"""
# define the test case:
import time
past_date = time.strftime('%Y-%m-%d', time.gmtime(time.time() - 24 * 3600 * 2))
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">ARTICLE</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="r">firerole: deny until '%(past_date)s'
allow any</subfield>
</datafield>
</record>
""" % {
'past_date': past_date,
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">ARTICLE</subfield>
</datafield>
</record>
- """ % {'siteurl': CFG_SITE_URL}
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
980__ $$aARTICLE
- """ % {'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" \
- % {'siteurl': CFG_SITE_URL}
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
result = urlopen(testrec_expected_url).read()
self.failIf("If you already have an account, please login using the form below." in result, result)
self.assertEqual(test_web_page_content(testrec_expected_url, 'hyde', 'h123yde', expected_text='Authorization failure'), [])
force_webcoll(recid)
self.assertEqual(test_web_page_content(testrec_expected_url, 'hyde', 'h123yde', expected_text=urlopen("%(siteurl)s/img/site_logo.gif" % {
'siteurl': CFG_SITE_URL
}).read()), [])
def test_exotic_format_fft_append(self):
"""bibupload - exotic format FFT append"""
# define the test case:
testfile = os.path.join(CFG_TMPDIR, 'test.ps.Z')
open(testfile, 'w').write('TEST')
email_tag = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][0:3]
email_ind1 = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][3]
email_ind2 = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][4]
email_code = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][5]
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
</record>
""" % {
'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code}
testrec_to_append = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s</subfield>
</datafield>
</record>
""" % testfile
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/test.ps.Z</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/test.ps.Z</subfield>
</datafield>
</record>
""" % {'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
%(email_tag)s%(email_ind1)s%(email_ind2)s $$%(email_code)sjekyll@cds.cern.ch
- 8564_ $$u%(siteurl)s/record/123456789/files/test.ps.Z
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/test.ps.Z
""" % {'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'email_tag': email_tag,
'email_ind1': email_ind1 == ' ' and '_' or email_ind1,
'email_ind2': email_ind2 == ' ' and '_' or email_ind2,
'email_code': email_code}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/test.ps.Z" \
- % {'siteurl': CFG_SITE_URL}
- testrec_expected_url2 = "%(siteurl)s/record/123456789/files/test?format=ps.Z" \
- % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/test.ps.Z" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url2 = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/test?format=ps.Z" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_to_append = testrec_to_append.replace('123456789',
str(recid))
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
testrec_expected_url2 = testrec_expected_url.replace('123456789',
str(recid))
recs = bibupload.xml_marc_to_records(testrec_to_append)
err, recid = bibupload.bibupload(recs[0], opt_mode='append')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self.assertEqual(test_web_page_content(testrec_expected_url, 'jekyll', 'j123ekyll', expected_text='TEST'), [])
self.assertEqual(test_web_page_content(testrec_expected_url2, 'jekyll', 'j123ekyll', expected_text='TEST'), [])
def test_fft_check_md5_through_bibrecdoc_str(self):
"""bibupload - simple FFT insert, check md5 through BibRecDocs.str()"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s/img/head.gif</subfield>
</datafield>
</record>
""" % CFG_SITE_URL
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
original_md5 = md5(urlopen('%s/img/head.gif' % CFG_SITE_URL).read()).hexdigest()
bibrec_str = str(BibRecDocs(int(recid)))
md5_found = False
for row in bibrec_str.split('\n'):
if 'checksum' in row:
if original_md5 in row:
md5_found = True
self.failUnless(md5_found)
def test_detailed_fft_insert(self):
"""bibupload - detailed FFT insert"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="t">SuperMain</subfield>
<subfield code="d">This is a description</subfield>
<subfield code="z">This is a comment</subfield>
<subfield code="n">CIDIESSE</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="t">SuperMain</subfield>
<subfield code="f">.jpeg</subfield>
<subfield code="d">This is a description</subfield>
<subfield code="z">This is a second comment</subfield>
<subfield code="n">CIDIESSE</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/CIDIESSE.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/CIDIESSE.gif</subfield>
<subfield code="y">This is a description</subfield>
<subfield code="z">This is a comment</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/CIDIESSE.jpeg</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/CIDIESSE.jpeg</subfield>
<subfield code="y">This is a description</subfield>
<subfield code="z">This is a second comment</subfield>
</datafield>
</record>
- """ % {'siteurl': CFG_SITE_URL}
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/CIDIESSE.gif$$yThis is a description$$zThis is a comment
- 8564_ $$u%(siteurl)s/record/123456789/files/CIDIESSE.jpeg$$yThis is a description$$zThis is a second comment
- """ % {'siteurl': CFG_SITE_URL}
- testrec_expected_url1 = "%(siteurl)s/record/123456789/files/CIDIESSE.gif" % {'siteurl': CFG_SITE_URL}
- testrec_expected_url2 = "%(siteurl)s/record/123456789/files/CIDIESSE.jpeg" % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/CIDIESSE.gif$$yThis is a description$$zThis is a comment
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/CIDIESSE.jpeg$$yThis is a description$$zThis is a second comment
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url1 = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/CIDIESSE.gif" % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url2 = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/CIDIESSE.jpeg" % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url1 = testrec_expected_url1.replace('123456789',
str(recid))
testrec_expected_url2 = testrec_expected_url1.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self.failUnless(try_url_download(testrec_expected_url1))
self.failUnless(try_url_download(testrec_expected_url2))
def test_simple_fft_insert_with_restriction(self):
"""bibupload - simple FFT insert with restriction"""
# define the test case:
email_tag = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][0:3]
email_ind1 = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][3]
email_ind2 = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][4]
email_code = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][5]
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">ARTICLE</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="r">thesis</subfield>
<subfield code="x">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code,
'siteurl': CFG_SITE_URL}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon</subfield>
<subfield code="x">icon</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">ARTICLE</subfield>
</datafield>
</record>
""" % {'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
%(email_tag)s%(email_ind1)s%(email_ind2)s $$%(email_code)sjekyll@cds.cern.ch
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon$$xicon
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon$$xicon
980__ $$aARTICLE
""" % {'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'email_tag': email_tag,
'email_ind1': email_ind1 == ' ' and '_' or email_ind1,
'email_ind2': email_ind2 == ' ' and '_' or email_ind2,
'email_code': email_code}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" \
- % {'siteurl': CFG_SITE_URL}
- testrec_expected_icon = "%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon" \
- % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_icon = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
testrec_expected_icon = testrec_expected_icon.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self.assertEqual(test_web_page_content(testrec_expected_icon, 'jekyll', 'j123ekyll', expected_text=urlopen('%(siteurl)s/img/site_logo.gif' % {
'siteurl': CFG_SITE_URL
}).read()), [])
self.assertEqual(test_web_page_content(testrec_expected_icon, 'hyde', 'h123yde', expected_text='Authorization failure'), [])
force_webcoll(recid)
self.assertEqual(test_web_page_content(testrec_expected_icon, 'hyde', 'h123yde', expected_text=urlopen('%(siteurl)s/img/restricted.gif' % {'siteurl': CFG_SITE_URL}).read()), [])
self.failUnless("HTTP Error 401: Unauthorized" in test_web_page_content(testrec_expected_url, 'hyde', 'h123yde')[0])
self.failUnless("This file is restricted." in urlopen(testrec_expected_url).read())
def test_simple_fft_insert_with_icon(self):
"""bibupload - simple FFT insert with icon"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="x">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon</subfield>
<subfield code="x">icon</subfield>
</datafield>
</record>
- """ % {'siteurl': CFG_SITE_URL}
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon$$xicon
- """ % {'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" \
- % {'siteurl': CFG_SITE_URL}
- testrec_expected_icon = "%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon" \
- % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon$$xicon
+ """ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_icon = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
testrec_expected_icon = testrec_expected_icon.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(try_url_download(testrec_expected_icon))
def test_multiple_fft_insert(self):
"""bibupload - multiple FFT insert"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/head.gif</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
- <subfield code="a">%(siteurl)s/record/95/files/9809057.pdf</subfield>
+ <subfield code="a">%(siteurl)s/%(CFG_SITE_RECORD)s/95/files/9809057.pdf</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(prefix)s/var/tmp/demobibdata.xml</subfield>
</datafield>
</record>
""" % {
'prefix': CFG_PREFIX,
- 'siteurl': CFG_SITE_URL
+ 'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/9809057.pdf</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/9809057.pdf</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/demobibdata.xml</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/demobibdata.xml</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/head.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/head.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/9809057.pdf
- 8564_ $$u%(siteurl)s/record/123456789/files/demobibdata.xml
- 8564_ $$u%(siteurl)s/record/123456789/files/head.gif
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
- """ % { 'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/9809057.pdf
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/demobibdata.xml
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/head.gif
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
testrec_expected_urls = []
for files in ('site_logo.gif', 'head.gif', '9809057.pdf', 'demobibdata.xml'):
- testrec_expected_urls.append('%(siteurl)s/record/123456789/files/%(files)s' % {'siteurl' : CFG_SITE_URL, 'files' : files})
+ testrec_expected_urls.append('%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/%(files)s' % {'siteurl' : CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD, 'files' : files})
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_urls = []
for files in ('site_logo.gif', 'head.gif', '9809057.pdf', 'demobibdata.xml'):
- testrec_expected_urls.append('%(siteurl)s/record/%(recid)s/files/%(files)s' % {'siteurl' : CFG_SITE_URL, 'files' : files, 'recid' : recid})
+ testrec_expected_urls.append('%(siteurl)s/%(CFG_SITE_RECORD)s/%(recid)s/files/%(files)s' % {'siteurl' : CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD, 'files' : files, 'recid' : recid})
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
for url in testrec_expected_urls:
self.failUnless(try_url_download(url))
self._test_bibdoc_status(recid, 'head', '')
self._test_bibdoc_status(recid, '9809057', '')
self._test_bibdoc_status(recid, 'site_logo', '')
self._test_bibdoc_status(recid, 'demobibdata', '')
def test_simple_fft_correct(self):
"""bibupload - simple FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
- """ % { 'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" \
- % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self._test_bibdoc_status(recid, 'site_logo', '')
def test_fft_implicit_fix_marc(self):
"""bibupload - FFT implicit FIX-MARC"""
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="0" ind2=" ">
<subfield code="f">foo@bar.com</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="f">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="856" ind1="0" ind2=" ">
<subfield code="f">foo@bar.com</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="u">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="0" ind2=" ">
<subfield code="f">foo@bar.com</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="u">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
8560_ $$ffoo@bar.com
8564_ $$u%(siteurl)s/img/site_logo.gif
""" % {
'siteurl': CFG_SITE_URL
}
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
test_to_correct = test_to_correct.replace('123456789',
str(recid))
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
# correct test record with implicit FIX-MARC:
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
def test_fft_vs_bibedit(self):
"""bibupload - FFT Vs. BibEdit compatibility"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
test_to_replace = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="u">http://www.google.com/</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="z">BibEdit Comment</subfield>
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
<subfield code="y">BibEdit Description</subfield>
<subfield code="x">01</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="u">http://cern.ch/</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_xm = str(test_to_replace)
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
8564_ $$uhttp://www.google.com/
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif$$x01$$yBibEdit Description$$zBibEdit Comment
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif$$x01$$yBibEdit Description$$zBibEdit Comment
8564_ $$uhttp://cern.ch/
- """ % { 'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" \
- % {'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_replace = test_to_replace.replace('123456789',
str(recid))
# correct test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_replace)
bibupload.bibupload(recs[0], opt_mode='replace')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self._test_bibdoc_status(recid, 'site_logo', '')
bibrecdocs = BibRecDocs(recid)
bibdoc = bibrecdocs.get_bibdoc('site_logo')
self.assertEqual(bibdoc.get_description('.gif'), 'BibEdit Description')
def test_detailed_fft_correct(self):
"""bibupload - detailed FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="d">Try</subfield>
<subfield code="z">Comment</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/head.gif</subfield>
<subfield code="n">site_logo</subfield>
<subfield code="m">patata</subfield>
<subfield code="d">Next Try</subfield>
<subfield code="z">KEEP-OLD-VALUE</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/patata.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif</subfield>
<subfield code="y">Next Try</subfield>
<subfield code="z">Comment</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/patata.gif$$yNext Try$$zComment
- """ % { 'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/patata.gif" \
- % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif$$yNext Try$$zComment
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self._test_bibdoc_status(recid, 'patata', '')
def test_no_url_fft_correct(self):
"""bibupload - no_url FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="d">Try</subfield>
<subfield code="z">Comment</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="n">site_logo</subfield>
<subfield code="m">patata</subfield>
<subfield code="f">.gif</subfield>
<subfield code="d">KEEP-OLD-VALUE</subfield>
<subfield code="z">Next Comment</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/patata.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif</subfield>
<subfield code="y">Try</subfield>
<subfield code="z">Next Comment</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/patata.gif$$yTry$$zNext Comment
- """ % { 'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/patata.gif" \
- % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif$$yTry$$zNext Comment
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self._test_bibdoc_status(recid, 'patata', '')
def test_new_icon_fft_append(self):
"""bibupload - new icon FFT append"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="n">site_logo</subfield>
<subfield code="x">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon</subfield>
<subfield code="x">icon</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon$$xicon
- """ % { 'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif?subformat=icon" \
- % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon$$xicon
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif?subformat=icon" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='append')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self._test_bibdoc_status(recid, 'site_logo', '')
def test_multiple_fft_correct(self):
"""bibupload - multiple FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="d">Try</subfield>
<subfield code="z">Comment</subfield>
<subfield code="r">Restricted</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="f">.jpeg</subfield>
<subfield code="d">Try jpeg</subfield>
<subfield code="z">Comment jpeg</subfield>
<subfield code="r">Restricted</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="m">patata</subfield>
<subfield code="f">.gif</subfield>
<subfield code="r">New restricted</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/patata.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/patata.gif
- """ % { 'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/patata.gif" \
- % {'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/patata.gif" \
+ % {'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless("This file is restricted." in urlopen(testrec_expected_url).read())
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self._test_bibdoc_status(recid, 'patata', 'New restricted')
def test_purge_fft_correct(self):
"""bibupload - purge FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/head.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
test_to_purge = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/site_logo.gif</subfield>
<subfield code="t">PURGE</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL
}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/head.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/head.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
</record>
- """ % { 'siteurl': CFG_SITE_URL}
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(siteurl)s/record/123456789/files/head.gif
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
- """ % { 'siteurl': CFG_SITE_URL}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" % { 'siteurl': CFG_SITE_URL}
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/head.gif
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
+ """ % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
test_to_purge = test_to_purge.replace('123456789',
str(recid))
# correct test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# purge test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_purge)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self._test_bibdoc_status(recid, 'site_logo', '')
self._test_bibdoc_status(recid, 'head', '')
def test_revert_fft_correct(self):
"""bibupload - revert FFT correct"""
# define the test case:
email_tag = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][0:3]
email_ind1 = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][3]
email_ind2 = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][4]
email_code = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][5]
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/iconpen.gif</subfield>
<subfield code="n">site_logo</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL,
'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code}
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s/img/head.gif</subfield>
<subfield code="n">site_logo</subfield>
</datafield>
</record>
""" % CFG_SITE_URL
test_to_revert = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="n">site_logo</subfield>
<subfield code="t">REVERT</subfield>
<subfield code="v">1</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/site_logo.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif</subfield>
</datafield>
</record>
""" % {'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
%(email_tag)s%(email_ind1)s%(email_ind2)s $$%(email_code)sjekyll@cds.cern.ch
- 8564_ $$u%(siteurl)s/record/123456789/files/site_logo.gif
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif
""" % {'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'email_tag': email_tag,
'email_ind1': email_ind1 == ' ' and '_' or email_ind1,
'email_ind2': email_ind2 == ' ' and '_' or email_ind2,
'email_code': email_code}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/site_logo.gif" % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/site_logo.gif" % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
test_to_revert = test_to_revert.replace('123456789',
str(recid))
# correct test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# revert test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_revert)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
self._test_bibdoc_status(recid, 'site_logo', '')
expected_content_version1 = urlopen('%s/img/iconpen.gif' % CFG_SITE_URL).read()
expected_content_version2 = urlopen('%s/img/head.gif' % CFG_SITE_URL).read()
expected_content_version3 = expected_content_version1
- self.assertEqual(test_web_page_content('%s/record/%s/files/site_logo.gif?version=1' % (CFG_SITE_URL, recid), 'jekyll', 'j123ekyll', expected_content_version1), [])
- self.assertEqual(test_web_page_content('%s/record/%s/files/site_logo.gif?version=2' % (CFG_SITE_URL, recid), 'jekyll', 'j123ekyll', expected_content_version2), [])
- self.assertEqual(test_web_page_content('%s/record/%s/files/site_logo.gif?version=3' % (CFG_SITE_URL, recid), 'jekyll', 'j123ekyll', expected_content_version3), [])
+ self.assertEqual(test_web_page_content('%s/%s/%s/files/site_logo.gif?version=1' % (CFG_SITE_URL, CFG_SITE_RECORD, recid), 'jekyll', 'j123ekyll', expected_content_version1), [])
+ self.assertEqual(test_web_page_content('%s/%s/%s/files/site_logo.gif?version=2' % (CFG_SITE_URL, CFG_SITE_RECORD, recid), 'jekyll', 'j123ekyll', expected_content_version2), [])
+ self.assertEqual(test_web_page_content('%s/%s/%s/files/site_logo.gif?version=3' % (CFG_SITE_URL, CFG_SITE_RECORD, recid), 'jekyll', 'j123ekyll', expected_content_version3), [])
def test_simple_fft_replace(self):
"""bibupload - simple FFT replace"""
# define the test case:
email_tag = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][0:3]
email_ind1 = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][3]
email_ind2 = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][4]
email_code = CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS[0][5]
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/iconpen.gif</subfield>
<subfield code="n">site_logo</subfield>
</datafield>
</record>
""" % {'siteurl': CFG_SITE_URL,
'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code}
test_to_replace = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(siteurl)s/img/head.gif</subfield>
</datafield>
</record>
""" % {'siteurl': CFG_SITE_URL,
'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code}
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="%(email_tag)s" ind1="%(email_ind1)s" ind2="%(email_ind2)s">
<subfield code="%(email_code)s">jekyll@cds.cern.ch</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(siteurl)s/record/123456789/files/head.gif</subfield>
+ <subfield code="u">%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/head.gif</subfield>
</datafield>
</record>
""" % {
'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'email_tag': email_tag,
'email_ind1': email_ind1 == '_' and ' ' or email_ind1,
'email_ind2': email_ind2 == '_' and ' ' or email_ind2,
'email_code': email_code}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
%(email_tag)s%(email_ind1)s%(email_ind2)s $$%(email_code)sjekyll@cds.cern.ch
- 8564_ $$u%(siteurl)s/record/123456789/files/head.gif
+ 8564_ $$u%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/head.gif
""" % {
'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'email_tag': email_tag,
'email_ind1': email_ind1 == ' ' and '_' or email_ind1,
'email_ind2': email_ind2 == ' ' and '_' or email_ind2,
'email_code': email_code}
- testrec_expected_url = "%(siteurl)s/record/123456789/files/head.gif" % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/%(CFG_SITE_RECORD)s/123456789/files/head.gif" % { 'siteurl': CFG_SITE_URL, 'CFG_SITE_RECORD': CFG_SITE_RECORD}
# insert test record:
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_replace = test_to_replace.replace('123456789',
str(recid))
# replace test record with new FFT:
recs = bibupload.xml_marc_to_records(test_to_replace)
bibupload.bibupload(recs[0], opt_mode='replace')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.assertEqual(compare_xmbuffers(inserted_xm,
testrec_expected_xm), '')
self.assertEqual(compare_hmbuffers(inserted_hm,
testrec_expected_hm), '')
expected_content_version = urlopen('%s/img/head.gif' % CFG_SITE_URL).read()
self.assertEqual(test_web_page_content(testrec_expected_url, 'hyde', 'h123yde', expected_text='Authorization failure'), [])
self.assertEqual(test_web_page_content(testrec_expected_url, 'jekyll', 'j123ekyll', expected_text=expected_content_version), [])
TEST_SUITE = make_test_suite(BibUploadHoldingPenTest,
BibUploadInsertModeTest,
BibUploadAppendModeTest,
BibUploadCorrectModeTest,
BibUploadDeleteModeTest,
BibUploadReplaceModeTest,
BibUploadReferencesModeTest,
BibUploadRecordsWithSYSNOTest,
BibUploadRecordsWithEXTOAIIDTest,
BibUploadRecordsWithOAIIDTest,
BibUploadFMTModeTest,
BibUploadIndicatorsTest,
BibUploadUpperLowerCaseTest,
BibUploadControlledProvenanceTest,
BibUploadStrongTagsTest,
BibUploadFFTModeTest,
BibUploadPretendTest,
)
if __name__ == "__main__":
run_test_suite(TEST_SUITE, warn_user=True)
diff --git a/modules/miscutil/lib/invenio_connector.py b/modules/miscutil/lib/invenio_connector.py
index e12685e20..8fb1c09c7 100644
--- a/modules/miscutil/lib/invenio_connector.py
+++ b/modules/miscutil/lib/invenio_connector.py
@@ -1,455 +1,455 @@
# -*- coding: utf-8 -*-
#
## This file is part of Invenio.
## Copyright (C) 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Tools to connect to distant Invenio servers using Invenio APIs.
Example of use:
from InvenioConnector import *
cds = InvenioConnector("http://cdsweb.cern.ch")
results = cds.search("higgs")
for record in results:
print record["245__a"][0]
print record["520__b"][0]
for author in record["100__"]:
print author["a"][0], author["u"][0]
FIXME:
- implement cache expiration
- exceptions handling
- parsing of <!-- Search-Engine-Total-Number-Of-Results: N -->
- better checking of input parameters (especially InvenioConnector.__init__ "url")
- improve behaviour when running locally (perform_request_search *requiring* "req" object)
"""
import urllib
import urllib2
import xml.sax
import re
import tempfile
import os
import time
import sys
try:
# if we are running locally, we can optimize :-)
- from invenio.config import CFG_SITE_URL
+ from invenio.config import CFG_SITE_URL, CFG_SITE_RECORD
from invenio.bibtask import task_low_level_submission
from invenio.search_engine import perform_request_search, collection_restricted_p
from invenio.bibformat import format_records
LOCAL_SITE_URL = CFG_SITE_URL
except ImportError:
LOCAL_SITE_URL = None
class InvenioConnector:
"""
Creates an connector to a server running Invenio
"""
def __init__(self, url, local_import_path="invenio"):
"""
Initialize a new instance of the server at given URL.
If the server happens to be running on the local machine, the
access will be done directly using the Python APIs. In that case
you can choose from which base path to import the necessary file
specifying the local_import_path parameter.
Parameters:
url - *str* the url to which this instance will
be connected
local_import_path - *str* the base path from which the
connector should try to load the local
connector, if available. Eg "invenio"
will lead to "import invenio.dbquery"
"""
self.server_url = url
self.cached_queries = {}
self.cached_records = {}
self.cached_baskets = {}
def search(self, p="", f="", c=None, rg=10, sf="", so="d", sp="",
rm="", of="", ot="", p1="", f1="", m1="", op1="",
p2="", f2="", m2="", op2="", p3="", f3="", m3="",
jrec=0, recid=-1, recidb=-1, d1="", d1y=0, d1m=0,
d1d=0, d2="", d2y=0, d2m=0, d2d=0, dt="", ap=0,
read_cache=True):
"""
Returns records corresponding to the given search query.
"""
parse_results = False
if of == "":
parse_results = True
of = "xm"
params = {'p': p, 'f': f, 'c': c, 'rg': rg,
'sf': sf, 'so': so, 'sp': sp,
'rm': rm, 'of': of,
'p1':p1, 'f1': f1, 'm1': m1, 'op1': op1,
'p2': p2, 'f2': f2, 'm2': m2, 'op2': op2,
'p3': p3, 'f3': f3, 'm3': m3, 'jrec':jrec,
'd1': d1, 'd1y':d1y, 'd1m': d1m, 'd1d': d1d,
'd2': d2, 'd2y': d2y, 'd2m': d2m, 'd2d': d2d,
'dt': dt, 'ap': ap , 'recid': recid, 'recidb': recidb,
'ot': ot}
if recid == -1:
del params['recid']
if recidb == -1:
del params['recidb']
params = urllib.urlencode(params, doseq=1)
# Are we running locally? If so, better directly access the
# search engine directly
if LOCAL_SITE_URL == self.server_url and \
of != 't':
# See if user tries to search any restricted collection
if type(c) is list:
colls = c
else:
colls = [c]
for collection in colls:
if collection_restricted_p(collection):
sys.stderr.write("Searching local restricted collections\
is NOT allowed. Aborting search.\n")
return []
results = perform_request_search(p=p, f=f, c=c, rg=rg, sf=sf, so=so, sp=so, rm=rm,
p1=p1, f1=f1, m1=m1, op1=op1,
p2=p2, f2=f2, m2=m2, op2=op2,
p3=p3, f3=f3, m3=m3, jrec=jrec,
recid=recid, recidb=recidb, of='id', ot=ot,
d1=d1, d1y=d1y, d1m=d1m, d1d=d1d,
d2=d2, d2y=d2y, d2m=d2m, d2d=d2d, dt=dt, ap=ap)
if of.lower() != 'id':
results = format_records(results, of)
else:
if not self.cached_queries.has_key(params + str(parse_results)) or not read_cache:
results = urllib2.urlopen(self.server_url + "/search?" + params)
else:
return self.cached_queries[params + str(parse_results)]
if parse_results:
# FIXME: we should not try to parse if results is string
parsed_records = self._parse_results(results, self.cached_records)
self.cached_queries[params + str(parse_results)] = parsed_records
return parsed_records
else:
# pylint: disable=E1103
# The whole point of the following code is to make sure we can
# handle two types of variable.
try:
res = results.read()
except AttributeError:
res = results
# pylint: enable=E1103
if of == "id":
try:
if type(res) is str:
# Transform to list
res = [int(recid.strip()) for recid in \
res.strip("[]").split(",") if recid.strip() != ""]
res.reverse()
except (ValueError, AttributeError):
res = []
self.cached_queries[params + str(parse_results)] = res
return self.cached_queries[params + str(parse_results)]
def search_with_retry(self, sleeptime=3.0, retrycount=3, **params):
"""
This function performs a search given a dictionary of search(..)
parameters. It accounts for server timeouts as necessary and
will retry some number of times.
@param sleeptime: number of seconds to sleep between retries
@type sleeptime: float
@param retrycount: number of times to retry given search
@type retrycount: int
@param params: search parameters
@type params: **kwds
@rtype: list
@return: returns records in given format
"""
results = []
count = 0
while count < retrycount:
try:
results = self.search(**params)
break
except urllib2.URLError:
sys.stderr.write("Timeout while searching...Retrying\n")
time.sleep(sleeptime)
count += 1
else:
sys.stderr.write("Aborting search after %d attempts.\n" % (retrycount,))
return results
def search_similar_records(self, recid):
"""
Returns the records similar to the given one
"""
return self.search(p="recid:" + str(recid), rm="wrd")
def search_records_cited_by(self, recid):
"""
Returns records cited by the given one
"""
return self.search(p="recid:" + str(recid), rm="citation")
def get_records_from_basket(self, bskid, read_cache=True):
"""
Returns the records from the (public) basket with given bskid
"""
if not self.cached_baskets.has_key(bskid) or not read_cache:
results = urllib2.urlopen(self.server_url + \
"/yourbaskets/display_public?of=xm&bskid=" + str(bskid))
else:
return self.cached_baskets[bskid]
parsed_records = self._parse_results(results, self.cached_records)
self.cached_baskets[bskid] = parsed_records
return parsed_records
def get_record(self, recid, read_cache=True):
"""
Returns the record with given recid
"""
if self.cached_records.has_key(recid) or not read_cache:
return self.cached_records[recid]
else:
return self.search(p="recid:" + str(recid))
def upload_marcxml(self, marcxml, mode):
"""
Uploads a record to the server
Parameters:
marcxml - *str* the XML to upload.
mode - *str* the mode to use for the upload.
"-i" insert new records
"-r" replace existing records
"-c" correct fields of records
"-a" append fields to records
"-ir" insert record or replace if it exists
"""
if mode not in ["-i", "-r", "-c", "-a", "-ir"]:
raise NameError, "Incorrect mode " + str(mode)
# Are we running locally? If so, submit directly
if LOCAL_SITE_URL == self.server_url:
(code, marcxml_filepath) = tempfile.mkstemp(prefix="upload_%s" % \
time.strftime("%Y%m%d_%H%M%S_",
time.localtime()))
marcxml_file_d = os.fdopen(code, "w")
marcxml_file_d.write(marcxml)
marcxml_file_d.close()
return task_low_level_submission("bibupload", "", mode, marcxml_filepath)
else:
params = urllib.urlencode({'file': marcxml,
'mode': mode})
return urllib2.urlopen(self.server_url + "/batchuploader/robotupload", params)
def _parse_results(self, results, cached_records):
"""
Parses the given results (in MARCXML format).
The given "cached_records" list is a pool of
already existing parsed records (in order to
avoid keeping several times the same records in memory)
"""
parser = xml.sax.make_parser()
handler = RecordsHandler(cached_records)
parser.setContentHandler(handler)
parser.parse(results)
return handler.records
class Record(dict):
"""
Represents a Invenio record
"""
def __init__(self, recid=None, marcxml=None, server_url=None):
#dict.__init__(self)
self.recid = recid
self.marcxml = ""
if marcxml is not None:
self.marcxml = marcxml
#self.record = {}
self.server_url = server_url
def __setitem__(self, item, value):
tag, ind1, ind2, subcode = decompose_code(item)
if subcode is not None:
#if not dict.has_key(self, tag + ind1 + ind2):
# dict.__setitem__(self, tag + ind1 + ind2, [])
dict.__setitem__(self, tag + ind1 + ind2, [{subcode: [value]}])
else:
dict.__setitem__(self, tag + ind1 + ind2, value)
def __getitem__(self, item):
tag, ind1, ind2, subcode = decompose_code(item)
datafields = dict.__getitem__(self, tag + ind1 + ind2)
if subcode is not None:
subfields = []
for datafield in datafields:
if datafield.has_key(subcode):
subfields.extend(datafield[subcode])
return subfields
else:
return datafields
def __contains__(self, item):
return dict.__contains__(item)
def __repr__(self):
return "Record(" + dict.__repr__(self) + ")"
def __str__(self):
return self.marcxml
def export(self, of="marcxml"):
"""
Returns the record in chosen format
"""
return self.marcxml
def url(self):
"""
Returns the URL to this record.
Returns None if not known
"""
if self.server_url is not None and \
self.recid is not None:
- return self.server_url + "/record/" + str(self.recid)
+ return self.server_url + "/"+ CFG_SITE_RECORD +"/" + str(self.recid)
else:
return None
class RecordsHandler(xml.sax.handler.ContentHandler):
"MARCXML Parser"
def __init__(self, records):
"""
Parameters:
records - *dict* a dictionary with an already existing cache of records
"""
self.cached_records = records
self.records = []
self.in_record = False
self.in_controlfield = False
self.in_datafield = False
self.in_subfield = False
self.cur_tag = None
self.cur_subfield = None
self.cur_controlfield = None
self.cur_datafield = None
self.cur_record = None
self.recid = 0
self.buffer = ""
self.counts = 0
def startElement(self, name, attributes):
if name == "record":
self.cur_record = Record()
self.in_record = True
elif name == "controlfield":
tag = attributes["tag"]
self.cur_datafield = ""
self.cur_tag = tag
self.cur_controlfield = []
if not self.cur_record.has_key(tag):
self.cur_record[tag] = self.cur_controlfield
self.in_controlfield = True
elif name == "datafield":
tag = attributes["tag"]
self.cur_tag = tag
ind1 = attributes["ind1"]
if ind1 == " ": ind1 = "_"
ind2 = attributes["ind2"]
if ind2 == " ": ind2 = "_"
if not self.cur_record.has_key(tag + ind1 + ind2):
self.cur_record[tag + ind1 + ind2] = []
self.cur_datafield = {}
self.cur_record[tag + ind1 + ind2].append(self.cur_datafield)
self.in_datafield = True
elif name == "subfield":
subcode = attributes["code"]
if not self.cur_datafield.has_key(subcode):
self.cur_subfield = []
self.cur_datafield[subcode] = self.cur_subfield
else:
self.cur_subfield = self.cur_datafield[subcode]
self.in_subfield = True
def characters(self, data):
if self.in_subfield:
self.buffer += data
elif self.in_controlfield:
self.buffer += data
elif "Search-Engine-Total-Number-Of-Results:" in data:
print data
match_obj = re.search("\d+", data)
if match_obj:
print int(match_obj.group())
self.counts = int(match_obj.group())
def endElement(self, name):
if name == "record":
self.in_record = False
elif name == "controlfield":
if self.cur_tag == "001":
self.recid = int(self.buffer)
if self.cached_records.has_key(self.recid):
# Record has already been parsed, no need to add
pass
else:
# Add record to the global cache
self.cached_records[self.recid] = self.cur_record
# Add record to the ordered list of results
self.records.append(self.cached_records[self.recid])
self.cur_controlfield.append(self.buffer)
self.in_controlfield = False
self.buffer = ""
elif name == "datafield":
self.in_datafield = False
elif name == "subfield":
self.in_subfield = False
self.cur_subfield.append(self.buffer)
self.buffer = ""
def decompose_code(code):
"""
Decomposes a MARC "code" into tag, ind1, ind2, subcode
"""
code = "%-6s" % code
ind1 = code[3:4]
if ind1 == " ": ind1 = "_"
ind2 = code[4:5]
if ind2 == " ": ind2 = "_"
subcode = code[5:6]
if subcode == " ": subcode = None
return (code[0:3], ind1, ind2, subcode)
diff --git a/modules/miscutil/lib/inveniocfg.py b/modules/miscutil/lib/inveniocfg.py
index 2f967e4db..697f40de8 100644
--- a/modules/miscutil/lib/inveniocfg.py
+++ b/modules/miscutil/lib/inveniocfg.py
@@ -1,1232 +1,1233 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Invenio configuration and administration CLI tool.
Usage: inveniocfg [options]
General options:
-h, --help print this help
-V, --version print version number
Options to finish your installation:
--create-apache-conf create Apache configuration files
--create-tables create DB tables for Invenio
--load-webstat-conf load the WebStat configuration
--drop-tables drop DB tables of Invenio
--check-openoffice check for correctly set up of openoffice temporary directory
Options to set up and test a demo site:
--create-demo-site create demo site
--load-demo-records load demo records
--remove-demo-records remove demo records, keeping demo site
--drop-demo-site drop demo site configurations too
--run-unit-tests run unit test suite (needs demo site)
--run-regression-tests run regression test suite (needs demo site)
--run-web-tests run web tests in a browser (needs demo site, Firefox, Selenium IDE)
Options to update config files in situ:
--update-all perform all the update options
--update-config-py update config.py file from invenio.conf file
--update-dbquery-py update dbquery.py with DB credentials from invenio.conf
--update-dbexec update dbexec with DB credentials from invenio.conf
--update-bibconvert-tpl update bibconvert templates with CFG_SITE_URL from invenio.conf
--update-web-tests update web test cases with CFG_SITE_URL from invenio.conf
Options to update DB tables:
--reset-all perform all the reset options
--reset-sitename reset tables to take account of new CFG_SITE_NAME*
--reset-siteadminemail reset tables to take account of new CFG_SITE_ADMIN_EMAIL
--reset-fieldnames reset tables to take account of new I18N names from PO files
--reset-recstruct-cache reset record structure cache according to CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE
Options to help the work:
--list print names and values of all options from conf files
--get <some-opt> get value of a given option from conf files
--conf-dir </some/path> path to directory where invenio*.conf files are [optional]
--detect-system-details print system details such as Apache/Python/MySQL versions
"""
__revision__ = "$Id$"
from ConfigParser import ConfigParser
import os
import re
import shutil
import socket
import sys
def print_usage():
"""Print help."""
print __doc__
def print_version():
"""Print version information."""
print __revision__
def convert_conf_option(option_name, option_value):
"""
Convert conf option into Python config.py line, converting
values to ints or strings as appropriate.
"""
## 1) convert option name to uppercase:
option_name = option_name.upper()
## 2) convert option value to int or string:
if option_name in ['CFG_BIBUPLOAD_REFERENCE_TAG',
'CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG',
'CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG',
'CFG_BIBUPLOAD_EXTERNAL_OAIID_PROVENANCE_TAG',
'CFG_BIBUPLOAD_STRONG_TAGS',
'CFG_BIBFORMAT_HIDDEN_TAGS',]:
# some options are supposed be string even when they look like
# numeric
option_value = '"' + option_value + '"'
else:
try:
option_value = int(option_value)
except ValueError:
option_value = '"' + option_value + '"'
## 3a) special cases: chars regexps
if option_name in ['CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS',
'CFG_BIBINDEX_CHARS_PUNCTUATION']:
option_value = 'r"[' + option_value[1:-1] + ']"'
## 3abis) special cases: real regexps
if option_name in ['CFG_BIBINDEX_PERFORM_OCR_ON_DOCNAMES']:
option_value = 'r"' + option_value[1:-1] + '"'
## 3b) special cases: True, False, None
if option_value in ['"True"', '"False"', '"None"']:
option_value = option_value[1:-1]
## 3c) special cases: dicts
if option_name in ['CFG_WEBSEARCH_FIELDS_CONVERT',
'CFG_BATCHUPLOADER_WEB_ROBOT_RIGHTS',
'CFG_SITE_EMERGENCY_EMAIL_ADDRESSES',
'CFG_BIBMATCH_FUZZY_WORDLIMITS',
'CFG_BIBMATCH_QUERY_TEMPLATES',
'CFG_WEBSEARCH_SYNONYM_KBRS',
'CFG_BIBINDEX_SYNONYM_KBRS']:
option_value = option_value[1:-1]
## 3cbis) very special cases: dicts with backward compatible string
if option_name in ['CFG_BIBINDEX_SPLASH_PAGES']:
if option_value.startswith('"{') and option_value.endswith('}"'):
option_value = option_value[1:-1]
else:
option_value = """{%s: ".*"}""" % option_value
## 3d) special cases: comma-separated lists
if option_name in ['CFG_SITE_LANGS',
'CFG_WEBSUBMIT_ADDITIONAL_KNOWN_FILE_EXTENSIONS',
'CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS',
'CFG_BIBUPLOAD_STRONG_TAGS',
'CFG_BIBFORMAT_HIDDEN_TAGS',
'CFG_BIBSCHED_GC_TASKS_TO_REMOVE',
'CFG_BIBSCHED_GC_TASKS_TO_ARCHIVE',
'CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS',
'CFG_BIBUPLOAD_CONTROLLED_PROVENANCE_TAGS',
'CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES',
'CFG_WEBSTYLE_HTTP_STATUS_ALERT_LIST',
'CFG_WEBSEARCH_RSS_I18N_COLLECTIONS',
'CFG_BATCHUPLOADER_FILENAME_MATCHING_POLICY',
'CFG_BATCHUPLOADER_WEB_ROBOT_AGENT',
'CFG_BIBAUTHORID_EXTERNAL_CLAIMED_RECORDS_KEY']:
out = "["
for elem in option_value[1:-1].split(","):
if elem:
if option_name in ['CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES']:
# 3d1) integer values
out += "%i, " % int(elem)
else:
# 3d2) string values
out += "'%s', " % elem
out += "]"
option_value = out
## 3e) special cases: multiline
if option_name == 'CFG_OAI_IDENTIFY_DESCRIPTION':
# make triple quotes
option_value = '""' + option_value + '""'
## 3f) ignore some options:
if option_name.startswith('CFG_SITE_NAME_INTL'):
# treated elsewhere
return
## 3g) special cases: float
if option_name in ['CFG_BIBDOCFILE_MD5_CHECK_PROBABILITY',
'CFG_BIBMATCH_LOCAL_SLEEPTIME',
'CFG_BIBMATCH_REMOTE_SLEEPTIME',
'CFG_BIBAUTHORID_PERSONID_MIN_P_FROM_BCTKD_RA',
'CFG_BIBAUTHORID_PERSONID_MIN_P_FROM_NEW_RA',
'CFG_BIBAUTHORID_PERSONID_MAX_COMP_LIST_MIN_TRSH',
'CFG_BIBAUTHORID_PERSONID_MAX_COMP_LIST_MIN_TRSH_P_N']:
option_value = float(option_value[1:-1])
## 4) finally, return output line:
return '%s = %s' % (option_name, option_value)
def cli_cmd_update_config_py(conf):
"""
Update new config.py from conf options, keeping previous
config.py in a backup copy.
"""
print ">>> Going to update config.py..."
## location where config.py is:
configpyfile = conf.get("Invenio", "CFG_PYLIBDIR") + \
os.sep + 'invenio' + os.sep + 'config.py'
## backup current config.py file:
if os.path.exists(configpyfile):
shutil.copy(configpyfile, configpyfile + '.OLD')
## here we go:
fdesc = open(configpyfile, 'w')
## generate preamble:
fdesc.write("# -*- coding: utf-8 -*-\n")
fdesc.write("# DO NOT EDIT THIS FILE! IT WAS AUTOMATICALLY GENERATED\n")
fdesc.write("# FROM INVENIO.CONF BY EXECUTING:\n")
fdesc.write("# " + " ".join(sys.argv) + "\n")
## special treatment for CFG_SITE_NAME_INTL options:
fdesc.write("CFG_SITE_NAME_INTL = {}\n")
for lang in conf.get("Invenio", "CFG_SITE_LANGS").split(","):
fdesc.write("CFG_SITE_NAME_INTL['%s'] = \"%s\"\n" % (lang, conf.get("Invenio",
"CFG_SITE_NAME_INTL_" + lang)))
## special treatment for CFG_SITE_SECURE_URL that may be empty, in
## which case it should be put equal to CFG_SITE_URL:
if not conf.get("Invenio", "CFG_SITE_SECURE_URL"):
conf.set("Invenio", "CFG_SITE_SECURE_URL",
conf.get("Invenio", "CFG_SITE_URL"))
## process all the options normally:
sections = conf.sections()
sections.sort()
for section in sections:
options = conf.options(section)
options.sort()
for option in options:
if not option.startswith('CFG_DATABASE_'):
# put all options except for db credentials into config.py
line_out = convert_conf_option(option, conf.get(section, option))
if line_out:
fdesc.write(line_out + "\n")
## FIXME: special treatment for experimental variables
## CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES and CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE
## (not offering them in invenio.conf since they will be refactored)
fdesc.write("CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE = 0\n")
fdesc.write("CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES = [0, 1,]\n")
## generate postamble:
fdesc.write("")
fdesc.write("# END OF GENERATED FILE")
## we are done:
fdesc.close()
print "You may want to restart Apache now."
print ">>> config.py updated successfully."
def cli_cmd_update_dbquery_py(conf):
"""
Update lib/dbquery.py file with DB parameters read from conf file.
Note: this edits dbquery.py in situ, taking a backup first.
Use only when you know what you are doing.
"""
print ">>> Going to update dbquery.py..."
## location where dbquery.py is:
dbquerypyfile = conf.get("Invenio", "CFG_PYLIBDIR") + \
os.sep + 'invenio' + os.sep + 'dbquery.py'
## backup current dbquery.py file:
if os.path.exists(dbquerypyfile):
shutil.copy(dbquerypyfile, dbquerypyfile + '.OLD')
## replace db parameters:
out = ''
for line in open(dbquerypyfile, 'r').readlines():
match = re.search(r'^CFG_DATABASE_(HOST|PORT|NAME|USER|PASS)(\s*=\s*)\'.*\'$', line)
if match:
dbparam = 'CFG_DATABASE_' + match.group(1)
out += "%s%s'%s'\n" % (dbparam, match.group(2),
conf.get('Invenio', dbparam))
else:
out += line
fdesc = open(dbquerypyfile, 'w')
fdesc.write(out)
fdesc.close()
print "You may want to restart Apache now."
print ">>> dbquery.py updated successfully."
def cli_cmd_update_dbexec(conf):
"""
Update bin/dbexec file with DB parameters read from conf file.
Note: this edits dbexec in situ, taking a backup first.
Use only when you know what you are doing.
"""
print ">>> Going to update dbexec..."
## location where dbexec is:
dbexecfile = conf.get("Invenio", "CFG_BINDIR") + \
os.sep + 'dbexec'
## backup current dbexec file:
if os.path.exists(dbexecfile):
shutil.copy(dbexecfile, dbexecfile + '.OLD')
## replace db parameters via sed:
out = ''
for line in open(dbexecfile, 'r').readlines():
match = re.search(r'^CFG_DATABASE_(HOST|PORT|NAME|USER|PASS)(\s*=\s*)\'.*\'$', line)
if match:
dbparam = 'CFG_DATABASE_' + match.group(1)
out += "%s%s'%s'\n" % (dbparam, match.group(2),
conf.get("Invenio", dbparam))
else:
out += line
fdesc = open(dbexecfile, 'w')
fdesc.write(out)
fdesc.close()
print ">>> dbexec updated successfully."
def cli_cmd_update_bibconvert_tpl(conf):
"""
Update bibconvert/config/*.tpl files looking for 856
- http://.../record/ lines, replacing URL with CFG_SITE_URL taken
+ http://.../CFG_SITE_RECORD lines, replacing URL with CFG_SITE_URL taken
from conf file. Note: this edits tpl files in situ, taking a
backup first. Use only when you know what you are doing.
"""
print ">>> Going to update bibconvert templates..."
## location where bibconvert/config/*.tpl are:
tpldir = conf.get("Invenio", 'CFG_ETCDIR') + \
os.sep + 'bibconvert' + os.sep + 'config'
## find all *.tpl files:
for tplfilename in os.listdir(tpldir):
if tplfilename.endswith(".tpl"):
## change tpl file:
tplfile = tpldir + os.sep + tplfilename
shutil.copy(tplfile, tplfile + '.OLD')
out = ''
for line in open(tplfile, 'r').readlines():
- match = re.search(r'^(.*)http://.*?/record/(.*)$', line)
+ match = re.search(r'^(.*)http://.*?/%s/(.*)$' % conf.get("Invenio", 'CFG_SITE_RECORD'), line)
if match:
- out += "%s%s/record/%s\n" % (match.group(1),
+ out += "%s%s/%s/%s\n" % (match.group(1),
conf.get("Invenio", 'CFG_SITE_URL'),
+ conf.get("Invenio", 'CFG_SITE_RECORD'),
match.group(2))
else:
out += line
fdesc = open(tplfile, 'w')
fdesc.write(out)
fdesc.close()
print ">>> bibconvert templates updated successfully."
def cli_cmd_update_web_tests(conf):
"""
Update web test cases lib/webtest/test_*.html looking for
<td>http://.+?[</] strings and replacing them with CFG_SITE_URL
taken from conf file. Note: this edits test files in situ, taking
a backup first. Use only when you know what you are doing.
"""
print ">>> Going to update web tests..."
## location where test_*.html files are:
testdir = conf.get("Invenio", 'CFG_PREFIX') + os.sep + \
'lib' + os.sep + 'webtest' + os.sep + 'invenio'
## find all test_*.html files:
for testfilename in os.listdir(testdir):
if testfilename.startswith("test_") and \
testfilename.endswith(".html"):
## change test file:
testfile = testdir + os.sep + testfilename
shutil.copy(testfile, testfile + '.OLD')
out = ''
for line in open(testfile, 'r').readlines():
match = re.search(r'^(.*<td>)http://.+?([</].*)$', line)
if match:
out += "%s%s%s\n" % (match.group(1),
conf.get("Invenio", 'CFG_SITE_URL'),
match.group(2))
else:
match = re.search(r'^(.*<td>)/opt/invenio(.*)$', line)
if match:
out += "%s%s%s\n" % (match.group(1),
conf.get("Invenio", 'CFG_PREFIX'),
match.group(2))
else:
out += line
fdesc = open(testfile, 'w')
fdesc.write(out)
fdesc.close()
print ">>> web tests updated successfully."
def cli_cmd_reset_sitename(conf):
"""
Reset collection-related tables with new CFG_SITE_NAME and
CFG_SITE_NAME_INTL* read from conf files.
"""
print ">>> Going to reset CFG_SITE_NAME and CFG_SITE_NAME_INTL..."
from invenio.dbquery import run_sql, IntegrityError
# reset CFG_SITE_NAME:
sitename = conf.get("Invenio", "CFG_SITE_NAME")
try:
run_sql("""INSERT INTO collection (id, name, dbquery, reclist) VALUES
(1,%s,NULL,NULL)""", (sitename,))
except IntegrityError:
run_sql("""UPDATE collection SET name=%s WHERE id=1""", (sitename,))
# reset CFG_SITE_NAME_INTL:
for lang in conf.get("Invenio", "CFG_SITE_LANGS").split(","):
sitename_lang = conf.get("Invenio", "CFG_SITE_NAME_INTL_" + lang)
try:
run_sql("""INSERT INTO collectionname (id_collection, ln, type, value) VALUES
(%s,%s,%s,%s)""", (1, lang, 'ln', sitename_lang))
except IntegrityError:
run_sql("""UPDATE collectionname SET value=%s
WHERE ln=%s AND id_collection=1 AND type='ln'""",
(sitename_lang, lang))
print "You may want to restart Apache now."
print ">>> CFG_SITE_NAME and CFG_SITE_NAME_INTL* reset successfully."
def cli_cmd_reset_recstruct_cache(conf):
"""If CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE is changed, this function
will adapt the database to either store or not store the recstruct
format."""
from invenio.intbitset import intbitset
from invenio.dbquery import run_sql, serialize_via_marshal
from invenio.search_engine import get_record
from invenio.bibsched import server_pid, pidfile
enable_recstruct_cache = conf.get("Invenio", "CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE")
enable_recstruct_cache = enable_recstruct_cache in ('True', '1')
pid = server_pid(ping_the_process=False)
if pid:
print >> sys.stderr, "ERROR: bibsched seems to run with pid %d, according to %s." % (pid, pidfile)
print >> sys.stderr, " Please stop bibsched before running this procedure."
sys.exit(1)
if enable_recstruct_cache:
print ">>> Searching records which need recstruct cache resetting; this may take a while..."
all_recids = intbitset(run_sql("SELECT id FROM bibrec"))
good_recids = intbitset(run_sql("SELECT bibrec.id FROM bibrec JOIN bibfmt ON bibrec.id = bibfmt.id_bibrec WHERE format='recstruct' AND modification_date < last_updated"))
recids = all_recids - good_recids
print ">>> Generating recstruct cache..."
tot = len(recids)
count = 0
for recid in recids:
value = serialize_via_marshal(get_record(recid))
run_sql("DELETE FROM bibfmt WHERE id_bibrec=%s AND format='recstruct'", (recid, ))
run_sql("INSERT INTO bibfmt(id_bibrec, format, last_updated, value) VALUES(%s, 'recstruct', NOW(), %s)", (recid, value))
count += 1
if count % 1000 == 0:
print " ... done records %s/%s" % (count, tot)
if count % 1000 != 0:
print " ... done records %s/%s" % (count, tot)
print ">>> recstruct cache generated successfully."
else:
print ">>> Cleaning recstruct cache..."
run_sql("DELETE FROM bibfmt WHERE format='recstruct'")
def cli_cmd_reset_siteadminemail(conf):
"""
Reset user-related tables with new CFG_SITE_ADMIN_EMAIL read from conf files.
"""
print ">>> Going to reset CFG_SITE_ADMIN_EMAIL..."
from invenio.dbquery import run_sql
siteadminemail = conf.get("Invenio", "CFG_SITE_ADMIN_EMAIL")
run_sql("DELETE FROM user WHERE id=1")
run_sql("""INSERT INTO user (id, email, password, note, nickname) VALUES
(1, %s, AES_ENCRYPT(email, ''), 1, 'admin')""",
(siteadminemail,))
print "You may want to restart Apache now."
print ">>> CFG_SITE_ADMIN_EMAIL reset successfully."
def cli_cmd_reset_fieldnames(conf):
"""
Reset I18N field names such as author, title, etc and other I18N
ranking method names such as word similarity. Their translations
are taken from the PO files.
"""
print ">>> Going to reset I18N field names..."
from invenio.messages import gettext_set_language, language_list_long
from invenio.dbquery import run_sql, IntegrityError
## get field id and name list:
field_id_name_list = run_sql("SELECT id, name FROM field")
## get rankmethod id and name list:
rankmethod_id_name_list = run_sql("SELECT id, name FROM rnkMETHOD")
## update names for every language:
for lang, dummy in language_list_long():
_ = gettext_set_language(lang)
## this list is put here in order for PO system to pick names
## suitable for translation
field_name_names = {"any field": _("any field"),
"title": _("title"),
"author": _("author"),
"abstract": _("abstract"),
"keyword": _("keyword"),
"report number": _("report number"),
"subject": _("subject"),
"reference": _("reference"),
"fulltext": _("fulltext"),
"collection": _("collection"),
"division": _("division"),
"year": _("year"),
"journal": _("journal"),
"experiment": _("experiment"),
"record ID": _("record ID")}
## update I18N names for every language:
for (field_id, field_name) in field_id_name_list:
if field_name_names.has_key(field_name):
try:
run_sql("""INSERT INTO fieldname (id_field,ln,type,value) VALUES
(%s,%s,%s,%s)""", (field_id, lang, 'ln',
field_name_names[field_name]))
except IntegrityError:
run_sql("""UPDATE fieldname SET value=%s
WHERE id_field=%s AND ln=%s AND type=%s""",
(field_name_names[field_name], field_id, lang, 'ln',))
## ditto for rank methods:
rankmethod_name_names = {"wrd": _("word similarity"),
"demo_jif": _("journal impact factor"),
"citation": _("times cited"),
"citerank_citation_t": _("time-decay cite count"),
"citerank_pagerank_c": _("all-time-best cite rank"),
"citerank_pagerank_t": _("time-decay cite rank"),}
for (rankmethod_id, rankmethod_name) in rankmethod_id_name_list:
try:
run_sql("""INSERT INTO rnkMETHODNAME (id_rnkMETHOD,ln,type,value) VALUES
(%s,%s,%s,%s)""", (rankmethod_id, lang, 'ln',
rankmethod_name_names[rankmethod_name]))
except IntegrityError:
run_sql("""UPDATE rnkMETHODNAME SET value=%s
WHERE id_rnkMETHOD=%s AND ln=%s AND type=%s""",
(rankmethod_name_names[rankmethod_name], rankmethod_id, lang, 'ln',))
print ">>> I18N field names reset successfully."
def cli_check_openoffice(conf):
"""
If OpenOffice.org integration is enabled, checks whether the system is
properly configured.
"""
from invenio.bibtask import check_running_process_user
from invenio.websubmit_file_converter import can_unoconv, get_file_converter_logger
logger = get_file_converter_logger()
for handler in logger.handlers:
logger.removeHandler(handler)
check_running_process_user()
print ">>> Checking if Libre/OpenOffice.org is correctly integrated...",
sys.stdout.flush()
if can_unoconv(True):
print "ok"
else:
sys.exit(1)
def test_db_connection():
"""
Test DB connection, and if fails, advise user how to set it up.
Useful to be called during table creation.
"""
print "Testing DB connection...",
from invenio.textutils import wrap_text_in_a_box
from invenio.dbquery import run_sql, Error
## first, test connection to the DB server:
try:
run_sql("SHOW TABLES")
except Error, err:
from invenio.dbquery import CFG_DATABASE_HOST, CFG_DATABASE_PORT, \
CFG_DATABASE_NAME, CFG_DATABASE_USER, CFG_DATABASE_PASS
print wrap_text_in_a_box("""\
DATABASE CONNECTIVITY ERROR %(errno)d: %(errmsg)s.\n
Perhaps you need to set up database and connection rights?
If yes, then please login as MySQL admin user and run the
following commands now:
$ mysql -h %(dbhost)s -P %(dbport)s -u root -p mysql
mysql> CREATE DATABASE %(dbname)s DEFAULT CHARACTER SET utf8;
mysql> GRANT ALL PRIVILEGES ON %(dbname)s.*
TO %(dbuser)s@%(webhost)s IDENTIFIED BY '%(dbpass)s';
mysql> QUIT
The values printed above were detected from your
configuration. If they are not right, then please edit your
invenio-local.conf file and rerun 'inveniocfg --update-all' first.
If the problem is of different nature, then please inspect
the above error message and fix the problem before continuing.""" % \
{'errno': err.args[0],
'errmsg': err.args[1],
'dbname': CFG_DATABASE_NAME,
'dbhost': CFG_DATABASE_HOST,
'dbport': CFG_DATABASE_PORT,
'dbuser': CFG_DATABASE_USER,
'dbpass': CFG_DATABASE_PASS,
'webhost': CFG_DATABASE_HOST == 'localhost' and 'localhost' or os.popen('hostname -f', 'r').read().strip(),
})
sys.exit(1)
print "ok"
## second, test insert/select of a Unicode string to detect
## possible Python/MySQL/MySQLdb mis-setup:
print "Testing Python/MySQL/MySQLdb UTF-8 chain...",
try:
beta_in_utf8 = "β" # Greek beta in UTF-8 is 0xCEB2
run_sql("CREATE TEMPORARY TABLE test__invenio__utf8 (x char(1), y varbinary(2)) DEFAULT CHARACTER SET utf8")
run_sql("INSERT INTO test__invenio__utf8 (x, y) VALUES (%s, %s)", (beta_in_utf8, beta_in_utf8))
res = run_sql("SELECT x,y,HEX(x),HEX(y),LENGTH(x),LENGTH(y),CHAR_LENGTH(x),CHAR_LENGTH(y) FROM test__invenio__utf8")
assert res[0] == ('\xce\xb2', '\xce\xb2', 'CEB2', 'CEB2', 2L, 2L, 1L, 2L)
run_sql("DROP TEMPORARY TABLE test__invenio__utf8")
except Exception, err:
print wrap_text_in_a_box("""\
DATABASE RELATED ERROR %s\n
A problem was detected with the UTF-8 treatment in the chain
between the Python application, the MySQLdb connector, and
the MySQL database. You may perhaps have installed older
versions of some prerequisite packages?\n
Please check the INSTALL file and please fix this problem
before continuing.""" % err)
sys.exit(1)
print "ok"
def cli_cmd_create_tables(conf):
"""Create and fill Invenio DB tables. Useful for the installation process."""
print ">>> Going to create and fill tables..."
from invenio.config import CFG_PREFIX
test_db_connection()
for cmd in ["%s/bin/dbexec < %s/lib/sql/invenio/tabcreate.sql" % (CFG_PREFIX, CFG_PREFIX),
"%s/bin/dbexec < %s/lib/sql/invenio/tabfill.sql" % (CFG_PREFIX, CFG_PREFIX)]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
cli_cmd_reset_sitename(conf)
cli_cmd_reset_siteadminemail(conf)
cli_cmd_reset_fieldnames(conf)
for cmd in ["%s/bin/webaccessadmin -u admin -c -a" % CFG_PREFIX]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Tables created and filled successfully."
def cli_cmd_load_webstat_conf(conf):
print ">>> Going to load WebStat config..."
from invenio.config import CFG_PREFIX
cmd = "%s/bin/webstatadmin --load-config" % CFG_PREFIX
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> WebStat config load successfully."
def cli_cmd_drop_tables(conf):
"""Drop Invenio DB tables. Useful for the uninstallation process."""
print ">>> Going to drop tables..."
from invenio.config import CFG_PREFIX
from invenio.textutils import wrap_text_in_a_box, wait_for_user
wait_for_user(wrap_text_in_a_box("""WARNING: You are going to destroy
your database tables!"""))
cmd = "%s/bin/dbexec < %s/lib/sql/invenio/tabdrop.sql" % (CFG_PREFIX, CFG_PREFIX)
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Tables dropped successfully."
def cli_cmd_create_demo_site(conf):
"""Create demo site. Useful for testing purposes."""
print ">>> Going to create demo site..."
from invenio.config import CFG_PREFIX
from invenio.dbquery import run_sql
run_sql("TRUNCATE schTASK")
run_sql("TRUNCATE session")
run_sql("DELETE FROM user WHERE email=''")
for cmd in ["%s/bin/dbexec < %s/lib/sql/invenio/democfgdata.sql" % \
(CFG_PREFIX, CFG_PREFIX),]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
cli_cmd_reset_fieldnames(conf) # needed for I18N demo ranking method names
for cmd in ["%s/bin/webaccessadmin -u admin -c -r -D" % CFG_PREFIX,
"%s/bin/webcoll -u admin" % CFG_PREFIX,
"%s/bin/webcoll 1" % CFG_PREFIX,]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Demo site created successfully."
def cli_cmd_load_demo_records(conf):
"""Load demo records. Useful for testing purposes."""
from invenio.config import CFG_PREFIX
from invenio.dbquery import run_sql
print ">>> Going to load demo records..."
run_sql("TRUNCATE schTASK")
for cmd in ["%s/bin/bibupload -u admin -i %s/var/tmp/demobibdata.xml" % (CFG_PREFIX, CFG_PREFIX),
"%s/bin/bibupload 1" % CFG_PREFIX,
"%s/bin/bibdocfile --textify --with-ocr --recid 97" % CFG_PREFIX,
"%s/bin/bibdocfile --textify --all" % CFG_PREFIX,
"%s/bin/bibindex -u admin" % CFG_PREFIX,
"%s/bin/bibindex 2" % CFG_PREFIX,
"%s/bin/bibreformat -u admin -o HB" % CFG_PREFIX,
"%s/bin/bibreformat 3" % CFG_PREFIX,
"%s/bin/webcoll -u admin" % CFG_PREFIX,
"%s/bin/webcoll 4" % CFG_PREFIX,
"%s/bin/bibrank -u admin" % CFG_PREFIX,
"%s/bin/bibrank 5" % CFG_PREFIX,]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Demo records loaded successfully."
def cli_cmd_remove_demo_records(conf):
"""Remove demo records. Useful when you are finished testing."""
print ">>> Going to remove demo records..."
from invenio.config import CFG_PREFIX
from invenio.dbquery import run_sql
from invenio.textutils import wrap_text_in_a_box, wait_for_user
wait_for_user(wrap_text_in_a_box("""WARNING: You are going to destroy
your records and documents!"""))
if os.path.exists(CFG_PREFIX + os.sep + 'var' + os.sep + 'data'):
shutil.rmtree(CFG_PREFIX + os.sep + 'var' + os.sep + 'data')
run_sql("TRUNCATE schTASK")
for cmd in ["%s/bin/dbexec < %s/lib/sql/invenio/tabbibclean.sql" % (CFG_PREFIX, CFG_PREFIX),
"%s/bin/webcoll -u admin" % CFG_PREFIX,
"%s/bin/webcoll 1" % CFG_PREFIX,]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Demo records removed successfully."
def cli_cmd_drop_demo_site(conf):
"""Drop demo site completely. Useful when you are finished testing."""
print ">>> Going to drop demo site..."
from invenio.textutils import wrap_text_in_a_box, wait_for_user
wait_for_user(wrap_text_in_a_box("""WARNING: You are going to destroy
your site and documents!"""))
cli_cmd_drop_tables(conf)
cli_cmd_create_tables(conf)
cli_cmd_remove_demo_records(conf)
print ">>> Demo site dropped successfully."
def cli_cmd_run_unit_tests(conf):
"""Run unit tests, usually on the working demo site."""
from invenio.testutils import build_and_run_unit_test_suite
build_and_run_unit_test_suite()
def cli_cmd_run_regression_tests(conf):
"""Run regression tests, usually on the working demo site."""
from invenio.testutils import build_and_run_regression_test_suite
build_and_run_regression_test_suite()
def cli_cmd_run_web_tests(conf):
"""Run web tests in a browser. Requires Firefox with
Selenium IDE extension."""
from invenio.testutils import build_and_run_web_test_suite
build_and_run_web_test_suite()
def _detect_ip_address():
"""Detect IP address of this computer. Useful for creating Apache
vhost conf snippet on RHEL like machines.
@return: IP address, or '*' if cannot detect
@rtype: string
@note: creates socket for real in order to detect real IP address,
not the loopback one.
"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('invenio-software.org', 0))
return s.getsockname()[0]
except:
return '*'
def cli_cmd_create_apache_conf(conf):
"""
Create Apache conf files for this site, keeping previous
files in a backup copy.
"""
print ">>> Going to create Apache conf files..."
from invenio.textutils import wrap_text_in_a_box
apache_conf_dir = conf.get("Invenio", 'CFG_ETCDIR') + \
os.sep + 'apache'
## Preparation of XSendFile directive
xsendfile_directive_needed = int(conf.get("Invenio", 'CFG_BIBDOCFILE_USE_XSENDFILE')) != 0
if xsendfile_directive_needed:
xsendfile_directive = "XSendFile On\n"
else:
xsendfile_directive = "#XSendFile On\n"
for path in (conf.get('Invenio', 'CFG_WEBSUBMIT_FILEDIR'), # BibDocFile
conf.get('Invenio', 'CFG_WEBDIR'),
conf.get('Invenio', 'CFG_WEBSUBMIT_STORAGEDIR'), # WebSubmit
conf.get('Invenio', 'CFG_TMPDIR'),
os.path.join(conf.get('Invenio', 'CFG_PREFIX'), 'var', 'tmp', 'attachfile'),
os.path.join(conf.get('Invenio', 'CFG_PREFIX'), 'var', 'data', 'comments'),
os.path.join(conf.get('Invenio', 'CFG_PREFIX'), 'var', 'data', 'baskets', 'comments'),
'/tmp'): # BibExport
if xsendfile_directive_needed:
xsendfile_directive += ' XSendFilePath %s\n' % path
else:
xsendfile_directive += ' #XSendFilePath %s\n' % path
xsendfile_directive = xsendfile_directive.strip()
## Apache vhost conf file is distro specific, so analyze needs:
# Gentoo (and generic defaults):
listen_directive_needed = True
ssl_pem_directive_needed = False
ssl_pem_path = '/etc/apache2/ssl/apache.pem'
ssl_crt_path = '/etc/apache2/ssl/server.crt'
ssl_key_path = '/etc/apache2/ssl/server.key'
vhost_ip_address_needed = False
wsgi_socket_directive_needed = False
# Debian:
if os.path.exists(os.path.sep + 'etc' + os.path.sep + 'debian_version'):
listen_directive_needed = False
ssl_pem_directive_needed = True
# RHEL/SLC:
if os.path.exists(os.path.sep + 'etc' + os.path.sep + 'redhat-release'):
listen_directive_needed = False
ssl_crt_path = '/etc/pki/tls/certs/localhost.crt'
ssl_key_path = '/etc/pki/tls/private/localhost.key'
vhost_ip_address_needed = True
wsgi_socket_directive_needed = True
## okay, let's create Apache vhost files:
if not os.path.exists(apache_conf_dir):
os.mkdir(apache_conf_dir)
apache_vhost_file = apache_conf_dir + os.sep + \
'invenio-apache-vhost.conf'
apache_vhost_ssl_file = apache_conf_dir + os.sep + \
'invenio-apache-vhost-ssl.conf'
apache_vhost_body = """\
AddDefaultCharset UTF-8
ServerSignature Off
ServerTokens Prod
NameVirtualHost %(vhost_ip_address)s:80
%(listen_directive)s
%(wsgi_socket_directive)s
WSGIRestrictStdout Off
#WSGIImportScript %(wsgidir)s/invenio.wsgi process-group=invenio application-group=%%{GLOBAL}
<Files *.pyc>
deny from all
</Files>
<Files *~>
deny from all
</Files>
<VirtualHost %(vhost_ip_address)s:80>
ServerName %(servername)s
ServerAlias %(serveralias)s
ServerAdmin %(serveradmin)s
DocumentRoot %(webdir)s
<Directory %(webdir)s>
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
ErrorLog %(logdir)s/apache.err
LogLevel warn
CustomLog %(logdir)s/apache.log combined
DirectoryIndex index.en.html index.html
Alias /img/ %(webdir)s/img/
Alias /css/ %(webdir)s/css/
Alias /js/ %(webdir)s/js/
Alias /export/ %(webdir)s/export/
Alias /MathJax/ %(webdir)s/MathJax/
Alias /jsCalendar/ %(webdir)s/jsCalendar/
Alias /fckeditor/ %(webdir)s/fckeditor/
AliasMatch /sitemap-(.*) %(webdir)s/sitemap-$1
Alias /robots.txt %(webdir)s/robots.txt
Alias /favicon.ico %(webdir)s/favicon.ico
WSGIDaemonProcess invenio processes=5 threads=1 display-name=%%{GROUP} inactivity-timeout=3600 maximum-requests=10000
WSGIScriptAlias / %(wsgidir)s/invenio.wsgi
WSGIPassAuthorization On
%(xsendfile_directive)s
<Directory %(wsgidir)s>
WSGIProcessGroup invenio
WSGIApplicationGroup %%{GLOBAL}
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
""" % {'servername': conf.get('Invenio', 'CFG_SITE_URL').replace("http://", ""),
'serveralias': conf.get('Invenio', 'CFG_SITE_URL').replace("http://", "").split('.')[0],
'serveradmin': conf.get('Invenio', 'CFG_SITE_ADMIN_EMAIL'),
'webdir': conf.get('Invenio', 'CFG_WEBDIR'),
'logdir': conf.get('Invenio', 'CFG_LOGDIR'),
'libdir' : conf.get('Invenio', 'CFG_PYLIBDIR'),
'wsgidir': os.path.join(conf.get('Invenio', 'CFG_PREFIX'), 'var', 'www-wsgi'),
'vhost_ip_address': vhost_ip_address_needed and _detect_ip_address() or '*',
'listen_directive': listen_directive_needed and 'Listen 80' or '#Listen 80',
'wsgi_socket_directive': (wsgi_socket_directive_needed and \
'WSGISocketPrefix ' or '#WSGISocketPrefix ') + \
conf.get('Invenio', 'CFG_PREFIX') + os.sep + 'var' + os.sep + 'run',
'xsendfile_directive' : xsendfile_directive,
}
apache_vhost_ssl_body = """\
ServerSignature Off
ServerTokens Prod
%(listen_directive)s
NameVirtualHost %(vhost_ip_address)s:443
%(ssl_pem_directive)s
%(ssl_crt_directive)s
%(ssl_key_directive)s
WSGIRestrictStdout Off
<Files *.pyc>
deny from all
</Files>
<Files *~>
deny from all
</Files>
<VirtualHost %(vhost_ip_address)s:443>
ServerName %(servername)s
ServerAlias %(serveralias)s
ServerAdmin %(serveradmin)s
SSLEngine on
DocumentRoot %(webdir)s
<Directory %(webdir)s>
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
ErrorLog %(logdir)s/apache-ssl.err
LogLevel warn
CustomLog %(logdir)s/apache-ssl.log combined
DirectoryIndex index.en.html index.html
Alias /img/ %(webdir)s/img/
Alias /css/ %(webdir)s/css/
Alias /js/ %(webdir)s/js/
Alias /export/ %(webdir)s/export/
Alias /MathJax/ %(webdir)s/MathJax/
Alias /jsCalendar/ %(webdir)s/jsCalendar/
Alias /fckeditor/ %(webdir)s/fckeditor/
AliasMatch /sitemap-(.*) %(webdir)s/sitemap-$1
Alias /robots.txt %(webdir)s/robots.txt
Alias /favicon.ico %(webdir)s/favicon.ico
WSGIScriptAlias / %(wsgidir)s/invenio.wsgi
WSGIPassAuthorization On
%(xsendfile_directive)s
<Directory %(wsgidir)s>
WSGIProcessGroup invenio
WSGIApplicationGroup %%{GLOBAL}
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
""" % {'servername': conf.get('Invenio', 'CFG_SITE_SECURE_URL').replace("https://", ""),
'serveralias': conf.get('Invenio', 'CFG_SITE_SECURE_URL').replace("https://", "").split('.')[0],
'serveradmin': conf.get('Invenio', 'CFG_SITE_ADMIN_EMAIL'),
'webdir': conf.get('Invenio', 'CFG_WEBDIR'),
'logdir': conf.get('Invenio', 'CFG_LOGDIR'),
'libdir' : conf.get('Invenio', 'CFG_PYLIBDIR'),
'wsgidir' : os.path.join(conf.get('Invenio', 'CFG_PREFIX'), 'var', 'www-wsgi'),
'vhost_ip_address': vhost_ip_address_needed and _detect_ip_address() or '*',
'listen_directive' : listen_directive_needed and 'Listen 443' or '#Listen 443',
'ssl_pem_directive': ssl_pem_directive_needed and \
'SSLCertificateFile %s' % ssl_pem_path or \
'#SSLCertificateFile %s' % ssl_pem_path,
'ssl_crt_directive': ssl_pem_directive_needed and \
'#SSLCertificateFile %s' % ssl_crt_path or \
'SSLCertificateFile %s' % ssl_crt_path,
'ssl_key_directive': ssl_pem_directive_needed and \
'#SSLCertificateKeyFile %s' % ssl_key_path or \
'SSLCertificateKeyFile %s' % ssl_key_path,
'xsendfile_directive' : xsendfile_directive,
}
# write HTTP vhost snippet:
if os.path.exists(apache_vhost_file):
shutil.copy(apache_vhost_file,
apache_vhost_file + '.OLD')
fdesc = open(apache_vhost_file, 'w')
fdesc.write(apache_vhost_body)
fdesc.close()
print
print "Created file", apache_vhost_file
# write HTTPS vhost snippet:
vhost_ssl_created = False
if conf.get('Invenio', 'CFG_SITE_SECURE_URL') != \
conf.get('Invenio', 'CFG_SITE_URL'):
if os.path.exists(apache_vhost_ssl_file):
shutil.copy(apache_vhost_ssl_file,
apache_vhost_ssl_file + '.OLD')
fdesc = open(apache_vhost_ssl_file, 'w')
fdesc.write(apache_vhost_ssl_body)
fdesc.close()
vhost_ssl_created = True
print "Created file", apache_vhost_ssl_file
print wrap_text_in_a_box("""\
Apache virtual host configuration file(s) for your Invenio site
was(were) created. Please check created file(s) and activate virtual
host(s). For example, you can put the following include statements in
your httpd.conf:\n
Include %s
%s
Please see the INSTALL file for more details.
""" % (apache_vhost_file, (vhost_ssl_created and 'Include ' or '#Include ') + apache_vhost_ssl_file))
print ">>> Apache conf files created."
def cli_cmd_get(conf, varname):
"""
Return value of VARNAME read from CONF files. Useful for
third-party programs to access values of conf options such as
CFG_PREFIX. Return None if VARNAME is not found.
"""
# do not pay attention to upper/lower case:
varname = varname.lower()
# do not pay attention to section names yet:
all_options = {}
for section in conf.sections():
for option in conf.options(section):
all_options[option] = conf.get(section, option)
return all_options.get(varname, None)
def cli_cmd_list(conf):
"""
Print a list of all conf options and values from CONF.
"""
sections = conf.sections()
sections.sort()
for section in sections:
options = conf.options(section)
options.sort()
for option in options:
print option.upper(), '=', conf.get(section, option)
def _grep_version_from_executable(path_to_exec, version_regexp):
"""
Try to detect a program version by digging into its binary
PATH_TO_EXEC and looking for VERSION_REGEXP. Return program
version as a string. Return empty string if not succeeded.
"""
from invenio.shellutils import run_shell_command
exec_version = ""
if os.path.exists(path_to_exec):
dummy1, cmd2_out, dummy2 = run_shell_command("strings %s | grep %s",
(path_to_exec, version_regexp))
if cmd2_out:
for cmd2_out_line in cmd2_out.split("\n"):
if len(cmd2_out_line) > len(exec_version):
# the longest the better
exec_version = cmd2_out_line
return exec_version
def detect_apache_version():
"""
Try to detect Apache version by localizing httpd or apache
executables and grepping inside binaries. Return list of all
found Apache versions and paths. (For a given executable, the
returned format is 'apache_version [apache_path]'.) Return empty
list if no success.
"""
from invenio.shellutils import run_shell_command
out = []
dummy1, cmd_out, dummy2 = run_shell_command("locate bin/httpd bin/apache")
for apache in cmd_out.split("\n"):
apache_version = _grep_version_from_executable(apache, '^Apache\/')
if apache_version:
out.append("%s [%s]" % (apache_version, apache))
return out
def cli_cmd_detect_system_details(conf):
"""
Detect and print system details such as Apache/Python/MySQL
versions etc. Useful for debugging problems on various OS.
"""
import MySQLdb
print ">>> Going to detect system details..."
print "* Hostname: " + socket.gethostname()
print "* Invenio version: " + conf.get("Invenio", "CFG_VERSION")
print "* Python version: " + sys.version.replace("\n", " ")
print "* Apache version: " + ";\n ".join(detect_apache_version())
print "* MySQLdb version: " + MySQLdb.__version__
try:
from invenio.dbquery import run_sql
print "* MySQL version:"
for key, val in run_sql("SHOW VARIABLES LIKE 'version%'") + \
run_sql("SHOW VARIABLES LIKE 'charact%'") + \
run_sql("SHOW VARIABLES LIKE 'collat%'"):
if False:
print " - %s: %s" % (key, val)
elif key in ['version',
'character_set_client',
'character_set_connection',
'character_set_database',
'character_set_results',
'character_set_server',
'character_set_system',
'collation_connection',
'collation_database',
'collation_server']:
print " - %s: %s" % (key, val)
except ImportError:
print "* ERROR: cannot import dbquery"
print ">>> System details detected successfully."
def main():
"""Main entry point."""
conf = ConfigParser()
if '--help' in sys.argv or \
'-h' in sys.argv:
print_usage()
elif '--version' in sys.argv or \
'-V' in sys.argv:
print_version()
else:
confdir = None
if '--conf-dir' in sys.argv:
try:
confdir = sys.argv[sys.argv.index('--conf-dir') + 1]
except IndexError:
pass # missing --conf-dir argument value
if not os.path.exists(confdir):
print "ERROR: bad or missing --conf-dir option value."
sys.exit(1)
else:
## try to detect path to conf dir (relative to this bin dir):
confdir = re.sub(r'/bin$', '/etc', sys.path[0])
## read conf files:
for conffile in [confdir + os.sep + 'invenio.conf',
confdir + os.sep + 'invenio-autotools.conf',
confdir + os.sep + 'invenio-local.conf',]:
if os.path.exists(conffile):
conf.read(conffile)
else:
if not conffile.endswith("invenio-local.conf"):
# invenio-local.conf is optional, otherwise stop
print "ERROR: Badly guessed conf file location", conffile
print "(Please use --conf-dir option.)"
sys.exit(1)
## decide what to do:
done = False
for opt_idx in range(0, len(sys.argv)):
opt = sys.argv[opt_idx]
if opt == '--conf-dir':
# already treated before, so skip silently:
pass
elif opt == '--get':
try:
varname = sys.argv[opt_idx + 1]
except IndexError:
print "ERROR: bad or missing --get option value."
sys.exit(1)
if varname.startswith('-'):
print "ERROR: bad or missing --get option value."
sys.exit(1)
varvalue = cli_cmd_get(conf, varname)
if varvalue is not None:
print varvalue
else:
sys.exit(1)
done = True
elif opt == '--list':
cli_cmd_list(conf)
done = True
elif opt == '--detect-system-details':
cli_cmd_detect_system_details(conf)
done = True
elif opt == '--create-tables':
cli_cmd_create_tables(conf)
done = True
elif opt == '--load-webstat-conf':
cli_cmd_load_webstat_conf(conf)
done = True
elif opt == '--drop-tables':
cli_cmd_drop_tables(conf)
done = True
elif opt == '--check-openoffice':
cli_check_openoffice(conf)
done = True
elif opt == '--create-demo-site':
cli_cmd_create_demo_site(conf)
done = True
elif opt == '--load-demo-records':
cli_cmd_load_demo_records(conf)
done = True
elif opt == '--remove-demo-records':
cli_cmd_remove_demo_records(conf)
done = True
elif opt == '--drop-demo-site':
cli_cmd_drop_demo_site(conf)
done = True
elif opt == '--run-unit-tests':
cli_cmd_run_unit_tests(conf)
done = True
elif opt == '--run-regression-tests':
cli_cmd_run_regression_tests(conf)
done = True
elif opt == '--run-web-tests':
cli_cmd_run_web_tests(conf)
done = True
elif opt == '--update-all':
cli_cmd_update_config_py(conf)
cli_cmd_update_dbquery_py(conf)
cli_cmd_update_dbexec(conf)
cli_cmd_update_bibconvert_tpl(conf)
cli_cmd_update_web_tests(conf)
done = True
elif opt == '--update-config-py':
cli_cmd_update_config_py(conf)
done = True
elif opt == '--update-dbquery-py':
cli_cmd_update_dbquery_py(conf)
done = True
elif opt == '--update-dbexec':
cli_cmd_update_dbexec(conf)
done = True
elif opt == '--update-bibconvert-tpl':
cli_cmd_update_bibconvert_tpl(conf)
done = True
elif opt == '--update-web-tests':
cli_cmd_update_web_tests(conf)
done = True
elif opt == '--reset-all':
cli_cmd_reset_sitename(conf)
cli_cmd_reset_siteadminemail(conf)
cli_cmd_reset_fieldnames(conf)
cli_cmd_reset_recstruct_cache(conf)
done = True
elif opt == '--reset-sitename':
cli_cmd_reset_sitename(conf)
done = True
elif opt == '--reset-siteadminemail':
cli_cmd_reset_siteadminemail(conf)
done = True
elif opt == '--reset-fieldnames':
cli_cmd_reset_fieldnames(conf)
done = True
elif opt == '--reset-recstruct-cache':
cli_cmd_reset_recstruct_cache(conf)
done = True
elif opt == '--create-apache-conf':
cli_cmd_create_apache_conf(conf)
done = True
elif opt.startswith("-") and opt != '--yes-i-know':
print "ERROR: unknown option", opt
sys.exit(1)
if not done:
print """ERROR: Please specify a command. Please see '--help'."""
sys.exit(1)
if __name__ == '__main__':
main()
diff --git a/modules/webaccess/lib/access_control_config.py b/modules/webaccess/lib/access_control_config.py
index 742db0f53..00942c81a 100644
--- a/modules/webaccess/lib/access_control_config.py
+++ b/modules/webaccess/lib/access_control_config.py
@@ -1,341 +1,341 @@
## This file is part of Invenio.
## Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Invenio Access Control Config. """
__revision__ = \
"$Id$"
# pylint: disable=C0301
from invenio.config import CFG_SITE_NAME, CFG_SITE_URL, CFG_SITE_LANG, \
CFG_SITE_SECURE_URL, CFG_SITE_SUPPORT_EMAIL, CFG_CERN_SITE, \
- CFG_OPENAIRE_SITE
+ CFG_OPENAIRE_SITE, CFG_SITE_RECORD
from invenio.messages import gettext_set_language
class InvenioWebAccessFireroleError(Exception):
"""Just an Exception to discover if it's a FireRole problem"""
pass
# VALUES TO BE EXPORTED
# CURRENTLY USED BY THE FILES access_control_engine.py access_control_admin.py webaccessadmin_lib.py
# name of the role giving superadmin rights
SUPERADMINROLE = 'superadmin'
# name of the webaccess webadmin role
WEBACCESSADMINROLE = 'webaccessadmin'
# name of the action allowing roles to access the web administrator interface
WEBACCESSACTION = 'cfgwebaccess'
# name of the action allowing roles to access the web administrator interface
VIEWRESTRCOLL = 'viewrestrcoll'
# name of the action allowing roles to delegate the rights to other roles
# ex: libraryadmin to delegate libraryworker
DELEGATEADDUSERROLE = 'accdelegaterole'
# max number of users to display in the drop down selects
MAXSELECTUSERS = 25
# max number of users to display in a page (mainly for user area)
MAXPAGEUSERS = 25
# default role definition, source:
CFG_ACC_EMPTY_ROLE_DEFINITION_SRC = 'deny all'
# default role definition, compiled:
CFG_ACC_EMPTY_ROLE_DEFINITION_OBJ = (False, ())
# default role definition, compiled and serialized:
CFG_ACC_EMPTY_ROLE_DEFINITION_SER = None
# List of tags containing (multiple) emails of users who should authorize
# to access the corresponding record regardless of collection restrictions.
if CFG_CERN_SITE:
CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS = ['859__f', '270__m', '506__m']
else:
CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS = ['8560_f']
# Use external source for access control?
# CFG_EXTERNAL_AUTHENTICATION -- this is a dictionary with the enabled login method.
# The key is the name of the login method and the value is an instance of
# of the login method (see /help/admin/webaccess-admin-guide#5). Set the value
# to None if you wish to use the local Invenio authentication method.
# CFG_EXTERNAL_AUTH_DEFAULT -- set this to the key in CFG_EXTERNAL_AUTHENTICATION
# that should be considered as default login method
# CFG_EXTERNAL_AUTH_USING_SSO -- set this to the login method name of an SSO
# login method, if any, otherwise set this to None.
# CFG_EXTERNAL_AUTH_LOGOUT_SSO -- if CFG_EXTERNAL_AUTH_USING_SSO was not None
# set this to the URL that should be contacted to perform an SSO logout
from invenio.external_authentication_robot import ExternalAuthRobot
if CFG_CERN_SITE:
import external_authentication_sso as ea_sso
CFG_EXTERNAL_AUTH_USING_SSO = "CERN"
CFG_EXTERNAL_AUTH_DEFAULT = CFG_EXTERNAL_AUTH_USING_SSO
CFG_EXTERNAL_AUTH_LOGOUT_SSO = 'https://login.cern.ch/adfs/ls/?wa=wsignout1.0'
CFG_EXTERNAL_AUTHENTICATION = {
CFG_EXTERNAL_AUTH_USING_SSO : ea_sso.ExternalAuthSSO(),
}
elif CFG_OPENAIRE_SITE:
CFG_EXTERNAL_AUTH_DEFAULT = 'Local'
CFG_EXTERNAL_AUTH_USING_SSO = False
CFG_EXTERNAL_AUTH_LOGOUT_SSO = None
CFG_EXTERNAL_AUTHENTICATION = {
"Local": None,
"OpenAIRE": ExternalAuthRobot(enforce_external_nicknames=True, use_zlib=False),
"ZOpenAIRE": ExternalAuthRobot(enforce_external_nicknames=True, use_zlib=True)
}
else:
CFG_EXTERNAL_AUTH_DEFAULT = 'Local'
CFG_EXTERNAL_AUTH_USING_SSO = False
CFG_EXTERNAL_AUTH_LOGOUT_SSO = None
CFG_EXTERNAL_AUTHENTICATION = {
"Local": None,
"Robot": ExternalAuthRobot(enforce_external_nicknames=True, use_zlib=False),
"ZRobot": ExternalAuthRobot(enforce_external_nicknames=True, use_zlib=True)
}
# default data for the add_default_settings function
# Note: by default the definition is set to deny any. This won't be a problem
# because userid directly connected with roles will still be allowed.
# roles
# name description definition
DEF_ROLES = ((SUPERADMINROLE, 'superuser with all rights', 'deny any'),
(WEBACCESSADMINROLE, 'WebAccess administrator', 'deny any'),
('anyuser', 'Any user', 'allow any'),
('basketusers', 'Users who can use baskets', 'allow any'),
('loanusers', 'Users who can use loans', 'allow any'),
('groupusers', 'Users who can use groups', 'allow any'),
('alertusers', 'Users who can use alerts', 'allow any'),
('messageusers', 'Users who can use messages', 'allow any'),
('holdingsusers', 'Users who can view holdings', 'allow any'),
('statisticsusers', 'Users who can view statistics', 'allow any'),
('claimpaperusers', 'Users who can perform changes to their own paper attributions without the need for an operator\'s approval', 'allow any'),
('claimpaperoperators', 'Users who can perform changes to _all_ paper attributions without the need for an operator\'s approval', 'deny any'),
('paperclaimviewers', 'Users who can view "claim my paper" facilities.', 'allow all'),
('paperattributionviewers', 'Users who can view "attribute this paper" facilities', 'allow all'),
('paperattributionlinkviewers', 'Users who can see attribution links in the search', 'allow all'),
)
# Demo site roles
DEF_DEMO_ROLES = (('photocurator', 'Photo collection curator', 'deny any'),
('thesesviewer', 'Theses viewer', 'allow group "Theses viewers"'),
('thesescurator', 'Theses collection curator', 'deny any'),
('swordcurator', 'BibSword client curator', 'deny any'),
('referee_DEMOBOO_*', 'Book collection curator', 'deny any'),
('restrictedpicturesviewer', 'Restricted pictures viewer', 'deny any'),
('curator', 'Curator', 'deny any'),
('basketusers', 'Users who can use baskets', 'deny email "hyde@cds.cern.ch"\nallow any'),
('claimpaperusers', 'Users who can perform changes to their own paper attributions without the need for an operator\'s approval', 'deny email "hyde@cds.cern.ch"\nallow any'),
('submit_DEMOJRN_*', 'Users who can submit (and modify) "Atlantis Times" articles', 'deny all'),
('atlantiseditor', 'Users who can configure "Atlantis Times" journal', 'deny all'),
('commentmoderator', 'Users who can moderate comments', 'deny all'),
('poetrycommentreader', 'Users who can view comments in Poetry collection', 'deny all'))
DEF_DEMO_USER_ROLES = (('jekyll@cds.cern.ch', 'thesesviewer'),
('jekyll@cds.cern.ch', 'swordcurator'),
('jekyll@cds.cern.ch', 'claimpaperusers'),
('dorian.gray@cds.cern.ch', 'referee_DEMOBOO_*'),
('balthasar.montague@cds.cern.ch', 'curator'),
('romeo.montague@cds.cern.ch', 'restrictedpicturesviewer'),
('romeo.montague@cds.cern.ch', 'swordcurator'),
('romeo.montague@cds.cern.ch', 'thesescurator'),
('juliet.capulet@cds.cern.ch', 'restrictedpicturesviewer'),
('juliet.capulet@cds.cern.ch', 'photocurator'),
('romeo.montague@cds.cern.ch', 'submit_DEMOJRN_*'),
('juliet.capulet@cds.cern.ch', 'submit_DEMOJRN_*'),
('balthasar.montague@cds.cern.ch', 'atlantiseditor'),
('romeo.montague@cds.cern.ch', 'poetrycommentreader'))
# users
# list of e-mail addresses
DEF_USERS = []
# actions
# name desc allowedkeywords optional
DEF_ACTIONS = (
('cfgwebsearch', 'configure WebSearch', '', 'no'),
('cfgbibformat', 'configure BibFormat', '', 'no'),
('cfgbibknowledge', 'configure BibKnowledge', '', 'no'),
('cfgwebsubmit', 'configure WebSubmit', '', 'no'),
('cfgbibrank', 'configure BibRank', '', 'no'),
('cfgwebcomment', 'configure WebComment', '', 'no'),
('cfgoaiharvest', 'configure OAI Harvest', '', 'no'),
('cfgoairepository', 'configure OAI Repository', '', 'no'),
('cfgbibindex', 'configure BibIndex', '', 'no'),
('cfgbibexport', 'configure BibExport', '', 'no'),
('cfgrobotkeys', 'configure Robot keys', 'login_method,robot', 'yes'),
('runbibindex', 'run BibIndex', '', 'no'),
('runbibupload', 'run BibUpload', '', 'no'),
('runwebcoll', 'run webcoll', 'collection', 'yes'),
('runbibformat', 'run BibFormat', 'format', 'yes'),
('runbibclassify', 'run BibClassify', 'taxonomy', 'yes'),
('runbibtaskex', 'run BibTaskEx example', '', 'no'),
('runbibrank', 'run BibRank', '', 'no'),
('runoaiharvest', 'run oaiharvest task', '', 'no'),
('runoairepository', 'run oairepositoryupdater task', '', 'no'),
('runbibedit', 'run Record Editor', 'collection', 'yes'),
('runbibeditmulti', 'run Multi-Record Editor', '', 'no'),
('runbibdocfile', 'run Document File Manager', '', 'no'),
('runbibmerge', 'run Record Merger', '', 'no'),
('runbibswordclient', 'run BibSword client', '', 'no'),
('runwebstatadmin', 'run WebStadAdmin', '', 'no'),
('runinveniogc', 'run InvenioGC', '', 'no'),
('runbibexport', 'run BibExport', '', 'no'),
('referee', 'referee document type doctype/category categ', 'doctype,categ', 'yes'),
('submit', 'use webSubmit', 'doctype,act,categ', 'yes'),
('viewrestrdoc', 'view restricted document', 'status', 'no'),
('viewrestrcomment', 'view restricted comment', 'status', 'no'),
(WEBACCESSACTION, 'configure WebAccess', '', 'no'),
(DELEGATEADDUSERROLE, 'delegate subroles inside WebAccess', 'role', 'no'),
(VIEWRESTRCOLL, 'view restricted collection', 'collection', 'no'),
('cfgwebjournal', 'configure WebJournal', 'name,with_editor_rights', 'no'),
('viewcomment', 'view comments', 'collection', 'no'),
('sendcomment', 'send comments', 'collection', 'no'),
('attachcommentfile', 'attach files to comments', 'collection', 'no'),
('attachsubmissionfile', 'upload files to drop box during submission', '', 'no'),
('cfgbibexport', 'configure BibExport', '', 'no'),
('runbibexport', 'run BibExport', '', 'no'),
('usebaskets', 'use baskets', '', 'no'),
('useloans', 'use loans', '', 'no'),
('usegroups', 'use groups', '', 'no'),
('usealerts', 'use alerts', '', 'no'),
('usemessages', 'use messages', '', 'no'),
('viewholdings', 'view holdings', 'collection', 'yes'),
('viewstatistics', 'view statistics', 'collection', 'yes'),
('runbibcirculation', 'run BibCirculation', '', 'no'),
('moderatecomments', 'moderate comments', 'collection', 'no'),
('runbatchuploader', 'run batchuploader', 'collection', 'yes'),
('claimpaper_view_pid_universe', 'View the Claim Paper interface', '', 'no'),
('claimpaper_claim_own_papers', 'Clam papers to his own personID', '', 'no'),
('claimpaper_claim_others_papers', 'Claim papers for others', '', 'no'),
('claimpaper_change_own_data', 'Change data associated to his own person ID', '', 'no'),
('claimpaper_change_others_data', 'Change data of any person ID', '', 'no'),
('runbibtasklet', 'run BibTaskLet', '', 'no')
)
# Default authorizations
# role action arguments
DEF_AUTHS = (('basketusers', 'usebaskets', {}),
('loanusers', 'useloans', {}),
('groupusers', 'usegroups', {}),
('alertusers', 'usealerts', {}),
('messageusers', 'usemessages', {}),
('holdingsusers', 'viewholdings', {}),
('statisticsusers', 'viewstatistics', {}),
('claimpaperusers', 'claimpaper_view_pid_universe', {}),
('claimpaperoperators', 'claimpaper_view_pid_universe', {}),
('claimpaperusers', 'claimpaper_claim_own_papers', {}),
('claimpaperoperators', 'claimpaper_claim_own_papers', {}),
('claimpaperoperators', 'claimpaper_claim_others_papers', {}),
('claimpaperusers', 'claimpaper_change_own_data', {}),
('claimpaperoperators', 'claimpaper_change_own_data', {}),
('claimpaperoperators', 'claimpaper_change_others_data', {}),
)
# Demo site authorizations
# role action arguments
DEF_DEMO_AUTHS = (
('photocurator', 'runwebcoll', {'collection': 'Pictures'}),
('restrictedpicturesviewer', 'viewrestrdoc', {'status': 'restricted_picture'}),
('thesesviewer', VIEWRESTRCOLL, {'collection': 'Theses'}),
('referee_DEMOBOO_*', 'referee', {'doctype': 'DEMOBOO', 'categ': '*'}),
('curator', 'cfgbibknowledge', {}),
('curator', 'runbibedit', {}),
('curator', 'runbibeditmulti', {}),
('curator', 'runbibmerge', {}),
('swordcurator', 'runbibswordclient', {}),
('thesescurator', 'runbibedit', {'collection': 'Theses'}),
('thesescurator', VIEWRESTRCOLL, {'collection': 'Theses'}),
('photocurator', 'runbibedit', {'collection': 'Pictures'}),
('referee_DEMOBOO_*', 'runbibedit', {'collection': 'Books'}),
('submit_DEMOJRN_*', 'submit', {'doctype': 'DEMOJRN', 'act': 'SBI', 'categ': '*'}),
('submit_DEMOJRN_*', 'submit', {'doctype': 'DEMOJRN', 'act': 'MBI', 'categ': '*'}),
('submit_DEMOJRN_*', 'cfgwebjournal', {'name': 'AtlantisTimes', 'with_editor_rights': 'no'}),
('atlantiseditor', 'cfgwebjournal', {'name': 'AtlantisTimes', 'with_editor_rights': 'yes'}),
('referee_DEMOBOO_*', 'runbatchuploader', {'collection': 'Books'}),
('poetrycommentreader', 'viewcomment', {'collection': 'Poetry'})
)
_ = gettext_set_language(CFG_SITE_LANG)
# Activities (i.e. actions) for which exists an administrative web interface.
CFG_ACC_ACTIVITIES_URLS = {
- 'runbibedit' : (_("Run Record Editor"), "%s/record/edit/?ln=%%s" % CFG_SITE_URL),
- 'runbibeditmulti' : (_("Run Multi-Record Editor"), "%s/record/multiedit/?ln=%%s" % CFG_SITE_URL),
+ 'runbibedit' : (_("Run Record Editor"), "%s/%s/edit/?ln=%%s" % (CFG_SITE_URL, CFG_SITE_RECORD)),
+ 'runbibeditmulti' : (_("Run Multi-Record Editor"), "%s/%s/multiedit/?ln=%%s" % (CFG_SITE_URL, CFG_SITE_RECORD)),
'runbibdocfile' : (_("Run Document File Manager"), "%s/submit/managedocfiles?ln=%%s" % CFG_SITE_URL),
- 'runbibmerge' : (_("Run Record Merger"), "%s/record/merge/?ln=%%s" % CFG_SITE_URL),
+ 'runbibmerge' : (_("Run Record Merger"), "%s/%s/merge/?ln=%%s" % (CFG_SITE_URL, CFG_SITE_RECORD)),
'runbibswordclient' : (_("Run BibSword client"), "%s/bibsword/?ln=%%s" % CFG_SITE_URL),
'cfgbibknowledge' : (_("Configure BibKnowledge"), "%s/kb?ln=%%s" % CFG_SITE_URL),
'cfgbibformat' : (_("Configure BibFormat"), "%s/admin/bibformat/bibformatadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgoaiharvest' : (_("Configure OAI Harvest"), "%s/admin/bibharvest/oaiharvestadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgoairepository' : (_("Configure OAI Repository"), "%s/admin/bibharvest/oairepositoryadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgbibindex' : (_("Configure BibIndex"), "%s/admin/bibindex/bibindexadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgbibrank' : (_("Configure BibRank"), "%s/admin/bibrank/bibrankadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebaccess' : (_("Configure WebAccess"), "%s/admin/webaccess/webaccessadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebcomment' : (_("Configure WebComment"), "%s/admin/webcomment/webcommentadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebsearch' : (_("Configure WebSearch"), "%s/admin/websearch/websearchadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebsubmit' : (_("Configure WebSubmit"), "%s/admin/websubmit/websubmitadmin.py?ln=%%s" % CFG_SITE_URL),
'cfgwebjournal' : (_("Configure WebJournal"), "%s/admin/webjournal/webjournaladmin.py?ln=%%s" % CFG_SITE_URL),
'runbibcirculation' : (_("Run BibCirculation"), "%s/admin/bibcirculation/bibcirculationadmin.py?ln=%%s" % CFG_SITE_URL),
'runbatchuploader' : (_("Run Batch Uploader"), "%s/batchuploader/metadata?ln=%%s" % CFG_SITE_URL),
'claimpaper_claim_others_papers' : (_("Run Person/Author Manager"), "%s/person/search?ln=%%s" % CFG_SITE_URL)
}
CFG_WEBACCESS_MSGS = {
0: 'Try to <a href="%s/youraccount/login?referer=%%s">login</a> with another account.' % (CFG_SITE_SECURE_URL),
1: '<br />If you think this is not correct, please contact: <a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL),
2: '<br />If you have any questions, please write to <a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL),
3: 'Guest users are not allowed, please <a href="%s/youraccount/login">login</a>.' % CFG_SITE_SECURE_URL,
4: 'The site is temporarily closed for maintenance. Please come back soon.',
5: 'Authorization failure',
6: '%s temporarily closed' % CFG_SITE_NAME,
7: 'This functionality is temporarily closed due to server maintenance. Please use only the search engine in the meantime.',
8: 'Functionality temporarily closed'
}
CFG_WEBACCESS_WARNING_MSGS = {
0: 'Authorization granted',
1: 'You are not authorized to perform this action.',
2: 'You are not authorized to perform any action.',
3: 'The action %s does not exist.',
4: 'Unexpected error occurred.',
5: 'Missing mandatory keyword argument(s) for this action.',
6: 'Guest accounts are not authorized to perform this action.',
7: 'Not enough arguments, user ID and action name required.',
8: 'Incorrect keyword argument(s) for this action.',
9: """Account '%s' is not yet activated.""",
10: """You were not authorized by the authentication method '%s'.""",
11: """The selected login method '%s' is not the default method for this account, please try another one.""",
12: """Selected login method '%s' does not exist.""",
13: """Could not register '%s' account.""",
14: """Could not login using '%s', because this user is unknown.""",
15: """Could not login using your '%s' account, because you have introduced a wrong password.""",
16: """External authentication troubles using '%s' (maybe temporary network problems).""",
17: """You have not yet confirmed the email address for the '%s' authentication method.""",
18: """The administrator has not yet activated your account for the '%s' authentication method.""",
19: """The site is having troubles in sending you an email for confirming your email address. The error has been logged and will be taken care of as soon as possible.""",
20: """No roles are authorized to perform action %s with the given parameters."""
}
diff --git a/modules/webalert/lib/webalert_templates.py b/modules/webalert/lib/webalert_templates.py
index d43dbc2eb..d838886cc 100644
--- a/modules/webalert/lib/webalert_templates.py
+++ b/modules/webalert/lib/webalert_templates.py
@@ -1,655 +1,656 @@
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import cgi
import time
from invenio.config import \
CFG_WEBALERT_ALERT_ENGINE_EMAIL, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_URL, \
- CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL
+ CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.messages import gettext_set_language
from invenio.htmlparser import get_as_text, wrap, wrap_records
from invenio.urlutils import create_html_link
from invenio.search_engine import guess_primary_collection_of_a_record, get_coll_ancestors
class Template:
def tmpl_errorMsg(self, ln, error_msg, rest = ""):
"""
Adds an error message to the output
Parameters:
- 'ln' *string* - The language to display the interface in
- 'error_msg' *string* - The error message
- 'rest' *string* - The rest of the page
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<div class="quicknote">%(error)s</div><br />%(rest)s""" % {
'error' : error_msg,
'rest' : rest
}
return out
def tmpl_textual_query_info_from_urlargs(self, ln, args):
"""
Displays a human inteligible textual representation of a query
Parameters:
- 'ln' *string* - The language to display the interface in
- 'args' *array* - The URL arguments array (parsed)
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if args.has_key('p'):
out += "<strong>" + _("Pattern") + ":</strong> " + "; ".join(args['p']) + "<br />"
if args.has_key('f'):
out += "<strong>" + _("Field") + ":</strong> " + "; ".join(args['f']) + "<br />"
if args.has_key('p1'):
out += "<strong>" + _("Pattern 1") + ":</strong> " + "; ".join(args['p1']) + "<br />"
if args.has_key('f1'):
out += "<strong>" + _("Field 1") + ":</strong> " + "; ".join(args['f1']) + "<br />"
if args.has_key('p2'):
out += "<strong>" + _("Pattern 2") + ":</strong> " + "; ".join(args['p2']) + "<br />"
if args.has_key('f2'):
out += "<strong>" + _("Field 2") + ":</strong> " + "; ".join(args['f2']) + "<br />"
if args.has_key('p3'):
out += "<strong>" + _("Pattern 3") + ":</strong> " + "; ".join(args['p3']) + "<br />"
if args.has_key('f3'):
out += "<strong>" + _("Field 3") + ":</strong> " + "; ".join(args['f3']) + "<br />"
if args.has_key('c'):
out += "<strong>" + _("Collections") + ":</strong> " + "; ".join(args['c']) + "<br />"
elif args.has_key('cc'):
out += "<strong>" + _("Collection") + ":</strong> " + "; ".join(args['cc']) + "<br />"
return out
def tmpl_account_list_alerts(self, ln, alerts):
"""
Displays all the alerts in the main "Your account" page
Parameters:
- 'ln' *string* - The language to display the interface in
- 'alerts' *array* - The existing alerts IDs ('id' + 'name' pairs)
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<form name="displayalert" action="../youralerts/list" method="post">
%(you_own)s:
<select name="id_alert">
<option value="0">- %(alert_name)s -</option>""" % {
'you_own' : _("You own the following alerts:"),
'alert_name' : _("alert name"),
}
for alert in alerts :
out += """<option value="%(id)s">%(name)s</option>""" % \
{'id': alert['id'], 'name': cgi.escape(alert['name'])}
out += """</select>
&nbsp;<code class="blocknote">
<input class="formbutton" type="submit" name="action" value="%(show)s" /></code>
</form>""" % {
'show' : _("SHOW"),
}
return out
def tmpl_input_alert(self, ln, query, alert_name, action, frequency, notification,
baskets, old_id_basket, id_basket, id_query,
guest, guesttxt):
"""
Displays an alert adding form.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'query' *string* - The HTML code of the textual representation of the query (as returned ultimately by tmpl_textual_query_info_from_urlargs...)
- 'alert_name' *string* - The alert name
- 'action' *string* - The action to complete ('update' or 'add')
- 'frequency' *string* - The frequency of alert running ('day', 'week', 'month')
- 'notification' *string* - If notification should be sent by email ('y', 'n')
- 'baskets' *array* - The existing baskets ('id' + 'name' pairs)
- 'old_id_basket' *string* - The id of the previous basket of this alert
- 'id_basket' *string* - The id of the basket of this alert
- 'id_query' *string* - The id of the query associated to this alert
- 'guest' *bool* - If the user is a guest user
- 'guesttxt' *string* - The HTML content of the warning box for guest users (produced by webaccount.tmpl_warning_guest_user)
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
out += """<table style="border: 0px; padding: 2px; width: 650px;">
<tr><td colspan="3">%(notify_cond)s</td></tr>
<tr>
<td></td>
<td style="text-align: left; vertical-align: top; width: 10px; font-weight: bold;">%(query_text)s:</td>
<td style="text-align: left; vertical-align: top; width: 500px">%(query)s</td>
</tr>
</table>""" % {
'notify_cond' : _("This alert will notify you each time/only if a new item satisfies the following query:"),
'query_text' : _("QUERY"),
'query' : query,
}
out += """<form name="setalert" action="../youralerts/%(action)s" method="get">
<table style="background-color:F1F1F1; border:thin groove grey; padding: 0px;">
<tr>
<td>
<table style="border: 0px; padding:10px;">
<tr>
<td style="text-align: right; vertical-align:top; font-weight: bold;">%(alert_name)s</td>
<td><input type="text" name="name" size="20" maxlength="30" value="%(alert)s" /></td>
</tr>
<tr>
<td style="text-align: right; font-weight: bold;">%(freq)s</td>
<td>
<select name="freq">
<option value="month" %(freq_month)s>%(monthly)s</option>
<option value="week" %(freq_week)s>%(weekly)s</option>
<option value="day" %(freq_day)s>%(daily)s</option>
</select>
</td>
</tr>
<tr>
<td style="text-align:right; font-weight: bold">%(send_email)s</td>
<td>
<select name="notif">
<option value="y" %(notif_yes)s>%(yes)s</option>
<option value="n" %(notif_no)s>%(no)s</option>
</select>
<small class="quicknote"> (%(specify)s)</small>
</td>
</tr>
<tr>
<td style="text-align: right; vertical-align:top; font-weight: bold;">%(store_basket)s</td>
<td>%(baskets)s
""" % {
'action': action,
'alert_name' : _("Alert identification name:"),
'alert' : cgi.escape(alert_name, 1),
'freq' : _("Search-checking frequency:"),
'freq_month' : (frequency == 'month' and 'selected="selected"' or ""),
'freq_week' : (frequency == 'week' and 'selected="selected"' or ""),
'freq_day' : (frequency == 'day' and 'selected="selected"' or ""),
'monthly' : _("monthly"),
'weekly' : _("weekly"),
'daily' : _("daily"),
'send_email' : _("Send notification email?"),
'notif_yes' : (notification == 'y' and 'selected="selected"' or ""),
'notif_no' : (notification == 'n' and 'selected="selected"' or ""),
'yes' : _("yes"),
'no' : _("no"),
'specify' : _("if %(x_fmt_open)sno%(x_fmt_close)s you must specify a basket") % {'x_fmt_open': '<b>',
'x_fmt_close': '</b>'},
'store_basket' : _("Store results in basket?"),
'baskets': baskets
}
out += """ </td>
</tr>
<tr>
<td colspan="2" style="text-align:center">
<input type="hidden" name="idq" value="%(idq)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<code class="blocknote"><input class="formbutton" type="submit" name="action" value="&nbsp;%(set_alert)s&nbsp;" /></code>&nbsp;
<code class="blocknote"><input class="formbutton" type="reset" value="%(clear_data)s" /></code>
</td>
</tr>
</table>
</td>
</tr>
</table>
""" % {
'idq' : id_query,
'ln' : ln,
'set_alert' : _("SET ALERT"),
'clear_data' : _("CLEAR DATA"),
}
if action == "update":
out += '<input type="hidden" name="old_idb" value="%s" />' % old_id_basket
out += "</form>"
if guest:
out += guesttxt
return out
def tmpl_list_alerts(self, ln, alerts, guest, guesttxt):
"""
Displays the list of alerts
Parameters:
- 'ln' *string* - The language to display the interface in
- 'alerts' *array* - The existing alerts:
- 'queryid' *string* - The id of the associated query
- 'queryargs' *string* - The query string
- 'textargs' *string* - The textual description of the query string
- 'userid' *string* - The user id
- 'basketid' *string* - The basket id
- 'basketname' *string* - The basket name
- 'alertname' *string* - The alert name
- 'frequency' *string* - The frequency of alert running ('day', 'week', 'month')
- 'notification' *string* - If notification should be sent by email ('y', 'n')
- 'created' *string* - The date of alert creation
- 'lastrun' *string* - The last running date
- 'guest' *bool* - If the user is a guest user
- 'guesttxt' *string* - The HTML content of the warning box for guest users (produced by webaccount.tmpl_warning_guest_user)
"""
# load the right message language
_ = gettext_set_language(ln)
out = '<p>' + _("Set a new alert from %(x_url1_open)syour searches%(x_url1_close)s, the %(x_url2_open)spopular searches%(x_url2_close)s, or the input form.") + '</p>'
out %= {'x_url1_open': '<a href="display?ln=' + ln + '">',
'x_url1_close': '</a>',
'x_url2_open': '<a href="display?ln=' + ln + '&amp;p=y">',
'x_url2_close': '</a>',
}
if len(alerts):
out += """<table class="alrtTable">
<tr class="pageboxlefttop" style="text-align: center;">
<td style="font-weight: bold">%(no)s</td>
<td style="font-weight: bold">%(name)s</td>
<td style="font-weight: bold">%(search_freq)s</td>
<td style="font-weight: bold">%(notification)s</td>
<td style="font-weight: bold">%(result_basket)s</td>
<td style="font-weight: bold">%(date_run)s</td>
<td style="font-weight: bold">%(date_created)s</td>
<td style="font-weight: bold">%(query)s</td>
<td style="font-weight: bold">%(action)s</td></tr>""" % {
'no' : _("No"),
'name' : _("Name"),
'search_freq' : _("Search checking frequency"),
'notification' : _("Notification by email"),
'result_basket' : _("Result in basket"),
'date_run' : _("Date last run"),
'date_created' : _("Creation date"),
'query' : _("Query"),
'action' : _("Action"),
}
i = 0
for alert in alerts:
i += 1
if alert['frequency'] == "day":
frequency = _("daily")
else:
if alert['frequency'] == "week":
frequency = _("weekly")
else:
frequency = _("monthly")
if alert['notification'] == "y":
notification = _("yes")
else:
notification = _("no")
# we clean up the HH:MM part of lastrun, since it is always 00:00
lastrun = alert['lastrun'].split(',')[0]
created = alert['created'].split(',')[0]
out += """<tr>
<td style="font-style: italic">#%(index)d</td>
<td style="font-weight: bold; text-wrap:none;">%(alertname)s</td>
<td>%(frequency)s</td>
<td style="text-align:center">%(notification)s</td>
<td style="text-wrap:none;">%(basketname)s</td>
<td style="text-wrap:none;">%(lastrun)s</td>
<td style="text-wrap:none;">%(created)s</td>
<td>%(textargs)s</td>
<td>
%(remove_link)s<br />
%(modify_link)s<br />
<a href="%(siteurl)s/search?%(queryargs)s&amp;ln=%(ln)s" style="white-space:nowrap">%(search)s</a>
</td>
</tr>""" % {
'index' : i,
'alertname' : cgi.escape(alert['alertname']),
'frequency' : frequency,
'notification' : notification,
'basketname' : alert['basketname'] and cgi.escape(alert['basketname']) \
or "- " + _("no basket") + " -",
'lastrun' : lastrun,
'created' : created,
'textargs' : alert['textargs'],
'queryid' : alert['queryid'],
'basketid' : alert['basketid'],
'freq' : alert['frequency'],
'notif' : alert['notification'],
'ln' : ln,
'modify_link': create_html_link("./modify",
{'ln': ln,
'idq': alert['queryid'],
'name': alert['alertname'],
'freq': frequency,
'notif':notification,
'idb':alert['basketid'],
'old_idb':alert['basketid']},
_("Modify")),
'remove_link': create_html_link("./remove",
{'ln': ln,
'idq': alert['queryid'],
'name': alert['alertname'],
'idb':alert['basketid']},
_("Remove")),
'siteurl' : CFG_SITE_URL,
'search' : _("Execute search"),
'queryargs' : cgi.escape(alert['queryargs'])
}
out += '</table>'
out += '<p>' + (_("You have defined %s alerts.") % ('<b>' + str(len(alerts)) + '</b>' )) + '</p>'
if guest:
out += guesttxt
return out
def tmpl_display_alerts(self, ln, permanent, nb_queries_total, nb_queries_distinct, queries, guest, guesttxt):
"""
Displays the list of alerts
Parameters:
- 'ln' *string* - The language to display the interface in
- 'permanent' *string* - If displaying most popular searches ('y') or only personal searches ('n')
- 'nb_queries_total' *string* - The number of personal queries in the last period
- 'nb_queries_distinct' *string* - The number of distinct queries in the last period
- 'queries' *array* - The existing queries:
- 'id' *string* - The id of the associated query
- 'args' *string* - The query string
- 'textargs' *string* - The textual description of the query string
- 'lastrun' *string* - The last running date (only for personal queries)
- 'guest' *bool* - If the user is a guest user
- 'guesttxt' *string* - The HTML content of the warning box for guest users (produced by webaccount.tmpl_warning_guest_user)
"""
# load the right message language
_ = gettext_set_language(ln)
if len(queries) == 0:
out = _("You have not executed any search yet. Please go to the %(x_url_open)ssearch interface%(x_url_close)s first.") % \
{'x_url_open': '<a href="' + CFG_SITE_URL + '/?ln=' + ln +'">',
'x_url_close': '</a>'}
return out
out = ''
# display message: number of items in the list
if permanent == "n":
msg = _("You have performed %(x_nb1)s searches (%(x_nb2)s different questions) during the last 30 days or so.") % {'x_nb1': nb_queries_total,
'x_nb2': nb_queries_distinct}
out += '<p>' + msg + '</p>'
else:
# permanent="y"
msg = _("Here are the %s most popular searches.")
msg %= ('<b>' + str(len(queries)) + '</b>')
out += '<p>' + msg + '</p>'
# display the list of searches
out += """<table class="alrtTable">
<tr class="pageboxlefttop">
<td style="font-weight: bold">%(no)s</td>
<td style="font-weight: bold">%(question)s</td>
<td style="font-weight: bold">%(action)s</td>""" % {
'no' : "#",
'question' : _("Question"),
'action' : _("Action")
}
if permanent == "n":
out += '<td style="font-weight: bold">%s</td>' % _("Last Run")
out += "</tr>\n"
i = 0
for query in queries :
i += 1
# id, pattern, base, search url and search set alert, date
out += """<tr>
<td style="font-style: italic;">#%(index)d</td>
<td>%(textargs)s</td>
<td><a href="%(siteurl)s/search?%(args)s">%(execute_query)s</a><br />
<a href="%(siteurl)s/youralerts/input?ln=%(ln)s&amp;idq=%(id)d">%(set_alert)s</a></td>""" % {
'index' : i,
'textargs' : query['textargs'],
'siteurl' : CFG_SITE_URL,
'args' : cgi.escape(query['args']),
'id' : query['id'],
'ln': ln,
'execute_query' : _("Execute search"),
'set_alert' : _("Set new alert")
}
if permanent == "n":
out += '<td>%s</td>' % query['lastrun']
out += """</tr>\n"""
out += "</table><br />\n"
if guest :
out += guesttxt
return out
def tmpl_alert_email_title(self, name):
return 'Alert %s run on %s' % (
name, time.strftime("%Y-%m-%d"))
def tmpl_alert_email_from(self):
return '%s Alert Engine <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL)
def tmpl_alert_email_body(self, name, url, records, pattern,
collection_list, frequency, add_to_basket_p):
recids_by_collection = {}
for recid in records[0]:
primary_collection = guess_primary_collection_of_a_record(recid)
if primary_collection in collection_list:
if not recids_by_collection.has_key(primary_collection):
recids_by_collection[primary_collection] = []
recids_by_collection[primary_collection].append(recid)
else:
ancestors = get_coll_ancestors(primary_collection)
ancestors.reverse()
nancestors = 0
for ancestor in ancestors:
nancestors += 1
if ancestor in collection_list:
if not recids_by_collection.has_key(ancestor):
recids_by_collection[ancestor] = []
recids_by_collection[ancestor].append(recid)
break
elif len(ancestors) == nancestors:
if not recids_by_collection.has_key('None of the above'):
recids_by_collection['None of the above'] = []
recids_by_collection['None of the above'].append(recid)
collection_list = [coll for coll in recids_by_collection.keys() if coll != 'None of the above']
for external_collection_results in records[1][0]:
if external_collection_results[1][0]:
collection_list.append(external_collection_results[0])
l = len(collection_list)
if l == 0:
collections = ''
elif l == 1:
collections = "collection: %s\n" % collection_list[0]
else:
collections = "collections: %s\n" % wrap(', '.join(collection_list))
l = len(records[0])
for external_collection_results in records[1][0]:
l += len(external_collection_results[1][0])
if l == 1:
total = '1 record'
else:
total = '%d records' % l
if pattern:
pattern = 'pattern: %s\n' % pattern
frequency = {'day': 'daily',
'week': 'weekly',
'month': 'monthly'}[frequency]
body = """\
Hello:
Below are the results of the email notification alert that
you set up with the %(sitename)s.
This is an automatic message, please don't reply to it.
For any question, please use <%(sitesupportemail)s> instead.
alert name: %(name)s
%(pattern)s%(collections)sfrequency: %(frequency)s
run time: %(runtime)s
found: %(total)s
url: <%(url)s>
""" % {'sitesupportemail': CFG_SITE_SUPPORT_EMAIL,
'name': name,
'sitename': CFG_SITE_NAME,
'pattern': pattern,
'collections': collections,
'frequency': frequency,
'runtime': time.strftime("%a %Y-%m-%d %H:%M:%S"),
'total': total,
'url': url}
index = 0
for collection_recids in recids_by_collection.items():
if collection_recids[0] != 'None of the above':
body += "\nCollection: %s\n" % collection_recids[0]
for recid in collection_recids[1]:
index += 1
body += "\n%i) " % (index)
body += self.tmpl_alert_email_record(recid=recid)
body += "\n"
if index == CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
break
if index == CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
break
if index < CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
if recids_by_collection.has_key('None of the above'):
if len(recids_by_collection.keys()) > 1:
body += "\nNone of the above collections:\n"
else:
# if the uncategorized collection is the only collection then present
# all the records as belonging to CFG_SITE_NAME
body += "\nCollection: %s\n" % CFG_SITE_NAME
for recid in recids_by_collection['None of the above']:
index += 1
body += "\n%i) " % (index)
body += self.tmpl_alert_email_record(recid=recid)
body += "\n"
if index == CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
break
if index < CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
for external_collection_results in records[1][0]:
body += "\nCollection: %s\n" % external_collection_results[0]
for recid in external_collection_results[1][0]:
index += 1
body += "\n%i) " % (index)
# TODO: extend function to accept xml_record!
body += self.tmpl_alert_email_record(xml_record=external_collection_results[1][1][recid])
body += "\n"
if index == CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
break
if index == CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
break
if l > CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
body += '''
Only the first %s records were displayed. Please consult the search
URL given at the top of this email to see all the results.
''' % (CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL,)
if add_to_basket_p:
body += '''
Only the first %s records were added to your basket. To manually add more
records please consult the search URL as described before.
''' % (CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL,)
body += '''
--
%s Alert Service <%s>
Unsubscribe? See <%s>
Need human intervention? Contact <%s>
''' % (CFG_SITE_NAME, CFG_SITE_URL, CFG_SITE_URL + '/youralerts/list', CFG_SITE_SUPPORT_EMAIL)
return body
def tmpl_alert_email_record(self, recid=0, xml_record=None):
""" Format a single record."""
if recid != 0:
out = wrap_records(get_as_text(record_id=recid))
- out += "\nDetailed record: <%s/record/%s>" % (CFG_SITE_URL, recid)
+ out += "\nDetailed record: <%s/%s/%s>" % (CFG_SITE_URL, CFG_SITE_RECORD, recid)
elif xml_record:
out = wrap_records(get_as_text(xml_record=xml_record))
# TODO: add Detailed record url for external records?
return out
diff --git a/modules/webbasket/lib/webbasket_templates.py b/modules/webbasket/lib/webbasket_templates.py
index f30a64502..883922c95 100644
--- a/modules/webbasket/lib/webbasket_templates.py
+++ b/modules/webbasket/lib/webbasket_templates.py
@@ -1,3959 +1,3962 @@
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Templating for webbasket module """
__revision__ = "$Id$"
import cgi
from invenio.messages import gettext_set_language
from invenio.webbasket_config import \
CFG_WEBBASKET_CATEGORIES, \
CFG_WEBBASKET_SHARE_LEVELS, \
CFG_WEBBASKET_DIRECTORY_BOX_NUMBER_OF_COLUMNS
from invenio.webmessage_mailutils import email_quoted_txt2html, \
email_quote_txt, \
escape_email_quoted_text
from invenio.htmlutils import get_html_text_editor
from invenio.config import \
CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_SITE_LANG, \
CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS, \
- CFG_WEBBASKET_USE_RICH_TEXT_EDITOR
+ CFG_WEBBASKET_USE_RICH_TEXT_EDITOR, \
+ CFG_SITE_RECORD
from invenio.webuser import get_user_info
from invenio.dateutils import convert_datetext_to_dategui
class Template:
"""Templating class for webbasket module"""
######################## General interface ################################
def tmpl_create_directory_box(self,
category, topic,
(grpid, group_name),
bskid,
(personal_info, personal_baskets_info),
(group_info, group_baskets_info),
public_info,
ln):
"""Template for the directory-like menu.
@param category: the selected category
@param topic: the selected topic (optional)
@param (grpid, groupname): the id and name of the selected group (optional)
@param bskid: the id of the selected basket (optional)
@param (personal_info, personal_baskets_info): personal baskets data
@param (group_info, group_baskets_info): group baskets data
@param public_info: public baskets data
@param ln: language"""
_ = gettext_set_language(ln)
def __calculate_prettify_name_char_limit(nb_baskets, max_chars=45, nb_dots=3):
"""Private function. Calculates the char_limit to be fed to the
prettify_name function according to the max_chars limit and the nb_dots."""
# Let's do some initial calculations:
D = nb_dots
B = nb_baskets
M = max_chars
# some assisting abbreviations
Y = ( B > 3 and 2 or B - 1 )
Z = ( B > 3 and 5 or 0 )
# and the desired result
X = ( ( M - Z - ( ( 2 + D ) * Y ) - D ) / ( Y + 1 ) )
return X
if not personal_info and not group_info and not public_info:
return """
%(no_baskets_label)s
<br /><br />
%(create_basket_label)s""" % \
{'no_baskets_label': _('You have no personal or group baskets or are subscribed to any public baskets.'),
'create_basket_label': _('You may want to start by %(x_url_open)screating a new basket%(x_url_close)s.') % \
{'x_url_open': '<a href="%s/yourbaskets/create_basket?ln=%s">' % (CFG_SITE_URL, ln),
'x_url_close': '</a>'}}
## Firstly, create the tabs area.
if personal_info:
## If a specific topic is selected display the name of the topic
## and the options on it.
if personal_baskets_info:
personalbaskets_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;ln=%(ln)s">%(label)s</a>""" % \
{'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['PRIVATE'],
'ln': ln,
'label': _('Personal baskets')}
topic_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;topic=%(topic)s&amp;ln=%(ln)s">%(label)s</a>""" % \
{'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['PRIVATE'],
'topic': topic,
'ln': ln,
'label': cgi.escape(topic, True)}
go_back_link = """<a href="%(url)s/yourbaskets/display?ln=%(ln)s"><img src="%(url)s/img/%(img)s" />%(label)s</a>""" % \
{'url': CFG_SITE_URL,
'ln': ln,
'img': 'wb-go-back.png',
'label': _('Back to Your Baskets')}
create_basket_link = """<a href="%(url)s/yourbaskets/create_basket?topic=%(topic)s&amp;ln=%(ln)s"><img src="%(url)s/img/%(img)s" />%(label)s</a>""" % \
{'url': CFG_SITE_URL,
'topic': cgi.escape(topic, True),
'ln': ln,
'img': 'wb-create-basket.png',
'label': _('Create basket')}
edit_topic_link = """<a href="%(url)s/yourbaskets/edit_topic?topic=%(topic)s&amp;ln=%(ln)s"><img src="%(url)s/img/%(img)s" />%(label)s</a>""" % \
{'url': CFG_SITE_URL,
'topic': cgi.escape(topic, True),
'ln': ln,
'img': 'wb-edit-topic.png',
'label': _('Edit topic')}
personal_tab = """
<td class="bsk_directory_box_nav_tab_content">
%(personalbaskets_link)s&nbsp;&gt;&nbsp;%(topic_link)s
</td>
<td class="bsk_directory_box_nav_tab_options">
%(go_back)s
&nbsp;&nbsp;
%(create_basket)s
&nbsp;&nbsp;
%(edit_topic)s
</td>""" % {'topic_link': topic_link,
'personalbaskets_link': personalbaskets_link,
'go_back': go_back_link,
'create_basket': create_basket_link,
'edit_topic': edit_topic_link}
## If no specific topic is selected display the personal baskets tab.
else:
personal_tab = """
<td class="%(class)s">
<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;ln=%(ln)s">%(label)s</a>
</td>""" % {'class': category == CFG_WEBBASKET_CATEGORIES['PRIVATE'] \
and "bsk_directory_box_tab_content_selected" \
or "bsk_directory_box_tab_content",
'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['PRIVATE'],
'ln': ln,
'label': _('Personal baskets')}
else:
personal_tab = """
<td class="%(class)s">
%(label)s
</td>""" % {'class': 'bsk_directory_box_tab_content_inactive',
'label': _('Personal baskets')}
if group_info:
## If a specific group is selected display the name of the group
## and the options on it.
if group_baskets_info:
groupbaskets_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;ln=%(ln)s">%(label)s</a>""" % \
{'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['GROUP'],
'ln': ln,
'label': _('Group baskets')}
group_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;group=%(grpid)i&amp;ln=%(ln)s">%(label)s</a>""" % \
{'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['GROUP'],
'grpid': grpid,
'ln': ln,
'label': group_name}
go_back_link = """<a href="%(url)s/yourbaskets/display?ln=%(ln)s"><img src="%(url)s/img/%(img)s" />%(label)s</a>""" % \
{'url': CFG_SITE_URL,
'ln': ln,
'img': 'wb-go-back.png',
'label': _('Back to Your Baskets')}
group_tab = """
<td class="bsk_directory_box_nav_tab_content">
%(groupbaskets_link)s&nbsp;&gt;&nbsp;%(group_link)s
</td>
<td class="bsk_directory_box_nav_tab_options">
%(go_back)s
</td>""" % {'groupbaskets_link': groupbaskets_link,
'group_link': group_link,
'go_back': go_back_link}
## If no specific group is selected display the group baskets tab.
else:
group_tab = """
<td class="%(class)s">
<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;ln=%(ln)s">%(label)s</a>
</td>""" % {'class': category == CFG_WEBBASKET_CATEGORIES['GROUP'] \
and "bsk_directory_box_tab_content_selected" \
or "bsk_directory_box_tab_content",
'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['GROUP'],
'ln': ln,
'label': _('Group baskets')}
else:
group_tab = """
<td class="%(class)s">
%(label)s
</td>""" % {'class': 'bsk_directory_box_tab_content_inactive',
'label': _('Group baskets')}
if public_info:
## Display the public baskets tab.
public_tab = """
<td class="%(class)s">
<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;ln=%(ln)s">%(label)s</a>
</td>""" % {'class': category == CFG_WEBBASKET_CATEGORIES['EXTERNAL'] \
and "bsk_directory_box_tab_content_selected" \
or "bsk_directory_box_tab_content",
'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['EXTERNAL'],
'ln': ln,
'label': _('Public baskets')}
else:
public_tab = """
<td class="%(class)s">
%(label)s
</td>""" % {'class': 'bsk_directory_box_tab_content_inactive',
'label': _('Public baskets')}
## If a specific topic is selected display the name of the topic
## and the options on it.
if personal_baskets_info:
tabs = """
<table cellspacing="0px" cellpadding="0px" class="bsk_directory_box_tabs">
<tr>%s
</tr>
</table>""" % (personal_tab,)
## If a specific group is selected display the name of the group
## and the options on it.
elif group_baskets_info:
tabs = """
<table cellspacing="0px" cellpadding="0px" class="bsk_directory_box_tabs">
<tr>
%s
</tr>
</table>""" % (group_tab,)
## If only a sepcific category is selected (or eveb none) display
## all the available tabs (selected, normal, inactive).
else:
tabs = """
<table cellspacing="0px" cellpadding="0px" class="bsk_directory_box_tabs">
<tr>
<td class="bsk_directory_box_tab_separator">
&nbsp;
</td>
%(personal_tab)s
<td class="bsk_directory_box_tab_separator">
&nbsp;
</td>
%(group_tab)s
<td class="bsk_directory_box_tab_separator">
&nbsp;
</td>
%(public_tab)s
<td class="bsk_directory_box_tab_separator_end">
&nbsp;
</td>
</tr>
</table>""" % {'personal_tab': personal_tab,
'group_tab': group_tab,
'public_tab': public_tab}
## Secondly, create the content.
if personal_info and category==CFG_WEBBASKET_CATEGORIES['PRIVATE']:
content_list = []
## If a specific topic is selected create a list of baskets for that topic.
if personal_baskets_info:
for basket in personal_baskets_info:
basket_id = basket[0]
basket_name = basket[1]
nb_items = basket[4]
basket_link = """%(opening_tag)s<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;topic=%(topic)s&amp;bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a>%(closing_tag)s <span class="bsk_directory_box_content_list_number_of">(%(nb_items)i)</span>""" % \
{'opening_tag': basket_id==bskid and "<em>" or "",
'closing_tag': basket_id==bskid and "</em>" or "",
'url': CFG_SITE_URL,
'category': category,
'topic': cgi.escape(topic, True),
'bskid': basket_id,
'ln': ln,
'title_name': cgi.escape(basket_name, True),
'basket_name': cgi.escape(prettify_name(basket_name, 27), True),
'nb_items': nb_items}
content_list_item = """
%(basket_link)s""" % {'basket_link': basket_link}
content_list.append(content_list_item)
## If no specific topic is selected create a list of topics with a preview of their baskets.
else:
for topic_and_baskets in personal_info:
topic_name = topic_and_baskets[0]
nb_baskets = topic_and_baskets[1]
topic_link = """<strong><a href="%(url)s/yourbaskets/display?category=%(category)s&amp;topic=%(topic)s&amp;ln=%(ln)s" title="%(title_name)s">%(topic_name)s</a></strong> <span class="bsk_directory_box_content_list_number_of">(%(nb_baskets)s)</span>""" % \
{'url': CFG_SITE_URL,
'category': category,
'topic': cgi.escape(topic_name, True),
'ln': ln,
'title_name': cgi.escape(topic_name, True),
'topic_name': cgi.escape(prettify_name(topic_name, 25), True),
'nb_baskets': nb_baskets}
baskets = eval(topic_and_baskets[2] + ',')
basket_links = ""
basket_links_list = []
for basket in baskets[:3]:
# TODO: adapt the prettify_name char_limit variable according to nb_baskets
basket_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;topic=%(topic)s&amp;bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a>""" % \
{'url': CFG_SITE_URL,
'category': category,
'topic': cgi.escape(topic_name, True),
'bskid': basket[0],
'ln': ln,
'title_name': cgi.escape(basket[1], True),
'basket_name': cgi.escape(prettify_name(basket[1], __calculate_prettify_name_char_limit(nb_baskets, 135/CFG_WEBBASKET_DIRECTORY_BOX_NUMBER_OF_COLUMNS)), True)}
basket_links_list.append(basket_link)
basket_links = ', '.join(basket_links_list)
if nb_baskets > 3:
basket_links += ", ..."
content_list_item = """
%(topic_link)s
<br />
<small>%(basket_links)s</small>""" % \
{'topic_link': topic_link,
'basket_links': basket_links}
content_list.append(content_list_item)
nb_cells = CFG_WEBBASKET_DIRECTORY_BOX_NUMBER_OF_COLUMNS
nb_items = len(content_list)
content_list.reverse()
content = """
<table cellspacing="0px" cellpadding="0px" align="center" width="100%">
<tr>"""
for i in range(nb_cells):
content += """
<td class="bsk_directory_box_content_list_cell" width="%s%%">""" % \
(100/nb_cells,)
nb_lines = (nb_items/nb_cells) + ((nb_items%nb_cells) > i and 1 or 0)
for j in range(nb_lines):
content += content_list.pop()
if j < (nb_lines-1):
content += personal_baskets_info and "<br />" or "<br /><br />"
content += """
</td>"""
content += """
</tr>
</table>"""
elif group_info and category==CFG_WEBBASKET_CATEGORIES['GROUP']:
content_list = []
## If a specific grpid is selected create a list of baskets for that group.
if group_baskets_info:
for basket in group_baskets_info:
basket_id = basket[0]
basket_name = basket[1]
nb_items = basket[4]
basket_link = """%(opening_tag)s<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;group=%(grpid)i&amp;bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a>%(closing_tag)s <span class="bsk_directory_box_content_list_number_of">(%(nb_items)i)</span>""" % \
{'opening_tag': basket_id==bskid and "<em>" or "",
'closing_tag': basket_id==bskid and "</em>" or "",
'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['GROUP'],
'grpid': grpid,
'bskid': basket_id,
'ln': ln,
'title_name': cgi.escape(basket_name, True),
'basket_name': cgi.escape(prettify_name(basket_name, 27), True),
'nb_items': nb_items}
content_list_item = """
%(basket_link)s""" % {'basket_link': basket_link}
content_list.append(content_list_item)
## If no specific grpid is selected create a list of groups with a preview of their baskets.
else:
for group_and_baskets in group_info:
group_id = group_and_baskets[0]
group_name = group_and_baskets[1]
nb_baskets = group_and_baskets[2]
group_link = """<strong><a href="%(url)s/yourbaskets/display?category=%(category)s&amp;group=%(group)i&amp;ln=%(ln)s" title="%(title_name)s">%(group_name)s</a></strong> <span class="bsk_directory_box_content_list_number_of">(%(nb_baskets)s)</span>""" % \
{'url': CFG_SITE_URL,
'category': category,
'group': group_id,
'ln': ln,
'title_name': cgi.escape(group_name, True),
'group_name': cgi.escape(prettify_name(group_name, 25), True),
'nb_baskets': nb_baskets}
baskets = eval(group_and_baskets[3] + ',')
basket_links = ""
basket_links_list = []
for basket in baskets[:3]:
# TODO: adapt the prettify_name char_limit variable according to nb_baskets
basket_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;group=%(group)i&amp;bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a>""" % \
{'url': CFG_SITE_URL,
'category': category,
'group': group_id,
'bskid': basket[0],
'ln': ln,
'title_name': cgi.escape(basket[1], True),
'basket_name': cgi.escape(prettify_name(basket[1], __calculate_prettify_name_char_limit(nb_baskets, 135/CFG_WEBBASKET_DIRECTORY_BOX_NUMBER_OF_COLUMNS)), True)}
basket_links_list.append(basket_link)
basket_links = ', '.join(basket_links_list)
if nb_baskets > 3:
basket_links += ", ..."
content_list_item = """
%(group_link)s
<br />
<small>%(basket_links)s</small>""" % \
{'group_link': group_link,
'basket_links': basket_links}
content_list.append(content_list_item)
nb_cells = CFG_WEBBASKET_DIRECTORY_BOX_NUMBER_OF_COLUMNS
nb_items = len(content_list)
content_list.reverse()
content = """
<table cellspacing="0px" cellpadding="0px" align="center" width="100%">
<tr>"""
for i in range(nb_cells):
content += """
<td class="bsk_directory_box_content_list_cell" width="%s%%">""" % \
(100/nb_cells,)
nb_lines = (nb_items/nb_cells) + ((nb_items%nb_cells) > i and 1 or 0)
for j in range(nb_lines):
content += content_list.pop()
if j < (nb_lines-1):
#content += "<br /><br />"
content += group_baskets_info and "<br />" or "<br /><br />"
content += """
</td>"""
content += """
</tr>
</table>"""
elif public_info and category==CFG_WEBBASKET_CATEGORIES['EXTERNAL']:
content_list = []
for basket in public_info:
basket_id = basket[0]
basket_name = basket[1]
nb_items = basket[2]
basket_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a> <span class="bsk_directory_box_content_list_number_of">(%(nb_items)i)</span>""" % \
{'url': CFG_SITE_URL,
'category': category,
'bskid': basket_id,
'ln': ln,
'title_name': cgi.escape(basket_name, True),
'basket_name': cgi.escape(prettify_name(basket_name, 27), True),
'nb_items': nb_items}
content_list_item = """
%(basket_link)s""" % {'basket_link': basket_link}
content_list.append(content_list_item)
nb_cells = CFG_WEBBASKET_DIRECTORY_BOX_NUMBER_OF_COLUMNS
nb_items = len(content_list)
content_list.reverse()
content = """
<table cellspacing="0px" cellpadding="0px" align="center" width="100%">
<tr>"""
for i in range(nb_cells):
content += """
<td class="bsk_directory_box_content_list_cell" width="%s%%">""" % \
(100/nb_cells,)
nb_lines = (nb_items/nb_cells) + ((nb_items%nb_cells) > i and 1 or 0)
for j in range(nb_lines):
content += content_list.pop()
if j < (nb_lines-1):
content += "<br />"
content += """
</td>"""
content += """
</tr>
</table>"""
out = """
<table cellspacing="0px" cellpadding="0px" class="bsk_directory_box">
<tr>
<td width="100%%">
%(tabs)s
</td>
</tr>
<tr>
<td width="100%%">
<table cellspacing="0px" cellpadding="0px" class="bsk_directory_box_content">
<tr>
<td class="%(class)s">
%(content)s
</td>
</tr>
</table>
</td>
</tr>
</table>""" % {'class': ((category == CFG_WEBBASKET_CATEGORIES['PRIVATE'] and topic) or \
(category == CFG_WEBBASKET_CATEGORIES['GROUP'] and grpid)) and \
"bsk_directory_box_content_list_baskets" or \
"bsk_directory_box_content_list_topics_groups",
'tabs': tabs,
'content': content}
return out
def tmpl_create_search_box(self,
category="",
topic="",
grpid=0,
topic_list=(),
group_list=(),
number_of_public_baskets=0,
p="",
n=0,
ln=CFG_SITE_LANG):
"""EXPERIMENTAL UI"""
_ = gettext_set_language(ln)
action = """%s/yourbaskets/search""" % (CFG_SITE_URL,)
select_options = create_search_box_select_options(category,
topic,
grpid,
topic_list,
group_list,
number_of_public_baskets,
ln)
out = """
<table cellspacing="0px" cellpadding="5px" class="bsk_search_box">
<form name="search_baskets" action="%(action)s" method="get">
<thead>
<tr>
<td colspan="4">
<small><strong>%(search_for_label)s:</strong><small>
</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<input name="p" value="%(p)s" type="text" />
</td>
<td>
<small><strong>in</strong><small>
</td>
<td>
<select name="b">%(select_options)s
</select>
</td>
<td>
<input class="formbutton" type="submit" value="Search" />
</td>
</tr>
<tr>
<td>
<input type="checkbox" name="n" value="1"%(notes_checked)s />
<small>%(notes_label)s</small>
</td>
</tr>
</tbody>
<input type="hidden" name="ln" value="%(ln)s" />
</form>
</table>""" % {'action': action,
'search_for_label': _('Search baskets for'),
'notes_label': _('Search also in notes (where allowed)'),
'notes_checked': n and ' checked="checked"' or '',
'p': p,
'select_options': select_options,
'ln': cgi.escape(ln, True)}
return out
def tmpl_search_results(self,
personal_search_results={},
total_no_personal_search_results=0,
group_search_results={},
total_no_group_search_results=0,
public_search_results={},
total_no_public_search_results=0,
all_public_search_results={},
total_no_all_public_search_results=0,
ln=CFG_SITE_LANG):
"""Template for the search results."""
_ = gettext_set_language(ln)
out = """
<table cellspacing="0px" cellpadding="5px" class="bsk_search_box">"""
total_no_search_results = total_no_personal_search_results + \
total_no_group_search_results + \
total_no_public_search_results + \
total_no_all_public_search_results
if total_no_search_results:
# INFO: Results overview is disabled for now.
# Remove "if False:" when needed again.
if False:
out += """
<tr>
<td style="border-top: 1px #fc0 solid; border-bottom: 1px #fc0 dotted; background-color: #ffc">
<strong>%(results_overview_label)s:</strong> %(items_found_label)s
</td>
</tr>""" % {'results_overview_label': _('Results overview'),
'items_found_label': _('%i items found') % total_no_search_results}
if total_no_personal_search_results:
out += """
<tr>
<td>
<a href="#%(personal_baskets_name)s">%(personal_baskets_label)s</a>: %(items_found_label)s
</td>
</tr>""" % {'personal_baskets_label': _('Personal baskets'),
'personal_baskets_name': "P",
'items_found_label': _('%i items found') % total_no_personal_search_results}
if total_no_group_search_results:
out += """
<tr>
<td>
<a href="#%(group_baskets_name)s">%(group_baskets_label)s<a/>: %(items_found_label)s
</td>
</tr>""" % {'group_baskets_label': _('Group baskets'),
'group_baskets_name': "G",
'items_found_label': _('%i items found') % total_no_group_search_results}
if total_no_public_search_results:
out += """
<tr>
<td>
<a href="#%(public_baskets_name)s">%(public_baskets_label)s</a>: %(items_found_label)s
</td>
</tr>""" % {'public_baskets_label': _('Public baskets'),
'public_baskets_name': "E",
'items_found_label': _('%i items found') % total_no_public_search_results}
if total_no_all_public_search_results:
out += """
<tr>
<td>
<a href="#%(all_public_baskets_name)s">%(all_public_baskets_label)s</a>: %(items_found_label)s
</td>
</tr>""" % {'all_public_baskets_label': _('All public baskets'),
'all_public_baskets_name': "A",
'items_found_label': _('%i items found') % total_no_all_public_search_results}
out += """
<tr>
<td>
&nbsp;
</td>
</tr>"""
else:
out += """
<tr>
<td>
%(no_items_found_label)s
</td>
</tr>""" % {'no_items_found_label': _('No items found.')}
if total_no_personal_search_results:
out += """
<tr>
<td style="border-top: 1px #fc0 solid; border-bottom: 1px #fc0 dotted; background-color: #ffc">
<a name="%(personal_baskets_name)s"></a><strong>%(personal_baskets_label)s:</strong> %(items_found_label)s
</td>
</tr>""" % {'personal_baskets_label': _('Personal baskets'),
'personal_baskets_name': "P",
'items_found_label': _('%i items found') % total_no_personal_search_results}
for personal_search_result in personal_search_results.iteritems():
basket_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;topic=%(topic)s&amp;bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a>""" % \
{'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['PRIVATE'],
'topic': cgi.escape(personal_search_result[1][1], True),
'bskid': personal_search_result[0],
'ln': ln,
'title_name': cgi.escape(personal_search_result[1][0], True),
'basket_name': cgi.escape(personal_search_result[1][0], True)}
out += """
<tr>
<td>
%(in_basket_label)s: %(items_found)s
</td>
</tr>""" % {'in_basket_label': _('In %(x_linked_basket_name)s') % \
{'x_linked_basket_name': basket_link},
'items_found': _('%i items found') % personal_search_result[1][2]}
out += """
<tr>
<td>
&nbsp;
</td>
</tr>"""
if total_no_group_search_results:
out += """
<tr>
<td style="border-top: 1px #fc0 solid; border-bottom: 1px #fc0 dotted; background-color: #ffc">
<a name="%(group_baskets_name)s"></a><strong>%(group_baskets_label)s:</strong> %(items_found_label)s
</td>
</tr>""" % {'group_baskets_label': _('Group baskets'),
'group_baskets_name': "G",
'items_found_label': _('%i items found') % total_no_group_search_results}
for group_search_result in group_search_results.iteritems():
basket_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;group=%(group)i&amp;bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a>""" % \
{'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['GROUP'],
'group': group_search_result[1][1],
'bskid': group_search_result[0],
'ln': ln,
'title_name': cgi.escape(group_search_result[1][0], True),
'basket_name': cgi.escape(group_search_result[1][0], True)}
out += """
<tr>
<td>
%(in_basket_label)s: %(items_found)s
</td>
</tr>""" % {'in_basket_label': _('In %(x_linked_basket_name)s') % \
{'x_linked_basket_name': basket_link},
'items_found': _('%i items found') % group_search_result[1][3]}
out += """
<tr>
<td>
&nbsp;
</td>
</tr>"""
if total_no_public_search_results:
out += """
<tr>
<td style="border-top: 1px #fc0 solid; border-bottom: 1px #fc0 dotted; background-color: #ffc">
<a name="%(public_baskets_name)s"></a><strong>%(public_baskets_label)s:</strong> %(items_found_label)s
</td>
</tr>""" % {'public_baskets_label': _('Public baskets'),
'public_baskets_name': "E",
'items_found_label': _('%i items found') % total_no_public_search_results}
for public_search_result in public_search_results.iteritems():
basket_link = """<a href="%(url)s/yourbaskets/display?category=%(category)s&amp;bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a>""" % \
{'url': CFG_SITE_URL,
'category': CFG_WEBBASKET_CATEGORIES['EXTERNAL'],
'bskid': public_search_result[0],
'ln': ln,
'title_name': cgi.escape(public_search_result[1][0], True),
'basket_name': cgi.escape(public_search_result[1][0], True)}
out += """
<tr>
<td>
%(in_basket_label)s: %(items_found)s
</td>
</tr>""" % {'in_basket_label': _('In %(x_linked_basket_name)s') % \
{'x_linked_basket_name': basket_link},
'items_found': _('%i items found') % public_search_result[1][1]}
out += """
<tr>
<td>
&nbsp;
</td>
</tr>"""
if total_no_all_public_search_results:
out += """
<tr>
<td style="border-top: 1px #fc0 solid; border-bottom: 1px #fc0 dotted; background-color: #ffc">
<a name="%(all_public_baskets_name)s"></a><strong>%(all_public_baskets_label)s:</strong> %(items_found_label)s
</td>
</tr>""" % {'all_public_baskets_label': _('All public baskets'),
'all_public_baskets_name': "A",
'items_found_label': _('%i items found') % total_no_all_public_search_results}
for all_public_search_result in all_public_search_results.iteritems():
basket_link = """<a href="%(url)s/yourbaskets/display_public?bskid=%(bskid)i&amp;ln=%(ln)s" title="%(title_name)s">%(basket_name)s</a>""" % \
{'url': CFG_SITE_URL,
'bskid': all_public_search_result[0],
'ln': ln,
'title_name': cgi.escape(all_public_search_result[1][0], True),
'basket_name': cgi.escape(all_public_search_result[1][0], True)}
out += """
<tr>
<td>
%(in_basket_label)s: %(items_found)s
</td>
</tr>""" % {'in_basket_label': _('In %(x_linked_basket_name)s') % \
{'x_linked_basket_name': basket_link},
'items_found': _('%i items found') % personal_search_result[1][2]}
out += """
<tr>
<td>
&nbsp;
</td>
</tr>"""
out += """
</table>"""
return out
def tmpl_display(self,
directory='',
content='',
search_box='',
search_results=''):
"""Template for the generic display.
@param directory: the directory-like menu (optional)
@param content: content (of a basket) (optional)
@param search_box: the search form (optional)
@param search_results: the search results (optional)"""
display_items = []
if directory:
container_directory = """
<div id="bskcontainerdirectory">%s
</div>
""" % (directory,)
display_items.append(container_directory)
if content:
container_content = """
<div id="bskcontainercontent">%s
</div>
""" % (content,)
display_items.append(container_content)
if search_box:
container_search_box = """
<div id="bskcontainersearch">%s
</div>
""" % (search_box,)
display_items.append(container_search_box)
if search_results:
container_search_results = """
<div id="bskcontainersearch">%s
</div>
""" % (search_results,)
display_items.append(container_search_results)
display_separator= """
<div height="10px">
&nbsp;
</div>
"""
display = display_separator.join(display_items)
out = """
<div id="bskcontainer">
%s
</div>""" % (display,)
return out
def tmpl_display_list_public_baskets(self,
all_public_baskets,
limit,
number_of_all_public_baskets,
sort,
asc,
nb_views_show_p=False,
ln=CFG_SITE_LANG):
"""Template for the list of public baskets.
@param all_public_baskets: tuple
(bskid, basket_name, owner_id, nickname, date_modification, nb_views, nb_items)
@param limit: display baskets from the incrementally numbered 'limit' and on
@param number_of_all_public_baskets: the number of all the public baskets
@param sort: 'name': sort by basket name
'views': sort by number of basket views
'nickname': sort by user nickname
'date': sort by basket modification date
'items': sort by number of basket items
@param asc: ascending sort or not
@param nb_views_show_p: show the views column or not
@param ln: language"""
_ = gettext_set_language(ln)
basket_name_label = _("Public basket")
owner_label = _("Owner")
date_modification_label = _("Last update")
nb_items_label = _("Items")
nb_views_label = _("Views")
if sort == "name":
if asc:
basket_name_sort_img = """<img src="%s/img/wb-sort-asc.gif" />""" % (CFG_SITE_URL,)
else:
basket_name_sort_img = """<img src="%s/img/wb-sort-desc.gif" />""" % (CFG_SITE_URL,)
else:
basket_name_sort_img = """<img src="%s/img/wb-sort-none.gif" />""" % (CFG_SITE_URL,)
if sort == "owner":
if asc:
owner_sort_img = """<img src="%s/img/wb-sort-asc.gif" />""" % (CFG_SITE_URL,)
else:
owner_sort_img = """<img src="%s/img/wb-sort-desc.gif" />""" % (CFG_SITE_URL,)
else:
owner_sort_img = """<img src="%s/img/wb-sort-none.gif" />""" % (CFG_SITE_URL,)
if sort == "date":
if asc:
date_modification_sort_img = """<img src="%s/img/wb-sort-asc.gif" />""" % (CFG_SITE_URL,)
else:
date_modification_sort_img = """<img src="%s/img/wb-sort-desc.gif" />""" % (CFG_SITE_URL,)
else:
date_modification_sort_img = """<img src="%s/img/wb-sort-none.gif" />""" % (CFG_SITE_URL,)
if sort == "items":
if asc:
nb_items_sort_img = """<img src="%s/img/wb-sort-asc.gif" />""" % (CFG_SITE_URL,)
else:
nb_items_sort_img = """<img src="%s/img/wb-sort-desc.gif" />""" % (CFG_SITE_URL,)
else:
nb_items_sort_img = """<img src="%s/img/wb-sort-none.gif" />""" % (CFG_SITE_URL,)
if sort == "views":
if asc:
nb_views_sort_img = """<img src="%s/img/wb-sort-asc.gif" />""" % (CFG_SITE_URL,)
else:
nb_views_sort_img = """<img src="%s/img/wb-sort-desc.gif" />""" % (CFG_SITE_URL,)
else:
nb_views_sort_img = """<img src="%s/img/wb-sort-none.gif" />""" % (CFG_SITE_URL,)
basket_name_sort = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=name&amp;asc=%i&amp;ln=%s">%s</a>""" % \
(CFG_SITE_URL, limit, not(asc), ln, basket_name_sort_img)
owner_sort = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=owner&amp;asc=%i&amp;ln=%s">%s</a>""" % \
(CFG_SITE_URL, limit, not(asc), ln, owner_sort_img)
date_modification_sort = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=date&amp;asc=%i&amp;ln=%s">%s</a>""" % \
(CFG_SITE_URL, limit, not(asc), ln, date_modification_sort_img)
nb_items_sort = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=items&amp;asc=%i&amp;ln=%s">%s</a>""" % \
(CFG_SITE_URL, limit, not(asc), ln, nb_items_sort_img)
nb_views_sort = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=views&amp;asc=%i&amp;ln=%s">%s</a>""" % \
(CFG_SITE_URL, limit, not(asc), ln, nb_views_sort_img)
baskets_html = ''
for (bskid, basket_name, owner_id, nickname, date_modification, nb_items, nb_views) in all_public_baskets:
if nickname:
display = nickname
else:
(owner_id, nickname, display) = get_user_info(owner_id)
webmessage_link = self.__create_webmessage_link(nickname, display, ln)
basket_link = """<a href="%s/yourbaskets/display_public?category=%s&amp;bskid=%s&amp;ln=%s">%s<a/>""" % \
(CFG_SITE_URL, CFG_WEBBASKET_CATEGORIES['EXTERNAL'], bskid, ln, cgi.escape(basket_name, True))
nb_views_td = """<td class="bsk_list_public_baskets_basket_right">%i</td>""" % (nb_views,)
baskets_html += """
<tr>
<td class="bsk_list_public_baskets_basket_left">%(basket_link)s</td>
<td class="bsk_list_public_baskets_basket_left">%(webmessage_link)s</td>
<td class="bsk_list_public_baskets_basket_left">%(date_modification)s</td>
<td class="bsk_list_public_baskets_basket_right">%(nb_items)i</td>
%(nb_views)s
</tr>""" % {'basket_link': basket_link,
'webmessage_link': webmessage_link,
'date_modification': date_modification,
'nb_items': nb_items,
'nb_views': nb_views_show_p and nb_views_td or ''}
if not all_public_baskets:
baskets_html = """
<tr>
<td colspan="%i">
%s
</td>
</tr>""" % (nb_views_show_p and 5 or 4,
_("There is currently no publicly accessible basket"))
footer = ''
if limit > CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS:
limit_first = 1
page_first = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=%s&amp;asc=%i&amp;ln=%s"><img src="%s" /></a>""" % \
(CFG_SITE_URL, limit_first, sort, asc, ln, '/img/sb.gif')
footer += page_first
if limit > 0:
limit_previous = limit > CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS \
and limit - CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS + 1 \
or 1
page_previous = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=%s&amp;asc=%i&amp;ln=%s"><img src="%s" /></a>""" % \
(CFG_SITE_URL, limit_previous, sort, asc, ln, '/img/sp.gif')
footer += page_previous
display_from = limit + 1
display_to = limit + CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS > number_of_all_public_baskets \
and number_of_all_public_baskets \
or limit + CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS
footer += _('Displaying public baskets %(x_from)i - %(x_to)i out of %(x_total_public_basket)i public baskets in total.') % \
{'x_from': display_from, 'x_to': display_to, 'x_total_public_basket': number_of_all_public_baskets}
if limit < number_of_all_public_baskets - CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS:
limit_next = limit + CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS + 1
page_next = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=%s&amp;asc=%i&amp;ln=%s"><img src="%s" /></a>""" % \
(CFG_SITE_URL, limit_next, sort, asc, ln, '/img/sn.gif')
footer += page_next
if limit < number_of_all_public_baskets - ( 2 * CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS ):
limit_last = number_of_all_public_baskets - CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS + 1
page_last = """<a href="%s/yourbaskets/list_public_baskets?limit=%i&amp;sort=%s&amp;asc=%i&amp;ln=%s"><img src="%s" /></a>""" % \
(CFG_SITE_URL, limit_last, sort, asc, ln, '/img/se.gif')
footer += page_last
if nb_views_show_p:
nb_views_label_td = """<td>%(nb_views_label)s&nbsp;%(nb_views_sort)s</td>""" % \
{'nb_views_label': nb_views_label,
'nb_views_sort': nb_views_sort}
out = """
<table class="bsk_list_public_baskets" cellpadding="5px">
<thead class="bsk_list_public_baskets_header">
<tr>
<td>%(basket_name_label)s&nbsp;%(basket_name_sort)s</td>
<td>%(owner_label)s&nbsp;%(owner_sort)s</td>
<td>%(date_modification_label)s&nbsp;%(date_modification_sort)s</td>
<td>%(nb_items_label)s&nbsp;%(nb_items_sort)s</td>
%(nb_views_label_td)s
</tr>
</thead>
<tfoot class="bsk_list_public_baskets_footer">
<tr>
<td colspan="%(colspan)s" style="text-align:center">%(footer)s</td>
</tr>
</tfoot>
<tbody>
%(baskets)s
</tbody>
</table>""" % {'basket_name_label': basket_name_label,
'basket_name_sort': basket_name_sort,
'owner_label': owner_label,
'owner_sort': owner_sort,
'date_modification_label': date_modification_label,
'date_modification_sort': date_modification_sort,
'nb_items_label': nb_items_label,
'nb_items_sort': nb_items_sort,
'nb_views_label_td': nb_views_show_p and nb_views_label_td or '',
'colspan': nb_views_show_p and 5 or 4,
'footer': footer,
'baskets': baskets_html}
return out
def tmpl_quote_comment_textual(self, title, uid, nickname, date, body, ln=CFG_SITE_LANG):
"""Return a comment in a quoted form (i.e. with '>' signs before each line)
@param title: title of comment to quote
@param uid: user id of user who posted comment to quote
@param nickname: nickname of user who posted comment to quote
@param date: date of post of comment to quote
@param body: body of comment to quote
@param ln: language"""
_ = gettext_set_language(ln)
if not(nickname):
nickname = get_user_info(uid)[2]
if title:
msg = _("%(x_title)s, by %(x_name)s on %(x_date)s:") % \
{'x_title': title, 'x_name': nickname, 'x_date': date}
else:
msg = _("%(x_name)s wrote on %(x_date)s:") % {'x_name': nickname, 'x_date': date}
msg += '\n\n'
msg += body
return email_quote_txt(msg)
def tmpl_quote_comment_html(self, title, uid, nickname, date, body, ln=CFG_SITE_LANG):
"""Return a comment in a quoted form (i.e. indented using HTML
table) for HTML output (i.e. in FCKeditor).
@param title: title of comment to quote
@param uid: user id of user who posted comment to quote
@param nickname: nickname of user who posted comment to quote
@param date: date of post of comment to quote
@param body: body of comment to quote
@param ln: language"""
_ = gettext_set_language(ln)
if not(nickname):
nickname = get_user_info(uid)[2]
if title:
msg = _("%(x_title)s, by %(x_name)s on %(x_date)s:") % \
{'x_title': title, 'x_name': nickname, 'x_date': date}
else:
msg = _("%(x_name)s wrote on %(x_date)s:") % {'x_name': nickname, 'x_date': date}
msg += '<br/><br/>'
msg += body
msg = email_quote_txt(text=msg)
msg = email_quoted_txt2html(text=msg)
return '<br/>' + msg + '<br/>'
def __tmpl_basket_box(self, img='', title='&nbsp;', subtitle='&nbsp;', body=''):
""" private function, display a basket/topic selection box """
out = """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>%(label)s</b><br />
%(count)s
</td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2">
<table>%(basket_list)s
</table>
</td>
</tr>
</tbody>
</table>"""
out %= {'logo': img,
'label': title, 'count': subtitle,
'basket_list': body}
return out
def tmpl_create_box(self, new_basket_name='', new_topic_name='',
topics=[], selected_topic="",
ln=CFG_SITE_LANG):
"""Display a HTML box for creation of a new basket
@param new_basket_name: prefilled value (string)
@param new_topic_name: prefilled value (string)
@param topics: list of topics (list of strings)
@param selected_topic: preselected value for topic selection
@param ln: language"""
_ = gettext_set_language(ln)
topics_html = ''
#if selected_topic:
# try:
# selected_topic = topics.index(selected_topic)
# except:
# selected_topic = None
if topics:
topics = zip(topics, topics)
topics.insert(0, (-1, _("Select topic")))
topics_html = self.__create_select_menu('create_in_topic', topics, selected_topic)
create_html = """
<tr>
<td style="padding: 10 5 0 5;">%s</td>
<td style="padding: 10 5 0 0;">%s</td>
</tr>
<tr>
<td style="padding: 10 5 0 5;">%s</td>
<td style="padding: 10 5 0 0;"><input type="text" name="new_topic_name" value="%s"/></td>
</tr>
<tr>
<td style="padding: 10 5 0 5;">%s</td>
<td style="padding: 10 5 0 0;">
<input type="text" name="new_basket_name" value="%s"/>
</td>
</tr>""" % (topics_html != '' and _("Choose topic") or '', topics_html,
topics_html != '' and _("or create a new one") or _("Create new topic"), new_topic_name,
_("Basket name"), new_basket_name,)
return self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_create.png',
title=_("Create a new basket"),
body=create_html)
def tmpl_create_basket(self, new_basket_name='',
new_topic_name='', create_in_topic=None, topics=[],
ln=CFG_SITE_LANG):
"""Template for basket creation
@param new_basket_name: prefilled value (string)
@param new_topic_name: prefilled value (string)
@param topics: list of topics (list of strings)
@param create_in_topic: preselected value for topic selection
@param ln: language"""
_ = gettext_set_language(ln)
out = """
<form name="create_basket" action="%(action)s" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<div style="padding:10px;">
%(create_box)s
<input type="submit" value="%(label)s" class="formbutton"/>
</div>
</form>""" % {'action': CFG_SITE_URL + '/yourbaskets/create_basket',
'ln': ln,
'create_box': self.tmpl_create_box(new_basket_name=new_basket_name,
new_topic_name=new_topic_name,
topics=topics,
selected_topic=create_in_topic,
ln=ln),
'label': _("Create new basket")}
return out
############################ external sources ###########################
def tmpl_external_source_add_box(self,
title="",
desc="",
url="",
ln=CFG_SITE_LANG):
"""Template for adding external items."""
_ = gettext_set_language(ln)
# Instead of the rich editor we choose to use everytime a simple textarea
# because a rich text editor may already be used in the add to baskets
# page to anotate.
#desc_editor = get_html_text_editor(name="es_desc",
# content=desc,
# textual_content=desc,
# width="640px",
# height="100px",
# enabled=CFG_WEBBASKET_USE_RICH_TEXT_EDITOR,
# toolbar_set="WebComment")
desc_editor = """<textarea name="es_desc" style="width: 640px; height: 100px;">%(value)s</textarea>""" % \
{'value': desc}
out = """
<table class="bskbasket" width="100%%">
<thead>
<tr>
<td class="bskbasketheader">
<table>
<tr>
<td class="bskbasketheadertitle">
<strong>
%(header_label)s
</strong>
</td>
</table>
</td>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 10px;">
%(instructions_label)s:
</td>
</tr>
<tr>
<td style="padding: 10px;">
<p align="left">
<small>%(title_label)s:</small>
<br />
<input type="text" name="es_title" size="65" value="%(es_title)s" />
</p>
<p align="left">
<small>%(desc_label)s:</small>
<br />
%(desc_editor)s
</p>
<p align="left">
<small>%(url_label)s:</small>
<br />
<input type="text" name="es_url" size="65" value="%(es_url)s" />
<input type="hidden" name="colid" value="-1" />
</p>
</td>
</tr>
</tbody>
</table>""" % {'header_label': _('External item'),
'instructions_label': _('Provide a url for the external item you wish to add and fill in a title and description'),
'title_label': _('Title'),
'es_title': title,
'desc_label': _('Description'),
'desc_editor': desc_editor,
'url_label': _('URL'),
'es_url': url}
return out
########################## functions on baskets #########################
def tmpl_add(self,
recids=[],
category="",
bskid=0,
colid=0,
es_title="",
es_desc="",
es_url="",
note_body="",
personal_basket_list=(),
group_basket_list=(),
successful_add=False,
copy=False,
referer='',
ln=CFG_SITE_LANG):
"""Template for addding items to baskets."""
_ = gettext_set_language(ln)
if successful_add:
out = """
%(success_label)s.
<br /><br />
%(proceed_label)s""" % {'success_label': _('%i items have been successfully added to your basket') % (colid == -1 and 1 or len(recids)),
'proceed_label': _('Proceed to the %(x_url_open)sbasket%(x_url_close)s') % \
{'x_url_open': '<a href="%s/yourbaskets/display?category=%s&amp;bskid=%i&amp;ln=%s">' % (CFG_SITE_URL, category, bskid, ln),
'x_url_close': "</a>"}}
if referer:
if copy:
out += _(' or return to your %(x_url_open)sprevious basket%(x_url_close)s') % \
{'x_url_open': '<a href="%s">' % referer,
'x_url_close': '</a>'}
else:
out += _(' or return to your %(x_url_open)ssearch%(x_url_close)s') % \
{'x_url_open': '<a href="%s">' % referer,
'x_url_close': '</a>'}
else:
out += "."
return out
#If no recids were specified the page is asking which external item to add, so we remind to the user to use the search engine for internal items
out=""
if len(recids) == 0:
out += """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>Adding items to your basket</b><br />
</td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2">
<table>
To add internal items to your basket please select them through the <a href="%(search_link)s">search page</a>
and use the "Add to basket" functionality. For any external resource please use the
"External item" form below.
</table>
</td>
</tr>
</tbody>
</table>"""
out %= {'logo': "%s/img/tick.gif"% (CFG_SITE_URL,),
'label':"tick",
'search_link':"%s"%(CFG_SITE_URL,) }
note_editor = get_html_text_editor(name="note_body",
content=note_body,
textual_content=note_body,
width="600px",
height="110px",
enabled=CFG_WEBBASKET_USE_RICH_TEXT_EDITOR,
toolbar_set="WebComment")
select_options = create_add_box_select_options(category,
bskid,
personal_basket_list,
group_basket_list,
ln)
hidden_recids = ""
for recid in recids:
hidden_recids += """
<input type="hidden" name="recid" value="%s" />""" % (recid,)
action = "%s/yourbaskets/add" % (CFG_SITE_URL,)
out += """
<form name="add_to_basket" action="%(action)s" method="post">""" % {'action': action}
if colid == -1:
out += self.tmpl_external_source_add_box(es_title, es_desc, es_url, ln)
out += """
<table class="bskbasket" width="100%%">
<thead>
<tr>
<td class="bskbasketheader">
<table>
<tr>
<td class="bskbasketheadertitle">
<strong>
%(header_label)s
</strong>
</td>
</table>
</td>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 10px;">
%(create_new_basket)s
<br />
</td>
</tr>
<tr>
<td style="padding: 10px;">
<p align="left">
<small>%(note_label)s:</small>
<br />
%(note_editor)s
</p>
</td>
</tr>
<tr>
<td style="padding: 10px;">
%(hidden_recids)s
<input type="hidden" name="colid" value="%(colid)s" />
<input type="hidden" name="copy" value="%(copy)i" />
<input type="hidden" name="referer" value="%(referer)s" />
<input type="submit" class="formbutton" value="%(add_label)s" />
<input type="button" class="nonsubmitbutton" value="%(cancel_label)s" onClick="window.location='/'" />
</td>
</tr>
</tbody>
</table>""" % {'header_label': _("Adding %i items to your baskets") % (colid == -1 and 1 or len(recids)),
'create_new_basket': _("Please choose a basket: %(x_basket_selection_box)s %(x_fmt_open)s(or %(x_url_open)screate a new one%(x_url_close)s first)%(x_fmt_close)s") % \
{'x_basket_selection_box': '&nbsp;<select name="b">%s</select>' % select_options,
'x_url_open': '<a href="%s/yourbaskets/create_basket">' % CFG_SITE_URL,
'x_url_close': '</a>',
'x_fmt_open': '<br /><small>',
'x_fmt_close': '</small>'},
'note_label': len(recids) > 1 and _('Optionally, add a note to each one of these items') \
or _('Optionally, add a note to this item'),
'note_editor': note_editor,
'hidden_recids': hidden_recids,
'colid': colid,
'copy': copy and 1 or 0,
'referer': referer,
'add_label': _('Add items'),
'cancel_label': _('Cancel')}
out += """
</form>"""
return out
def tmpl_confirm_delete(self, bskid,
(nb_users, nb_groups, nb_alerts),
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic="", selected_group_id=0,
ln=CFG_SITE_LANG):
"""
display a confirm message
@param bskid: basket id
@param nb*: nb of users/groups/alerts linked to this basket
@param category: private, group or external baskets are selected
@param selected_topic: if private baskets, topic nb
@param selected_group_id: if group: group to display baskets of
@param ln: language
@return: html output
"""
_ = gettext_set_language(ln)
message = _("Are you sure you want to delete this basket?")
if nb_users:
message += '<p>' + _("%i users are subscribed to this basket.")% nb_users + '</p>'
if nb_groups:
message += '<p>' + _("%i user groups are subscribed to this basket.")% nb_groups + '</p>'
if nb_alerts:
message += '<p>' + _("You have set %i alerts on this basket.")% nb_alerts + '</p>'
out = """
<table class="confirmoperation">
<tr>
<td colspan="2" class="confirmmessage">
%(message)s
</td>
</tr>
<tr>
<td>
<form name="validate" action="%(url_ok)s" method="post">
<input type="hidden" name="confirmed" value="1" />
<input type="hidden" name="category" value="%(category)s" />
<input type="hidden" name="group" value="%(group)i" />
<input type="hidden" name="topic" value="%(topic)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="bskid" value="%(bskid)i" />
<input type="submit" value="%(yes_label)s" class="formbutton" />
</form>
</td>
<td>
<form name="cancel" action="%(url_cancel)s" method="get">
<input type="hidden" name="category" value="%(category)s" />
<input type="hidden" name="group" value="%(group)i" />
<input type="hidden" name="topic" value="%(topic)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="submit" value="%(no_label)s" class="formbutton" />
</form>
</td>
</tr>
</table>"""% {'message': message,
'bskid': bskid,
'url_ok': 'delete',
'url_cancel': 'display',
'category': category,
'topic': selected_topic,
'group': selected_group_id,
'ln':ln,
'yes_label': _("Yes"),
'no_label': _("Cancel")}
return out
def tmpl_edit(self, bskid, bsk_name, topic, topics, groups_rights, external_rights,
display_general=0, display_sharing=0, display_delete=0, ln=CFG_SITE_LANG):
"""Display interface for rights management over the given basket
@param group_rights: list of (group id, name, rights) tuples
@param external_rights: rights as defined in CFG_WEBBASKET_SHARE_LEVELS for public access.
@param display_general: display fields name and topic, used with personal baskets
@param display_sharing: display sharing possibilities
@param display_delete: display delete basket button
"""
_ = gettext_set_language(ln)
general_body = ''
if display_general:
general_body = """
<tr>
<td class="bskcontentcol">%s</td>
<td class="bskcontentcol"><input type="text" name="new_name" value="%s"/></td>
</tr>""" % (_("Basket name"), cgi.escape(bsk_name, 1))
#topics_selection = zip(range(len(topics)), topics)
topics_selection = zip(topics, topics)
topics_selection.insert(0, (-1, _("Choose topic")))
topics_body = """
<tr>
<td style="padding: 10 5 0 5;">%s</td>
<td style="padding: 10 5 0 0;">%s</td>
</tr>
<tr>
<td style="padding: 0 5 10 5;">%s</td>
<td style="padding: 0 5 10 0;"><input type="text" name="new_topic_name" />
</tr>""" % (_("Choose topic"),
self.__create_select_menu('new_topic', topics_selection, topic),
_("or create a new one"))
general_body += topics_body
general_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_user.png',
title=_("General settings"),
body = general_body)
groups_body = ''
if display_sharing:
for (group_id, name, rights) in groups_rights:
groups_body += """
<tr>
<td>%s</td>
<td>%s</td>
</tr>""" % (name, self.__create_group_rights_selection_menu(group_id, rights, ln))
groups_body += """
<tr>
<td colspan="2">
<input type="submit" name="add_group" class="nonsubmitbutton" value="%s"/>
</td>
</tr>""" % _("Add group")
else:
groups_body = '<tr><td colspan="2">%s</td></tr>'
groups_body %= self.tmpl_create_guest_forbidden_box(ln)
groups_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_usergroup.png',
title=_("Manage group rights"),
body=groups_body)
if display_sharing:
external_body = """
<tr>
<td>%s</td>
</tr>""" % self.__create_rights_selection_menu('external', external_rights, ln)
else:
external_body = '<tr><td colspan="2">%s</td></tr>'
external_body %= self.tmpl_create_guest_forbidden_box(ln)
external_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_world.png',
title=_("Manage global sharing rights"),
body=external_body)
delete_button = ''
if display_delete:
delete_button = '<input type="submit" class="nonsubmitbutton" name="delete" value="%s" />'
delete_button %= _("Delete basket")
out = """
<form name="edit" action="%(action)s" method="post">
<p>%(label)s</p>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="bskid" value="%(bskid)i" />
<input type="hidden" name="topic" value ="%(topic)s" />
<table>
<tr>
<td colspan="3">%(general)s</td>
</tr>
<tr>
<td colspan="3">%(groups)s</td>
</tr>
<tr>
<td colspan="3">%(external)s</td>
</tr>
<tr>
<td><input type="submit" class="formbutton" name="submit" value="%(submit_label)s" /></td>
<td><input type="submit" class="nonsubmitbutton" name="cancel" value="%(cancel_label)s" /></td>
<td>%(delete_button)s</td>
</tr>
</table>
</form>""" % {'label': _('Editing basket %(x_basket_name)s') % \
{'x_basket_name': cgi.escape(bsk_name)},
'action': CFG_SITE_URL + '/yourbaskets/edit',
'ln': ln,
'topic': topic,
'bskid': bskid,
'general': general_box,
'groups': groups_box,
'external': external_box,
'submit_label': _("Save changes"),
'cancel_label': _("Cancel"),
'delete_button': delete_button}
return out
def tmpl_edit_topic(self, topic, display_general=0, display_delete=0, ln=CFG_SITE_LANG):
"""Display interface for topic editing.
@param display_general: display topic name
@param display_delete: display delete topic button
"""
_ = gettext_set_language(ln)
general_body = ''
if not topic:
general_body = """<div class="important" style="padding: 10px;">%s</div>"""
general_body %= ("You must provide a valid topic name.",)
display_general = False
if display_general:
general_body = """
<tr>
<td>%s</td>
<td><input type="text" name="new_name" value="%s"/></td>
</tr>""" % (_("Topic name"), cgi.escape(topic, True))
#<td class="bskcontentcol">%s</td>
#<td class="bskcontentcol"><input type="text" name="new_name" value="%s"/></td>
general_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_user.png',
title=_("General settings"),
body = general_body)
delete_button = ''
display_delete = False
if display_delete:
delete_button = '<input type="submit" class="nonsubmitbutton" name="delete" value="%s" />'
delete_button %= _("Delete basket")
out = """
<form name="edit" action="%(action)s" method="post">
<p>%(label)s</p>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="topic" value ="%(topic)s" />
<table>
<tr>
<td colspan="3">%(general)s</td>
</tr>
<tr>
<td><input type="submit" class="formbutton" name="submit" value="%(submit_label)s" /></td>
<td><input type="submit" class="nonsubmitbutton" name="cancel" value="%(cancel_label)s" /></td>
<td>%(delete_button)s</td>
</tr>
</table>
</form>""" % {'label': _('Editing topic: %(x_topic_name)s') % {'x_topic_name': cgi.escape(topic, True)},
'action': CFG_SITE_URL + '/yourbaskets/edit_topic',
'ln': ln,
'topic': cgi.escape(topic, True),
'general': general_box,
'submit_label': _("Save changes"),
'cancel_label': _("Cancel"),
'delete_button': delete_button}
return out
def __create_rights_selection_menu(self, name, current_rights, ln=CFG_SITE_LANG):
"""Private function. create a drop down menu for selection of rights
@param name: name of menu (for HTML name attribute)
@param current_rights: rights as defined in CFG_WEBBASKET_SHARE_LEVELS
@param ln: language
"""
_ = gettext_set_language(ln)
elements = [('NO', _("No rights")),
(CFG_WEBBASKET_SHARE_LEVELS['READITM'],
_("View records")),
(CFG_WEBBASKET_SHARE_LEVELS['READCMT'],
'... ' + _("and") + ' ' + _("view comments")),
(CFG_WEBBASKET_SHARE_LEVELS['ADDCMT'],
'... ' + _("and") + ' ' + _("add comments"))]
return self.__create_select_menu(name, elements, current_rights)
def __create_group_rights_selection_menu(self, group_id, current_rights, ln=CFG_SITE_LANG):
"""Private function. create a drop down menu for selection of rights
@param current_rights: rights as defined in CFG_WEBBASKET_SHARE_LEVELS
@param ln: language
"""
_ = gettext_set_language(ln)
elements = [(str(group_id) + '_' + 'NO', _("No rights")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['READITM'],
_("View records")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['READCMT'],
'... ' + _("and") + ' ' + _("view notes")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['ADDCMT'],
'... ' + _("and") + ' ' + _("add notes")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['ADDITM'],
'... ' + _("and") + ' ' + _("add records")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['DELCMT'],
'... ' + _("and") + ' ' + _("delete notes")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['DELITM'],
'... ' + _("and") + ' ' + _("remove records")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['MANAGE'],
'... ' + _("and") + ' ' + _("manage sharing rights"))
]
return self.__create_select_menu('groups', elements, str(group_id) + '_' + current_rights)
def tmpl_add_group(self, bskid, selected_topic, groups=[], ln=CFG_SITE_LANG):
"""
return form for selection of groups.
@param bskid: basket id (int)
@param selected_topic: topic currently displayed (int)
@param groups: list of tuples (group id, group name)
@param ln: language
"""
_ = gettext_set_language(ln)
if len(groups):
groups_body = """
<tr>
<td>%s</td>
</tr>""" % self.__create_select_menu('new_group', groups, selected_key=None)
else:
groups_body = """
<tr>
<td>%s</td>
</tr>""" % _("You are not a member of a group.")
groups_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_usergroup.png',
title=_("Add group"),
body=groups_body)
out = """
<form name="add_group" action="%(action)s" method="post">
<p>%(label)s</p>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="bskid" value="%(bskid)i" />
<input type="hidden" name="topic" value ="%(topic)s" />
<table style="width:100%%;">
<tr>
<td style="width:50%%;vertical-align:top;">%(groups)s</td>
<td style="width:50%%;vertical-align:top;"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" class="formbutton" name="group_cancel" value="%(cancel_label)s" />
<input type="submit" class="formbutton" name="add_group" value="%(submit_label)s" />
</td>
</tr>
</table>
</form>""" % {'label': _('Sharing basket to a new group'),
'action': CFG_SITE_URL + '/yourbaskets/edit',
'ln': ln,
'topic': selected_topic,
'bskid': bskid,
'groups': groups_box,
'cancel_label': _("Cancel"),
'submit_label': _("Add group")}
return out
def tmpl_personal_baskets_selection_box(self,
baskets=[],
select_box_name='baskets',
selected_bskid=None,
ln=CFG_SITE_LANG):
"""return an HTML popupmenu
@param baskets: list of (bskid, bsk_name, bsk_topic) tuples
@param select_box_name: name that will be used for the control
@param selected_bskid: id of the selcte basket, use None for no selection
@param ln: language"""
_ = gettext_set_language(ln)
elements = [(0, '- ' + _("no basket") + ' -')]
for (bskid, bsk_name, bsk_topic) in baskets:
elements.append((bskid, bsk_topic + ' > ' + bsk_name))
return self.__create_select_menu(select_box_name, elements, selected_bskid)
def tmpl_create_guest_warning_box(self, ln=CFG_SITE_LANG):
"""return html warning box for non registered users"""
_ = gettext_set_language(ln)
message = _("You are logged in as a guest user, so your baskets will disappear at the end of the current session.") + ' '
message += _("If you wish you can %(x_url_open)slogin or register here%(x_url_close)s.") %\
{'x_url_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/login?ln=' + ln + '">',
'x_url_close': '</a>'}
out = """
<table class="errorbox">
<tr>
<th class="errorboxheader">%s</th>
</tr>
</table>"""
return out % message
def tmpl_create_guest_forbidden_box(self, ln=CFG_SITE_LANG):
"""return html warning box for non registered users"""
_ = gettext_set_language(ln)
message = _("This functionality is forbidden to guest users.") + ' '
message += _("If you wish you can %(x_url_open)slogin or register here%(x_url_close)s.") %\
{'x_url_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/login?ln=' + ln + '">',
'x_url_close': '</a>'}
out = """
<table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">%s</th>
</tr>
</thead>
</table>"""
return out % message
############################ Utilities ###################################
def __create_select_menu(self, name, elements, selected_key=None):
""" private function, returns a popup menu
@param name: name of HTML control
@param elements: list of (key, value)
@param selected_key: item that should be selected (key of elements tuple)
"""
out = '<select name="%s">' % name
for (key, label) in elements:
selected = ''
if key == selected_key:
selected = ' selected="selected"'
out += '<option value="%s"%s>%s</option>'% (key, selected, cgi.escape(label))
out += '</select>'
return out
def tmpl_warnings(self, warnings=[], ln=CFG_SITE_LANG):
""" returns HTML for warnings """
from invenio.errorlib import get_msgs_for_code_list
out = ""
if type(warnings) is not list:
warnings = [warnings]
if warnings:
warnings_parsed = get_msgs_for_code_list(warnings, 'warning', ln)
for (dummy, warning_text) in warnings_parsed:
out += """
<p class="important">%s</p>
""" % warning_text
return out
def tmpl_back_link(self, link, ln=CFG_SITE_LANG):
""" returns HTML for a link whose label should be
'Back to search results'
"""
_ = gettext_set_language(ln)
label = _("Back to search results")
out = '<a href="%s">%s</a>' % (link, label)
return out
def __create_webmessage_link(self, to, display_name, ln=CFG_SITE_LANG):
"""prints a link to the messaging system"""
link = "%s/yourmessages/write?msg_to=%s&amp;ln=%s" % (CFG_SITE_URL, to, ln)
if to:
return '<a href="%s" class="maillink">%s</a>' % (link, display_name)
else:
return display_name
def tmpl_xml_basket(self, items=[]):
"""Template for XML output of basket
@param items: XML version of each item (list)"""
items_xml = ''
for item in items:
items_xml += ' ' + item + '\n'
return """<?xml version="1.0" encoding="UTF-8"?>
<collection>
%s
</collection>
""" % items_xml
############################ Baskets ###################################
##################################
########### BASKET VIEW ##########
##################################
def tmpl_basket(self,
bskid,
name,
date_modification,
nb_items,
nb_subscribers,
(user_can_view_content,
user_can_edit_basket,
user_can_view_notes,
user_can_add_notes,
user_can_add_item,
user_can_delete_item),
nb_comments,
share_level,
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic="",
selected_group=0,
items=[],
of='hb',
ln=CFG_SITE_LANG):
"""Template for basket display."""
if not of.startswith('x'):
out = """
<table class="bskbasket" width="100%">"""
else:
out = ""
if not of.startswith('x'):
out += self.tmpl_basket_header(bskid,
name,
nb_items,
nb_subscribers,
date_modification,
(user_can_view_content,
user_can_edit_basket,
user_can_view_notes),
selected_category,
nb_comments,
selected_topic,
share_level,
ln)
if not of.startswith('x'):
out += self.tmpl_basket_footer(bskid,
nb_items,
(user_can_view_content,
user_can_edit_basket),
selected_category,
selected_topic,
share_level,
ln)
out += self.tmpl_basket_content(bskid,
(user_can_view_content,
user_can_view_notes,
user_can_add_notes,
user_can_add_item,
user_can_delete_item),
selected_category,
selected_topic,
selected_group,
items,
of,
ln)
if not of.startswith('x'):
out += """
</table>"""
if not of.startswith('x'):
out += self.tmpl_create_export_as_list(selected_category,
selected_topic,
selected_group,
bskid,
None,
False)
return out
def tmpl_basket_header(self,
bskid,
name,
nb_items,
nb_subscribers,
date_modification,
(user_can_view_content,
user_can_edit_basket,
user_can_view_notes),
selected_category,
nb_comments,
selected_topic,
share_level,
ln=CFG_SITE_LANG):
"""Template for basket header display."""
_ = gettext_set_language(ln)
optional_colspan = nb_items and user_can_view_content and ' colspan="3"' or ''
records_field = '<br />' + _('%i items') % nb_items
comments_field = user_can_view_notes and (nb_comments and (', ' + _('%i notes') % nb_comments) or ', ' + _('no notes yet')) or ''
subscribers_field = selected_category == CFG_WEBBASKET_CATEGORIES['PRIVATE'] and \
share_level == 0 and \
', ' + (_('%i subscribers') % nb_subscribers) or \
''
last_update_field = '<br />' + _('last update') + ': ' + date_modification
if user_can_edit_basket:
add_ext_resource_url = """%s/yourbaskets/add?category=%s&bskid=%i""" % (CFG_SITE_URL,selected_category,bskid,)
add_ext_resource_logo = """<img src="%s/img/wb-create-basket.png" />""" % (CFG_SITE_URL,)
add_ext_resource = """<a href="%s">%s%s</a>""" % (add_ext_resource_url, add_ext_resource_logo, _("Add item"))
edit_basket_url = """%s/yourbaskets/edit?bskid=%i&amp;topic=%s&amp;ln=%s""" % (CFG_SITE_URL, bskid, cgi.escape(selected_topic, True), ln)
edit_basket_logo = """<img src="%s/img/wb-edit-basket.png" />""" % (CFG_SITE_URL,)
edit_basket = """<a href="%s">%s%s</a>""" % (edit_basket_url, edit_basket_logo, _("Edit basket"))
delete_basket_url = """%s/yourbaskets/edit?bskid=%i&amp;topic=%s&amp;delete=1&amp;ln=%s""" % (CFG_SITE_URL, bskid, cgi.escape(selected_topic, True), ln)
delete_basket_logo = """<img src="%s/img/wb-delete-basket.png" />""" % (CFG_SITE_URL,)
delete_basket = """<a href="%s">%s%s</a>""" % (delete_basket_url, delete_basket_logo, _("Delete basket"))
else:
#edit_basket = """<small>%s</small>""" % (_("You cannot edit this basket"),)
#delete_basket = """<small>%s</small>""" % (_("You cannot delete this basket"),)
edit_basket = ""
delete_basket = ""
add_ext_resource = ""
if selected_category==CFG_WEBBASKET_CATEGORIES['EXTERNAL']:
unsubscribe_url = """%s/yourbaskets/unsubscribe?bskid=%i&amp;ln=%s""" % (CFG_SITE_URL, bskid, ln)
unsubscribe_logo = """<img src="%s/img/wb-unsubscribe.png" />""" % (CFG_SITE_URL,)
unsubscribe = """&nbsp;&nbsp;\n<a href="%s">%s%s</a>""" % (unsubscribe_url, unsubscribe_logo, _("Unsubscribe from basket"))
else:
unsubscribe = ""
out = """
<thead>
<tr>
<td class="bskbasketheader"%(optional_colspan)s>
<table>
<tr>
<td class="bskbasketheadertitle">
<strong>
%(name)s
</strong>
<small>
%(records_field)s%(comments_field)s%(subscribers_field)s
%(last_update_field)s
</small>
</td>
<td class="bskbasketheaderoptions">
%(add_ext_resource)s
&nbsp;&nbsp;
%(edit_basket)s
&nbsp;&nbsp;
%(delete_basket)s
%(unsubscribe)s
</td>
</table>
</td>
</tr>
</thead>"""
out %= {'optional_colspan': optional_colspan,
'name': cgi.escape(name, True),
'nb_items': nb_items,
'records_field': records_field,
'comments_field': comments_field,
'subscribers_field': subscribers_field,
'last_update_field': last_update_field,
'add_ext_resource': add_ext_resource,
'edit_basket': edit_basket,
'delete_basket': delete_basket,
'unsubscribe': unsubscribe,
}
return out
def tmpl_basket_footer(self,
bskid,
nb_items,
(user_can_view_content,
user_can_edit_basket),
selected_category,
selected_topic,
share_level=None,
ln=CFG_SITE_LANG):
"""Template for basket footer display."""
_ = gettext_set_language(ln)
optional_colspan = nb_items and user_can_view_content and ' colspan="3"' or ''
if user_can_edit_basket:
add_ext_resource_url = """%s/yourbaskets/add?category=%s&bskid=%i""" % (CFG_SITE_URL,selected_category,bskid,)
add_ext_resource_logo = """<img src="%s/img/wb-create-basket.png" />""" % (CFG_SITE_URL,)
add_ext_resource = """<a href="%s">%s%s</a>""" % (add_ext_resource_url, add_ext_resource_logo, _("Add item"))
edit_basket_url = """%s/yourbaskets/edit?bskid=%i&amp;topic=%s&amp;ln=%s""" % (CFG_SITE_URL, bskid, selected_topic, ln)
edit_basket_logo = """<img src="%s/img/wb-edit-basket.png" />""" % (CFG_SITE_URL,)
edit_basket = """<a href="%s">%s%s</a>""" % (edit_basket_url, edit_basket_logo, _("Edit basket"))
delete_basket_url = """%s/yourbaskets/edit?bskid=%i&amp;topic=%s&amp;delete=1&amp;ln=%s""" % (CFG_SITE_URL, bskid, selected_topic, ln)
delete_basket_logo = """<img src="%s/img/wb-delete-basket.png" />""" % (CFG_SITE_URL,)
delete_basket = """<a href="%s">%s%s</a>""" % (delete_basket_url, delete_basket_logo, _("Delete basket"))
else:
edit_basket = ""
delete_basket = ""
add_ext_resource = ""
if selected_category==CFG_WEBBASKET_CATEGORIES['EXTERNAL']:
unsubscribe_url = """%s/yourbaskets/unsubscribe?bskid=%i&amp;ln=%s""" % (CFG_SITE_URL, bskid, ln)
unsubscribe_logo = """<img src="%s/img/wb-unsubscribe.png" />""" % (CFG_SITE_URL,)
unsubscribe = """&nbsp;&nbsp;\n<a href="%s">%s%s</a>""" % (unsubscribe_url, unsubscribe_logo, _("Unsubscribe from basket"))
else:
unsubscribe = ""
if share_level == 0:
display_public_url = """%s/yourbaskets/display_public?bskid=%i""" % (CFG_SITE_URL, bskid)
display_public_text = _("This basket is publicly accessible at the following address:")
display_public = """%s<br /><a href="%s">%s</a>""" % (display_public_text, display_public_url, display_public_url)
else:
display_public = ""
out = """
<tfoot>
<tr>
<td class="bskbasketfooter"%(optional_colspan)s>
<table>
<tr>
<td class="bskbasketfootertitle">
<small>
%(display_public)s
</small>
</td>
<td class="bskbasketfooteroptions">
%(add_ext_resource)s
&nbsp;&nbsp;
%(edit_basket)s
&nbsp;&nbsp;
%(delete_basket)s
%(unsubscribe)s
</td>
</tr>
</table>
</td>
</tr>
</tfoot>"""
out %= {'optional_colspan': optional_colspan,
'display_public': display_public,
'add_ext_resource': add_ext_resource,
'edit_basket': edit_basket,
'delete_basket': delete_basket,
'unsubscribe': unsubscribe}
return out
def tmpl_basket_content(self,
bskid,
(user_can_view_content,
user_can_view_notes,
user_can_add_notes,
user_can_add_item,
user_can_delete_item),
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic="",
selected_group=0,
items=[],
of='hb',
ln=CFG_SITE_LANG):
"""Template for basket content display."""
if not of.startswith('x'):
_ = gettext_set_language(ln)
items_html = """
<tbody>"""
if user_can_view_content:
if not(items):
items_html += """
<tr>
<td style="text-align:center; height:100px">
%s
</td>
</tr>""" % _("Basket is empty")
else:
count = 0
for item in items:
count += 1
copy = 1
go_up = go_down = delete = 0
if user_can_add_item:
go_up = go_down = 1
if item == items[0]:
go_up = 0
if item == items[-1]:
go_down = 0
if user_can_delete_item:
delete = 1
items_html += self.__tmpl_basket_item(count=count,
bskid=bskid,
item=item,
uparrow=go_up,
downarrow=go_down,
copy_item=copy,
delete_item=delete,
view_notes=user_can_view_notes,
add_notes=user_can_add_notes,
selected_category=selected_category,
selected_topic=selected_topic,
selected_group=selected_group,
ln=ln)
else:
items_html += """
<tr>
<td style="text-align:center; height:100px">
%s
</td>
</tr>""" % _("You do not have sufficient rights to view this basket's content.")
items_html += """
</tbody>"""
return items_html
else:
items_xml = ""
for item in items:
items_xml += item[4] + "\n"
return items_xml
def __tmpl_basket_item(self,
count,
bskid,
item,
uparrow=0,
downarrow=0,
copy_item=0,
delete_item=0,
view_notes=0,
add_notes=0,
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic="",
selected_group=0,
ln=CFG_SITE_LANG):
"""Template for basket item display within the basket content."""
_ = gettext_set_language(ln)
(recid, colid, nb_cmt, last_cmt, val, dummy) = item
if uparrow:
moveup_url = "%(siteurl)s/yourbaskets/modify?action=moveup&amp;bskid=%(bskid)i&amp;recid=%(recid)i"\
"&amp;category=%(category)s&amp;topic=%(topic)s&amp;group_id=%(group)i&amp;ln=%(ln)s" % \
{'siteurl': CFG_SITE_URL,
'bskid': bskid,
'recid': recid,
'category': selected_category,
'topic': selected_topic,
'group': selected_group,
'ln': ln}
moveup_img = "%s/img/wb-move-item-up.png" % (CFG_SITE_URL,)
moveup = """<a href="%s"><img src="%s" alt="%s" /></a>""" % \
(moveup_url, moveup_img, _("Move item up"))
else:
moveup_img = "%s/img/wb-move-item-up-disabled.png" % (CFG_SITE_URL,)
moveup = """<img src="%s" alt="%s" />""" % \
(moveup_img, _("You cannot move this item up"))
if downarrow:
movedown_url = "%(siteurl)s/yourbaskets/modify?action=movedown&amp;bskid=%(bskid)i&amp;recid=%(recid)i"\
"&amp;category=%(category)s&amp;topic=%(topic)s&amp;group_id=%(group)i&amp;ln=%(ln)s" % \
{'siteurl': CFG_SITE_URL,
'bskid': bskid,
'recid': recid,
'category': selected_category,
'topic': selected_topic,
'group': selected_group,
'ln': ln}
movedown_img = "%s/img/wb-move-item-down.png" % (CFG_SITE_URL,)
movedown = """<a href="%s"><img src="%s" alt="%s" /></a>""" % \
(movedown_url, movedown_img, _("Move item down"))
else:
movedown_img = "%s/img/wb-move-item-down-disabled.png" % (CFG_SITE_URL,)
movedown = """<img src="%s" alt="%s" />""" % \
(movedown_img, _("You cannot move this item down"))
if copy_item:
copy_url = "%(siteurl)s/yourbaskets/modify?action=copy&amp;bskid=%(bskid)i&amp;recid=%(recid)i"\
"&amp;category=%(category)s&amp;topic=%(topic)s&amp;group_id=%(group)i&amp;ln=%(ln)s" % \
{'siteurl': CFG_SITE_URL,
'bskid': bskid,
'recid': recid,
'category': selected_category,
'topic': selected_topic,
'group': selected_group,
'ln': ln}
copy_img = "%s/img/wb-copy-item.png" % (CFG_SITE_URL,)
copy = """<a href="%s"><img src="%s" alt="%s" />%s</a>""" % \
(copy_url, copy_img, _("Copy item"), _("Copy item"))
else:
copy = ""
if delete_item:
remove_url = "%(siteurl)s/yourbaskets/modify?action=delete&amp;bskid=%(bskid)i&amp;recid=%(recid)i"\
"&amp;category=%(category)s&amp;topic=%(topic)s&amp;group=%(group)i&amp;ln=%(ln)s" % \
{'siteurl': CFG_SITE_URL,
'bskid': bskid,
'recid': recid,
'category': selected_category,
'topic': selected_topic,
'group': selected_group,
'ln': ln}
remove_img = "%s/img/wb-delete-item.png" % (CFG_SITE_URL,)
remove = """<a href="%s"><img src="%s" alt="%s" />%s</a>""" % \
(remove_url, remove_img, _("Remove item"), _("Remove item"))
else:
remove = ""
if recid < 0:
external_item_img = '<img src="%s/img/wb-external-item.png" alt="%s" style="vertical-align: top;" />&nbsp;' % \
(CFG_SITE_URL, _("External item"))
else:
external_item_img = ''
out = """
<tr>
<td style="border-bottom: 1px solid #fc0;">
<table>
<tr>
<td class="bskcontentcount">
%(count)i.
</td>
<td class="bskcontentcol" colspan="2">
%(icon)s%(content)s
</td>
</tr>
<tr>
<td class="bskcontentoptions">
%(moveup)s%(movedown)s
</td>
<td>
<span class="moreinfo">"""
if item[0] > 0:
- detailed_record = """<a class="moreinfo" href="%(siteurl)s/record/%(recid)s">%(detailed_record_label)s</a>"""
+ detailed_record = """<a class="moreinfo" href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recid)s">%(detailed_record_label)s</a>"""
out += detailed_record + (view_notes and " - " or "")
external_url = ""
else:
## Uncomment the following lines if you want the Detailed record link to be
## displayed for external records but not for external sources (such as urls)
#external_colid_and_url = db.get_external_colid_and_url(item[0])
#if external_colid_and_url and external_colid_and_url[0][0] and external_colid_and_url[0][1]:
# detailed_record = '<a class="moreinfo" href="%(external_url)s">%(detailed_record_label)s</a>'
# out += detailed_record + (view_notes and " - " or "")
# external_url = external_colid_and_url[0][1]
#else:
# external_url = ""
## Currently no external items (records or sources) have a Detailed record link
external_url = ""
# TODO: If a user has the right to view the notes but not to add new ones,
# and there are no notes for some item an anchor to write notes will be
# created but with no text, hence invisible. Fix this so that no anchor
# is created whatsoever.
if view_notes:
notes = """\n<a class="moreinfo" href="%(siteurl)s/yourbaskets/%(add_and_view_notes_action)s?"""\
"""category=%(category)s&amp;topic=%(topic)s&amp;group=%(group)i&amp;"""\
"""bskid=%(bskid)s&amp;recid=%(recid)i&amp;ln=%(ln)s%(add_and_view_notes_inline_anchor)s">%(add_and_view_notes_label)s</a>"""
out += notes
out += """
</span>
</td>
<td class="bskbasketheaderoptions">
%(copy)s
&nbsp;
%(remove)s
</td>
</tr>
</table>
</td>
</tr>"""
out = out % {'moveup': moveup,
'movedown': movedown,
'count': count,
'icon': external_item_img,
'content': colid >= 0 and val or self.tmpl_create_pseudo_item(val),
'add_and_view_notes_action': nb_cmt and 'display' or 'write_note',
'add_and_view_notes_inline_anchor': not nb_cmt and '#note' or '',
'add_and_view_notes_label': nb_cmt and _('Notes') + ' (' + str(nb_cmt) + ')' or add_notes and _('Add a note...') or '',
'last_cmt': last_cmt,
'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'bskid': bskid,
'recid': recid,
'external_url': external_url,
'detailed_record_label': _("Detailed record"),
'category': selected_category,
'topic': selected_topic,
'group': selected_group,
'copy': copy,
'remove': remove,
'ln': ln}
return out
#############################################
########## BASKET SINGLE ITEM VIEW ##########
#############################################
def tmpl_basket_single_item(self,
bskid,
name,
nb_items,
(user_can_view_content,
user_can_view_notes,
user_can_add_notes,
user_can_delete_notes),
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic="",
selected_group=0,
item=(),
comments=(),
previous_item_recid=0,
next_item_recid=0,
item_index=0,
optional_params={},
of='hb',
ln=CFG_SITE_LANG):
"""Template for basket's single item display."""
if of != 'xm':
out = """
<table class="bskbasket" width="100%">"""
else:
out = ""
if of != 'xm':
out += self.tmpl_basket_single_item_header(bskid,
name,
nb_items,
selected_category,
selected_topic,
selected_group,
previous_item_recid,
next_item_recid,
item_index,
ln)
if of != 'xm':
out += self.tmpl_basket_single_item_footer(bskid,
selected_category,
selected_topic,
selected_group,
previous_item_recid,
next_item_recid,
ln)
out += self.tmpl_basket_single_item_content(bskid,
(user_can_view_content,
user_can_view_notes,
user_can_add_notes,
user_can_delete_notes),
selected_category,
selected_topic,
selected_group,
item,
comments,
item_index,
optional_params,
of,
ln)
if of != 'xm':
out += """
</table>"""
if of != 'xm':
out += self.tmpl_create_export_as_list(selected_category,
selected_topic,
selected_group,
bskid,
item,
False)
return out
def tmpl_basket_single_item_header(self,
bskid,
name,
nb_items,
selected_category,
selected_topic,
selected_group,
previous_item_recid,
next_item_recid,
item_index,
ln=CFG_SITE_LANG):
"""Template for basket's single item header display."""
_ = gettext_set_language(ln)
records_field = '<br />' + _('Item %(x_item_index)i of %(x_item_total)i') % \
{'x_item_index': item_index, 'x_item_total': nb_items}
if previous_item_recid:
previous_item_url = """%s/yourbaskets/display?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%s&amp;ln=%s""" % \
(CFG_SITE_URL,
selected_category,
selected_topic,
selected_group,
bskid,
previous_item_recid,
ln)
previous_item_logo = """<img src="%s/img/wb-previous-item.png" />""" % (CFG_SITE_URL,)
previous_item = """<a href="%s">%s%s</a>""" % (previous_item_url, previous_item_logo, _("Previous item"))
else:
previous_item_logo = """<img src="%s/img/wb-previous-item-disabled.png" />""" % (CFG_SITE_URL,)
previous_item = """%s%s""" % (previous_item_logo, _("Previous item"))
if next_item_recid:
next_item_url = """%s/yourbaskets/display?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%s&amp;ln=%s""" % \
(CFG_SITE_URL,
selected_category,
selected_topic,
selected_group,
bskid,
next_item_recid,
ln)
next_item_logo = """<img src="%s/img/wb-next-item.png" />""" % (CFG_SITE_URL,)
next_item = """<a href="%s">%s%s</a>""" % (next_item_url, next_item_logo, _("Next item"))
else:
next_item_logo = """<img src="%s/img/wb-next-item-disabled.png" />""" % (CFG_SITE_URL,)
next_item = """%s%s""" % (next_item_logo, _("Next item"))
go_back_url = """%s/yourbaskets/display?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;ln=%s""" % \
(CFG_SITE_URL,
selected_category,
selected_topic,
selected_group,
bskid,
ln)
go_back_logo = """<img src="%s/img/wb-go-back.png" />""" % (CFG_SITE_URL,)
go_back = """<a href="%s">%s%s</a>""" % (go_back_url, go_back_logo, _("Return to basket"))
out = """
<thead>
<tr>
<td class="bskbasketheader">
<table>
<tr>
<td class="bskbasketheadertitle">
<strong>
%(name)s
</strong>
<small>
%(records_field)s
</small>
</td>
<td class="bskbasketheaderoptions">
%(go_back)s
&nbsp;&nbsp;
%(previous_item)s
&nbsp;&nbsp;
%(next_item)s
</td>
</table>
</td>
</tr>
</thead>"""
out %= {'name': name,
'records_field': records_field,
'go_back': go_back,
'previous_item': previous_item,
'next_item': next_item,
}
return out
def tmpl_basket_single_item_footer(self,
bskid,
selected_category,
selected_topic,
selected_group,
previous_item_recid,
next_item_recid,
ln=CFG_SITE_LANG):
"""Template for basket's single item footer display."""
_ = gettext_set_language(ln)
if previous_item_recid:
previous_item_url = """%s/yourbaskets/display?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%s&amp;ln=%s""" % \
(CFG_SITE_URL,
selected_category,
selected_topic,
selected_group,
bskid,
previous_item_recid,
ln)
previous_item_logo = """<img src="%s/img/wb-previous-item.png" />""" % (CFG_SITE_URL,)
previous_item = """<a href="%s">%s%s</a>""" % (previous_item_url, previous_item_logo, _("Previous item"))
else:
previous_item_logo = """<img src="%s/img/wb-previous-item-disabled.png" />""" % (CFG_SITE_URL,)
previous_item = """%s%s""" % (previous_item_logo, _("Previous item"))
if next_item_recid:
next_item_url = """%s/yourbaskets/display?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%s&amp;ln=%s""" % \
(CFG_SITE_URL,
selected_category,
selected_topic,
selected_group,
bskid,
next_item_recid,
ln)
next_item_logo = """<img src="%s/img/wb-next-item.png" />""" % (CFG_SITE_URL,)
next_item = """<a href="%s">%s%s</a>""" % (next_item_url, next_item_logo, _("Next item"))
else:
next_item_logo = """<img src="%s/img/wb-next-item-disabled.png" />""" % (CFG_SITE_URL,)
next_item = """%s%s""" % (next_item_logo, _("Next item"))
go_back_url = """%s/yourbaskets/display?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;ln=%s""" % \
(CFG_SITE_URL,
selected_category,
selected_topic,
selected_group,
bskid,
ln)
go_back_logo = """<img src="%s/img/wb-go-back.png" />""" % (CFG_SITE_URL,)
go_back = """<a href="%s">%s%s</a>""" % (go_back_url, go_back_logo, _("Return to basket"))
out = """
<tfoot>
<tr>
<td class="bskbasketfooter">
<table>
<tr>
<td class="bskbasketfootertitle">
&nbsp;
</td>
<td class="bskbasketfooteroptions">
%(go_back)s
&nbsp;&nbsp;
%(previous_item)s
&nbsp;&nbsp;
%(next_item)s
</td>
</table>
</td>
</tr>
</tfoot>"""
out %= {'go_back': go_back,
'previous_item': previous_item,
'next_item': next_item,
}
return out
def tmpl_basket_single_item_content(self,
bskid,
(user_can_view_content,
user_can_view_notes,
user_can_add_notes,
user_can_delete_notes),
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic="",
selected_group=0,
item=(),
notes=(),
index_item=0,
optional_params={},
of='hb',
ln=CFG_SITE_LANG):
"""Template for basket's single item content display."""
if of != 'xm':
_ = gettext_set_language(ln)
item_html = """
<tbody>"""
if user_can_view_content:
if not item:
item_html += """
<tr>
<td style="text-align: center; height: 100px">
%s
</td>
</tr>""" % _("The item you have selected does not exist.")
else:
(recid, colid, dummy, last_cmt, val, dummy) = item
if recid < 0:
external_item_img = '<img src="%s/img/wb-external-item.png" alt="%s" style="vertical-align: top;" />&nbsp;' % \
(CFG_SITE_URL, _("External item"))
else:
external_item_img = ''
if user_can_view_notes:
notes_html = self.__tmpl_display_notes(recid,
bskid,
(user_can_add_notes,
user_can_delete_notes),
selected_category,
selected_topic,
selected_group,
notes,
optional_params,
ln)
notes = """
<tr>
<td colspan="2" class="bskcontentnotes">%(notes_html)s
</td>
</tr>""" % {'notes_html': notes_html}
else:
notes_msg = _("You do not have sufficient rights to view this item's notes.")
notes = """
<tr>
<td colspan="2" style="text-align: center; height: 50px">
%(notes_msg)s
</td>
</tr>""" % {'notes_msg': notes_msg}
item_html += """
<tr>
<td style="border-bottom: 1px solid #fc0;">
<table>
<tr>
<td class="bskcontentcount">
%(count)i.
</td>
<td class="bskcontentcol">
%(icon)s%(content)s
</td>
</tr>%(notes)s
</table>
</td>
</tr>""" % {'count': index_item,
'icon': external_item_img,
'content': colid >=0 and val or self.tmpl_create_pseudo_item(val),
'last_cmt': last_cmt,
'siteurl': CFG_SITE_URL,
'bskid': bskid,
'recid': recid,
'category': selected_category,
'topic': selected_topic,
'group': selected_group,
'notes': notes,
'ln': ln}
else:
item_html += """
<tr>
<td style="text-align: center; height: 100px">
%s
</td>
</tr>""" % _("You do not have sufficient rights to view this item.")
item_html += """
</tbody>"""
return item_html
else:
item_xml = item[4]
return item_xml
def __tmpl_display_notes(self,
recid,
bskid,
(user_can_add_notes,
user_can_delete_notes),
selected_category,
selected_topic,
selected_group,
notes,
optional_params,
ln=CFG_SITE_LANG):
"""Template for basket's single item notes display."""
_ = gettext_set_language(ln)
warnings_html = ""
add_note_p = False
if user_can_add_notes and (optional_params.has_key("Add note") or optional_params.has_key("Incomplete note")):
add_note_p = True
if optional_params.has_key("Add note") and optional_params['Add note']:
replied_to_note = optional_params['Add note']
note_body_html = self.tmpl_quote_comment_html(replied_to_note[2],
replied_to_note[1],
replied_to_note[0],
replied_to_note[4],
replied_to_note[3],
ln)
note_body_textual = self.tmpl_quote_comment_textual(replied_to_note[2],
replied_to_note[1],
replied_to_note[0],
replied_to_note[4],
replied_to_note[3],
ln)
note_title = "Re: " + replied_to_note[2]
elif optional_params.has_key("Incomplete note") and optional_params['Incomplete note']:
incomplete_note = optional_params['Incomplete note']
note_body_html = incomplete_note[1]
# TODO: Do we need to format incomplete body correctly as textual
# and html as above?
note_body_textual = incomplete_note[1]
note_title = incomplete_note[0]
if optional_params.has_key("Warnings"):
warnings = optional_params["Warnings"]
warnings_html = self.tmpl_warnings(warnings, ln)
else:
note_body_html = ""
note_body_textual = ""
note_title = ""
if optional_params.has_key("Warnings"):
warnings = optional_params["Warnings"]
warnings_html = self.tmpl_warnings(warnings, ln)
# TODO: calculate the url
file_upload_url = ""
action = """%s/yourbaskets/save_note?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%i&amp;ln=%s%s""" % \
(CFG_SITE_URL, selected_category, selected_topic, selected_group, bskid, recid, ln, '#note')
cancel = """%s/yourbaskets/display?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%i&amp;ln=%s""" % \
(CFG_SITE_URL, selected_category, selected_topic, selected_group, bskid, recid, ln)
editor = get_html_text_editor(name="note_body",
content=note_body_html,
textual_content=note_body_textual,
width="99%",
height="160px",
enabled=CFG_WEBBASKET_USE_RICH_TEXT_EDITOR,
file_upload_url=file_upload_url,
toolbar_set="WebComment")
add_note_html = """
<table cellspacing="0" cellpadding="0" class="bsknotescontentaddnote">
<tr>
<td class="bsknotescontentaddform">
<form name="write_note" method="post" action="%(action)s">
<a name="note"></a><strong>%(add_a_note_label)s</strong>
%(warnings_html)s
<p align="left">
<small>Subject:</small>
<br />
<input type="text" name="note_title" size="65" value="%(note_title)s" />
</p>
<p align="left">
<small>Note:</small>
<br />
%(editor)s
</p>
<input type="hidden" name="reply_to" value="%(reply_to)s" />
<p align="left">
<input type="submit" class="formbutton" value="%(submit_label)s" />
<input type="button" class="nonsubmitbutton" value="%(cancel_label)s" onClick="window.location='%(cancel)s'" />
</p>
</form>
</td>
</tr>
</table>""" % {'action': action,
'warnings_html': warnings_html,
'cancel': cancel,
'cancel_label': _('Cancel'),
'note_title': note_title,
'editor': editor,
'add_a_note_label': _('Add a note'),
'submit_label': _('Add note'),
'reply_to': optional_params.get("Reply to")}
notes_icon = '<img src="%s/img/wb-notes.png" style="vertical-align: top;" />&nbsp;' % (CFG_SITE_URL,)
if user_can_add_notes and not add_note_p:
add_note_url = """%s/yourbaskets/write_note?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%i&amp;ln=%s%s""" % \
(CFG_SITE_URL, selected_category, selected_topic, selected_group, bskid, recid, ln, '#note')
add_note_logo = """<img src="%s/img/wb-add-note.png" />""" % (CFG_SITE_URL,)
add_note = """<a href="%s">%s%s</a>""" % (add_note_url, add_note_logo, _("Add a note"))
else:
add_note = ""
notes_html = """
<table>
<tr>
<td class="bsknotesheadertitle">
<br />
<strong>%(notes_icon)s%(notes_label)s</strong>
<br />
<small>%(nb_notes)i notes in total</small>
</td>
<td class="bsknotesheaderoptions">
%(add_note)s
</td>
</tr>""" % {'notes_label': _('Notes'),
'notes_icon': notes_icon,
'add_note': (notes and user_can_add_notes and not add_note_p) and add_note or "&nbsp;",
'nb_notes': len(notes)}
if notes or add_note or add_note_p:
notes_html += """
<tr>
<td colspan="2" class="bsknotescontent">"""
thread_history = [0]
for (cmt_uid, cmt_nickname, cmt_title, cmt_body, cmt_date, dummy, cmtid, reply_to) in notes:
if reply_to not in thread_history:
# Going one level down in the thread
thread_history.append(reply_to)
depth = thread_history.index(reply_to)
else:
depth = thread_history.index(reply_to)
thread_history = thread_history[:depth + 1]
notes_html += '<div style="margin-left:%spx">' % (depth*20)
if user_can_add_notes:
reply_to_note = """<a href="%s/yourbaskets/write_note?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%i&amp;cmtid=%i&amp;ln=%s%s">%s</a>""" % \
(CFG_SITE_URL, selected_category, cgi.escape(selected_topic, True), selected_group, bskid, recid, cmtid, ln, '#note', _('Reply'))
else:
reply_to_note = ""
if user_can_delete_notes:
delete_note = """&nbsp;|&nbsp;<a href="%s/yourbaskets/delete_note?category=%s&amp;topic=%s&amp;group=%i&amp;bskid=%i&amp;recid=%i&amp;cmtid=%i&amp;ln=%s">%s</a>""" % \
(CFG_SITE_URL, selected_category, cgi.escape(selected_topic, True), selected_group, bskid, recid, cmtid, ln, _('Delete'))
else:
delete_note = ""
notes_html += """
<table cellspacing="0" cellpadding="0" class="bsknotescontentnote">
<tr>
<td class="bsknotescontenttitle">
%(inline_anchor)s<img src="%(CFG_SITE_URL)s/img/user-icon-1-24x24.gif" />%(authorship)s
</td>
</tr>
<tr>
<td class="bsknotescontentbody">
<blockquote>
%(body)s
</blockquote>
</td>
</tr>
<tr>
<td class="bsknotescontentoptions">
%(reply_to_note)s%(delete_note)s
</td>
</tr>
</table>
<br />""" % {'inline_anchor': (not add_note_p and notes[-1][-1]==cmtid) and '<a name="note"></a>' or '',
'CFG_SITE_URL': CFG_SITE_URL,
'authorship': _("%(x_title)s, by %(x_name)s on %(x_date)s") % \
{'x_title': '<strong>' + (cmt_title and cgi.escape(cmt_title, True) \
or _('Note')) + '</strong>',
'x_name': '<a href="%(CFG_SITE_URL)s/yourmessages/write?msg_to=%(user)s">%(user_display)s</a>' % \
{'CFG_SITE_URL': CFG_SITE_URL,
'user': cmt_nickname or cmt_uid,
'user_display': cmt_nickname or get_user_info(cmt_uid)[2]},
'x_date': '<em>' + convert_datetext_to_dategui(cmt_date) + '</em>'},
'body': email_quoted_txt2html(escape_email_quoted_text(cmt_body)),
'reply_to_note': reply_to_note,
'delete_note': delete_note}
notes_html += '</div>'
if add_note_p:
notes_html += add_note_html
notes_html += """
</td>
</tr>"""
notes_html += """
<tr>
<td class="bsknotesfootertitle">
&nbsp;
</td>
<td class="bsknotesfooteroptions">
%(add_note)s
</td>
</tr>
</table>""" % {'add_note': (user_can_add_notes and not add_note_p) and add_note or '&nbsp;'}
return notes_html
########################################
########## PUBLIC BASKET VIEW ##########
########################################
def tmpl_public_basket(self,
bskid,
basket_name,
date_modification,
nb_items,
(user_can_view_comments,),
nb_comments,
items=[],
id_owner=0,
subscription_status=0,
of='hb',
ln=CFG_SITE_LANG):
"""Template for public basket display."""
if of == 'hb':
out = """
<table class="bskbasket" width="100%">"""
else:
out = ""
if of == 'hb':
out += self.tmpl_public_basket_header(bskid,
basket_name,
nb_items,
date_modification,
(user_can_view_comments,),
nb_comments,
subscription_status,
ln)
if of == 'hb':
out += self.tmpl_public_basket_footer(bskid,
nb_items,
id_owner,
subscription_status,
ln)
out += self.tmpl_public_basket_content(bskid,
(user_can_view_comments,),
items,
of,
ln)
if of == 'hb':
out += """
</table>"""
if of == 'hb':
out += self.tmpl_create_export_as_list(bskid=bskid,
item=None,
public=True)
return out
def tmpl_public_basket_header(self,
bskid,
name,
nb_items,
date_modification,
(user_can_view_comments,),
nb_comments,
subscription_status,
ln=CFG_SITE_LANG):
"""Template for public basket header display."""
_ = gettext_set_language(ln)
optional_colspan = nb_items and ' colspan="3"' or ''
records_field = '<br />' + _('%i items') % nb_items
comments_field = user_can_view_comments and \
(nb_comments and ', ' + (_('%i notes') % nb_comments) or ', ' + _('no notes yet')) \
or ''
last_update_field = '<br />' + _('last update') + ': ' + date_modification
if subscription_status:
subscribe_url = """%s/yourbaskets/subscribe?bskid=%i&amp;ln=%s""" % (CFG_SITE_URL, bskid, ln)
subscribe_logo = """<img src="%s/img/wb-subscribe.png" />""" % (CFG_SITE_URL,)
subscribe = """<a href="%s">%s%s</a>""" % (subscribe_url, subscribe_logo, _("Subscribe to basket"))
unsubscribe_url = """%s/yourbaskets/unsubscribe?bskid=%i&amp;ln=%s""" % (CFG_SITE_URL, bskid, ln)
unsubscribe_logo = """<img src="%s/img/wb-unsubscribe.png" />""" % (CFG_SITE_URL,)
unsubscribe = """<a href="%s">%s%s</a>""" % (unsubscribe_url, unsubscribe_logo, _("Unsubscribe from basket"))
out = """
<thead>
<tr>
<td class="bskbasketheader"%(optional_colspan)s>
<table>
<tr>
<td class="bskbasketheadertitle">
<strong>
%(name)s
</strong>
<small>
%(records_field)s%(comments_field)s
%(last_update_field)s
</small>
</td>
<td class="bskbasketheaderoptions">
%(subscribe_unsubscribe_basket)s
</td>
</table>
</td>
</tr>
</thead>"""
out %= {'optional_colspan': optional_colspan,
'name': name,
'nb_items': nb_items,
'records_field': records_field,
'comments_field': comments_field,
'last_update_field': last_update_field,
'subscribe_unsubscribe_basket': subscription_status > 0 and unsubscribe or subscription_status < 0 and subscribe or not subscription_status and '&nbsp;'}
return out
def tmpl_public_basket_footer(self,
bskid,
nb_items,
id_owner,
subscription_status,
ln=CFG_SITE_LANG):
"""Template for public basket footer display."""
_ = gettext_set_language(ln)
optional_colspan = nb_items and ' colspan="3"' or ''
if subscription_status:
subscribe_url = """%s/yourbaskets/subscribe?bskid=%i&amp;ln=%s""" % (CFG_SITE_URL, bskid, ln)
subscribe_logo = """<img src="%s/img/wb-subscribe.png" />""" % (CFG_SITE_URL,)
subscribe = """<a href="%s">%s%s</a>""" % (subscribe_url, subscribe_logo, _("Subscribe to basket"))
unsubscribe_url = """%s/yourbaskets/unsubscribe?bskid=%i&amp;ln=%s""" % (CFG_SITE_URL, bskid, ln)
unsubscribe_logo = """<img src="%s/img/wb-unsubscribe.png" />""" % (CFG_SITE_URL,)
unsubscribe = """<a href="%s">%s%s</a>""" % (unsubscribe_url, unsubscribe_logo, _("Unsubscribe from basket"))
(uid, nickname, display_name) = get_user_info(id_owner)
display_owner_url = """%s/yourmessages/write?msg_to=%s""" % (CFG_SITE_URL, nickname or str(uid))
display_owner_text = _("This public basket belongs to the user ")
display_owner = """%s<a href="%s">%s</a>.""" % (display_owner_text, display_owner_url, nickname or display_name)
out = """
<tfoot>
<tr>
<td class="bskbasketfooter"%(optional_colspan)s>
<table>
<tr>
<td class="bskbasketfootertitle">
<small>
%(display_owner)s
</small>
</td>
<td class="bskbasketfooteroptions">
%(subscribe_unsubscribe_basket)s
</td>
</tr>
</table>
</td>
</tr>
</tfoot>"""
out %= {'optional_colspan': optional_colspan,
'display_owner': subscription_status and display_owner or _('This public basket belongs to you.'),
'subscribe_unsubscribe_basket': subscription_status > 0 and unsubscribe or subscription_status < 0 and subscribe or not subscription_status and '&nbsp;'}
return out
def tmpl_public_basket_content(self,
bskid,
(user_can_view_comments,),
items=[],
of='hb',
ln=CFG_SITE_LANG):
"""Template for public basket footer display."""
if of == 'hb':
_ = gettext_set_language(ln)
items_html = """
<tbody>"""
if not(items):
items_html += """
<tr>
<td style="text-align:center; height:100px">
%s
</td>
</tr>""" % _("Basket is empty")
else:
count = 0
for item in items:
count += 1
items_html += self.__tmpl_public_basket_item(count=count,
bskid=bskid,
item=item,
view_notes=user_can_view_comments,
ln=ln)
items_html += """
</tbody>"""
return items_html
elif of == 'xm':
items_xml = ""
for item in items:
items_xml += item[4] + "\n"
return items_xml
else:
return ""
def __tmpl_public_basket_item(self,
count,
bskid,
item,
view_notes=0,
ln=CFG_SITE_LANG):
"""Template for basket item display within the basket content."""
_ = gettext_set_language(ln)
(recid, colid, nb_cmt, last_cmt, val, dummy) = item
copy_url = "%(siteurl)s/yourbaskets/modify?action=copy&amp;bskid=%(bskid)i&amp;recid=%(recid)i&amp;ln=%(ln)s" % \
{'siteurl': CFG_SITE_URL,
'bskid': bskid,
'recid': recid,
'ln': ln}
copy_img = "%s/img/wb-copy-item.png" % (CFG_SITE_URL,)
copy = """<a href="%s"><img src="%s" alt="%s" />%s</a>""" % \
(copy_url, copy_img, _("Copy item"), _("Copy item"))
if recid < 0:
external_item_img = '<img src="%s/img/wb-external-item.png" alt="%s" style="vertical-align: top;" />&nbsp;' % \
(CFG_SITE_URL, _("External item"))
else:
external_item_img = ''
out = """
<tr>
<td style="border-bottom: 1px solid #fc0;">
<table>
<tr>
<td class="bskcontentcount">
%(count)i.
</td>
<td class="bskcontentcol" colspan="2">
%(icon)s%(content)s
</td>
</tr>
<tr>
<td class="bskcontentoptions">
&nbsp;
</td>
<td>
<span class="moreinfo">"""
if item[0] > 0:
- detailed_record = """<a class="moreinfo" href="%(siteurl)s/record/%(recid)s">%(detailed_record_label)s</a>"""
+ detailed_record = """<a class="moreinfo" href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recid)s">%(detailed_record_label)s</a>"""
out += detailed_record + (view_notes and " - " or "")
external_url = ""
else:
## Uncomment the following lines if you want the Detailed record link to be
## displayed for external records but not for external sources (such as urls)
#external_colid_and_url = db.get_external_colid_and_url(item[0])
#if external_colid_and_url and external_colid_and_url[0][0] and external_colid_and_url[0][1]:
# detailed_record = '<a class="moreinfo" href="%(external_url)s">%(detailed_record_label)s</a>'
# out += detailed_record + (view_notes and " - " or "")
# external_url = external_colid_and_url[0][1]
#else:
# external_url = ""
## Currently no external items (records or sources) have a Detailed record link
external_url = ""
if view_notes:
notes = """\n<a class="moreinfo" href="%(siteurl)s/yourbaskets/%(add_and_view_notes_action)s?"""\
"""bskid=%(bskid)s&amp;recid=%(recid)i&amp;ln=%(ln)s%(add_and_view_notes_inline_anchor)s">%(add_and_view_notes_label)s</a>"""
out += notes
out += """
</span>
</td>
<td class="bskbasketheaderoptions">
%(copy)s
</td>
</tr>
</table>
</td>
</tr>"""
out = out % {'count': count,
'icon': external_item_img,
'content': colid >= 0 and val or self.tmpl_create_pseudo_item(val),
'add_and_view_notes_action': nb_cmt and 'display_public' or 'write_public_note',
'add_and_view_notes_inline_anchor': not nb_cmt and '#note' or '',
'add_and_view_notes_label': nb_cmt and _('Notes') + ' (' + str(nb_cmt) + ')' or _('Add a note...'),
'last_cmt': last_cmt,
'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'bskid': bskid,
'recid': recid,
'external_url': external_url,
'detailed_record_label': _("Detailed record"),
'copy': copy,
'ln': ln}
return out
####################################################
########## PUBLIC BASKET SINGLE ITEM VIEW ##########
####################################################
def tmpl_public_basket_single_item(self,
bskid,
name,
nb_items,
(user_can_view_notes,
user_can_add_notes),
item=(),
notes=(),
previous_item_recid=0,
next_item_recid=0,
item_index=0,
optional_params={},
of='hb',
ln=CFG_SITE_LANG):
"""Template for public basket's single item display."""
_ = gettext_set_language(ln)
if of == 'hb':
out = """
<table class="bskbasket" width="100%">"""
else:
out = ""
if of == 'hb':
out += self.tmpl_public_basket_single_item_header(bskid,
name,
nb_items,
previous_item_recid,
next_item_recid,
item_index,
ln=CFG_SITE_LANG)
if of == 'hb':
out += self.tmpl_public_basket_single_item_footer(bskid,
previous_item_recid,
next_item_recid,
ln=CFG_SITE_LANG)
out += self.tmpl_public_basket_single_item_content(bskid,
(user_can_view_notes,
user_can_add_notes),
item,
notes,
item_index,
optional_params,
of,
ln=CFG_SITE_LANG)
if of == 'hb':
out += """
</table>"""
if of == 'hb':
out += self.tmpl_create_export_as_list(bskid=bskid,
item=item,
public=True)
return out
def tmpl_public_basket_single_item_header(self,
bskid,
name,
nb_items,
previous_item_recid,
next_item_recid,
item_index,
ln=CFG_SITE_LANG):
"""Template for public basket's single item header display."""
_ = gettext_set_language(ln)
records_field = '<br />' + _('Item %(x_item_index)i of %(x_item_total)i') % \
{'x_item_index': item_index, 'x_item_total': nb_items}
if previous_item_recid:
previous_item_url = """%s/yourbaskets/display_public?bskid=%i&amp;recid=%s&amp;ln=%s""" % \
(CFG_SITE_URL,
bskid,
previous_item_recid,
ln)
previous_item_logo = """<img src="%s/img/wb-previous-item.png" />""" % (CFG_SITE_URL,)
previous_item = """<a href="%s">%s%s</a>""" % (previous_item_url, previous_item_logo, _("Previous item"))
else:
previous_item_logo = """<img src="%s/img/wb-previous-item-disabled.png" />""" % (CFG_SITE_URL,)
previous_item = """%s%s""" % (previous_item_logo, _("Previous item"))
if next_item_recid:
next_item_url = """%s/yourbaskets/display_public?bskid=%i&amp;recid=%s&amp;ln=%s""" % \
(CFG_SITE_URL,
bskid,
next_item_recid,
ln)
next_item_logo = """<img src="%s/img/wb-next-item.png" />""" % (CFG_SITE_URL,)
next_item = """<a href="%s">%s%s</a>""" % (next_item_url, next_item_logo, _("Next item"))
else:
next_item_logo = """<img src="%s/img/wb-next-item-disabled.png" />""" % (CFG_SITE_URL,)
next_item = """%s%s""" % (next_item_logo, _("Next item"))
go_back_url = """%s/yourbaskets/display_public?bskid=%i&amp;ln=%s""" % \
(CFG_SITE_URL,
bskid,
ln)
go_back_logo = """<img src="%s/img/wb-go-back.png" />""" % (CFG_SITE_URL,)
go_back = """<a href="%s">%s%s</a>""" % (go_back_url, go_back_logo, _("Return to basket"))
out = """
<thead>
<tr>
<td class="bskbasketheader">
<table>
<tr>
<td class="bskbasketheadertitle">
<strong>
%(name)s
</strong>
<small>
%(records_field)s
</small>
</td>
<td class="bskbasketheaderoptions">
%(go_back)s
&nbsp;&nbsp;
%(previous_item)s
&nbsp;&nbsp;
%(next_item)s
</td>
</table>
</td>
</tr>
</thead>"""
out %= {'name': name,
'records_field': records_field,
'go_back': go_back,
'previous_item': previous_item,
'next_item': next_item,
}
return out
def tmpl_public_basket_single_item_footer(self,
bskid,
previous_item_recid,
next_item_recid,
ln=CFG_SITE_LANG):
"""Template for public basket's single item footer display."""
_ = gettext_set_language(ln)
if previous_item_recid:
previous_item_url = """%s/yourbaskets/display_public?bskid=%i&amp;recid=%s&amp;ln=%s""" % \
(CFG_SITE_URL,
bskid,
previous_item_recid,
ln)
previous_item_logo = """<img src="%s/img/wb-previous-item.png" />""" % (CFG_SITE_URL,)
previous_item = """<a href="%s">%s%s</a>""" % (previous_item_url, previous_item_logo, _("Previous item"))
else:
previous_item_logo = """<img src="%s/img/wb-previous-item-disabled.png" />""" % (CFG_SITE_URL,)
previous_item = """%s%s""" % (previous_item_logo, _("Previous item"))
if next_item_recid:
next_item_url = """%s/yourbaskets/display_public?bskid=%i&amp;recid=%s&amp;ln=%s""" % \
(CFG_SITE_URL,
bskid,
next_item_recid,
ln)
next_item_logo = """<img src="%s/img/wb-next-item.png" />""" % (CFG_SITE_URL,)
next_item = """<a href="%s">%s%s</a>""" % (next_item_url, next_item_logo, _("Next item"))
else:
next_item_logo = """<img src="%s/img/wb-next-item-disabled.png" />""" % (CFG_SITE_URL,)
next_item = """%s%s""" % (next_item_logo, _("Next item"))
go_back_url = """%s/yourbaskets/display_public?bskid=%i&amp;ln=%s""" % \
(CFG_SITE_URL,
bskid,
ln)
go_back_logo = """<img src="%s/img/wb-go-back.png" />""" % (CFG_SITE_URL,)
go_back = """<a href="%s">%s%s</a>""" % (go_back_url, go_back_logo, _("Return to basket"))
out = """
<tfoot>
<tr>
<td class="bskbasketfooter">
<table>
<tr>
<td class="bskbasketfootertitle">
&nbsp;
</td>
<td class="bskbasketfooteroptions">
%(go_back)s
&nbsp;&nbsp;
%(previous_item)s
&nbsp;&nbsp;
%(next_item)s
</td>
</table>
</td>
</tr>
</tfoot>"""
out %= {'go_back': go_back,
'previous_item': previous_item,
'next_item': next_item,
}
return out
def tmpl_public_basket_single_item_content(self,
bskid,
(user_can_view_notes,
user_can_add_notes),
item=(),
notes=(),
index_item=0,
optional_params={},
of='hb',
ln=CFG_SITE_LANG):
"""Template for public basket's single item content display."""
if of == 'hb':
_ = gettext_set_language(ln)
item_html = """
<tbody>"""
if not item:
item_html += """
<tr>
<td style="text-align: center; height: 100px">
%s
</td>
</tr>""" % _("The item you have selected does not exist.")
else:
(recid, colid, dummy, dummy, val, dummy) = item
if recid < 0:
external_item_img = '<img src="%s/img/wb-external-item.png" alt="%s" style="vertical-align: top;" />&nbsp;' % \
(CFG_SITE_URL, _("External item"))
else:
external_item_img = ''
if user_can_view_notes:
notes_html = self.__tmpl_display_public_notes(recid,
bskid,
(user_can_add_notes,),
notes,
optional_params,
ln)
notes = """
<tr>
<td colspan="2" class="bskcontentnotes">%(notes_html)s
</td>
</tr>""" % {'notes_html': notes_html}
else:
notes_msg = _("You do not have sufficient rights to view this item's notes.")
notes = """
<tr>
<td colspan="2" style="text-align: center; height: 50px">
%(notes_msg)s
</td>
</tr>""" % {'notes_msg': notes_msg}
item_html += """
<tr>
<td style="border-bottom: 1px solid #fc0;">
<table>
<tr>
<td class="bskcontentcount">
%(count)i.
</td>
<td class="bskcontentcol">
%(icon)s%(content)s
</td>
</tr>%(notes)s
</table>
</td>
</tr>""" % {'count': index_item,
'icon': external_item_img,
'content': colid >= 0 and val or self.tmpl_create_pseudo_item(val),
'notes': notes,
'ln': ln}
item_html += """
</tbody>"""
return item_html
elif of == 'xm':
item_xml = item[4]
return item_xml
def __tmpl_display_public_notes(self,
recid,
bskid,
(user_can_add_notes,),
notes,
optional_params,
ln=CFG_SITE_LANG):
"""Template for public basket's single item notes display."""
_ = gettext_set_language(ln)
warnings_html = ""
add_note_p = False
if user_can_add_notes and (optional_params.has_key("Add note") or optional_params.has_key("Incomplete note")):
add_note_p = True
if optional_params.has_key("Add note") and optional_params['Add note']:
replied_to_note = optional_params['Add note']
note_body_html = self.tmpl_quote_comment_html(replied_to_note[2],
replied_to_note[1],
replied_to_note[0],
replied_to_note[4],
replied_to_note[3],
ln)
note_body_textual = self.tmpl_quote_comment_textual(replied_to_note[2],
replied_to_note[1],
replied_to_note[0],
replied_to_note[4],
replied_to_note[3],
ln)
note_title = "Re: " + replied_to_note[2]
elif optional_params.has_key("Incomplete note") and optional_params['Incomplete note']:
incomplete_note = optional_params['Incomplete note']
note_body_html = incomplete_note[1]
# TODO: Do we need to format incomplete body correctly as textual
# and html as above?
note_body_textual = incomplete_note[1]
note_title = incomplete_note[0]
if optional_params.has_key("Warnings"):
warnings = optional_params["Warnings"]
warnings_html = self.tmpl_warnings(warnings, ln)
else:
note_body_html = ""
note_body_textual = ""
note_title = ""
if optional_params.has_key("Warnings"):
warnings = optional_params["Warnings"]
warnings_html = self.tmpl_warnings(warnings, ln)
# TODO: calculate the url
file_upload_url = ""
action = """%s/yourbaskets/save_public_note?bskid=%i&amp;recid=%i&amp;ln=%s%s""" % \
(CFG_SITE_URL, bskid, recid, ln, '#note')
cancel = """%s/yourbaskets/display_public?bskid=%i&amp;recid=%i&amp;ln=%s""" % \
(CFG_SITE_URL, bskid, recid, ln)
editor = get_html_text_editor(name="note_body",
content=note_body_html,
textual_content=note_body_textual,
width="100%",
height="200px",
enabled=CFG_WEBBASKET_USE_RICH_TEXT_EDITOR,
file_upload_url=file_upload_url,
toolbar_set="WebComment")
add_note_html = """
<table cellspacing="0" cellpadding="0" class="bsknotescontentaddnote">
<tr>
<td class="bsknotescontentaddform">
<form name="write_note" method="post" action="%(action)s">
<a name="note"></a><strong>%(add_a_note_label)s</strong>
%(warnings_html)s
<p align="left">
<small>Subject:</small>
<br />
<input type="text" name="note_title" size="65" value="%(note_title)s" />
</p>
<p align="left">
<small>Note:</small>
<br />
%(editor)s
</p>
<input type="hidden" name="reply_to" value="%(reply_to)s" />
<p align="right">
<input type="submit" class="formbutton" value="%(submit_label)s" />
<input type="button" class="nonsubmitbutton" value="%(cancel_label)s" onClick="window.location='%(cancel)s'" />
</p>
</form>
</td>
</tr>
</table>""" % {'action': action,
'warnings_html': warnings_html,
'cancel': cancel,
'cancel_label': _('Cancel'),
'note_title': note_title,
'editor': editor,
'add_a_note_label': _('Add a note'),
'submit_label': _('Add note'),
'reply_to': optional_params.get("Reply to")}
notes_icon = '<img src="%s/img/wb-notes.png" style="vertical-align: top;" />&nbsp;' % (CFG_SITE_URL,)
if user_can_add_notes and not add_note_p:
add_note_url = """%s/yourbaskets/write_public_note?bskid=%i&amp;recid=%i&amp;ln=%s%s""" % \
(CFG_SITE_URL, bskid, recid, ln, '#note')
add_note_logo = """<img src="%s/img/wb-add-note.png" />""" % (CFG_SITE_URL,)
add_note = """<a href="%s">%s%s</a>""" % (add_note_url, add_note_logo, _("Add a note"))
else:
add_note = ""
notes_html = """
<table>
<tr>
<td class="bsknotesheadertitle">
<br />
<strong>%(notes_icon)s%(notes_label)s</strong>
<br />
<small>%(nb_notes)i notes in total</small>
</td>
<td class="bsknotesheaderoptions">
%(add_note)s
</td>
</tr>""" % {'notes_label': _('Notes'),
'notes_icon': notes_icon,
'add_note': (notes and user_can_add_notes and not add_note_p) and add_note or "&nbsp;",
'nb_notes': len(notes)}
if notes or add_note or add_note_p:
notes_html += """
<tr>
<td colspan="2" class="bsknotescontent">"""
thread_history = [0]
for (cmt_uid, cmt_nickname, cmt_title, cmt_body, cmt_date, dummy, cmtid, reply_to) in notes:
if reply_to not in thread_history:
# Going one level down in the thread
thread_history.append(reply_to)
depth = thread_history.index(reply_to)
else:
depth = thread_history.index(reply_to)
thread_history = thread_history[:depth + 1]
notes_html += '<div style="margin-left:%spx">' % (depth*20)
if user_can_add_notes:
reply_to_note = """<a href="%s/yourbaskets/write_public_note?bskid=%i&amp;recid=%i&amp;cmtid=%i&amp;ln=%s%s">%s</a>""" % \
(CFG_SITE_URL, bskid, recid, cmtid, ln, '#note', _('Reply'))
else:
reply_to_note = ""
notes_html += """
<table cellspacing="0" cellpadding="0" class="bsknotescontentnote">
<tr>
<td class="bsknotescontenttitle">
%(inline_anchor)s<img src="%(CFG_SITE_URL)s/img/user-icon-1-24x24.gif" />%(authorship)s
</td>
</tr>
<tr>
<td class="bsknotescontentbody">
<blockquote>
%(body)s
</blockquote>
</td>
</tr>
<tr>
<td class="bsknotescontentoptions">
%(reply_to_note)s
</td>
</tr>
</table>
<br />""" % {'inline_anchor': (not add_note_p and notes[-1][-1]==cmtid) and '<a name="note"></a>' or '',
'CFG_SITE_URL': CFG_SITE_URL,
'authorship': _("%(x_title)s, by %(x_name)s on %(x_date)s") % \
{'x_title': '<strong>' + (cmt_title and cgi.escape(cmt_title, True) \
or _('Note')) + '</strong>',
'x_name': '<a href="%(CFG_SITE_URL)s/yourmessages/write?msg_to=%(user)s">%(user_display)s</a>' % \
{'CFG_SITE_URL': CFG_SITE_URL,
'user': cmt_nickname or cmt_uid,
'user_display': cmt_nickname or get_user_info(cmt_uid)[2]},
'x_date': '<em>' + convert_datetext_to_dategui(cmt_date) + '</em>'},
'body': email_quoted_txt2html(escape_email_quoted_text(cmt_body)),
'reply_to_note': reply_to_note}
notes_html += '</div>'
if add_note_p:
notes_html += add_note_html
notes_html += """
</td>
</tr>"""
notes_html += """
<tr>
<td class="bsknotesfootertitle">
&nbsp;
</td>
<td class="bsknotesfooteroptions">
%(add_note)s
</td>
</tr>
</table>""" % {'add_note': (user_can_add_notes and not add_note_p) and add_note or '&nbsp;'}
return notes_html
def tmpl_create_pseudo_item(self, item, of='hb'):
""""""
if of == 'hb':
(es_title, es_desc, es_url) = tuple(item.split('\n'))
es_title = cgi.escape(es_title, True)
es_desc = cgi.escape(es_desc.replace('<br />', '\n'), True).replace('\n', '<br />')
out = """<strong>%s</strong>
<br />
<small>%s
<br />
<strong>URL:</strong> <a class="note" target="_blank" href="%s">%s</a>
</small>
""" % (es_title, es_desc, es_url, prettify_url(es_url))
if of == 'xm':
# TODO: xml output...
out = ""
return out
def tmpl_export_xml(self, body):
"""Template for the xml represantation for the selected basket/items."""
out = """
<collection xmlns="http://www.loc.gov/MARC21/slim">
%s
</collection>""" % (body,)
return out
def tmpl_create_export_as_list(self,
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic="",
selected_group=0,
bskid=0,
item=(),
public=False):
"""Tamplate that creates a bullet list of export as formats for a basket or an item."""
list_of_export_as_formats = [('BibTeX','hx'), ('DC','xd'), ('EndNote','xe'), ('MARCXML', 'xm'), ('NLM','xn'), ('RefWorks','xw')]
recid = item and "&recid=" + str(item[0]) or ""
if not public:
href = "%s/yourbaskets/display?category=%s&topic=%s&group=%i&bskid=%i%s" % \
(CFG_SITE_URL,
selected_category,
selected_topic,
selected_group,
bskid,
recid)
else:
href = "%s/yourbaskets/display_public?bskid=%i%s" % \
(CFG_SITE_URL,
bskid,
recid)
export_as_html = ""
for format in list_of_export_as_formats:
export_as_html += """<a style="text-decoration:underline;font-weight:normal" href="%s&of=%s">%s</a>, """ % \
(href, format[1], format[0])
if export_as_html:
export_as_html = export_as_html[:-2]
out = """
<div style="float:right; text-align:right;">
<ul class="bsk_export_as_list">
<li>Export as
%s
</li>
</ul>
</div>""" % (export_as_html,)
return out
#############################################
########## SUPPLEMENTARY FUNCTIONS ##########
#############################################
def prettify_name(name, char_limit=10, nb_dots=3):
"""If name has more characters than char_limit return a shortened version of it
keeping the beginning (up to char_limit) and replacing the rest with dots."""
name = unicode(name, 'utf-8')
if len(name) > char_limit:
while name[char_limit-1] == ' ':
char_limit -= 1
prettified_name = name[:char_limit] + '.'*nb_dots
return prettified_name.encode('utf-8')
else:
return name.encode('utf-8')
def prettify_url(url, char_limit=50, nb_dots=3):
"""If the url has more characters than char_limit return a shortened version of it
keeping the beginning and ending and replacing the rest with dots."""
if len(url) > char_limit:
# let's set a minimum character limit
if char_limit < 5:
char_limit = 5
# let's set a maximum number of dots in relation to the character limit
if nb_dots > char_limit/4:
nb_dots = char_limit/5
nb_char_url = char_limit - nb_dots
nb_char_end = nb_char_url/4
nb_char_beg = nb_char_url - nb_char_end
return url[:nb_char_beg] + '.'*nb_dots + url[-nb_char_end:]
else:
return url
def create_search_box_select_options(category,
topic,
grpid,
topic_list,
group_list,
number_of_public_baskets,
ln):
"""Returns an html list of options for the select form field of the search box."""
_ = gettext_set_language(ln)
out = ""
if category:
if topic:
b = CFG_WEBBASKET_CATEGORIES['PRIVATE'] + '_' + cgi.escape(topic, True)
elif grpid:
b = CFG_WEBBASKET_CATEGORIES['GROUP'] + '_' + str(grpid)
else:
b = category
else:
b = ""
options = []
if topic_list or group_list:
options.append((_("All your baskets"), "", True, False))
if topic_list:
options.append((_("Your personal baskets"), CFG_WEBBASKET_CATEGORIES['PRIVATE'], False, True))
for topic_name in topic_list:
topic_label = cgi.escape(topic_name[0], True)
topic_value = "P_%s" % (cgi.escape(topic_name[0], True),)
options.append((topic_label, topic_value, False, False))
if group_list:
options.append((_("Your group baskets"), CFG_WEBBASKET_CATEGORIES['GROUP'], False, True))
for group_id_and_name in group_list:
group_label = cgi.escape(group_id_and_name[1], True)
group_value = "G_%i" % (group_id_and_name[0],)
options.append((group_label, group_value, False, False))
if number_of_public_baskets:
options.append((_("Your public baskets"), CFG_WEBBASKET_CATEGORIES['EXTERNAL'], False, True))
options.append((_("All the public baskets"), CFG_WEBBASKET_CATEGORIES['ALLPUBLIC'], True, False))
for option in options:
out += """
<option style="%(style)s" value="%(value)s"%(selected)s>%(label)s</option>""" % \
{'value': option[1],
'label': option[0],
'selected': option[1] == b and ' selected="selected"' or '',
#'style': option[2] and \
#( ( not option[1] or option[1] == CFG_WEBBASKET_CATEGORIES['ALLPUBLIC'] ) and "font-weight: bold;" or \
#( option[1] and option[1] != CFG_WEBBASKET_CATEGORIES['ALLPUBLIC'] ) and "font-weight: bold; margin-left: 5px;" ) or \
#"font-weight: normal; margin-left: 10px;"}
'style': option[2] and "font-weight: bold;" or \
option[3] and "font-weight: bold; margin-left: 5px;" or \
"font-weight: normal; margin-left: 10px;"}
return out
def create_add_box_select_options(category,
bskid,
personal_basket_list,
group_basket_list,
ln):
"""Returns an html list of options for the select form field of the add box."""
_ = gettext_set_language(ln)
out = ""
options = []
if category and bskid:
if category == CFG_WEBBASKET_CATEGORIES['PRIVATE']:
b = CFG_WEBBASKET_CATEGORIES['PRIVATE'] + '_' + str(bskid)
elif category == CFG_WEBBASKET_CATEGORIES['GROUP']:
b = CFG_WEBBASKET_CATEGORIES['GROUP'] + '_' + str(bskid)
elif category == CFG_WEBBASKET_CATEGORIES['EXTERNAL']:
b = CFG_WEBBASKET_CATEGORIES['EXTERNAL'] + '_' + str(bskid)
else:
b = ""
else:
b = ""
#option list format: [ name, value, 1st level: True/False, 2nd level: True/False]
# name: the name of the option, it will be used as its label in the list.
# value: the value of the option that will be sent as a POST variable through
# the select form field
# 1st level: bold, no margin, used for categories
# 2nd level: bold, small margin, used for topics and groups
# * when both levels are False: normal font, big margin,
# used for actual options *
# Let's set the default "Choose a basket..." option first.
#options= [(_("Choose a basket..."), str(-1), False, False)]
out += """
<option style="%(style)s" value="%(value)i">%(label)s</option>""" % \
{'style': "font-weight: normal;",
'value': -1,
'label': _("*** basket name ***")}
# Then, we parse the personal and group basket lists and dynamically create
# the list of options
if personal_basket_list:
options.append((_("Your personal baskets"), None, True, False))
for personal_topic in personal_basket_list:
personal_topic_name = cgi.escape(personal_topic[0], True)
personal_baskets = eval(personal_topic[1] + ",")
options.append((personal_topic_name, None, False, True))
for personal_basket in personal_baskets:
personal_basket_name = cgi.escape(personal_basket[1], True)
personal_basket_value = CFG_WEBBASKET_CATEGORIES['PRIVATE'] + "_" + str(personal_basket[0])
options.append((personal_basket_name, personal_basket_value, False, False))
if group_basket_list:
options.append((_("Your group baskets"), None, True, False))
for group_group in group_basket_list:
group_group_name = cgi.escape(group_group[0], True)
group_baskets = eval(group_group[1] + ",")
options.append((group_group_name, None, False, True))
for group_basket in group_baskets:
group_basket_name = cgi.escape(group_basket[1], True)
group_basket_value = CFG_WEBBASKET_CATEGORIES['GROUP'] + "_" + str(group_basket[0])
options.append((group_basket_name, group_basket_value, False, False))
if len(options) == 3:
# In case we only have 1 option, pretend b has the value of that option
# so that it is selected by default.
b = options[2][1]
for option in options:
out += """
<option style="%(style)s"%(value)s%(selected)s%(disabled)s>%(label)s</option>""" % \
{'value': not ( option[2] or option[3] ) and ' value="' + option[1] + '"' or '',
'label': option[0],
'selected': option[1] == b and ' selected="selected"' or '',
'disabled': ( option[2] or option[3] ) and ' disabled="disabled"' or '',
'style': option[2] and "font-weight: bold;" or \
option[3] and "font-weight: bold; margin-left: 5px;" or \
"font-weight: normal; margin-left: 10px;"}
return out
diff --git a/modules/webcomment/lib/webcomment.py b/modules/webcomment/lib/webcomment.py
index d05713cbe..c133c70b1 100644
--- a/modules/webcomment/lib/webcomment.py
+++ b/modules/webcomment/lib/webcomment.py
@@ -1,1817 +1,1819 @@
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Comments and reviews for records """
__revision__ = "$Id$"
# non Invenio imports:
import time
import math
import os
import cgi
import re
from datetime import datetime, timedelta
# Invenio imports:
from invenio.dbquery import run_sql
from invenio.config import CFG_PREFIX, \
CFG_SITE_LANG, \
CFG_WEBALERT_ALERT_ENGINE_EMAIL,\
CFG_SITE_SUPPORT_EMAIL,\
CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL,\
CFG_SITE_URL,\
CFG_SITE_NAME,\
CFG_WEBCOMMENT_ALLOW_REVIEWS,\
CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS,\
CFG_WEBCOMMENT_ALLOW_COMMENTS,\
CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL,\
CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN,\
CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS,\
- CFG_WEBCOMMENT_DEFAULT_MODERATOR
+ CFG_WEBCOMMENT_DEFAULT_MODERATOR, \
+ CFG_SITE_RECORD
from invenio.webmessage_mailutils import \
email_quote_txt, \
email_quoted_txt2html
from invenio.webuser import get_user_info, get_email, collect_user_info
from invenio.dateutils import convert_datetext_to_dategui, \
datetext_default, \
convert_datestruct_to_datetext
from invenio.mailutils import send_email
from invenio.errorlib import register_exception
from invenio.messages import wash_language, gettext_set_language
from invenio.urlutils import wash_url_argument
from invenio.webcomment_config import CFG_WEBCOMMENT_ACTION_CODE, \
CFG_WEBCOMMENT_EMAIL_REPLIES_TO, \
CFG_WEBCOMMENT_ROUND_DATAFIELD, \
CFG_WEBCOMMENT_RESTRICTION_DATAFIELD, \
CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH
from invenio.access_control_engine import acc_authorize_action
from invenio.search_engine import \
guess_primary_collection_of_a_record, \
check_user_can_view_record, \
get_fieldvalues, \
get_collection_reclist, \
get_colID
from invenio.webcomment_washer import EmailWasher
try:
import invenio.template
webcomment_templates = invenio.template.load('webcomment')
except:
pass
def perform_request_display_comments_or_remarks(req, recID, display_order='od', display_since='all', nb_per_page=100, page=1, ln=CFG_SITE_LANG, voted=-1, reported=-1, subscribed=0, reviews=0, uid=-1, can_send_comments=False, can_attach_files=False, user_is_subscribed_to_discussion=False, user_can_unsubscribe_from_discussion=False, display_comment_rounds=None):
"""
Returns all the comments (reviews) of a specific internal record or external basket record.
@param recID: record id where (internal record IDs > 0) or (external basket record IDs < -100)
@param display_order: hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param display_since: all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb_per_page: number of results per page
@param page: results page
@param voted: boolean, active if user voted for a review, see perform_request_vote function
@param reported: boolean, active if user reported a certain comment/review, perform_request_report function
@param subscribed: int, 1 if user just subscribed to discussion, -1 if unsubscribed
@param reviews: boolean, enabled if reviews, disabled for comments
@param uid: the id of the user who is reading comments
@param can_send_comments: if user can send comment or not
@param can_attach_files: if user can attach file to comment or not
@param user_is_subscribed_to_discussion: True if user already receives new comments by email
@param user_can_unsubscribe_from_discussion: True is user is allowed to unsubscribe from discussion
@return html body.
"""
errors = []
warnings = []
nb_reviews = 0
nb_comments = 0
# wash arguments
recID = wash_url_argument(recID, 'int')
ln = wash_language(ln)
display_order = wash_url_argument(display_order, 'str')
display_since = wash_url_argument(display_since, 'str')
nb_per_page = wash_url_argument(nb_per_page, 'int')
page = wash_url_argument(page, 'int')
voted = wash_url_argument(voted, 'int')
reported = wash_url_argument(reported, 'int')
reviews = wash_url_argument(reviews, 'int')
# vital argument check
(valid, error_body) = check_recID_is_in_range(recID, warnings, ln)
if not(valid):
return (error_body, errors, warnings)
# CERN hack begins: filter out ATLAS comments
from invenio.config import CFG_CERN_SITE
if CFG_CERN_SITE:
restricted_comments_p = False
for report_number in get_fieldvalues(recID, '088__a'):
if report_number.startswith("ATL-"):
restricted_comments_p = True
break
if restricted_comments_p:
err_code, err_msg = acc_authorize_action(uid, 'viewrestrcoll',
collection='ATLAS Communications')
if err_code:
return (err_msg, errors, warnings)
# CERN hack ends
# Query the database and filter results
user_info = collect_user_info(uid)
res = query_retrieve_comments_or_remarks(recID, display_order, display_since, reviews, user_info=user_info)
res2 = query_retrieve_comments_or_remarks(recID, display_order, display_since, not reviews, user_info=user_info)
nb_res = len(res)
if reviews:
nb_reviews = nb_res
nb_comments = len(res2)
else:
nb_reviews = len(res2)
nb_comments = nb_res
# checking non vital arguemnts - will be set to default if wrong
#if page <= 0 or page.lower() != 'all':
if page < 0:
page = 1
warnings.append(('WRN_WEBCOMMENT_INVALID_PAGE_NB',))
if nb_per_page < 0:
nb_per_page = 100
warnings.append(('WRN_WEBCOMMENT_INVALID_NB_RESULTS_PER_PAGE',))
if CFG_WEBCOMMENT_ALLOW_REVIEWS and reviews:
if display_order not in ['od', 'nd', 'hh', 'lh', 'hs', 'ls']:
display_order = 'hh'
warnings.append(('WRN_WEBCOMMENT_INVALID_REVIEW_DISPLAY_ORDER',))
else:
if display_order not in ['od', 'nd']:
display_order = 'od'
warnings.append(('WRN_WEBCOMMENT_INVALID_DISPLAY_ORDER',))
if not display_comment_rounds:
display_comment_rounds = []
# filter results according to page and number of reults per page
if nb_per_page > 0:
if nb_res > 0:
last_page = int(math.ceil(nb_res / float(nb_per_page)))
else:
last_page = 1
if page > last_page:
page = 1
warnings.append(("WRN_WEBCOMMENT_INVALID_PAGE_NB",))
if nb_res > nb_per_page: # if more than one page of results
if page < last_page:
res = res[(page-1)*(nb_per_page) : (page*nb_per_page)]
else:
res = res[(page-1)*(nb_per_page) : ]
else: # one page of results
pass
else:
last_page = 1
# Send to template
avg_score = 0.0
if not CFG_WEBCOMMENT_ALLOW_COMMENTS and not CFG_WEBCOMMENT_ALLOW_REVIEWS: # comments not allowed by admin
errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',))
if reported > 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED',))
elif reported == 0:
warnings.append(('WRN_WEBCOMMENT_ALREADY_REPORTED',))
elif reported == -2:
warnings.append(('WRN_WEBCOMMENT_INVALID_REPORT',))
if CFG_WEBCOMMENT_ALLOW_REVIEWS and reviews:
avg_score = calculate_avg_score(res)
if voted > 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED',))
elif voted == 0:
warnings.append(('WRN_WEBCOMMENT_ALREADY_VOTED',))
if subscribed == 1:
warnings.append(('WRN_WEBCOMMENT_SUBSCRIBED',))
elif subscribed == -1:
warnings.append(('WRN_WEBCOMMENT_UNSUBSCRIBED',))
grouped_comments = group_comments_by_round(res, reviews)
# Clean list of comments round names
if not display_comment_rounds:
display_comment_rounds = []
elif 'all' in display_comment_rounds:
display_comment_rounds = [cmtgrp[0] for cmtgrp in grouped_comments]
elif 'latest' in display_comment_rounds:
if grouped_comments:
display_comment_rounds.append(grouped_comments[-1][0])
display_comment_rounds.remove('latest')
body = webcomment_templates.tmpl_get_comments(req,
recID,
ln,
nb_per_page, page, last_page,
display_order, display_since,
CFG_WEBCOMMENT_ALLOW_REVIEWS,
grouped_comments, nb_comments, avg_score,
warnings,
border=0,
reviews=reviews,
total_nb_reviews=nb_reviews,
uid=uid,
can_send_comments=can_send_comments,
can_attach_files=can_attach_files,
user_is_subscribed_to_discussion=\
user_is_subscribed_to_discussion,
user_can_unsubscribe_from_discussion=\
user_can_unsubscribe_from_discussion,
display_comment_rounds=display_comment_rounds)
return (body, errors, warnings)
def perform_request_vote(cmt_id, client_ip_address, value, uid=-1):
"""
Vote positively or negatively for a comment/review
@param cmt_id: review id
@param value: +1 for voting positively
-1 for voting negatively
@return: integer 1 if successful, integer 0 if not
"""
cmt_id = wash_url_argument(cmt_id, 'int')
client_ip_address = wash_url_argument(client_ip_address, 'str')
value = wash_url_argument(value, 'int')
uid = wash_url_argument(uid, 'int')
if cmt_id > 0 and value in [-1, 1] and check_user_can_vote(cmt_id, client_ip_address, uid):
action_date = convert_datestruct_to_datetext(time.localtime())
action_code = CFG_WEBCOMMENT_ACTION_CODE['VOTE']
query = """INSERT INTO cmtACTIONHISTORY (id_cmtRECORDCOMMENT,
id_bibrec, id_user, client_host, action_time,
action_code)
VALUES (%s, NULL ,%s, inet_aton(%s), %s, %s)"""
params = (cmt_id, uid, client_ip_address, action_date, action_code)
run_sql(query, params)
return query_record_useful_review(cmt_id, value)
else:
return 0
def check_user_can_comment(recID, client_ip_address, uid=-1):
""" Check if a user hasn't already commented within the last seconds
time limit: CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS
@param recID: record id
@param client_ip_address: IP => use: str(req.remote_ip)
@param uid: user id, as given by invenio.webuser.getUid(req)
"""
recID = wash_url_argument(recID, 'int')
client_ip_address = wash_url_argument(client_ip_address, 'str')
uid = wash_url_argument(uid, 'int')
max_action_time = time.time() - CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS
max_action_time = convert_datestruct_to_datetext(time.localtime(max_action_time))
action_code = CFG_WEBCOMMENT_ACTION_CODE['ADD_COMMENT']
query = """SELECT id_bibrec
FROM cmtACTIONHISTORY
WHERE id_bibrec=%s AND
action_code=%s AND
action_time>%s
"""
params = (recID, action_code, max_action_time)
if uid < 0:
query += " AND client_host=inet_aton(%s)"
params += (client_ip_address,)
else:
query += " AND id_user=%s"
params += (uid,)
res = run_sql(query, params)
return len(res) == 0
def check_user_can_review(recID, client_ip_address, uid=-1):
""" Check if a user hasn't already reviewed within the last seconds
time limit: CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_REVIEWS_IN_SECONDS
@param recID: record ID
@param client_ip_address: IP => use: str(req.remote_ip)
@param uid: user id, as given by invenio.webuser.getUid(req)
"""
action_code = CFG_WEBCOMMENT_ACTION_CODE['ADD_REVIEW']
query = """SELECT id_bibrec
FROM cmtACTIONHISTORY
WHERE id_bibrec=%s AND
action_code=%s
"""
params = (recID, action_code)
if uid < 0:
query += " AND client_host=inet_aton(%s)"
params += (client_ip_address,)
else:
query += " AND id_user=%s"
params += (uid,)
res = run_sql(query, params)
return len(res) == 0
def check_user_can_vote(cmt_id, client_ip_address, uid=-1):
""" Checks if a user hasn't already voted
@param cmt_id: comment id
@param client_ip_address: IP => use: str(req.remote_ip)
@param uid: user id, as given by invenio.webuser.getUid(req)
"""
cmt_id = wash_url_argument(cmt_id, 'int')
client_ip_address = wash_url_argument(client_ip_address, 'str')
uid = wash_url_argument(uid, 'int')
query = """SELECT id_cmtRECORDCOMMENT
FROM cmtACTIONHISTORY
WHERE id_cmtRECORDCOMMENT=%s"""
params = (cmt_id,)
if uid < 0:
query += " AND client_host=inet_aton(%s)"
params += (client_ip_address,)
else:
query += " AND id_user=%s"
params += (uid, )
res = run_sql(query, params)
return (len(res) == 0)
def get_comment_collection(cmt_id):
"""
Extract the collection where the comment is written
"""
query = "SELECT id_bibrec FROM cmtRECORDCOMMENT WHERE id=%s"
recid = run_sql(query, (cmt_id,))
record_primary_collection = guess_primary_collection_of_a_record(recid[0][0])
return record_primary_collection
def get_collection_moderators(collection):
"""
Return the list of comment moderators for the given collection.
"""
from invenio.access_control_engine import acc_get_authorized_emails
res = list(acc_get_authorized_emails('moderatecomments', collection=collection))
if not res:
return [CFG_WEBCOMMENT_DEFAULT_MODERATOR,]
return res
def perform_request_report(cmt_id, client_ip_address, uid=-1):
"""
Report a comment/review for inappropriate content.
Will send an email to the administrator if number of reports is a multiple of CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN
@param cmt_id: comment id
@return: integer 1 if successful, integer 0 if not. -2 if comment does not exist
"""
cmt_id = wash_url_argument(cmt_id, 'int')
if cmt_id <= 0:
return 0
(query_res, nb_abuse_reports) = query_record_report_this(cmt_id)
if query_res == 0:
return 0
elif query_res == -2:
return -2
if not(check_user_can_report(cmt_id, client_ip_address, uid)):
return 0
action_date = convert_datestruct_to_datetext(time.localtime())
action_code = CFG_WEBCOMMENT_ACTION_CODE['REPORT_ABUSE']
query = """INSERT INTO cmtACTIONHISTORY (id_cmtRECORDCOMMENT, id_bibrec,
id_user, client_host, action_time, action_code)
VALUES (%s, NULL, %s, inet_aton(%s), %s, %s)"""
params = (cmt_id, uid, client_ip_address, action_date, action_code)
run_sql(query, params)
if nb_abuse_reports % CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN == 0:
(cmt_id2,
id_bibrec,
id_user,
cmt_body,
cmt_date,
cmt_star,
cmt_vote, cmt_nb_votes_total,
cmt_title,
cmt_reported,
round_name,
restriction) = query_get_comment(cmt_id)
(user_nb_abuse_reports,
user_votes,
user_nb_votes_total) = query_get_user_reports_and_votes(int(id_user))
(nickname, user_email, last_login) = query_get_user_contact_info(id_user)
from_addr = '%s Alert Engine <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL)
comment_collection = get_comment_collection(cmt_id)
to_addrs = get_collection_moderators(comment_collection)
subject = "A comment has been reported as inappropriate by a user"
body = '''
The following comment has been reported a total of %(cmt_reported)s times.
Author: nickname = %(nickname)s
email = %(user_email)s
user_id = %(uid)s
This user has:
total number of reports = %(user_nb_abuse_reports)s
%(votes)s
Comment: comment_id = %(cmt_id)s
record_id = %(id_bibrec)s
date written = %(cmt_date)s
nb reports = %(cmt_reported)s
%(review_stuff)s
body =
---start body---
%(cmt_body)s
---end body---
Please go to the record page %(comment_admin_link)s to delete this message if necessary. A warning will be sent to the user in question.''' % \
{ 'cfg-report_max' : CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN,
'nickname' : nickname,
'user_email' : user_email,
'uid' : id_user,
'user_nb_abuse_reports' : user_nb_abuse_reports,
'user_votes' : user_votes,
'votes' : CFG_WEBCOMMENT_ALLOW_REVIEWS and \
"total number of positive votes\t= %s\n\t\ttotal number of negative votes\t= %s" % \
(user_votes, (user_nb_votes_total - user_votes)) or "\n",
'cmt_id' : cmt_id,
'id_bibrec' : id_bibrec,
'cmt_date' : cmt_date,
'cmt_reported' : cmt_reported,
'review_stuff' : CFG_WEBCOMMENT_ALLOW_REVIEWS and \
"star score\t= %s\n\treview title\t= %s" % (cmt_star, cmt_title) or "",
'cmt_body' : cmt_body,
- 'comment_admin_link' : CFG_SITE_URL + "/record/" + str(id_bibrec) + '/comments#' + str(cmt_id),
+ 'comment_admin_link' : CFG_SITE_URL + "/"+ CFG_SITE_RECORD +"/" + str(id_bibrec) + '/comments#' + str(cmt_id),
'user_admin_link' : "user_admin_link" #! FIXME
}
#FIXME to be added to email when websession module is over:
#If you wish to ban the user, you can do so via the User Admin Panel %(user_admin_link)s.
send_email(from_addr, to_addrs, subject, body)
return 1
def check_user_can_report(cmt_id, client_ip_address, uid=-1):
""" Checks if a user hasn't already reported a comment
@param cmt_id: comment id
@param client_ip_address: IP => use: str(req.remote_ip)
@param uid: user id, as given by invenio.webuser.getUid(req)
"""
cmt_id = wash_url_argument(cmt_id, 'int')
client_ip_address = wash_url_argument(client_ip_address, 'str')
uid = wash_url_argument(uid, 'int')
query = """SELECT id_cmtRECORDCOMMENT
FROM cmtACTIONHISTORY
WHERE id_cmtRECORDCOMMENT=%s"""
params = (uid,)
if uid < 0:
query += " AND client_host=inet_aton(%s)"
params += (client_ip_address,)
else:
query += " AND id_user=%s"
params += (uid,)
res = run_sql(query, params)
return (len(res) == 0)
def query_get_user_contact_info(uid):
"""
Get the user contact information
@return: tuple (nickname, email, last_login), if none found return ()
Note: for the moment, if no nickname, will return email address up to the '@'
"""
query1 = """SELECT nickname, email,
DATE_FORMAT(last_login, '%%Y-%%m-%%d %%H:%%i:%%s')
FROM user WHERE id=%s"""
params1 = (uid,)
res1 = run_sql(query1, params1)
if res1:
return res1[0]
else:
return ()
def query_get_user_reports_and_votes(uid):
"""
Retrieve total number of reports and votes of a particular user
@param uid: user id
@return: tuple (total_nb_reports, total_nb_votes_yes, total_nb_votes_total)
if none found return ()
"""
query1 = """SELECT nb_votes_yes,
nb_votes_total,
nb_abuse_reports
FROM cmtRECORDCOMMENT
WHERE id_user=%s"""
params1 = (uid,)
res1 = run_sql(query1, params1)
if len(res1) == 0:
return ()
nb_votes_yes = nb_votes_total = nb_abuse_reports = 0
for cmt_tuple in res1:
nb_votes_yes += int(cmt_tuple[0])
nb_votes_total += int(cmt_tuple[1])
nb_abuse_reports += int(cmt_tuple[2])
return (nb_abuse_reports, nb_votes_yes, nb_votes_total)
def query_get_comment(comID):
"""
Get all fields of a comment
@param comID: comment id
@return: tuple (comID, id_bibrec, id_user, body, date_creation, star_score, nb_votes_yes, nb_votes_total, title, nb_abuse_reports, round_name, restriction)
if none found return ()
"""
query1 = """SELECT id,
id_bibrec,
id_user,
body,
DATE_FORMAT(date_creation, '%%Y-%%m-%%d %%H:%%i:%%s'),
star_score,
nb_votes_yes,
nb_votes_total,
title,
nb_abuse_reports,
round_name,
restriction
FROM cmtRECORDCOMMENT
WHERE id=%s"""
params1 = (comID,)
res1 = run_sql(query1, params1)
if len(res1)>0:
return res1[0]
else:
return ()
def query_record_report_this(comID):
"""
Increment the number of reports for a comment
@param comID: comment id
@return: tuple (success, new_total_nb_reports_for_this_comment) where
success is integer 1 if success, integer 0 if not, -2 if comment does not exist
"""
#retrieve nb_abuse_reports
query1 = "SELECT nb_abuse_reports FROM cmtRECORDCOMMENT WHERE id=%s"
params1 = (comID,)
res1 = run_sql(query1, params1)
if len(res1) == 0:
return (-2, 0)
#increment and update
nb_abuse_reports = int(res1[0][0]) + 1
query2 = "UPDATE cmtRECORDCOMMENT SET nb_abuse_reports=%s WHERE id=%s"
params2 = (nb_abuse_reports, comID)
res2 = run_sql(query2, params2)
return (int(res2), nb_abuse_reports)
def query_record_useful_review(comID, value):
"""
private funciton
Adjust the number of useful votes and number of total votes for a comment.
@param comID: comment id
@param value: +1 or -1
@return: integer 1 if successful, integer 0 if not
"""
# retrieve nb_useful votes
query1 = "SELECT nb_votes_total, nb_votes_yes FROM cmtRECORDCOMMENT WHERE id=%s"
params1 = (comID,)
res1 = run_sql(query1, params1)
if len(res1)==0:
return 0
# modify and insert new nb_useful votes
nb_votes_yes = int(res1[0][1])
if value >= 1:
nb_votes_yes = int(res1[0][1]) + 1
nb_votes_total = int(res1[0][0]) + 1
query2 = "UPDATE cmtRECORDCOMMENT SET nb_votes_total=%s, nb_votes_yes=%s WHERE id=%s"
params2 = (nb_votes_total, nb_votes_yes, comID)
res2 = run_sql(query2, params2)
return int(res2)
def query_retrieve_comments_or_remarks(recID, display_order='od', display_since='0000-00-00 00:00:00',
ranking=0, limit='all', user_info=None):
"""
Private function
Retrieve tuple of comments or remarks from the database
@param recID: record id
@param display_order: hh = highest helpful score
lh = lowest helpful score
hs = highest star score
ls = lowest star score
od = oldest date
nd = newest date
@param display_since: datetime, e.g. 0000-00-00 00:00:00
@param ranking: boolean, enabled if reviews, disabled for comments
@param limit: number of comments/review to return
@return: tuple of comment where comment is
tuple (nickname, uid, date_creation, body, status, id) if ranking disabled or
tuple (nickname, uid, date_creation, body, status, nb_votes_yes, nb_votes_total, star_score, title, id)
Note: for the moment, if no nickname, will return email address up to '@'
"""
display_since = calculate_start_date(display_since)
order_dict = { 'hh' : "cmt.nb_votes_yes/(cmt.nb_votes_total+1) DESC, cmt.date_creation DESC ",
'lh' : "cmt.nb_votes_yes/(cmt.nb_votes_total+1) ASC, cmt.date_creation ASC ",
'ls' : "cmt.star_score ASC, cmt.date_creation DESC ",
'hs' : "cmt.star_score DESC, cmt.date_creation DESC ",
'nd' : "cmt.reply_order_cached_data DESC ",
'od' : "cmt.reply_order_cached_data ASC "
}
# Ranking only done for comments and when allowed
if ranking and recID > 0:
try:
display_order = order_dict[display_order]
except:
display_order = order_dict['od']
else:
# in case of recID > 0 => external record => no ranking!
ranking = 0
try:
if display_order[-1] == 'd':
display_order = order_dict[display_order]
else:
display_order = order_dict['od']
except:
display_order = order_dict['od']
#display_order = order_dict['nd']
query = """SELECT user.nickname,
cmt.id_user,
DATE_FORMAT(cmt.date_creation, '%%%%Y-%%%%m-%%%%d %%%%H:%%%%i:%%%%s'),
cmt.body,
cmt.status,
cmt.nb_abuse_reports,
%(ranking)s cmt.id,
cmt.round_name,
cmt.restriction,
%(reply_to_column)s
FROM cmtRECORDCOMMENT cmt LEFT JOIN user ON
user.id=cmt.id_user
WHERE cmt.id_bibrec=%%s
%(ranking_only)s
%(display_since)s
ORDER BY %(display_order)s
""" % {'ranking' : ranking and ' cmt.nb_votes_yes, cmt.nb_votes_total, cmt.star_score, cmt.title, ' or '',
'ranking_only' : ranking and ' AND cmt.star_score>0 ' or ' AND cmt.star_score=0 ',
# 'id_bibrec' : recID > 0 and 'cmt.id_bibrec' or 'cmt.id_bibrec_or_bskEXTREC',
# 'table' : recID > 0 and 'cmtRECORDCOMMENT' or 'bskRECORDCOMMENT',
'display_since' : display_since == '0000-00-00 00:00:00' and ' ' or 'AND cmt.date_creation>=\'%s\' ' % display_since,
'display_order': display_order,
'reply_to_column': recID > 0 and 'cmt.in_reply_to_id_cmtRECORDCOMMENT' or 'cmt.in_reply_to_id_bskRECORDCOMMENT'}
params = (recID,)
res = run_sql(query, params)
# return res
new_limit = limit
comments_list = []
for row in res:
if ranking:
# when dealing with reviews, row[12] holds restriction info:
restriction = row[12]
else:
# when dealing with comments, row[8] holds restriction info:
restriction = row[8]
if user_info and check_user_can_view_comment(user_info, None, restriction)[0] != 0:
# User cannot view comment. Look further
continue
comments_list.append(row)
if limit.isdigit():
new_limit -= 1
if limit < 1:
break
if comments_list:
if limit.isdigit():
return comments_list[:limit]
else:
return comments_list
return ()
## def get_comment_children(comID):
## """
## Returns the list of children (i.e. direct descendants) ordered by time of addition.
## @param comID: the ID of the comment for which we want to retrieve children
## @type comID: int
## @return the list of children
## @rtype: list
## """
## res = run_sql("SELECT id FROM cmtRECORDCOMMENT WHERE in_reply_to_id_cmtRECORDCOMMENT=%s", (comID,))
## return [row[0] for row in res]
## def get_comment_descendants(comID, depth=None):
## """
## Returns the list of descendants of the given comment, orderd from
## oldest to newest ("top-down"), down to depth specified as parameter.
## @param comID: the ID of the comment for which we want to retrieve descendant
## @type comID: int
## @param depth: the max depth down to which we want to retrieve
## descendants. Specify None for no limit, 1 for direct
## children only, etc.
## @return the list of ancestors
## @rtype: list(tuple(comment ID, descendants comments IDs))
## """
## if depth == 0:
## return (comID, [])
## res = run_sql("SELECT id FROM cmtRECORDCOMMENT WHERE in_reply_to_id_cmtRECORDCOMMENT=%s", (comID,))
## if res:
## children_comID = [row[0] for row in res]
## children_descendants = []
## if depth:
## depth -= 1
## children_descendants = [get_comment_descendants(child_comID, depth) for child_comID in children_comID]
## return (comID, children_descendants)
## else:
## return (comID, [])
def get_comment_ancestors(comID, depth=None):
"""
Returns the list of ancestors of the given comment, ordered from
oldest to newest ("top-down": direct parent of comID is at last position),
up to given depth
@param comID: the ID of the comment for which we want to retrieve ancestors
@type comID: int
@param depth: the maximum of levels up from the given comment we
want to retrieve ancestors. None for no limit, 1 for
direct parent only, etc.
@type depth: int
@return the list of ancestors
@rtype: list
"""
if depth == 0:
return []
res = run_sql("SELECT in_reply_to_id_cmtRECORDCOMMENT FROM cmtRECORDCOMMENT WHERE id=%s", (comID,))
if res:
parent_comID = res[0][0]
if parent_comID == 0:
return []
parent_ancestors = []
if depth:
depth -= 1
parent_ancestors = get_comment_ancestors(parent_comID, depth)
parent_ancestors.append(parent_comID)
return parent_ancestors
else:
return []
def get_reply_order_cache_data(comid):
"""
Prepare a representation of the comment ID given as parameter so
that it is suitable for byte ordering in MySQL.
"""
return "%s%s%s%s" % (chr((comid >> 24) % 256), chr((comid >> 16) % 256),
chr((comid >> 8) % 256), chr(comid % 256))
def query_add_comment_or_remark(reviews=0, recID=0, uid=-1, msg="",
note="", score=0, priority=0,
client_ip_address='', editor_type='textarea',
req=None, reply_to=None, attached_files=None):
"""
Private function
Insert a comment/review or remarkinto the database
@param recID: record id
@param uid: user id
@param msg: comment body
@param note: comment title
@param score: review star score
@param priority: remark priority #!FIXME
@param editor_type: the kind of editor used to submit the comment: 'textarea', 'fckeditor'
@param req: request object. If provided, email notification are sent after we reply to user request.
@param reply_to: the id of the comment we are replying to with this inserted comment.
@return: integer >0 representing id if successful, integer 0 if not
"""
current_date = calculate_start_date('0d')
#change utf-8 message into general unicode
msg = msg.decode('utf-8')
note = note.decode('utf-8')
#change general unicode back to utf-8
msg = msg.encode('utf-8')
note = note.encode('utf-8')
(restriction, round_name) = get_record_status(recID)
if attached_files is None:
attached_files = {}
if reply_to and CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH >= 0:
# Check that we have not reached max depth
comment_ancestors = get_comment_ancestors(reply_to)
if len(comment_ancestors) >= CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH:
if CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH == 0:
reply_to = None
else:
reply_to = comment_ancestors[CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH - 1]
# Inherit restriction and group/round of 'parent'
comment = query_get_comment(reply_to)
if comment:
(round_name, restriction) = comment[10:12]
if editor_type == 'fckeditor':
# Here we remove the line feeds introduced by FCKeditor (they
# have no meaning for the user) and replace the HTML line
# breaks by linefeeds, so that we are close to an input that
# would be done without the FCKeditor. That's much better if a
# reply to a comment is made with a browser that does not
# support FCKeditor.
msg = msg.replace('\n', '').replace('\r', '')
msg = re.sub('<br .*?(/>)', '\n', msg)
# We clean the quotes that could have been introduced by
# FCKeditor when clicking the 'quote' button, as well as those
# that we have introduced when quoting the original message
msg = re.sub('<blockquote\s*>\s*<div.*?>', '>>', msg)
msg = re.sub('</div>\s*</blockquote>', '', msg)
msg = msg.replace('&nbsp;', ' ')
# In case additional <p> or <div> got inserted, interpret
# these as new lines (with a sad trick to do it only once)
msg = msg.replace('</div><', '</div>\n<')
msg = msg.replace('</p><', '</p>\n<')
query = """INSERT INTO cmtRECORDCOMMENT (id_bibrec,
id_user,
body,
date_creation,
star_score,
nb_votes_total,
title,
round_name,
restriction,
in_reply_to_id_cmtRECORDCOMMENT)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
params = (recID, uid, msg, current_date, score, 0, note, round_name, restriction, reply_to or 0)
res = run_sql(query, params)
if res:
new_comid = int(res)
move_attached_files_to_storage(attached_files, recID, new_comid)
parent_reply_order = run_sql("""SELECT reply_order_cached_data from cmtRECORDCOMMENT where id=%s""", (reply_to,))
if not parent_reply_order or parent_reply_order[0][0] is None:
# This is not a reply, but a first 0-level comment
parent_reply_order = ''
else:
parent_reply_order = parent_reply_order[0][0]
run_sql("""UPDATE cmtRECORDCOMMENT SET reply_order_cached_data=%s WHERE id=%s""",
(parent_reply_order + get_reply_order_cache_data(new_comid), new_comid))
action_code = CFG_WEBCOMMENT_ACTION_CODE[reviews and 'ADD_REVIEW' or 'ADD_COMMENT']
action_time = convert_datestruct_to_datetext(time.localtime())
query2 = """INSERT INTO cmtACTIONHISTORY (id_cmtRECORDCOMMENT,
id_bibrec, id_user, client_host, action_time, action_code)
VALUES (%s, %s, %s, inet_aton(%s), %s, %s)"""
params2 = (res, recID, uid, client_ip_address, action_time, action_code)
run_sql(query2, params2)
def notify_subscribers_callback(data):
"""
Define a callback that retrieves subscribed users, and
notify them by email.
@param data: contains the necessary parameters in a tuple:
(recid, uid, comid, msg, note, score, editor_type, reviews)
"""
recid, uid, comid, msg, note, score, editor_type, reviews = data
# Email this comment to 'subscribers'
(subscribers_emails1, subscribers_emails2) = \
get_users_subscribed_to_discussion(recid)
email_subscribers_about_new_comment(recid, reviews=reviews,
emails1=subscribers_emails1,
emails2=subscribers_emails2,
comID=comid, msg=msg,
note=note, score=score,
editor_type=editor_type, uid=uid)
# Register our callback to notify subscribed people after
# having replied to our current user.
data = (recID, uid, res, msg, note, score, editor_type, reviews)
if req:
req.register_cleanup(notify_subscribers_callback, data)
else:
notify_subscribers_callback(data)
return int(res)
def move_attached_files_to_storage(attached_files, recID, comid):
"""
Move the files that were just attached to a new comment to their
final location.
@param attached_files: the mappings of desired filename to attach
and path where to find the original file
@type attached_files: dict {filename, filepath}
@param recID: the record ID to which we attach the files
@param comid: the comment ID to which we attach the files
"""
for filename, filepath in attached_files.iteritems():
os.renames(filepath,
os.path.join(CFG_PREFIX, 'var', 'data', 'comments',
str(recID), str(comid), filename))
def get_attached_files(recid, comid):
"""
Returns a list with tuples (filename, filepath, fileurl)
@param recid: the recid to which the comment belong
@param comid: the commment id for which we want to retrieve files
"""
base_dir = os.path.join(CFG_PREFIX, 'var', 'data', 'comments',
str(recid), str(comid))
if os.path.isdir(base_dir):
filenames = os.listdir(base_dir)
return [(filename, os.path.join(CFG_PREFIX, 'var', 'data', 'comments',
str(recid), str(comid), filename),
- CFG_SITE_URL + '/record/' + str(recid) + '/comments/attachments/get/' + str(comid) + '/' + filename) \
+ CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recid) + '/comments/attachments/get/' + str(comid) + '/' + filename) \
for filename in filenames]
else:
return []
def subscribe_user_to_discussion(recID, uid):
"""
Subscribe a user to a discussion, so the she receives by emails
all new new comments for this record.
@param recID: record ID corresponding to the discussion we want to
subscribe the user
@param uid: user id
"""
query = """INSERT INTO cmtSUBSCRIPTION (id_bibrec, id_user, creation_time)
VALUES (%s, %s, %s)"""
params = (recID, uid, convert_datestruct_to_datetext(time.localtime()))
try:
run_sql(query, params)
except:
return 0
return 1
def unsubscribe_user_from_discussion(recID, uid):
"""
Unsubscribe users from a discussion.
@param recID: record ID corresponding to the discussion we want to
unsubscribe the user
@param uid: user id
@return 1 if successful, 0 if not
"""
query = """DELETE FROM cmtSUBSCRIPTION
WHERE id_bibrec=%s AND id_user=%s"""
params = (recID, uid)
try:
res = run_sql(query, params)
except:
return 0
if res > 0:
return 1
return 0
def get_user_subscription_to_discussion(recID, uid):
"""
Returns the type of subscription for the given user to this
discussion. This does not check authorizations (for eg. if user
was subscribed, but is suddenly no longer authorized).
@param recID: record ID
@param uid: user id
@return:
- 0 if user is not subscribed to discussion
- 1 if user is subscribed, and is allowed to unsubscribe
- 2 if user is subscribed, but cannot unsubscribe
"""
user_email = get_email(uid)
(emails1, emails2) = get_users_subscribed_to_discussion(recID, check_authorizations=False)
if user_email in emails1:
return 1
elif user_email in emails2:
return 2
else:
return 0
def get_users_subscribed_to_discussion(recID, check_authorizations=True):
"""
Returns the lists of users subscribed to a given discussion.
Two lists are returned: the first one is the list of emails for
users who can unsubscribe from the discussion, the second list
contains the emails of users who cannot unsubscribe (for eg. author
of the document, etc).
Users appear in only one list. If a user has manually subscribed
to a discussion AND is an automatic recipients for updates, it
will only appear in the second list.
@param recID: record ID for which we want to retrieve subscribed users
@param check_authorizations: if True, check again if users are authorized to view comment
@return tuple (emails1, emails2)
"""
subscribers_emails = {}
# Get users that have subscribed to this discussion
query = """SELECT id_user FROM cmtSUBSCRIPTION WHERE id_bibrec=%s"""
params = (recID,)
res = run_sql(query, params)
for row in res:
uid = row[0]
if check_authorizations:
user_info = collect_user_info(uid)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, recID)
else:
# Don't check and grant access
auth_code = False
if auth_code:
# User is no longer authorized to view comments.
# Delete subscription
unsubscribe_user_from_discussion(recID, uid)
else:
email = get_email(uid)
if '@' in email:
subscribers_emails[email] = True
# Get users automatically subscribed, based on the record metadata
collections_with_auto_replies = CFG_WEBCOMMENT_EMAIL_REPLIES_TO.keys()
for collection in collections_with_auto_replies:
if (get_colID(collection) is not None) and \
(recID in get_collection_reclist(collection)):
fields = CFG_WEBCOMMENT_EMAIL_REPLIES_TO[collection]
for field in fields:
emails = get_fieldvalues(recID, field)
for email in emails:
if not '@' in email:
# Is a group: add domain name
subscribers_emails[email + '@' + \
CFG_SITE_SUPPORT_EMAIL.split('@')[1]] = False
else:
subscribers_emails[email] = False
return ([email for email, can_unsubscribe_p \
in subscribers_emails.iteritems() if can_unsubscribe_p],
[email for email, can_unsubscribe_p \
in subscribers_emails.iteritems() if not can_unsubscribe_p] )
def email_subscribers_about_new_comment(recID, reviews, emails1,
emails2, comID, msg="",
note="", score=0,
editor_type='textarea',
ln=CFG_SITE_LANG, uid=-1):
"""
Notify subscribers that a new comment was posted.
FIXME: consider recipient preference to send email in correct language.
@param recID: record id
@param emails1: list of emails for users who can unsubscribe from discussion
@param emails2: list of emails for users who cannot unsubscribe from discussion
@param comID: the comment id
@param msg: comment body
@param note: comment title
@param score: review star score
@param editor_type: the kind of editor used to submit the comment: 'textarea', 'fckeditor'
@rtype: bool
@return: True if email was sent okay, False if it was not.
"""
_ = gettext_set_language(ln)
if not emails1 and not emails2:
return 0
# Get title
titles = get_fieldvalues(recID, "245__a")
if not titles:
# usual title not found, try conference title:
titles = get_fieldvalues(recID, "111__a")
title = ''
if titles:
title = titles[0]
else:
title = _("Record %i") % recID
# Get report number
report_numbers = get_fieldvalues(recID, "037__a")
if not report_numbers:
report_numbers = get_fieldvalues(recID, "088__a")
if not report_numbers:
report_numbers = get_fieldvalues(recID, "021__a")
# Prepare email subject and body
if reviews:
email_subject = _('%(report_number)s"%(title)s" has been reviewed') % \
{'report_number': report_numbers and ('[' + report_numbers[0] + '] ') or '',
'title': title}
else:
email_subject = _('%(report_number)s"%(title)s" has been commented') % \
{'report_number': report_numbers and ('[' + report_numbers[0] + '] ') or '',
'title': title}
washer = EmailWasher()
msg = washer.wash(msg)
msg = msg.replace('&gt;&gt;', '>')
email_content = msg
if note:
email_content = note + email_content
# Send emails to people who can unsubscribe
email_header = webcomment_templates.tmpl_email_new_comment_header(recID,
title,
reviews,
comID,
report_numbers,
can_unsubscribe=True,
ln=ln,
uid=uid)
email_footer = webcomment_templates.tmpl_email_new_comment_footer(recID,
title,
reviews,
comID,
report_numbers,
can_unsubscribe=True,
ln=ln)
res1 = True
if emails1:
res1 = send_email(fromaddr=CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL,
toaddr=emails1,
subject=email_subject,
content=email_content,
header=email_header,
footer=email_footer,
ln=ln)
# Then send email to people who have been automatically
# subscribed to the discussion (they cannot unsubscribe)
email_header = webcomment_templates.tmpl_email_new_comment_header(recID,
title,
reviews,
comID,
report_numbers,
can_unsubscribe=False,
ln=ln,
uid=uid)
email_footer = webcomment_templates.tmpl_email_new_comment_footer(recID,
title,
reviews,
comID,
report_numbers,
can_unsubscribe=False,
ln=ln)
res2 = True
if emails2:
res2 = send_email(fromaddr=CFG_WEBCOMMENT_ALERT_ENGINE_EMAIL,
toaddr=emails2,
subject=email_subject,
content=email_content,
header=email_header,
footer=email_footer,
ln=ln)
return res1 and res2
def get_record_status(recid):
"""
Returns the current status of the record, i.e. current restriction to apply for newly submitted
comments, and current commenting round.
The restriction to apply can be found in the record metadata, in
field(s) defined by config CFG_WEBCOMMENT_RESTRICTION_DATAFIELD. The restriction is empty string ""
in cases where the restriction has not explicitely been set, even
if the record itself is restricted.
@param recid: the record id
@type recid: int
@return tuple(restriction, round_name), where 'restriction' is empty string when no restriction applies
@rtype (string, int)
"""
collections_with_rounds = CFG_WEBCOMMENT_ROUND_DATAFIELD.keys()
commenting_round = ""
for collection in collections_with_rounds:
# Find the first collection defines rounds field for this
# record
if get_colID(collection) is not None and \
(recid in get_collection_reclist(collection)):
commenting_rounds = get_fieldvalues(recid, CFG_WEBCOMMENT_ROUND_DATAFIELD.get(collection, ""))
if commenting_rounds:
commenting_round = commenting_rounds[0]
break
collections_with_restrictions = CFG_WEBCOMMENT_RESTRICTION_DATAFIELD.keys()
restriction = ""
for collection in collections_with_restrictions:
# Find the first collection that defines restriction field for
# this record
if get_colID(collection) is not None and \
recid in get_collection_reclist(collection):
restrictions = get_fieldvalues(recid, CFG_WEBCOMMENT_RESTRICTION_DATAFIELD.get(collection, ""))
if restrictions:
restriction = restrictions[0]
break
return (restriction, commenting_round)
def calculate_start_date(display_since):
"""
Private function
Returns the datetime of display_since argument in MYSQL datetime format
calculated according to the local time.
@param display_since: = all= no filtering
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit number
@return: string of wanted datetime.
If 'all' given as argument, will return datetext_default
datetext_default is defined in miscutils/lib/dateutils and
equals 0000-00-00 00:00:00 => MySQL format
If bad arguement given, will return datetext_default
If library 'dateutil' is not found return datetext_default
and register exception.
"""
time_types = {'d':0, 'w':0, 'm':0, 'y':0}
today = datetime.today()
try:
nb = int(display_since[:-1])
except:
return datetext_default
if display_since in [None, 'all']:
return datetext_default
if str(display_since[-1]) in time_types:
time_type = str(display_since[-1])
else:
return datetext_default
# year
if time_type == 'y':
if (int(display_since[:-1]) > today.year - 1) or (int(display_since[:-1]) < 1):
# 1 < nb years < 2008
return datetext_default
else:
final_nb_year = today.year - nb
yesterday = today.replace(year=final_nb_year)
# month
elif time_type == 'm':
try:
from dateutil.relativedelta import relativedelta
except ImportError:
# The dateutil library is only recommended: if not
# available, then send warning about this.
register_exception(alert_admin=True)
return datetext_default
# obtain only the date: yyyy-mm-dd
date_today = datetime.now().date()
final_date = date_today - relativedelta(months=nb)
yesterday = today.replace(year=final_date.year, month=final_date.month, day=final_date.day)
# week
elif time_type == 'w':
delta = timedelta(weeks=nb)
yesterday = today - delta
# day
elif time_type == 'd':
delta = timedelta(days=nb)
yesterday = today - delta
return yesterday.strftime("%Y-%m-%d %H:%M:%S")
def count_comments(recID):
"""
Returns the number of comments made on a record.
"""
recID = int(recID)
query = """SELECT count(id) FROM cmtRECORDCOMMENT
WHERE id_bibrec=%s AND star_score=0"""
return run_sql(query, (recID,))[0][0]
def count_reviews(recID):
"""
Returns the number of reviews made on a record.
"""
recID = int(recID)
query = """SELECT count(id) FROM cmtRECORDCOMMENT
WHERE id_bibrec=%s AND star_score>0"""
return run_sql(query, (recID,))[0][0]
def get_first_comments_or_remarks(recID=-1,
ln=CFG_SITE_LANG,
nb_comments='all',
nb_reviews='all',
voted=-1,
reported=-1,
user_info=None):
"""
Gets nb number comments/reviews or remarks.
In the case of comments, will get both comments and reviews
Comments and remarks sorted by most recent date, reviews sorted by highest helpful score
@param recID: record id
@param ln: language
@param nb_comments: number of comment or remarks to get
@param nb_reviews: number of reviews or remarks to get
@param voted: 1 if user has voted for a remark
@param reported: 1 if user has reported a comment or review
@return: if comment, tuple (comments, reviews) both being html of first nb comments/reviews
if remark, tuple (remakrs, None)
"""
warnings = []
errors = []
voted = wash_url_argument(voted, 'int')
reported = wash_url_argument(reported, 'int')
## check recID argument
if type(recID) is not int:
return ()
if recID >= 1: #comment or review. NB: suppressed reference to basket (handled in webbasket)
if CFG_WEBCOMMENT_ALLOW_REVIEWS:
res_reviews = query_retrieve_comments_or_remarks(recID=recID, display_order="hh", ranking=1,
limit=nb_comments, user_info=user_info)
nb_res_reviews = len(res_reviews)
## check nb argument
if type(nb_reviews) is int and nb_reviews < len(res_reviews):
first_res_reviews = res_reviews[:nb_reviews]
else:
first_res_reviews = res_reviews
if CFG_WEBCOMMENT_ALLOW_COMMENTS:
res_comments = query_retrieve_comments_or_remarks(recID=recID, display_order="od", ranking=0,
limit=nb_reviews, user_info=user_info)
nb_res_comments = len(res_comments)
## check nb argument
if type(nb_comments) is int and nb_comments < len(res_comments):
first_res_comments = res_comments[:nb_comments]
else:
first_res_comments = res_comments
else: #error
errors.append(('ERR_WEBCOMMENT_RECID_INVALID', recID)) #!FIXME dont return error anywhere since search page
# comment
if recID >= 1:
comments = reviews = ""
if reported > 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED_GREEN_TEXT',))
elif reported == 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_NOT_RECORDED_RED_TEXT',))
if CFG_WEBCOMMENT_ALLOW_COMMENTS: # normal comments
grouped_comments = group_comments_by_round(first_res_comments, ranking=0)
comments = webcomment_templates.tmpl_get_first_comments_without_ranking(recID, ln, grouped_comments, nb_res_comments, warnings)
if CFG_WEBCOMMENT_ALLOW_REVIEWS: # ranked comments
#calculate average score
avg_score = calculate_avg_score(res_reviews)
if voted > 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED_GREEN_TEXT',))
elif voted == 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_NOT_RECORDED_RED_TEXT',))
grouped_reviews = group_comments_by_round(first_res_reviews, ranking=0)
reviews = webcomment_templates.tmpl_get_first_comments_with_ranking(recID, ln, grouped_reviews, nb_res_reviews, avg_score, warnings)
return (comments, reviews)
# remark
else:
return(webcomment_templates.tmpl_get_first_remarks(first_res_comments, ln, nb_res_comments), None)
def group_comments_by_round(comments, ranking=0):
"""
Group comments by the round to which they belong
"""
comment_rounds = {}
ordered_comment_round_names = []
for comment in comments:
comment_round_name = ranking and comment[11] or comment[7]
if not comment_rounds.has_key(comment_round_name):
comment_rounds[comment_round_name] = []
ordered_comment_round_names.append(comment_round_name)
comment_rounds[comment_round_name].append(comment)
return [(comment_round_name, comment_rounds[comment_round_name]) \
for comment_round_name in ordered_comment_round_names]
def calculate_avg_score(res):
"""
private function
Calculate the avg score of reviews present in res
@param res: tuple of tuple returned from query_retrieve_comments_or_remarks
@return: a float of the average score rounded to the closest 0.5
"""
c_star_score = 6
avg_score = 0.0
nb_reviews = 0
for comment in res:
if comment[c_star_score] > 0:
avg_score += comment[c_star_score]
nb_reviews += 1
if nb_reviews == 0:
return 0.0
avg_score = avg_score / nb_reviews
avg_score_unit = avg_score - math.floor(avg_score)
if avg_score_unit < 0.25:
avg_score = math.floor(avg_score)
elif avg_score_unit > 0.75:
avg_score = math.floor(avg_score) + 1
else:
avg_score = math.floor(avg_score) + 0.5
if avg_score > 5:
avg_score = 5.0
return avg_score
def perform_request_add_comment_or_remark(recID=0,
uid=-1,
action='DISPLAY',
ln=CFG_SITE_LANG,
msg=None,
score=None,
note=None,
priority=None,
reviews=0,
comID=0,
client_ip_address=None,
editor_type='textarea',
can_attach_files=False,
subscribe=False,
req=None,
attached_files=None,
warnings=None,
errors=None):
"""
Add a comment/review or remark
@param recID: record id
@param uid: user id
@param action: 'DISPLAY' to display add form
'SUBMIT' to submit comment once form is filled
'REPLY' to reply to an existing comment
@param ln: language
@param msg: the body of the comment/review or remark
@param score: star score of the review
@param note: title of the review
@param priority: priority of remark (int)
@param reviews: boolean, if enabled will add a review, if disabled will add a comment
@param comID: if replying, this is the comment id of the comment we are replying to
@param editor_type: the kind of editor/input used for the comment: 'textarea', 'fckeditor'
@param can_attach_files: if user can attach files to comments or not
@param subscribe: if True, subscribe user to receive new comments by email
@param req: request object. Used to register callback to send email notification
@param attached_files: newly attached files to this comment, mapping filename to filepath
@type attached_files: dict
@param warning_msgs: list of standard warnings that should be considered
@param errors_msgs: list of standard errors that should be considered
@return:
- html add form if action is display or reply
- html successful added form if action is submit
"""
if warnings is None:
warnings = []
if errors is None:
errors = []
actions = ['DISPLAY', 'REPLY', 'SUBMIT']
_ = gettext_set_language(ln)
## check arguments
check_recID_is_in_range(recID, warnings, ln)
if uid <= 0:
errors.append(('ERR_WEBCOMMENT_UID_INVALID', uid))
return ('', errors, warnings)
if attached_files is None:
attached_files = {}
user_contact_info = query_get_user_contact_info(uid)
nickname = ''
if user_contact_info:
if user_contact_info[0]:
nickname = user_contact_info[0]
# show the form
if action == 'DISPLAY':
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
return (webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings, can_attach_files=can_attach_files), errors, warnings)
elif not reviews and CFG_WEBCOMMENT_ALLOW_COMMENTS:
return (webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings, can_attach_files=can_attach_files), errors, warnings)
else:
errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',))
elif action == 'REPLY':
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
errors.append(('ERR_WEBCOMMENT_REPLY_REVIEW',))
return (webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings, can_attach_files=can_attach_files), errors, warnings)
elif not reviews and CFG_WEBCOMMENT_ALLOW_COMMENTS:
textual_msg = msg
if comID > 0:
comment = query_get_comment(comID)
if comment:
user_info = get_user_info(comment[2])
if user_info:
date_creation = convert_datetext_to_dategui(str(comment[4]))
# Build two msg: one mostly textual, the other one with HTML markup, for the FCKeditor.
msg = _("%(x_name)s wrote on %(x_date)s:")% {'x_name': user_info[2], 'x_date': date_creation}
textual_msg = msg
# 1 For FCKeditor input
msg += '\n\n'
msg += comment[3]
msg = email_quote_txt(text=msg)
# Now that we have a text-quoted version, transform into
# something that FCKeditor likes, using <blockquote> that
# do still enable users to insert comments inline
msg = email_quoted_txt2html(text=msg,
indent_html=('<blockquote><div>', '&nbsp;</div></blockquote>'),
linebreak_html="&nbsp;<br/>",
indent_block=False)
# Add some space for users to easily add text
# around the quoted message
msg = '<br/>' + msg + '<br/>'
# Due to how things are done, we need to
# escape the whole msg again for the editor
msg = cgi.escape(msg)
# 2 For textarea input
textual_msg += "\n\n"
textual_msg += comment[3]
textual_msg = email_quote_txt(text=textual_msg)
return (webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings, textual_msg, can_attach_files=can_attach_files, reply_to=comID), errors, warnings)
else:
errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',))
# check before submitting form
elif action == 'SUBMIT':
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
if note.strip() in ["", "None"] and not CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS:
warnings.append(('WRN_WEBCOMMENT_ADD_NO_TITLE',))
if score == 0 or score > 5:
warnings.append(("WRN_WEBCOMMENT_ADD_NO_SCORE",))
if msg.strip() in ["", "None"] and not CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS:
warnings.append(('WRN_WEBCOMMENT_ADD_NO_BODY',))
# if no warnings, submit
if len(warnings) == 0:
if reviews:
if check_user_can_review(recID, client_ip_address, uid):
success = query_add_comment_or_remark(reviews, recID=recID, uid=uid, msg=msg,
note=note, score=score, priority=0,
client_ip_address=client_ip_address,
editor_type=editor_type,
req=req,
reply_to=comID)
else:
warnings.append('WRN_WEBCOMMENT_CANNOT_REVIEW_TWICE')
success = 1
else:
if check_user_can_comment(recID, client_ip_address, uid):
success = query_add_comment_or_remark(reviews, recID=recID, uid=uid, msg=msg,
note=note, score=score, priority=0,
client_ip_address=client_ip_address,
editor_type=editor_type,
req=req,
reply_to=comID, attached_files=attached_files)
if success > 0 and subscribe:
subscribe_user_to_discussion(recID, uid)
else:
warnings.append('WRN_WEBCOMMENT_TIMELIMIT')
success = 1
if success > 0:
if CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL > 0:
notify_admin_of_new_comment(comID=success)
return (webcomment_templates.tmpl_add_comment_successful(recID, ln, reviews, warnings, success), errors, warnings)
else:
errors.append(('ERR_WEBCOMMENT_DB_INSERT_ERROR'))
# if are warnings or if inserting comment failed, show user where warnings are
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
return (webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings, can_attach_files=can_attach_files), errors, warnings)
else:
return (webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings, can_attach_files=can_attach_files), errors, warnings)
# unknown action send to display
else:
warnings.append(('WRN_WEBCOMMENT_ADD_UNKNOWN_ACTION',))
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
return (webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, ln, msg, score, note, warnings, can_attach_files=can_attach_files), errors, warnings)
else:
return (webcomment_templates.tmpl_add_comment_form(recID, uid, ln, msg, warnings, can_attach_files=can_attach_files), errors, warnings)
return ('', errors, warnings)
def notify_admin_of_new_comment(comID):
"""
Sends an email to the admin with details regarding comment with ID = comID
"""
comment = query_get_comment(comID)
if len(comment) > 0:
(comID2,
id_bibrec,
id_user,
body,
date_creation,
star_score, nb_votes_yes, nb_votes_total,
title,
nb_abuse_reports, round_name, restriction) = comment
else:
return
user_info = query_get_user_contact_info(id_user)
if len(user_info) > 0:
(nickname, email, last_login) = user_info
if not len(nickname) > 0:
nickname = email.split('@')[0]
else:
nickname = email = last_login = "ERROR: Could not retrieve"
review_stuff = '''
Star score = %s
Title = %s''' % (star_score, title)
washer = EmailWasher()
try:
body = washer.wash(body)
except:
body = cgi.escape(body)
record_info = webcomment_templates.tmpl_email_new_comment_admin(id_bibrec)
out = '''
The following %(comment_or_review)s has just been posted (%(date)s).
AUTHOR:
Nickname = %(nickname)s
Email = %(email)s
User ID = %(uid)s
RECORD CONCERNED:
Record ID = %(recID)s
- URL = <%(siteurl)s/record/%(recID)s/%(comments_or_reviews)s/>
+ URL = <%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(comments_or_reviews)s/>
%(record_details)s
%(comment_or_review_caps)s:
%(comment_or_review)s ID = %(comID)s %(review_stuff)s
Body =
<--------------->
%(body)s
<--------------->
ADMIN OPTIONS:
-To moderate the %(comment_or_review)s go to %(siteurl)s/record/%(recID)s/%(comments_or_reviews)s/display?%(arguments)s
+To moderate the %(comment_or_review)s go to %(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(comments_or_reviews)s/display?%(arguments)s
''' % \
{ 'comment_or_review' : star_score > 0 and 'review' or 'comment',
'comment_or_review_caps': star_score > 0 and 'REVIEW' or 'COMMENT',
'comments_or_reviews' : star_score > 0 and 'reviews' or 'comments',
'date' : date_creation,
'nickname' : nickname,
'email' : email,
'uid' : id_user,
'recID' : id_bibrec,
'record_details' : record_info,
'comID' : comID2,
'review_stuff' : star_score > 0 and review_stuff or "",
'body' : body.replace('<br />','\n'),
'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'arguments' : 'ln=en&do=od#%s' % comID
}
from_addr = '%s WebComment <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL)
comment_collection = get_comment_collection(comID)
to_addrs = get_collection_moderators(comment_collection)
rec_collection = guess_primary_collection_of_a_record(id_bibrec)
report_nums = get_fieldvalues(id_bibrec, "037__a")
report_nums += get_fieldvalues(id_bibrec, "088__a")
report_nums = ', '.join(report_nums)
subject = "A new comment/review has just been posted [%s|%s]" % (rec_collection, report_nums)
send_email(from_addr, to_addrs, subject, out)
def check_recID_is_in_range(recID, warnings=[], ln=CFG_SITE_LANG):
"""
Check that recID is >= 0
Append error messages to errors listi
@param recID: record id
@param warnings: the warnings list of the calling function
@return: tuple (boolean, html) where boolean (1=true, 0=false)
and html is the body of the page to display if there was a problem
"""
# Make errors into a list if needed
if type(warnings) is not list:
errors = [warnings]
try:
recID = int(recID)
except:
pass
if type(recID) is int:
if recID > 0:
from invenio.search_engine import record_exists
success = record_exists(recID)
if success == 1:
return (1,"")
else:
warnings.append(('ERR_WEBCOMMENT_RECID_INEXISTANT', recID))
return (0, webcomment_templates.tmpl_record_not_found(status='inexistant', recID=recID, ln=ln))
elif recID == 0:
warnings.append(('ERR_WEBCOMMENT_RECID_MISSING',))
return (0, webcomment_templates.tmpl_record_not_found(status='missing', recID=recID, ln=ln))
else:
warnings.append(('ERR_WEBCOMMENT_RECID_INVALID', recID))
return (0, webcomment_templates.tmpl_record_not_found(status='invalid', recID=recID, ln=ln))
else:
warnings.append(('ERR_WEBCOMMENT_RECID_NAN', recID))
return (0, webcomment_templates.tmpl_record_not_found(status='nan', recID=recID, ln=ln))
def check_int_arg_is_in_range(value, name, errors, gte_value, lte_value=None):
"""
Check that variable with name 'name' >= gte_value and optionally <= lte_value
Append error messages to errors list
@param value: variable value
@param name: variable name
@param errors: list of error tuples (error_id, value)
@param gte_value: greater than or equal to value
@param lte_value: less than or equal to value
@return: boolean (1=true, 0=false)
"""
# Make errors into a list if needed
if type(errors) is not list:
errors = [errors]
if type(value) is not int or type(gte_value) is not int:
errors.append(('ERR_WEBCOMMENT_PROGRAMNING_ERROR',))
return 0
if type(value) is not int:
errors.append(('ERR_WEBCOMMENT_ARGUMENT_NAN', value))
return 0
if value < gte_value:
errors.append(('ERR_WEBCOMMENT_ARGUMENT_INVALID', value))
return 0
if lte_value:
if type(lte_value) is not int:
errors.append(('ERR_WEBCOMMENT_PROGRAMNING_ERROR',))
return 0
if value > lte_value:
errors.append(('ERR_WEBCOMMENT_ARGUMENT_INVALID', value))
return 0
return 1
def get_mini_reviews(recid, ln=CFG_SITE_LANG):
"""
Returns the web controls to add reviews to a record from the
detailed record pages mini-panel.
@param recid: the id of the displayed record
@param ln: the user's language
"""
if CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS:
action = 'SUBMIT'
else:
action = 'DISPLAY'
reviews = query_retrieve_comments_or_remarks(recid, ranking=1)
return webcomment_templates.tmpl_mini_review(recid, ln, action=action,
avg_score=calculate_avg_score(reviews),
nb_comments_total=len(reviews))
def check_user_can_view_comments(user_info, recid):
"""Check if the user is authorized to view comments for given
recid.
Returns the same type as acc_authorize_action
"""
# Check user can view the record itself first
(auth_code, auth_msg) = check_user_can_view_record(user_info, recid)
if auth_code:
return (auth_code, auth_msg)
# Check if user can view the comments
## But first can we find an authorization for this case action,
## for this collection?
record_primary_collection = guess_primary_collection_of_a_record(recid)
return acc_authorize_action(user_info, 'viewcomment', authorized_if_no_roles=True, collection=record_primary_collection)
def check_user_can_view_comment(user_info, comid, restriction=None):
"""Check if the user is authorized to view a particular comment,
given the comment restriction. Note that this function does not
check if the record itself is restricted to the user, which would
mean that the user should not see the comment.
You can omit 'comid' if you already know the 'restriction'
@param user_info: the user info object
@param comid: the comment id of that we want to check
@param restriction: the restriction applied to given comment (if known. Otherwise retrieved automatically)
@return: the same type as acc_authorize_action
"""
if restriction is None:
comment = query_get_comment(comid)
if comment:
restriction = comment[11]
else:
return (1, 'Comment %i does not exist' % comid)
if restriction == "":
return (0, '')
return acc_authorize_action(user_info, 'viewrestrcomment', status=restriction)
def check_user_can_send_comments(user_info, recid):
"""Check if the user is authorized to comment the given
recid. This function does not check that user can view the record
or view the comments
Returns the same type as acc_authorize_action
"""
## First can we find an authorization for this case, action + collection
record_primary_collection = guess_primary_collection_of_a_record(recid)
return acc_authorize_action(user_info, 'sendcomment', authorized_if_no_roles=True, collection=record_primary_collection)
def check_user_can_attach_file_to_comments(user_info, recid):
"""Check if the user is authorized to attach a file to comments
for given recid. This function does not check that user can view
the comments or send comments.
Returns the same type as acc_authorize_action
"""
## First can we find an authorization for this case action, for
## this collection?
record_primary_collection = guess_primary_collection_of_a_record(recid)
return acc_authorize_action(user_info, 'attachcommentfile', authorized_if_no_roles=False, collection=record_primary_collection)
diff --git a/modules/webcomment/lib/webcomment_regression_tests.py b/modules/webcomment/lib/webcomment_regression_tests.py
index 884f2e828..cc4b8d2bf 100644
--- a/modules/webcomment/lib/webcomment_regression_tests.py
+++ b/modules/webcomment/lib/webcomment_regression_tests.py
@@ -1,362 +1,363 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebComment Regression Test Suite."""
__revision__ = "$Id$"
import unittest
import shutil
from mechanize import Browser, LinkNotFoundError, HTTPError
from invenio.config import \
CFG_SITE_URL, \
CFG_WEBDIR, \
- CFG_TMPDIR
+ CFG_TMPDIR, \
+ CFG_SITE_RECORD
from invenio.testutils import make_test_suite, run_test_suite, \
test_web_page_content, merge_error_messages
from invenio.dbquery import run_sql
from invenio.webcomment import query_add_comment_or_remark
def prepare_attachments():
"""
We copy necessary files to temporary directory. Every time we will
attach files to a comment, these files get moved, so this function
must be called again.
"""
shutil.copy(CFG_WEBDIR + '/img/journal_water_dog.gif', CFG_TMPDIR)
shutil.copy(CFG_WEBDIR + '/img/invenio.css', CFG_TMPDIR)
class WebCommentWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebComment web pages whether they are up or not."""
def test_your_baskets_pages_availability(self):
"""webcomment - availability of comments pages"""
- baseurl = CFG_SITE_URL + '/record/10/comments/'
+ baseurl = CFG_SITE_URL + '/%s/10/comments/' % CFG_SITE_RECORD
_exports = ['', 'display', 'add', 'vote', 'report']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_webcomment_admin_interface_availability(self):
"""webcomment - availability of WebComment Admin interface pages"""
baseurl = CFG_SITE_URL + '/admin/webcomment/webcommentadmin.py/'
_exports = ['', 'comments', 'delete', 'users']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_webcomment_admin_guide_availability(self):
"""webcomment - availability of WebComment Admin Guide"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/admin/webcomment-admin-guide',
expected_text="WebComment Admin Guide"))
return
def test_webcomment_mini_review_availability(self):
"""webcomment - availability of mini-review panel on detailed record page"""
- url = CFG_SITE_URL + '/record/12'
+ url = CFG_SITE_URL + '/%s/12' % CFG_SITE_RECORD
error_messages = test_web_page_content(url,
expected_text="(Not yet reviewed)")
class WebCommentRestrictionsTest(unittest.TestCase):
"""Check WebComment restrictions"""
def setUp(self):
"""Insert some comments in some records"""
# Comments have access restrictions when:
# - the comment is in a restricted collection ('viewrestrcoll' action)
# - the comment is in a restricted discussion page ('viewcomment' action)
# - the comment itself is restricted ('viewrestrcomment'
# action), either because of the markup of the record, or
# because it is a reply to a restricted comment.
self.public_record = 5
self.public_record_restr_comment = 6
self.restr_record = 42
self.restr_record_restr_comment = 41
self.restricted_discussion = 76
self.romeo_uid = 5
self.jekyll_uid = 2
self.attached_files = {'file1': CFG_TMPDIR + '/journal_water_dog.gif',
'file2': CFG_TMPDIR + '/invenio.css'}
# Load content of texual file2
prepare_attachments()
fp = file(self.attached_files['file2'])
self.attached_file2_content = fp.read()
fp.close()
# Insert a public comment in a public record (public collection)
self.msg1 = "A test comment 1"
self.public_comid = query_add_comment_or_remark(reviews=0, recID=self.public_record,
uid=self.romeo_uid, msg=self.msg1,
editor_type='textarea',
attached_files=self.attached_files)
# Insert a public comment in a restricted record (restricted collection)
self.msg2 = "A test comment 2"
prepare_attachments()
self.restr_comid_1 = \
query_add_comment_or_remark(reviews=0, recID=self.restr_record,
uid=self.jekyll_uid, msg=self.msg2,
editor_type='textarea',
attached_files=self.attached_files)
# Insert a restricted comment in a public collection
self.msg3 = "A test comment 3"
prepare_attachments()
self.restr_comid_2 = \
query_add_comment_or_remark(reviews=0, recID=self.public_record_restr_comment,
uid=self.jekyll_uid, msg=self.msg3,
editor_type='textarea',
attached_files=self.attached_files)
# Insert a restricted comment, in a restricted collection
self.msg5 = "A test comment 5"
prepare_attachments()
self.restr_comid_4 = \
query_add_comment_or_remark(reviews=0, recID=self.restr_record_restr_comment,
uid=self.romeo_uid, msg=self.msg5,
editor_type='textarea',
attached_files=self.attached_files)
# Insert a public comment in a restricted discussion
self.msg6 = "A test comment 6"
prepare_attachments()
self.restr_comid_5 = \
query_add_comment_or_remark(reviews=0, recID=self.restricted_discussion,
uid=self.romeo_uid, msg=self.msg6,
editor_type='textarea',
attached_files=self.attached_files)
self.restr_comid_3 = None
def tearDown(self):
"""Remove inserted comments"""
run_sql("""DELETE FROM cmtRECORDCOMMENT WHERE id=%s""", (self.public_comid,))
run_sql("""DELETE FROM cmtRECORDCOMMENT WHERE id=%s""", (self.restr_comid_1,))
run_sql("""DELETE FROM cmtRECORDCOMMENT WHERE id=%s""", (self.restr_comid_2,))
if self.restr_comid_3:
run_sql("""DELETE FROM cmtRECORDCOMMENT WHERE id=%s""", (self.restr_comid_3,))
run_sql("""DELETE FROM cmtRECORDCOMMENT WHERE id=%s""", (self.restr_comid_4,))
run_sql("""DELETE FROM cmtRECORDCOMMENT WHERE id=%s""", (self.restr_comid_5,))
pass
def test_access_public_record_public_discussion_public_comment(self):
"""webcomment - accessing "public" comment in a "public" discussion of a restricted record"""
# Guest user should not be able to access it
self.assertNotEqual([],
- test_web_page_content("%s/record/%i/comments/" % (CFG_SITE_URL, self.restr_record),
+ test_web_page_content("%s/%s/%i/comments/" % (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record),
expected_text=self.msg2))
# Accessing a non existing file for a restricted comment should also ask to login
self.assertEqual([],
- test_web_page_content("%s/record/%i/comments/attachments/get/%i/not_existing_file" % \
- (CFG_SITE_URL, self.restr_record, self.restr_comid_1),
+ test_web_page_content("%s/%s/%i/comments/attachments/get/%i/not_existing_file" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record, self.restr_comid_1),
expected_text='You can use your nickname or your email address to login'))
# Check accessing file of a restricted comment
self.assertEqual([],
- test_web_page_content("%s/record/%i/comments/attachments/get/%i/file2" % \
- (CFG_SITE_URL, self.restr_record, self.restr_comid_1),
+ test_web_page_content("%s/%s/%i/comments/attachments/get/%i/file2" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record, self.restr_comid_1),
expected_text='You can use your nickname or your email address to login'))
def test_access_restricted_record_public_discussion_public_comment(self):
"""webcomment - accessing "public" comment in a "public" discussion of a restricted record"""
# Guest user should not be able to access it
self.assertNotEqual([],
- test_web_page_content("%s/record/%i/comments/" % (CFG_SITE_URL, self.restr_record),
+ test_web_page_content("%s/%s/%i/comments/" % (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record),
expected_text=self.msg2))
# Accessing a non existing file for a restricted comment should also ask to login
self.assertEqual([],
- test_web_page_content("%s/record/%i/comments/attachments/get/%i/not_existing_file" % \
- (CFG_SITE_URL, self.restr_record, self.restr_comid_1),
+ test_web_page_content("%s/%s/%i/comments/attachments/get/%i/not_existing_file" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record, self.restr_comid_1),
expected_text='You can use your nickname or your email address to login'))
# Check accessing file of a restricted comment
self.assertEqual([],
- test_web_page_content("%s/record/%i/comments/attachments/get/%i/file2" % \
- (CFG_SITE_URL, self.restr_record, self.restr_comid_1),
+ test_web_page_content("%s/%s/%i/comments/attachments/get/%i/file2" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record, self.restr_comid_1),
expected_text='You can use your nickname or your email address to login'))
# Juliet should not be able to access the comment
br = Browser()
br.open(CFG_SITE_URL + '/youraccount/login')
br.select_form(nr=0)
br['p_un'] = 'juliet'
br['p_pw'] = 'j123uliet'
br.submit()
- br.open("%s/record/%i/comments/" % (CFG_SITE_URL, self.restr_record))
+ br.open("%s/%s/%i/comments/" % (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record))
response = br.response().read()
if not self.msg2 in response:
pass
else:
self.fail("Oops, this user should not have access to this comment")
# Juliet should not be able to access the attached files
- br.open("%s/record/%i/comments/attachments/get/%i/file2" % \
- (CFG_SITE_URL, self.restr_record, self.restr_comid_1))
+ br.open("%s/%s/%i/comments/attachments/get/%i/file2" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record, self.restr_comid_1))
response = br.response().read()
if "You are not authorized" in response:
pass
else:
self.fail("Oops, this user should not have access to this comment attachment")
# Jekyll should be able to access the comment
br = Browser()
br.open(CFG_SITE_URL + '/youraccount/login')
br.select_form(nr=0)
br['p_un'] = 'jekyll'
br['p_pw'] = 'j123ekyll'
br.submit()
- br.open("%s/record/%i/comments/" % (CFG_SITE_URL, self.restr_record))
+ br.open("%s/%s/%i/comments/" % (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record))
response = br.response().read()
if not self.msg2 in response:
self.fail("Oops, this user should have access to this comment")
# Jekyll should be able to access the attached files
- br.open("%s/record/%i/comments/attachments/get/%i/file2" % \
- (CFG_SITE_URL, self.restr_record, self.restr_comid_1))
+ br.open("%s/%s/%i/comments/attachments/get/%i/file2" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restr_record, self.restr_comid_1))
response = br.response().read()
self.assertEqual(self.attached_file2_content, response)
def test_access_public_record_restricted_discussion_public_comment(self):
"""webcomment - accessing "public" comment in a restricted discussion of a public record"""
# Guest user should not be able to access it
self.assertNotEqual([],
- test_web_page_content("%s/record/%i/comments/" % (CFG_SITE_URL, self.restricted_discussion),
+ test_web_page_content("%s/%s/%i/comments/" % (CFG_SITE_URL, CFG_SITE_RECORD, self.restricted_discussion),
expected_text=self.msg2))
# Accessing a non existing file for a restricted comment should also ask to login
self.assertEqual([],
- test_web_page_content("%s/record/%i/comments/attachments/get/%i/not_existing_file" % \
- (CFG_SITE_URL, self.restricted_discussion, self.restr_comid_5),
+ test_web_page_content("%s/%s/%i/comments/attachments/get/%i/not_existing_file" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restricted_discussion, self.restr_comid_5),
expected_text='You can use your nickname or your email address to login'))
# Check accessing file of a restricted comment
self.assertEqual([],
- test_web_page_content("%s/record/%i/comments/attachments/get/%i/file2" % \
- (CFG_SITE_URL, self.restricted_discussion, self.restr_comid_5),
+ test_web_page_content("%s/%s/%i/comments/attachments/get/%i/file2" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restricted_discussion, self.restr_comid_5),
expected_text='You can use your nickname or your email address to login'))
# Juliet should not be able to access the comment
br = Browser()
br.open(CFG_SITE_URL + '/youraccount/login')
br.select_form(nr=0)
br['p_un'] = 'juliet'
br['p_pw'] = 'j123uliet'
br.submit()
- br.open("%s/record/%i/comments/" % (CFG_SITE_URL, self.restricted_discussion))
+ br.open("%s/%s/%i/comments/" % (CFG_SITE_URL, CFG_SITE_RECORD, self.restricted_discussion))
response = br.response().read()
if not self.msg6 in response:
pass
else:
self.fail("Oops, this user should not have access to this comment")
# Juliet should not be able to access the attached files
- br.open("%s/record/%i/comments/attachments/get/%i/file2" % \
- (CFG_SITE_URL, self.restricted_discussion, self.restr_comid_5))
+ br.open("%s/%s/%i/comments/attachments/get/%i/file2" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restricted_discussion, self.restr_comid_5))
response = br.response().read()
if "You are not authorized" in response:
pass
else:
self.fail("Oops, this user should not have access to this comment attachment")
# Romeo should be able to access the comment
br = Browser()
br.open(CFG_SITE_URL + '/youraccount/login')
br.select_form(nr=0)
br['p_un'] = 'romeo'
br['p_pw'] = 'r123omeo'
br.submit()
- br.open("%s/record/%i/comments/" % (CFG_SITE_URL, self.restricted_discussion))
+ br.open("%s/%s/%i/comments/" % (CFG_SITE_URL, CFG_SITE_RECORD, self.restricted_discussion))
response = br.response().read()
if not self.msg6 in response:
self.fail("Oops, this user should have access to this comment")
# Romeo should be able to access the attached files
- br.open("%s/record/%i/comments/attachments/get/%i/file2" % \
- (CFG_SITE_URL, self.restricted_discussion, self.restr_comid_5))
+ br.open("%s/%s/%i/comments/attachments/get/%i/file2" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.restricted_discussion, self.restr_comid_5))
response = br.response().read()
self.assertEqual(self.attached_file2_content, response)
def test_comment_replies_inherit_restrictions(self):
"""webcomment - a reply to a comment inherits restrictions"""
# In this test we reply to a restricted comment, and check if
# the restriction is inherited. However, in order to make sure
# that the comment restriction is inherited, and not the
# record restriction, we temporary change the restriction of
# the parent.
self.public_record_restr_comment
original_restriction = run_sql("SELECT restriction FROM cmtRECORDCOMMENT WHERE id=%s",
(self.restr_comid_2,))[0][0]
restriction_to_inherit = 'juliet_only'
run_sql("UPDATE cmtRECORDCOMMENT SET restriction=%s WHERE id=%s",
(restriction_to_inherit, self.restr_comid_2))
# Reply to a restricted comment
self.msg4 = "A test comment 4"
prepare_attachments()
self.restr_comid_3 = \
query_add_comment_or_remark(reviews=0, recID=self.public_record_restr_comment,
uid=self.jekyll_uid, msg=self.msg4,
editor_type='textarea',
attached_files=self.attached_files,
reply_to=self.restr_comid_2)
inherited_restriction = run_sql("SELECT restriction FROM cmtRECORDCOMMENT WHERE id=%s",
(self.restr_comid_3,))[0][0]
self.assertEqual(restriction_to_inherit, inherited_restriction)
# Restore original restriction
run_sql("UPDATE cmtRECORDCOMMENT SET restriction=%s WHERE id=%s",
(original_restriction, self.restr_comid_2))
TEST_SUITE = make_test_suite(WebCommentWebPagesAvailabilityTest,
WebCommentRestrictionsTest)
if __name__ == "__main__":
run_test_suite(TEST_SUITE, warn_user=True)
diff --git a/modules/webcomment/lib/webcomment_templates.py b/modules/webcomment/lib/webcomment_templates.py
index 9587237da..79a209751 100644
--- a/modules/webcomment/lib/webcomment_templates.py
+++ b/modules/webcomment/lib/webcomment_templates.py
@@ -1,2194 +1,2202 @@
# -*- coding: utf-8 -*-
## Comments and reviews for records.
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""HTML Templates for commenting features """
__revision__ = "$Id$"
import cgi
# Invenio imports
from invenio.urlutils import create_html_link
from invenio.webuser import get_user_info, collect_user_info, isGuestUser, get_email
from invenio.dateutils import convert_datetext_to_dategui
from invenio.webmessage_mailutils import email_quoted_txt2html
from invenio.webcomment_config import \
CFG_WEBCOMMENT_MAX_ATTACHED_FILES, \
CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE
from invenio.config import CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL,\
CFG_SITE_SUPPORT_EMAIL,\
CFG_WEBCOMMENT_ALLOW_REVIEWS, \
CFG_WEBCOMMENT_ALLOW_COMMENTS, \
CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR, \
CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN, \
CFG_WEBCOMMENT_AUTHOR_DELETE_COMMENT_OPTION, \
- CFG_CERN_SITE
+ CFG_CERN_SITE, \
+ CFG_SITE_RECORD
from invenio.htmlutils import get_html_text_editor
from invenio.messages import gettext_set_language
from invenio.bibformat import format_record
from invenio.access_control_engine import acc_authorize_action
from invenio.websearch_templates import get_fieldvalues
class Template:
"""templating class, refer to webcomment.py for examples of call"""
def tmpl_get_first_comments_without_ranking(self, recID, ln, comments, nb_comments_total, warnings):
"""
@param recID: record id
@param ln: language
@param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks
@param nb_comments_total: total number of comments for this record
@param warnings: list of warning tuples (warning_msg, arg1, arg2, ...)
@return: html of comments
"""
# load the right message language
_ = gettext_set_language(ln)
# naming data fields of comments
c_nickname = 0
c_user_id = 1
c_date_creation = 2
c_body = 3
c_id = 4
warnings = self.tmpl_warnings(warnings, ln)
# comments
comment_rows = ''
last_comment_round_name = None
comment_round_names = [comment[0] for comment in comments]
if comment_round_names:
last_comment_round_name = comment_round_names[-1]
for comment_round_name, comments_list in comments:
comment_rows += '<div id="cmtRound%s" class="cmtRound">' % (comment_round_name)
comment_rows += _('%(x_nb)i comments for round "%(x_name)s"') % {'x_nb': len(comments_list), 'x_name': comment_round_name} + "<br/>"
for comment in comments_list:
if comment[c_nickname]:
nickname = comment[c_nickname]
display = nickname
else:
(uid, nickname, display) = get_user_info(comment[c_user_id])
messaging_link = self.create_messaging_link(nickname, display, ln)
comment_rows += """
<tr>
<td>"""
- report_link = '%s/record/%s/comments/report?ln=%s&amp;comid=%s' % (CFG_SITE_URL, recID, ln, comment[c_id])
- reply_link = '%s/record/%s/comments/add?ln=%s&amp;comid=%s&amp;action=REPLY' % (CFG_SITE_URL, recID, ln, comment[c_id])
+ report_link = '%s/%s/%s/comments/report?ln=%s&amp;comid=%s' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln, comment[c_id])
+ reply_link = '%s/%s/%s/comments/add?ln=%s&amp;comid=%s&amp;action=REPLY' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln, comment[c_id])
comment_rows += self.tmpl_get_comment_without_ranking(req=None, ln=ln, nickname=messaging_link, comment_uid=comment[c_user_id],
date_creation=comment[c_date_creation],
body=comment[c_body], status='', nb_reports=0,
report_link=report_link, reply_link=reply_link, recID=recID)
comment_rows += """
<br />
<br />
</td>
</tr>"""
# Close comment round
comment_rows += '</div>'
# write button
write_button_label = _("Write a comment")
- write_button_link = '%s/record/%s/comments/add' % (CFG_SITE_URL, recID)
+ write_button_link = '%s/%s/%s/comments/add' % (CFG_SITE_URL, CFG_SITE_RECORD, recID)
write_button_form = '<input type="hidden" name="ln" value="%s"/>' % ln
write_button_form = self.createhiddenform(action=write_button_link, method="get", text=write_button_form, button=write_button_label)
# output
if nb_comments_total > 0:
out = warnings
comments_label = len(comments) > 1 and _("Showing the latest %i comments:") % len(comments) \
or ""
out += """
<table>
<tr>
<td class="blocknote">%(comment_title)s</td>
</tr>
</table>
%(comments_label)s<br />
<table border="0" cellspacing="5" cellpadding="5" width="100%%">
%(comment_rows)s
</table>
%(view_all_comments_link)s
<br />
<br />
%(write_button_form)s<br />""" % \
{'comment_title': _("Discuss this document"),
'comments_label': comments_label,
'nb_comments_total' : nb_comments_total,
'recID': recID,
'comment_rows': comment_rows,
'tab': '&nbsp;'*4,
'siteurl': CFG_SITE_URL,
's': nb_comments_total>1 and 's' or "",
- 'view_all_comments_link': nb_comments_total>0 and '''<a href="%s/record/%s/comments/display">View all %s comments</a>''' \
- % (CFG_SITE_URL, recID, nb_comments_total) or "",
+ 'view_all_comments_link': nb_comments_total>0 and '''<a href="%s/%s/%s/comments/display">View all %s comments</a>''' \
+ % (CFG_SITE_URL, CFG_SITE_RECORD, recID, nb_comments_total) or "",
'write_button_form': write_button_form,
'nb_comments': len(comments)
}
else:
out = """
<!-- comments title table -->
<table>
<tr>
<td class="blocknote">%(discuss_label)s:</td>
</tr>
</table>
%(detailed_info)s
<br />
%(form)s
<br />""" % {'form': write_button_form,
'discuss_label': _("Discuss this document"),
'detailed_info': _("Start a discussion about any aspect of this document.")
}
return out
def tmpl_record_not_found(self, status='missing', recID="", ln=CFG_SITE_LANG):
"""
Displays a page when bad or missing record ID was given.
@param status: 'missing' : no recID was given
'inexistant': recID doesn't have an entry in the database
'nan' : recID is not a number
'invalid' : recID is an error code, i.e. in the interval [-99,-1]
@param return: body of the page
"""
_ = gettext_set_language(ln)
if status == 'inexistant':
body = _("Sorry, the record %s does not seem to exist.") % (recID,)
elif status in ('nan', 'invalid'):
body = _("Sorry, %s is not a valid ID value.") % (recID,)
else:
body = _("Sorry, no record ID was provided.")
body += "<br /><br />"
link = "<a href=\"%s?ln=%s\">%s</a>." % (CFG_SITE_URL, ln, CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME))
body += _("You may want to start browsing from %s") % link
return body
def tmpl_get_first_comments_with_ranking(self, recID, ln, comments=None, nb_comments_total=None, avg_score=None, warnings=[]):
"""
@param recID: record id
@param ln: language
@param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks
@param nb_comments_total: total number of comments for this record
@param avg_score: average score of all reviews
@param warnings: list of warning tuples (warning_msg, arg1, arg2, ...)
@return: html of comments
"""
# load the right message language
_ = gettext_set_language(ln)
# naming data fields of comments
c_nickname = 0
c_user_id = 1
c_date_creation = 2
c_body = 3
c_nb_votes_yes = 4
c_nb_votes_total = 5
c_star_score = 6
c_title = 7
c_id = 8
warnings = self.tmpl_warnings(warnings, ln)
#stars
if avg_score > 0:
avg_score_img = 'stars-' + str(avg_score).split('.')[0] + '-' + str(avg_score).split('.')[1] + '.png'
else:
avg_score_img = "stars-0-0.png"
# voting links
useful_dict = { 'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'recID' : recID,
'ln' : ln,
'yes_img' : 'smchk_gr.gif', #'yes.gif',
'no_img' : 'iconcross.gif' #'no.gif'
}
- link = '<a href="%(siteurl)s/record/%(recID)s/reviews/vote?ln=%(ln)s&amp;comid=%%(comid)s' % useful_dict
+ link = '<a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/reviews/vote?ln=%(ln)s&amp;comid=%%(comid)s' % useful_dict
useful_yes = link + '&amp;com_value=1">' + _("Yes") + '</a>'
useful_no = link + '&amp;com_value=-1">' + _("No") + '</a>'
#comment row
comment_rows = ' '
last_comment_round_name = None
comment_round_names = [comment[0] for comment in comments]
if comment_round_names:
last_comment_round_name = comment_round_names[-1]
for comment_round_name, comments_list in comments:
comment_rows += '<div id="cmtRound%s" class="cmtRound">' % (comment_round_name)
comment_rows += _('%(x_nb)i comments for round "%(x_name)s"') % {'x_nb': len(comments_list), 'x_name': comment_round_name} + "<br/>"
for comment in comments_list:
if comment[c_nickname]:
nickname = comment[c_nickname]
display = nickname
else:
(uid, nickname, display) = get_user_info(comment[c_user_id])
messaging_link = self.create_messaging_link(nickname, display, ln)
comment_rows += '''
<tr>
<td>'''
- report_link = '%s/record/%s/reviews/report?ln=%s&amp;comid=%s' % (CFG_SITE_URL, recID, ln, comment[c_id])
+ report_link = '%s/%s/%s/reviews/report?ln=%s&amp;comid=%s' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln, comment[c_id])
comment_rows += self.tmpl_get_comment_with_ranking(None, ln=ln, nickname=messaging_link,
comment_uid=comment[c_user_id],
date_creation=comment[c_date_creation],
body=comment[c_body],
status='', nb_reports=0,
nb_votes_total=comment[c_nb_votes_total],
nb_votes_yes=comment[c_nb_votes_yes],
star_score=comment[c_star_score],
title=comment[c_title], report_link=report_link, recID=recID)
comment_rows += '''
%s %s / %s<br />''' % (_("Was this review helpful?"), useful_yes % {'comid':comment[c_id]}, useful_no % {'comid':comment[c_id]})
comment_rows += '''
<br />
</td>
</tr>'''
# Close comment round
comment_rows += '</div>'
# write button
- write_button_link = '''%s/record/%s/reviews/add''' % (CFG_SITE_URL, recID)
+ write_button_link = '''%s/%s/%s/reviews/add''' % (CFG_SITE_URL, CFG_SITE_RECORD, recID)
write_button_form = ' <input type="hidden" name="ln" value="%s"/>' % ln
write_button_form = self.createhiddenform(action=write_button_link, method="get", text=write_button_form, button=_("Write a review"))
if nb_comments_total > 0:
avg_score_img = str(avg_score_img)
avg_score = str(avg_score)
nb_comments_total = str(nb_comments_total)
score = '<b>'
score += _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \
{'x_nb_score': '</b><img src="' + CFG_SITE_URL + '/img/' + avg_score_img + '" alt="' + avg_score + '" />',
'x_nb_reviews': nb_comments_total}
useful_label = _("Readers found the following %s reviews to be most helpful.")
useful_label %= len(comments) > 1 and len(comments) or ""
- view_all_comments_link ='<a href="%s/record/%s/reviews/display?ln=%s&amp;do=hh">' % (CFG_SITE_URL, recID, ln)
+ view_all_comments_link ='<a href="%s/%s/%s/reviews/display?ln=%s&amp;do=hh">' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln)
view_all_comments_link += _("View all %s reviews") % nb_comments_total
view_all_comments_link += '</a><br />'
out = warnings + """
<!-- review title table -->
<table>
<tr>
<td class="blocknote">%(comment_title)s:</td>
</tr>
</table>
%(score_label)s<br />
%(useful_label)s
<!-- review table -->
<table style="border: 0px; border-collapse: separate; border-spacing: 5px; padding: 5px; width: 100%%">
%(comment_rows)s
</table>
%(view_all_comments_link)s
%(write_button_form)s<br />
""" % \
{ 'comment_title' : _("Rate this document"),
'score_label' : score,
'useful_label' : useful_label,
'recID' : recID,
'view_all_comments' : _("View all %s reviews") % (nb_comments_total,),
'write_comment' : _("Write a review"),
'comment_rows' : comment_rows,
'tab' : '&nbsp;'*4,
'siteurl' : CFG_SITE_URL,
'view_all_comments_link': nb_comments_total>0 and view_all_comments_link or "",
'write_button_form' : write_button_form
}
else:
out = '''
<!-- review title table -->
<table>
<tr>
<td class="blocknote">%s:</td>
</tr>
</table>
%s<br />
%s
<br />''' % (_("Rate this document"),
_("Be the first to review this document."),
write_button_form)
return out
def tmpl_get_comment_without_ranking(self, req, ln, nickname, comment_uid, date_creation, body, status, nb_reports, reply_link=None, report_link=None, undelete_link=None, delete_links=None, unreport_link=None, recID=-1, com_id='', attached_files=None):
"""
private function
@param req: request object to fetch user info
@param ln: language
@param nickname: nickname
@param date_creation: date comment was written
@param body: comment body
@param status: status of the comment:
da: deleted by author
dm: deleted by moderator
ok: active
@param nb_reports: number of reports the comment has
@param reply_link: if want reply and report, give the http links
@param report_link: if want reply and report, give the http links
@param undelete_link: http link to delete the message
@param delete_links: http links to delete the message
@param unreport_link: http link to unreport the comment
@param recID: recID where the comment is posted
@param com_id: ID of the comment displayed
@param attached_files: list of attached files
@return: html table of comment
"""
from invenio.search_engine import guess_primary_collection_of_a_record
# load the right message language
_ = gettext_set_language(ln)
date_creation = convert_datetext_to_dategui(date_creation, ln=ln)
if attached_files is None:
attached_files = []
out = ''
final_body = email_quoted_txt2html(body)
title = _('%(x_name)s wrote on %(x_date)s:') % {'x_name': nickname,
'x_date': '<i>' + date_creation + '</i>'}
title += '<a name=%s></a>' % com_id
links = ''
moderator_links = ''
if reply_link:
links += '<a href="' + reply_link +'">' + _("Reply") +'</a>'
if report_link and status != 'ap':
links += ' | '
if report_link and status != 'ap':
links += '<a href="' + report_link +'">' + _("Report abuse") + '</a>'
# Check if user is a comment moderator
record_primary_collection = guess_primary_collection_of_a_record(recID)
user_info = collect_user_info(req)
(auth_code, auth_msg) = acc_authorize_action(user_info, 'moderatecomments', collection=record_primary_collection)
if status in ['dm', 'da'] and req:
if not auth_code:
if status == 'dm':
final_body = '<div style="color:#a3a3a3;font-style:italic;">(Comment deleted by the moderator) - not visible for users<br /><br />' +\
final_body + '</div>'
else:
final_body = '<div style="color:#a3a3a3;font-style:italic;">(Comment deleted by the author) - not visible for users<br /><br />' +\
final_body + '</div>'
links = ''
moderator_links += '<a style="color:#8B0000;" href="' + undelete_link + '">' + _("Undelete comment") + '</a>'
else:
if status == 'dm':
final_body = '<div style="color:#a3a3a3;font-style:italic;">Comment deleted by the moderator</div>'
else:
final_body = '<div style="color:#a3a3a3;font-style:italic;">Comment deleted by the author</div>'
links = ''
else:
if not auth_code:
moderator_links += '<a style="color:#8B0000;" href="' + delete_links['mod'] +'">' + _("Delete comment") + '</a>'
elif (user_info['uid'] == comment_uid) and CFG_WEBCOMMENT_AUTHOR_DELETE_COMMENT_OPTION:
moderator_links += '<a style="color:#8B0000;" href="' + delete_links['auth'] +'">' + _("Delete comment") + '</a>'
if nb_reports >= CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN:
if not auth_code:
final_body = '<div style="color:#a3a3a3;font-style:italic;">(Comment reported. Pending approval) - not visible for users<br /><br />' + final_body + '</div>'
links = ''
moderator_links += ' | '
moderator_links += '<a style="color:#8B0000;" href="' + unreport_link +'">' + _("Unreport comment") + '</a>'
else:
final_body = '<div style="color:#a3a3a3;font-style:italic;">This comment is pending approval due to user reports</div>'
links = ''
if links and moderator_links:
links = links + ' || ' + moderator_links
elif not links:
links = moderator_links
attached_files_html = ''
if attached_files:
attached_files_html = '<div class="cmtfilesblock"><b>%s:</b><br/>' % (len(attached_files) == 1 and _("Attached file") or _("Attached files"))
for (filename, filepath, fileurl) in attached_files:
attached_files_html += create_html_link(urlbase=fileurl, urlargd={},
link_label=cgi.escape(filename)) + '<br />'
attached_files_html += '</div>'
out += """
<div style="margin-bottom:20px;background:#F9F9F9;border:1px solid #DDD">%(title)s<br />
<blockquote>
%(body)s
</blockquote>
<br />
%(attached_files_html)s
<div style="float:right">%(links)s</div>
</div>""" % \
{'title' : '<div style="background-color:#EEE;padding:2px;"><img src="%s/img/user-icon-1-24x24.gif" alt="" />&nbsp;%s</div>' % (CFG_SITE_URL, title),
'body' : final_body,
'links' : links,
'attached_files_html': attached_files_html}
return out
def tmpl_get_comment_with_ranking(self, req, ln, nickname, comment_uid, date_creation, body, status, nb_reports, nb_votes_total, nb_votes_yes, star_score, title, report_link=None, delete_links=None, undelete_link=None, unreport_link=None, recID=-1):
"""
private function
@param req: request object to fetch user info
@param ln: language
@param nickname: nickname
@param date_creation: date comment was written
@param body: comment body
@param status: status of the comment
@param nb_reports: number of reports the comment has
@param nb_votes_total: total number of votes for this review
@param nb_votes_yes: number of positive votes for this record
@param star_score: star score for this record
@param title: title of review
@param report_link: if want reply and report, give the http links
@param undelete_link: http link to delete the message
@param delete_link: http link to delete the message
@param unreport_link: http link to unreport the comment
@param recID: recID where the comment is posted
@return: html table of review
"""
from invenio.search_engine import guess_primary_collection_of_a_record
# load the right message language
_ = gettext_set_language(ln)
if star_score > 0:
star_score_img = 'stars-' + str(star_score) + '-0.png'
else:
star_score_img = 'stars-0-0.png'
out = ""
date_creation = convert_datetext_to_dategui(date_creation, ln=ln)
reviewed_label = _("Reviewed by %(x_nickname)s on %(x_date)s") % {'x_nickname': nickname, 'x_date':date_creation}
useful_label = _("%(x_nb_people)i out of %(x_nb_total)i people found this review useful") % {'x_nb_people': nb_votes_yes,
'x_nb_total': nb_votes_total}
links = ''
_body = ''
if body != '':
_body = '''
<blockquote>
%s
</blockquote>''' % email_quoted_txt2html(body, linebreak_html='')
# Check if user is a comment moderator
record_primary_collection = guess_primary_collection_of_a_record(recID)
user_info = collect_user_info(req)
(auth_code, auth_msg) = acc_authorize_action(user_info, 'moderatecomments', collection=record_primary_collection)
if status in ['dm', 'da'] and req:
if not auth_code:
if status == 'dm':
_body = '<div style="color:#a3a3a3;font-style:italic;">(Review deleted by moderator) - not visible for users<br /><br />' +\
_body + '</div>'
else:
_body = '<div style="color:#a3a3a3;font-style:italic;">(Review deleted by author) - not visible for users<br /><br />' +\
_body + '</div>'
links = '<a style="color:#8B0000;" href="' + undelete_link + '">' + _("Undelete review") + '</a>'
else:
if status == 'dm':
_body = '<div style="color:#a3a3a3;font-style:italic;">Review deleted by moderator</div>'
else:
_body = '<div style="color:#a3a3a3;font-style:italic;">Review deleted by author</div>'
links = ''
else:
if not auth_code:
links += '<a style="color:#8B0000;" href="' + delete_links['mod'] +'">' + _("Delete review") + '</a>'
if nb_reports >= CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN:
if not auth_code:
_body = '<div style="color:#a3a3a3;font-style:italic;">(Review reported. Pending approval) - not visible for users<br /><br />' + _body + '</div>'
links += ' | '
links += '<a style="color:#8B0000;" href="' + unreport_link +'">' + _("Unreport review") + '</a>'
else:
_body = '<div style="color:#a3a3a3;font-style:italic;">This review is pending approval due to user reports.</div>'
links = ''
out += '''
<div style="background:#F9F9F9;border:1px solid #DDD">
<div style="background-color:#EEE;padding:2px;">
<img src="%(siteurl)s/img/%(star_score_img)s" alt="%(star_score)s" style="margin-right:10px;"/><b>%(title)s</b><br />
%(reviewed_label)s<br />
%(useful_label)s
</div>
%(body)s
</div>
%(abuse)s''' % {'siteurl' : CFG_SITE_URL,
'star_score_img': star_score_img,
'star_score' : star_score,
'title' : title,
'reviewed_label': reviewed_label,
'useful_label' : useful_label,
'body' : _body,
'abuse' : links
}
return out
def tmpl_get_comments(self, req, recID, ln,
nb_per_page, page, nb_pages,
display_order, display_since,
CFG_WEBCOMMENT_ALLOW_REVIEWS,
comments, total_nb_comments,
avg_score,
warnings,
border=0, reviews=0,
total_nb_reviews=0,
nickname='', uid=-1, note='',score=5,
can_send_comments=False,
can_attach_files=False,
user_is_subscribed_to_discussion=False,
user_can_unsubscribe_from_discussion=False,
display_comment_rounds=None):
"""
Get table of all comments
@param recID: record id
@param ln: language
@param nb_per_page: number of results per page
@param page: page number
@param display_order: hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param display_since: all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param CFG_WEBCOMMENT_ALLOW_REVIEWS: is ranking enable, get from config.py/CFG_WEBCOMMENT_ALLOW_REVIEWS
@param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks
@param total_nb_comments: total number of comments for this record
@param avg_score: average score of reviews for this record
@param warnings: list of warning tuples (warning_msg, color)
@param border: boolean, active if want to show border around each comment/review
@param reviews: boolean, enabled for reviews, disabled for comments
@param can_send_comments: boolean, if user can send comments or not
@param can_attach_files: boolean, if user can attach file to comment or not
@param user_is_subscribed_to_discussion: True if user already receives new comments by email
@param user_can_unsubscribe_from_discussion: True is user is allowed to unsubscribe from discussion
"""
# load the right message language
_ = gettext_set_language(ln)
# CERN hack begins: display full ATLAS user name. Check further below too.
current_user_fullname = ""
override_nickname_p = False
if CFG_CERN_SITE:
from invenio.search_engine import get_all_collections_of_a_record
user_info = collect_user_info(uid)
if 'atlas-readaccess-active-members [CERN]' in user_info['group']:
# An ATLAS member is never anonymous to its colleagues
# when commenting inside ATLAS collections
recid_collections = get_all_collections_of_a_record(recID)
if 'ATLAS' in str(recid_collections):
override_nickname_p = True
current_user_fullname = user_info.get('external_fullname', '')
# CERN hack ends
# naming data fields of comments
if reviews:
c_nickname = 0
c_user_id = 1
c_date_creation = 2
c_body = 3
c_status = 4
c_nb_reports = 5
c_nb_votes_yes = 6
c_nb_votes_total = 7
c_star_score = 8
c_title = 9
c_id = 10
c_round_name = 11
c_restriction = 12
reply_to = 13
discussion = 'reviews'
- comments_link = '<a href="%s/record/%s/comments/">%s</a> (%i)' % (CFG_SITE_URL, recID, _('Comments'), total_nb_comments)
+ comments_link = '<a href="%s/%s/%s/comments/">%s</a> (%i)' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, _('Comments'), total_nb_comments)
reviews_link = '<b>%s (%i)</b>' % (_('Reviews'), total_nb_reviews)
add_comment_or_review = self.tmpl_add_comment_form_with_ranking(recID, uid, current_user_fullname or nickname, ln, '', score, note, warnings, show_title_p=True, can_attach_files=can_attach_files)
else:
c_nickname = 0
c_user_id = 1
c_date_creation = 2
c_body = 3
c_status = 4
c_nb_reports = 5
c_id = 6
c_round_name = 7
c_restriction = 8
reply_to = 9
discussion = 'comments'
comments_link = '<b>%s (%i)</b>' % (_('Comments'), total_nb_comments)
- reviews_link = '<a href="%s/record/%s/reviews/">%s</a> (%i)' % (CFG_SITE_URL, recID, _('Reviews'), total_nb_reviews)
+ reviews_link = '<a href="%s/%s/%s/reviews/">%s</a> (%i)' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, _('Reviews'), total_nb_reviews)
add_comment_or_review = self.tmpl_add_comment_form(recID, uid, nickname, ln, note, warnings, can_attach_files=can_attach_files, user_is_subscribed_to_discussion=user_is_subscribed_to_discussion)
# voting links
useful_dict = { 'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'recID' : recID,
'ln' : ln,
'do' : display_order,
'ds' : display_since,
'nb' : nb_per_page,
'p' : page,
'reviews' : reviews,
'discussion' : discussion
}
- useful_yes = '<a href="%(siteurl)s/record/%(recID)s/%(discussion)s/vote?ln=%(ln)s&amp;comid=%%(comid)s&amp;com_value=1&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/record/%(recID)s/%(discussion)s/display">' + _("Yes") + '</a>'
+ useful_yes = '<a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/vote?ln=%(ln)s&amp;comid=%%(comid)s&amp;com_value=1&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/display">' + _("Yes") + '</a>'
useful_yes %= useful_dict
- useful_no = '<a href="%(siteurl)s/record/%(recID)s/%(discussion)s/vote?ln=%(ln)s&amp;comid=%%(comid)s&amp;com_value=-1&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/record/%(recID)s/%(discussion)s/display">' + _("No") + '</a>'
+ useful_no = '<a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/vote?ln=%(ln)s&amp;comid=%%(comid)s&amp;com_value=-1&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/display">' + _("No") + '</a>'
useful_no %= useful_dict
warnings = self.tmpl_warnings(warnings, ln)
link_dic = { 'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'module' : 'comments',
'function' : 'index',
'discussion': discussion,
'arguments' : 'do=%s&amp;ds=%s&amp;nb=%s' % (display_order, display_since, nb_per_page),
'arg_page' : '&amp;p=%s' % page,
'page' : page,
'rec_id' : recID}
if not req:
req = None
## comments table
comments_rows = ''
last_comment_round_name = None
comment_round_names = [comment[0] for comment in comments]
if comment_round_names:
last_comment_round_name = comment_round_names[-1]
for comment_round_name, comments_list in comments:
comment_round_style = "display:none;"
comment_round_is_open = False
if comment_round_name in display_comment_rounds:
comment_round_is_open = True
comment_round_style = ""
comments_rows += '<div id="cmtRound%s" class="cmtround">' % (comment_round_name)
if not comment_round_is_open and \
(comment_round_name or len(comment_round_names) > 1):
new_cmtgrp = list(display_comment_rounds)
new_cmtgrp.append(comment_round_name)
comments_rows += '''<img src="/img/right-trans.gif" id="cmtarrowiconright%(grp_id)s" alt="Open group" /><img src="/img/down-trans.gif" id="cmtarrowicondown%(grp_id)s" alt="Close group" style="display:none" />
<a class="cmtgrpswitch" name="cmtgrpLink%(grp_id)s" onclick="var cmtarrowicondown=document.getElementById('cmtarrowicondown%(grp_id)s');var cmtarrowiconright=document.getElementById('cmtarrowiconright%(grp_id)s');var subgrp=document.getElementById('cmtSubRound%(grp_id)s');if (subgrp.style.display==''){subgrp.style.display='none';cmtarrowiconright.style.display='';cmtarrowicondown.style.display='none';}else{subgrp.style.display='';cmtarrowiconright.style.display='none';cmtarrowicondown.style.display='';};return false;"''' % {'grp_id': comment_round_name}
- comments_rows += 'href=\"%(siteurl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s' % link_dic
+ comments_rows += 'href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s' % link_dic
comments_rows += '&amp;' + '&amp;'.join(["cmtgrp=" + grp for grp in new_cmtgrp if grp != 'none']) + \
'#cmtgrpLink%s' % (comment_round_name) + '\">'
comments_rows += _('%(x_nb)i comments for round "%(x_name)s"') % {'x_nb': len(comments_list), 'x_name': comment_round_name} + "</a><br/>"
elif comment_round_name or len(comment_round_names) > 1:
new_cmtgrp = list(display_comment_rounds)
new_cmtgrp.remove(comment_round_name)
comments_rows += '''<img src="/img/right-trans.gif" id="cmtarrowiconright%(grp_id)s" alt="Open group" style="display:none" /><img src="/img/down-trans.gif" id="cmtarrowicondown%(grp_id)s" alt="Close group" />
<a class="cmtgrpswitch" name="cmtgrpLink%(grp_id)s" onclick="var cmtarrowicondown=document.getElementById('cmtarrowicondown%(grp_id)s');var cmtarrowiconright=document.getElementById('cmtarrowiconright%(grp_id)s');var subgrp=document.getElementById('cmtSubRound%(grp_id)s');if (subgrp.style.display==''){subgrp.style.display='none';cmtarrowiconright.style.display='';cmtarrowicondown.style.display='none';}else{subgrp.style.display='';cmtarrowiconright.style.display='none';cmtarrowicondown.style.display='';};return false;"''' % {'grp_id': comment_round_name}
- comments_rows += 'href=\"%(siteurl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s' % link_dic
+ comments_rows += 'href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s' % link_dic
comments_rows += '&amp;' + ('&amp;'.join(["cmtgrp=" + grp for grp in new_cmtgrp if grp != 'none']) or 'cmtgrp=none' ) + \
'#cmtgrpLink%s' % (comment_round_name) + '\">'
comments_rows += _('%(x_nb)i comments for round "%(x_name)s"') % {'x_nb': len(comments_list), 'x_name': comment_round_name}+ "</a><br/>"
comments_rows += '<div id="cmtSubRound%s" class="cmtsubround" style="%s">' % (comment_round_name,
comment_round_style)
thread_history = [0]
for comment in comments_list:
if comment[reply_to] not in thread_history:
# Going one level down in the thread
thread_history.append(comment[reply_to])
depth = thread_history.index(comment[reply_to])
else:
depth = thread_history.index(comment[reply_to])
thread_history = thread_history[:depth + 1]
# CERN hack begins: display full ATLAS user name.
comment_user_fullname = ""
if CFG_CERN_SITE and override_nickname_p:
comment_user_fullname = get_email(comment[c_user_id])
# CERN hack ends
if comment[c_nickname]:
_nickname = comment[c_nickname]
display = _nickname
else:
(uid, _nickname, display) = get_user_info(comment[c_user_id])
messaging_link = self.create_messaging_link(_nickname, comment_user_fullname or display, ln)
from invenio.webcomment import get_attached_files # FIXME
files = get_attached_files(recID, comment[c_id])
# do NOT delete the HTML comment below. It is used for parsing... (I plead unguilty!)
comments_rows += """
<!-- start comment row -->
<div style="margin-left:%spx">""" % (depth*20)
delete_links = {}
if not reviews:
- report_link = '%(siteurl)s/record/%(recID)s/comments/report?ln=%(ln)s&amp;comid=%%(comid)s&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/record/%(recID)s/comments/display' % useful_dict % {'comid':comment[c_id]}
- reply_link = '%(siteurl)s/record/%(recID)s/comments/add?ln=%(ln)s&amp;action=REPLY&amp;comid=%%(comid)s' % useful_dict % {'comid':comment[c_id]}
+ report_link = '%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/report?ln=%(ln)s&amp;comid=%%(comid)s&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/display' % useful_dict % {'comid':comment[c_id]}
+ reply_link = '%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/add?ln=%(ln)s&amp;action=REPLY&amp;comid=%%(comid)s' % useful_dict % {'comid':comment[c_id]}
delete_links['mod'] = "%s/admin/webcomment/webcommentadmin.py/del_single_com_mod?ln=%s&amp;id=%s" % (CFG_SITE_URL, ln, comment[c_id])
delete_links['auth'] = "%s/admin/webcomment/webcommentadmin.py/del_single_com_auth?ln=%s&amp;id=%s" % (CFG_SITE_URL, ln, comment[c_id])
undelete_link = "%s/admin/webcomment/webcommentadmin.py/undel_com?ln=%s&amp;id=%s" % (CFG_SITE_URL, ln, comment[c_id])
unreport_link = "%s/admin/webcomment/webcommentadmin.py/unreport_com?ln=%s&amp;id=%s" % (CFG_SITE_URL, ln, comment[c_id])
comments_rows += self.tmpl_get_comment_without_ranking(req, ln, messaging_link, comment[c_user_id], comment[c_date_creation], comment[c_body], comment[c_status], comment[c_nb_reports], reply_link, report_link, undelete_link, delete_links, unreport_link, recID, comment[c_id], files)
else:
- report_link = '%(siteurl)s/record/%(recID)s/reviews/report?ln=%(ln)s&amp;comid=%%(comid)s&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/record/%(recID)s/reviews/display' % useful_dict % {'comid': comment[c_id]}
+ report_link = '%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/reviews/report?ln=%(ln)s&amp;comid=%%(comid)s&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/reviews/display' % useful_dict % {'comid': comment[c_id]}
delete_links['mod'] = "%s/admin/webcomment/webcommentadmin.py/del_single_com_mod?ln=%s&amp;id=%s" % (CFG_SITE_URL, ln, comment[c_id])
delete_links['auth'] = "%s/admin/webcomment/webcommentadmin.py/del_single_com_auth?ln=%s&amp;id=%s" % (CFG_SITE_URL, ln, comment[c_id])
undelete_link = "%s/admin/webcomment/webcommentadmin.py/undel_com?ln=%s&amp;id=%s" % (CFG_SITE_URL, ln, comment[c_id])
unreport_link = "%s/admin/webcomment/webcommentadmin.py/unreport_com?ln=%s&amp;id=%s" % (CFG_SITE_URL, ln, comment[c_id])
comments_rows += self.tmpl_get_comment_with_ranking(req, ln, messaging_link, comment[c_user_id], comment[c_date_creation], comment[c_body], comment[c_status], comment[c_nb_reports], comment[c_nb_votes_total], comment[c_nb_votes_yes], comment[c_star_score], comment[c_title], report_link, delete_links, undelete_link, unreport_link, recID)
helpful_label = _("Was this review helpful?")
report_abuse_label = "(" + _("Report abuse") + ")"
yes_no_separator = '<td> / </td>'
if comment[c_nb_reports] >= CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN or comment[c_status] in ['dm', 'da']:
report_abuse_label = ""
helpful_label = ""
useful_yes = ""
useful_no = ""
yes_no_separator = ""
comments_rows += """
<table>
<tr>
<td>%(helpful_label)s %(tab)s</td>
<td> %(yes)s </td>
%(yes_no_separator)s
<td> %(no)s </td>
<td class="reportabuse">%(tab)s%(tab)s<a href="%(report)s">%(report_abuse_label)s</a></td>
</tr>
</table>""" \
% {'helpful_label': helpful_label,
'yes' : useful_yes % {'comid':comment[c_id]},
'yes_no_separator': yes_no_separator,
'no' : useful_no % {'comid':comment[c_id]},
'report' : report_link % {'comid':comment[c_id]},
'report_abuse_label': comment[c_nb_reports] >= CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN and '' or report_abuse_label,
'tab' : '&nbsp;'*2}
# do NOT remove HTML comment below. It is used for parsing...
comments_rows += """
</div>
<!-- end comment row -->"""
comments_rows += '</div></div>'
## page links
page_links = ''
# Previous
if page != 1:
link_dic['arg_page'] = 'p=%s' % (page - 1)
- page_links += '<a href=\"%(siteurl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">&lt;&lt;</a> ' % link_dic
+ page_links += '<a href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">&lt;&lt;</a> ' % link_dic
else:
page_links += ' %s ' % ('&nbsp;'*(len(_('Previous'))+7))
# Page Numbers
for i in range(1, nb_pages+1):
link_dic['arg_page'] = 'p=%s' % i
link_dic['page'] = '%s' % i
if i != page:
page_links += '''
- <a href=\"%(siteurl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">%(page)s</a> ''' % link_dic
+ <a href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">%(page)s</a> ''' % link_dic
else:
page_links += ''' <b>%s</b> ''' % i
# Next
if page != nb_pages:
link_dic['arg_page'] = 'p=%s' % (page + 1)
page_links += '''
- <a href=\"%(siteurl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">&gt;&gt;</a> ''' % link_dic
+ <a href=\"%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">&gt;&gt;</a> ''' % link_dic
else:
page_links += '%s' % ('&nbsp;'*(len(_('Next'))+7))
## stuff for ranking if enabled
if reviews:
if avg_score > 0:
avg_score_img = 'stars-' + str(avg_score).split('.')[0] + '-' + str(avg_score).split('.')[1] + '.png'
else:
avg_score_img = "stars-0-0.png"
ranking_average = '<br /><b>'
ranking_average += _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \
{'x_nb_score': '</b><img src="' + CFG_SITE_URL + '/img/' + avg_score_img + '" alt="' + str(avg_score) + '" />',
'x_nb_reviews': str(total_nb_reviews)}
ranking_average += '<br />'
else:
ranking_average = ""
- write_button_link = '''%s/record/%s/%s/add''' % (CFG_SITE_URL, recID, discussion)
+ write_button_link = '''%s/%s/%s/%s/add''' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, discussion)
write_button_form = '<input type="hidden" name="ln" value="%s"/>'
write_button_form = self.createhiddenform(action=write_button_link,
method="get",
text=write_button_form,
button = reviews and _('Write a review') or _('Write a comment'))
if reviews:
total_label = _("There is a total of %s reviews")
else:
total_label = _("There is a total of %s comments")
total_label %= total_nb_comments
review_or_comment_first = ''
if reviews == 0 and total_nb_comments == 0 and can_send_comments:
review_or_comment_first = _("Start a discussion about any aspect of this document.") + '<br />'
elif reviews == 1 and total_nb_reviews == 0 and can_send_comments:
review_or_comment_first = _("Be the first to review this document.") + '<br />'
# do NOT remove the HTML comments below. Used for parsing
body = '''
%(comments_and_review_tabs)s
<!-- start comments table -->
<div style="border: %(border)spx solid black; width: 95%%; margin:10px;font-size:small">
%(comments_rows)s
</div>
<!-- end comments table -->
%(review_or_comment_first)s
<br />''' % \
{ 'record_label': _("Record"),
'back_label': _("Back to search results"),
'total_label': total_label,
'write_button_form' : write_button_form,
'write_button_form_again' : total_nb_comments>3 and write_button_form or "",
'comments_rows' : comments_rows,
'total_nb_comments' : total_nb_comments,
'comments_or_reviews' : reviews and _('review') or _('comment'),
'comments_or_reviews_title' : reviews and _('Review') or _('Comment'),
'siteurl' : CFG_SITE_URL,
'module' : "comments",
'recid' : recID,
'ln' : ln,
'border' : border,
'ranking_avg' : ranking_average,
'comments_and_review_tabs' : CFG_WEBCOMMENT_ALLOW_REVIEWS and \
CFG_WEBCOMMENT_ALLOW_COMMENTS and \
'%s | %s <br />' % \
(comments_link, reviews_link) or '',
'review_or_comment_first' : review_or_comment_first
}
# form is not currently used. reserved for an eventual purpose
#form = """
# Display <select name="nb" size="1"> per page
# <option value="all">All</option>
# <option value="10">10</option>
# <option value="25">20</option>
# <option value="50">50</option>
# <option value="100" selected="selected">100</option>
# </select>
# comments per page that are <select name="ds" size="1">
# <option value="all" selected="selected">Any age</option>
# <option value="1d">1 day old</option>
# <option value="3d">3 days old</option>
# <option value="1w">1 week old</option>
# <option value="2w">2 weeks old</option>
# <option value="1m">1 month old</option>
# <option value="3m">3 months old</option>
# <option value="6m">6 months old</option>
# <option value="1y">1 year old</option>
# </select>
# and sorted by <select name="do" size="1">
# <option value="od" selected="selected">Oldest first</option>
# <option value="nd">Newest first</option>
# %s
# </select>
# """ % \
# (reviews==1 and '''
# <option value=\"hh\">most helpful</option>
# <option value=\"lh\">least helpful</option>
# <option value=\"hs\">highest star ranking</option>
# <option value=\"ls\">lowest star ranking</option>
# </select>''' or '''
# </select>''')
#
#form_link = "%(siteurl)s/%(module)s/%(function)s" % link_dic
#form = self.createhiddenform(action=form_link, method="get", text=form, button='Go', recid=recID, p=1)
pages = """
<div>
%(v_label)s %(comments_or_reviews)s %(results_nb_lower)s-%(results_nb_higher)s <br />
%(page_links)s
</div>
""" % \
{'v_label': _("Viewing"),
'page_links': _("Page:") + page_links ,
'comments_or_reviews': reviews and _('review') or _('comment'),
'results_nb_lower': len(comments)>0 and ((page-1) * nb_per_page)+1 or 0,
'results_nb_higher': page == nb_pages and (((page-1) * nb_per_page) + len(comments)) or (page * nb_per_page)}
if nb_pages > 1:
#body = warnings + body + form + pages
body = warnings + body + pages
else:
body = warnings + body
if reviews == 0:
if not user_is_subscribed_to_discussion:
body += '<small>'
body += '<div class="comment-subscribe">' + '<img src="%s/img/mail-icon-12x8.gif" border="0" alt="" />' % CFG_SITE_URL + \
- '&nbsp;' + '<b>' + create_html_link(urlbase=CFG_SITE_URL + '/record/' + \
+ '&nbsp;' + '<b>' + create_html_link(urlbase=CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + \
str(recID) + '/comments/subscribe',
urlargd={},
link_label=_('Subscribe')) + \
'</b>' + ' to this discussion. You will then receive all new comments by email.' + '</div>'
body += '</small><br />'
elif user_can_unsubscribe_from_discussion:
body += '<small>'
body += '<div class="comment-subscribe">' + '<img src="%s/img/mail-icon-12x8.gif" border="0" alt="" />' % CFG_SITE_URL + \
- '&nbsp;' + '<b>' + create_html_link(urlbase=CFG_SITE_URL + '/record/' + \
+ '&nbsp;' + '<b>' + create_html_link(urlbase=CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + \
str(recID) + '/comments/unsubscribe',
urlargd={},
link_label=_('Unsubscribe')) + \
'</b>' + ' from this discussion. You will no longer receive emails about new comments.' + '</div>'
body += '</small><br />'
if can_send_comments:
body += add_comment_or_review
else:
body += '<br/><em>' + _("You are not authorized to comment or review.") + '</em>'
return '<div style="margin-left:10px;margin-right:10px;">' + body + '</div>'
def create_messaging_link(self, to, display_name, ln=CFG_SITE_LANG):
"""prints a link to the messaging system"""
link = "%s/yourmessages/write?msg_to=%s&amp;ln=%s" % (CFG_SITE_URL, to, ln)
if to:
return '<a href="%s" class="maillink">%s</a>' % (link, display_name)
else:
return display_name
def createhiddenform(self, action="", method="get", text="", button="confirm", cnfrm='', **hidden):
"""
create select with hidden values and submit button
@param action: name of the action to perform on submit
@param method: 'get' or 'post'
@param text: additional text, can also be used to add non hidden input
@param button: value/caption on the submit button
@param cnfrm: if given, must check checkbox to confirm
@param **hidden: dictionary with name=value pairs for hidden input
@return: html form
"""
output = """
<form action="%s" method="%s">""" % (action, method.lower().strip() in ['get', 'post'] and method or 'get')
output += """
<table style="width:90%">
<tr>
<td style="vertical-align: top">
"""
output += text + '\n'
if cnfrm:
output += """
<input type="checkbox" name="confirm" value="1" />"""
for key in hidden.keys():
if type(hidden[key]) is list:
for value in hidden[key]:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, value)
else:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, hidden[key])
output += """
</td>
</tr>
<tr>
<td>"""
output += """
<input class="adminbutton" type="submit" value="%s" />""" % (button, )
output += """
</td>
</tr>
</table>
</form>"""
return output
def create_write_comment_hiddenform(self, action="", method="get", text="", button="confirm", cnfrm='',
enctype='', form_id=None, form_name=None, **hidden):
"""
create select with hidden values and submit button
@param action: name of the action to perform on submit
@param method: 'get' or 'post'
@param text: additional text, can also be used to add non hidden input
@param button: value/caption on the submit button
@param cnfrm: if given, must check checkbox to confirm
@param form_id: HTML 'id' attribute of the form tag
@param form_name: HTML 'name' attribute of the form tag
@param **hidden: dictionary with name=value pairs for hidden input
@return: html form
"""
enctype_attr = ''
if enctype:
enctype_attr = 'enctype=' + enctype
output = """
<form action="%s" method="%s" %s%s%s>""" % \
(action, method.lower().strip() in ['get', 'post'] and method or 'get',
enctype_attr, form_name and ' name="%s"' % form_name or '',
form_id and ' id="%s"' % form_id or '')
if cnfrm:
output += """
<input type="checkbox" name="confirm" value="1" />"""
for key in hidden.keys():
if type(hidden[key]) is list:
for value in hidden[key]:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, value)
else:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, hidden[key])
output += text + '\n'
output += """
</form>"""
return output
def tmpl_warnings(self, warnings, ln=CFG_SITE_LANG):
"""
Prepare the warnings list
@param warnings: list of warning tuples (warning_msg, arg1, arg2, etc)
@return: html string of warnings
"""
red_text_warnings = ['WRN_WEBCOMMENT_FEEDBACK_NOT_RECORDED',
'WRN_WEBCOMMENT_ALREADY_VOTED']
green_text_warnings = ['WRN_WEBCOMMENT_FEEDBACK_RECORDED',
'WRN_WEBCOMMENT_SUBSCRIBED',
'WRN_WEBCOMMENT_UNSUBSCRIBED']
from invenio.errorlib import get_msgs_for_code_list
span_class = 'important'
out = ""
if type(warnings) is not list:
warnings = [warnings]
if len(warnings) > 0:
warnings_parsed = get_msgs_for_code_list(warnings, 'warning', ln)
for (warning_code, warning_text) in warnings_parsed:
if not warning_code.startswith('WRN'):
#display only warnings that begin with WRN to user
continue
if warning_code in red_text_warnings:
span_class = 'important'
elif warning_code in green_text_warnings:
span_class = 'exampleleader'
else:
span_class = 'important'
out += '''
<span class="%(span_class)s">%(warning)s</span><br />''' % \
{ 'span_class' : span_class,
'warning' : warning_text }
return out
else:
return ""
def tmpl_add_comment_form(self, recID, uid, nickname, ln, msg,
warnings, textual_msg=None, can_attach_files=False,
user_is_subscribed_to_discussion=False, reply_to=None):
"""
Add form for comments
@param recID: record id
@param uid: user id
@param ln: language
@param msg: comment body contents for when refreshing due to
warning, or when replying to a comment
@param textual_msg: same as 'msg', but contains the textual
version in case user cannot display FCKeditor
@param warnings: list of warning tuples (warning_msg, color)
@param can_attach_files: if user can upload attach file to record or not
@param user_is_subscribed_to_discussion: True if user already receives new comments by email
@param reply_to: the ID of the comment we are replying to. None if not replying
@return html add comment form
"""
_ = gettext_set_language(ln)
link_dic = { 'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'module' : 'comments',
'function' : 'add',
'arguments' : 'ln=%s&amp;action=%s' % (ln, 'SUBMIT'),
'recID' : recID}
if textual_msg is None:
textual_msg = msg
# FIXME a cleaner handling of nicknames is needed.
if not nickname:
(uid, nickname, display) = get_user_info(uid)
if nickname:
note = _("Note: Your nickname, %s, will be displayed as author of this comment.") % ('<i>' + nickname + '</i>')
else:
(uid, nickname, display) = get_user_info(uid)
link = '<a href="%s/youraccount/edit">' % CFG_SITE_SECURE_URL
note = _("Note: you have not %(x_url_open)sdefined your nickname%(x_url_close)s. %(x_nickname)s will be displayed as the author of this comment.") % \
{'x_url_open': link,
'x_url_close': '</a>',
'x_nickname': ' <br /><i>' + display + '</i>'}
if not CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR:
note += '<br />' + '&nbsp;'*10 + cgi.escape('You can use some HTML tags: <a href>, <strong>, <blockquote>, <br />, <p>, <em>, <ul>, <li>, <b>, <i>')
#from invenio.search_engine import print_record
#record_details = print_record(recID=recID, format='hb', ln=ln)
warnings = self.tmpl_warnings(warnings, ln)
# Prepare file upload settings. We must enable file upload in
# the fckeditor + a simple file upload interface (independant from editor)
file_upload_url = None
simple_attach_file_interface = ''
if isGuestUser(uid):
simple_attach_file_interface = "<small><em>%s</em></small><br/>" % _("Once logged in, authorized users can also attach files.")
if can_attach_files:
# Note that files can be uploaded only when user is logged in
- #file_upload_url = '%s/record/%i/comments/attachments/put' % \
- # (CFG_SITE_URL, recID)
+ #file_upload_url = '%s/%s/%i/comments/attachments/put' % \
+ # (CFG_SITE_URL, CFG_SITE_RECORD, recID)
simple_attach_file_interface = '''
<div id="uploadcommentattachmentsinterface">
<small>%(attach_msg)s: <em>(%(nb_files_limit_msg)s. %(file_size_limit_msg)s)</em></small><br />
<input class="multi max-%(CFG_WEBCOMMENT_MAX_ATTACHED_FILES)s" type="file" name="commentattachment[]"/><br />
<noscript>
<input type="file" name="commentattachment[]" /><br />
</noscript>
</div>
''' % \
{'CFG_WEBCOMMENT_MAX_ATTACHED_FILES': CFG_WEBCOMMENT_MAX_ATTACHED_FILES,
'attach_msg': CFG_WEBCOMMENT_MAX_ATTACHED_FILES == 1 and _("Optionally, attach a file to this comment") or \
_("Optionally, attach files to this comment"),
'nb_files_limit_msg': _("Max one file") and CFG_WEBCOMMENT_MAX_ATTACHED_FILES == 1 or \
_("Max %i files") % CFG_WEBCOMMENT_MAX_ATTACHED_FILES,
'file_size_limit_msg': CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE > 0 and _("Max %(x_nb_bytes)s per file") % {'x_nb_bytes': (CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE < 1024*1024 and (str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/1024) + 'KB') or (str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/(1024*1024)) + 'MB'))} or ''}
editor = get_html_text_editor(name='msg',
content=msg,
textual_content=textual_msg,
width='100%',
height='400px',
enabled=CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR,
file_upload_url=file_upload_url,
toolbar_set = "WebComment")
subscribe_to_discussion = ''
if not user_is_subscribed_to_discussion:
# Offer to subscribe to discussion
subscribe_to_discussion = '<small><input type="checkbox" name="subscribe" id="subscribe"/><label for="subscribe">%s</label></small>' % _("Send me an email when a new comment is posted")
form = """<div id="comment-write"><h2>%(add_comment)s</h2>
%(editor)s
<br />
%(simple_attach_file_interface)s
<span class="reportabuse">%(note)s</span>
<div class="submit-area">
%(subscribe_to_discussion)s<br />
<input class="adminbutton" type="submit" value="Add comment" onclick="user_must_confirm_before_leaving_page = false;return true;"/>
%(reply_to)s
</div>
""" % {'note': note,
'record_label': _("Article") + ":",
'comment_label': _("Comment") + ":",
'add_comment': _('Add comment'),
'editor': editor,
'subscribe_to_discussion': subscribe_to_discussion,
'reply_to': reply_to and '<input type="hidden" name="comid" value="%s"/>' % reply_to or '',
'simple_attach_file_interface': simple_attach_file_interface}
- form_link = "%(siteurl)s/record/%(recID)s/comments/%(function)s?%(arguments)s" % link_dic
+ form_link = "%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/comments/%(function)s?%(arguments)s" % link_dic
form = self.create_write_comment_hiddenform(action=form_link, method="post", text=form, button='Add comment',
enctype='multipart/form-data', form_id='cmtForm',
form_name='cmtForm')
form += '</div>'
return warnings + form + self.tmpl_page_do_not_leave_comment_page_js(ln=ln)
def tmpl_add_comment_form_with_ranking(self, recID, uid, nickname, ln, msg, score, note,
warnings, textual_msg=None, show_title_p=False,
can_attach_files=False):
"""
Add form for reviews
@param recID: record id
@param uid: user id
@param ln: language
@param msg: comment body contents for when refreshing due to warning
@param textual_msg: the textual version of 'msg' when user cannot display FCKeditor
@param score: review score
@param note: review title
@param warnings: list of warning tuples (warning_msg, color)
@param show_title_p: if True, prefix the form with "Add Review" as title
@param can_attach_files: if user can upload attach file to record or not
@return: html add review form
"""
_ = gettext_set_language(ln)
link_dic = { 'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'module' : 'comments',
'function' : 'add',
'arguments' : 'ln=%s&amp;action=%s' % (ln, 'SUBMIT'),
'recID' : recID}
warnings = self.tmpl_warnings(warnings, ln)
if textual_msg is None:
textual_msg = msg
#from search_engine import print_record
#record_details = print_record(recID=recID, format='hb', ln=ln)
if nickname:
note_label = _("Note: Your nickname, %s, will be displayed as the author of this review.")
note_label %= ('<i>' + nickname + '</i>')
else:
(uid, nickname, display) = get_user_info(uid)
link = '<a href="%s/youraccount/edit">' % CFG_SITE_SECURE_URL
note_label = _("Note: you have not %(x_url_open)sdefined your nickname%(x_url_close)s. %(x_nickname)s will be displayed as the author of this comment.") % \
{'x_url_open': link,
'x_url_close': '</a>',
'x_nickname': ' <br /><i>' + display + '</i>'}
selected0 = ''
selected1 = ''
selected2 = ''
selected3 = ''
selected4 = ''
selected5 = ''
if score == 0:
selected0 = ' selected="selected"'
elif score == 1:
selected1 = ' selected="selected"'
elif score == 2:
selected2 = ' selected="selected"'
elif score == 3:
selected3 = ' selected="selected"'
elif score == 4:
selected4 = ' selected="selected"'
elif score == 5:
selected5 = ' selected="selected"'
## file_upload_url = None
## if can_attach_files:
-## file_upload_url = '%s/record/%i/comments/attachments/put' % \
-## (CFG_SITE_URL, recID)
+## file_upload_url = '%s/%s/%i/comments/attachments/put' % \
+## (CFG_SITE_URL, CFG_SITE_RECORD, recID)
editor = get_html_text_editor(name='msg',
content=msg,
textual_content=msg,
width='90%',
height='400px',
enabled=CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR,
# file_upload_url=file_upload_url,
toolbar_set = "WebComment")
form = """%(add_review)s
<table style="width: 100%%">
<tr>
<td style="padding-bottom: 10px;">%(rate_label)s:
<select name=\"score\" size=\"1\">
<option value=\"0\"%(selected0)s>-%(select_label)s-</option>
<option value=\"5\"%(selected5)s>***** (best)</option>
<option value=\"4\"%(selected4)s>****</option>
<option value=\"3\"%(selected3)s>***</option>
<option value=\"2\"%(selected2)s>**</option>
<option value=\"1\"%(selected1)s>* (worst)</option>
</select>
</td>
</tr>
<tr>
<td>%(title_label)s:</td>
</tr>
<tr>
<td style="padding-bottom: 10px;">
<input type="text" name="note" maxlength="250" style="width:90%%" value="%(note)s" />
</td>
</tr>
<tr>
<td>%(write_label)s:</td>
</tr>
<tr>
<td>
%(editor)s
</td>
</tr>
<tr>
<td class="reportabuse">%(note_label)s</td></tr>
</table>
""" % {'article_label': _('Article'),
'rate_label': _("Rate this article"),
'select_label': _("Select a score"),
'title_label': _("Give a title to your review"),
'write_label': _("Write your review"),
'note_label': note_label,
'note' : note!='' and note or "",
'msg' : msg!='' and msg or "",
#'record' : record_details
'add_review': show_title_p and ('<h2>'+_('Add review')+'</h2>') or '',
'selected0': selected0,
'selected1': selected1,
'selected2': selected2,
'selected3': selected3,
'selected4': selected4,
'selected5': selected5,
'editor': editor,
}
- form_link = "%(siteurl)s/record/%(recID)s/reviews/%(function)s?%(arguments)s" % link_dic
+ form_link = "%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/reviews/%(function)s?%(arguments)s" % link_dic
form = self.createhiddenform(action=form_link, method="post", text=form, button=_('Add Review'))
return warnings + form
def tmpl_add_comment_successful(self, recID, ln, reviews, warnings, success):
"""
@param recID: record id
@param ln: language
@return: html page of successfully added comment/review
"""
_ = gettext_set_language(ln)
link_dic = { 'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'module' : 'comments',
'function' : 'display',
'arguments' : 'ln=%s&amp;do=od' % ln,
'recID' : recID,
'discussion': reviews == 1 and 'reviews' or 'comments'}
- link = "%(siteurl)s/record/%(recID)s/%(discussion)s/%(function)s?%(arguments)s" % link_dic
+ link = "%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/%(discussion)s/%(function)s?%(arguments)s" % link_dic
if warnings:
out = self.tmpl_warnings(warnings, ln) + '<br /><br />'
else:
if reviews:
out = _("Your review was successfully added.") + '<br /><br />'
else:
out = _("Your comment was successfully added.") + '<br /><br />'
link += "#%s" % success
out += '<a href="%s">' % link
out += _('Back to record') + '</a>'
return out
def tmpl_create_multiple_actions_form(self,
form_name="",
form_action="",
method="get",
action_display={},
action_field_name="",
button_label="",
button_name="",
content="",
**hidden):
""" Creates an HTML form with a multiple choice of actions and a button to select it.
@param form_action: link to the receiver of the formular
@param form_name: name of the HTML formular
@param method: either 'GET' or 'POST'
@param action_display: dictionary of actions.
action is HTML name (name of action)
display is the string provided in the popup
@param action_field_name: html name of action field
@param button_label: what's written on the button
@param button_name: html name of the button
@param content: what's inside te formular
@param **hidden: dictionary of name/value pairs of hidden fields.
"""
output = """
<form action="%s" method="%s">""" % (form_action, method)
output += """
<table>
<tr>
<td style="vertical-align: top" colspan="2">
"""
output += content + '\n'
for key in hidden.keys():
if type(hidden[key]) is list:
for value in hidden[key]:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, value)
else:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, hidden[key])
output += """
</td>
</tr>
<tr>
<td style="text-align:right;">"""
if type(action_display) is dict and len(action_display.keys()):
output += """
<select name="%s">""" % action_field_name
for (key, value) in action_display.items():
output += """
<option value="%s">%s</option>""" % (key, value)
output += """
</select>"""
output += """
</td>
<td style="text-align:left;">
<input class="adminbutton" type="submit" value="%s" name="%s"/>""" % (button_label, button_name)
output += """
</td>
</tr>
</table>
</form>"""
return output
def tmpl_admin_index(self, ln):
"""
Index page
"""
# load the right message language
_ = gettext_set_language(ln)
out = '<ol>'
if CFG_WEBCOMMENT_ALLOW_COMMENTS or CFG_WEBCOMMENT_ALLOW_REVIEWS:
if CFG_WEBCOMMENT_ALLOW_COMMENTS:
out += '<h3>Comments status</h3>'
out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/hot?ln=%(ln)s&amp;comments=1">%(hot_cmt_label)s</a></li>' % \
{'siteurl': CFG_SITE_URL, 'ln': ln, 'hot_cmt_label': _("View most commented records")}
out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/latest?ln=%(ln)s&amp;comments=1">%(latest_cmt_label)s</a></li>' % \
{'siteurl': CFG_SITE_URL, 'ln': ln, 'latest_cmt_label': _("View latest commented records")}
out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/comments?ln=%(ln)s&amp;reviews=0">%(reported_cmt_label)s</a></li>' % \
{'siteurl': CFG_SITE_URL, 'ln': ln, 'reported_cmt_label': _("View all comments reported as abuse")}
if CFG_WEBCOMMENT_ALLOW_REVIEWS:
out += '<h3>Reviews status</h3>'
out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/hot?ln=%(ln)s&amp;comments=0">%(hot_rev_label)s</a></li>' % \
{'siteurl': CFG_SITE_URL, 'ln': ln, 'hot_rev_label': _("View most reviewed records")}
out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/latest?ln=%(ln)s&amp;comments=0">%(latest_rev_label)s</a></li>' % \
{'siteurl': CFG_SITE_URL, 'ln': ln, 'latest_rev_label': _("View latest reviewed records")}
out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/comments?ln=%(ln)s&amp;reviews=1">%(reported_rev_label)s</a></li>' % \
{'siteurl': CFG_SITE_URL, 'ln': ln, 'reported_rev_label': _("View all reviews reported as abuse")}
#<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/delete?ln=%(ln)s&amp;comid=-1">%(delete_label)s</a></li>
out +="""
<h3>General</h3>
<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/users?ln=%(ln)s">%(view_users)s</a></li>
<li><a href="%(siteurl)s/help/admin/webcomment-admin-guide">%(guide)s</a></li>
""" % {'siteurl' : CFG_SITE_URL,
#'delete_label': _("Delete/Undelete comment(s) or suppress abuse report(s)"),
'view_users': _("View all users who have been reported"),
'ln' : ln,
'guide' : _("Guide")}
else:
out += _("Comments and reviews are disabled") + '<br />'
out += '</ol>'
from invenio.bibrankadminlib import addadminbox
return addadminbox('<b>%s</b>'% _("Menu"), [out])
def tmpl_admin_delete_form(self, ln, warnings):
"""
Display admin interface to fetch list of records to delete
@param warnings: list of warning_tuples where warning_tuple is (warning_message, text_color)
see tmpl_warnings, color is optional
"""
# load the right message language
_ = gettext_set_language(ln)
warnings = self.tmpl_warnings(warnings, ln)
out = '''
<br />
%s<br />
<br />'''% _("Please enter the ID of the comment/review so that you can view it before deciding whether to delete it or not")
form = '''
<table>
<tr>
<td>%s</td>
<td><input type=text name="comid" size="10" maxlength="10" value="" /></td>
</tr>
<tr>
<td><br /></td>
<tr>
</table>
<br />
%s <br/>
<br />
<table>
<tr>
<td>%s</td>
<td><input type=text name="recid" size="10" maxlength="10" value="" /></td>
</tr>
<tr>
<td><br /></td>
<tr>
</table>
<br />
''' % (_("Comment ID:"),
_("Or enter a record ID to list all the associated comments/reviews:"),
_("Record ID:"))
form_link = "%s/admin/webcomment/webcommentadmin.py/delete?ln=%s" % (CFG_SITE_URL, ln)
form = self.createhiddenform(action=form_link, method="get", text=form, button=_('View Comment'))
return warnings + out + form
def tmpl_admin_users(self, ln, users_data):
"""
@param users_data: tuple of ct, i.e. (ct, ct, ...)
where ct is a tuple (total_number_reported, total_comments_reported, total_reviews_reported, total_nb_votes_yes_of_reported,
total_nb_votes_total_of_reported, user_id, user_email, user_nickname)
sorted by order of ct having highest total_number_reported
"""
_ = gettext_set_language(ln)
u_reports = 0
u_comment_reports = 1
u_reviews_reports = 2
u_nb_votes_yes = 3
u_nb_votes_total = 4
u_uid = 5
u_email = 6
u_nickname = 7
if not users_data:
return self.tmpl_warnings([(_("There have been no reports so far."), 'green')])
user_rows = ""
for utuple in users_data:
com_label = _("View all %s reported comments") % utuple[u_comment_reports]
com_link = '''<a href="%s/admin/webcomment/webcommentadmin.py/comments?ln=%s&amp;uid=%s&amp;reviews=0">%s</a><br />''' % \
(CFG_SITE_URL, ln, utuple[u_uid], com_label)
rev_label = _("View all %s reported reviews") % utuple[u_reviews_reports]
rev_link = '''<a href="%s/admin/webcomment/webcommentadmin.py/comments?ln=%s&amp;uid=%s&amp;reviews=1">%s</a>''' % \
(CFG_SITE_URL, ln, utuple[u_uid], rev_label)
if not utuple[u_nickname]:
user_info = get_user_info(utuple[u_uid])
nickname = user_info[2]
else:
nickname = utuple[u_nickname]
if CFG_WEBCOMMENT_ALLOW_REVIEWS:
review_row = """
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>"""
review_row %= (utuple[u_nb_votes_yes],
utuple[u_nb_votes_total] - utuple[u_nb_votes_yes],
utuple[u_nb_votes_total])
else:
review_row = ''
user_rows += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(nickname)s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(email)s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(uid)s</td>%(review_row)s
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray; font-weight: bold;">%(reports)s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(com_link)s%(rev_link)s</td>
</tr>""" % { 'nickname' : nickname,
'email' : utuple[u_email],
'uid' : utuple[u_uid],
'reports' : utuple[u_reports],
'review_row': review_row,
'siteurl' : CFG_SITE_URL,
'ln' : ln,
'com_link' : CFG_WEBCOMMENT_ALLOW_COMMENTS and com_link or "",
'rev_link' : CFG_WEBCOMMENT_ALLOW_REVIEWS and rev_link or ""
}
out = "<br />"
out += _("Here is a list, sorted by total number of reports, of all users who have had a comment reported at least once.")
out += """
<br />
<br />
<table class="admin_wvar" style="width: 100%%;">
<thead>
<tr class="adminheaderleft">
<th>"""
out += _("Nickname") + '</th>\n'
out += '<th>' + _("Email") + '</th>\n'
out += '<th>' + _("User ID") + '</th>\n'
if CFG_WEBCOMMENT_ALLOW_REVIEWS > 0:
out += '<th>' + _("Number positive votes") + '</th>\n'
out += '<th>' + _("Number negative votes") + '</th>\n'
out += '<th>' + _("Total number votes") + '</th>\n'
out += '<th>' + _("Total number of reports") + '</th>\n'
out += '<th>' + _("View all user's reported comments/reviews") + '</th>\n'
out += """
</tr>
</thead>
<tbody>%s
</tbody>
</table>
""" % user_rows
return out
def tmpl_admin_select_comment_checkbox(self, cmt_id):
""" outputs a checkbox named "comidXX" where XX is cmt_id """
return '<input type="checkbox" name="comid%i" />' % int(cmt_id)
def tmpl_admin_user_info(self, ln, nickname, uid, email):
""" prepares informations about a user"""
_ = gettext_set_language(ln)
out = """
%(nickname_label)s: %(messaging)s<br />
%(uid_label)s: %(uid)i<br />
%(email_label)s: <a href="mailto:%(email)s">%(email)s</a>"""
out %= {'nickname_label': _("Nickname"),
'messaging': self.create_messaging_link(uid, nickname, ln),
'uid_label': _("User ID"),
'uid': int(uid),
'email_label': _("Email"),
'email': email}
return out
def tmpl_admin_review_info(self, ln, reviews, nb_reports, cmt_id, rec_id, status):
""" outputs information about a review """
_ = gettext_set_language(ln)
if reviews:
reported_label = _("This review has been reported %i times")
else:
reported_label = _("This comment has been reported %i times")
reported_label %= int(nb_reports)
out = """
%(reported_label)s<br />
-<a href="%(siteurl)s/record/%(rec_id)i?ln=%(ln)s">%(rec_id_label)s</a><br />
+<a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(rec_id)i?ln=%(ln)s">%(rec_id_label)s</a><br />
%(cmt_id_label)s"""
out %= {'reported_label': reported_label,
'rec_id_label': _("Record") + ' #' + str(rec_id),
'siteurl': CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'rec_id': int(rec_id),
'cmt_id_label': _("Comment") + ' #' + str(cmt_id),
'ln': ln}
if status in ['dm', 'da']:
out += '<br /><div style="color:red;">Marked as deleted</div>'
return out
def tmpl_admin_latest(self, ln, comment_data, comments, error, user_collections, collection):
"""
@param comment_data: same type of tuple as that
which is return by webcommentadminlib.py/query_get_latest i.e.
tuple (nickname, uid, date_creation, body, id) if latest comments or
tuple (nickname, uid, date_creation, body, star_score, id) if latest reviews
"""
_ = gettext_set_language(ln)
out = """
<script type='text/javascript'>
function collectionChange()
{
document.collection_form.submit();
}
</script>
"""
out += '<form method="get" name="collection_form" action="%s/admin/webcomment/webcommentadmin.py/latest?ln=%s&comments=%s">' % (CFG_SITE_URL, ln, comments)
out += '<input type="hidden" name="ln" value=%s>' % ln
out += '<input type="hidden" name="comments" value=%s>' % comments
out += '<div> Filter by collection: <select name="collection" onchange="javascript:collectionChange();">'
for collection_name in user_collections:
if collection_name == collection:
out += '<option "SELECTED" value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)}
else:
out += '<option value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)}
out += '</select></div></form><br />'
if error == 1:
out += "<i>User is not authorized to view such collection.</i><br />"
return out
elif error == 2:
out += "<i>There are no %s for this collection.</i><br />" % (comments and 'comments' or 'reviews')
return out
out += """
<ol>
"""
for (cmt_tuple, meta_data) in comment_data:
bibrec_id = meta_data[3]
content = format_record(bibrec_id, "hs")
if not comments:
out += """
<li> %(content)s <br/> <span class="moreinfo"> <a class="moreinfo" href=%(comment_url)s> reviewed by %(user)s</a>
(%(stars)s) \"%(body)s\" on <i> %(date)s </i></li> </span> <br/>
""" % {'content': content,
- 'comment_url': CFG_SITE_URL + '/record/' + str(bibrec_id) + '/reviews',
+ 'comment_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(bibrec_id) + '/reviews',
'user':cmt_tuple[0] ,
'stars': '*' * int(cmt_tuple[4]) ,
'body': cmt_tuple[3][:20] + '...',
'date': cmt_tuple[2]}
else:
out += """
<li> %(content)s <br/> <span class="moreinfo"> <a class="moreinfo" href=%(comment_url)s> commented by %(user)s</a>,
\"%(body)s\" on <i> %(date)s </i></li> </span> <br/>
""" % {'content': content,
- 'comment_url': CFG_SITE_URL + '/record/' + str(bibrec_id) + '/comments',
+ 'comment_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(bibrec_id) + '/comments',
'user':cmt_tuple[0] ,
'body': cmt_tuple[3][:20] + '...',
'date': cmt_tuple[2]}
out += """</ol>"""
return out
def tmpl_admin_hot(self, ln, comment_data, comments, error, user_collections, collection):
"""
@param comment_data: same type of tuple as that
which is return by webcommentadminlib.py/query_get_hot i.e.
tuple (id_bibrec, date_last_comment, users, count)
"""
_ = gettext_set_language(ln)
out = """
<script type='text/javascript'>
function collectionChange()
{
document.collection_form.submit();
}
</script>
"""
out += '<form method="get" name="collection_form" action="%s/admin/webcomment/webcommentadmin.py/hot?ln=%s&comments=%s">' % (CFG_SITE_URL, ln, comments)
out += '<input type="hidden" name="ln" value=%s>' % ln
out += '<input type="hidden" name="comments" value=%s>' % comments
out += '<div> Filter by collection: <select name="collection" onchange="javascript:collectionChange();">'
for collection_name in user_collections:
if collection_name == collection:
out += '<option "SELECTED" value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)}
else:
out += '<option value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)}
out += '</select></div></form><br />'
if error == 1:
out += "<i>User is not authorized to view such collection.</i><br />"
return out
elif error == 2:
out += "<i>There are no %s for this collection.</i><br />" % (comments and 'comments' or 'reviews')
return out
for cmt_tuple in comment_data:
bibrec_id = cmt_tuple[0]
content = format_record(bibrec_id, "hs")
last_comment_date = cmt_tuple[1]
total_users = cmt_tuple[2]
total_comments = cmt_tuple[3]
if comments:
- comment_url = CFG_SITE_URL + '/record/' + str(bibrec_id) + '/comments'
+ comment_url = CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(bibrec_id) + '/comments'
str_comment = int(total_comments) > 1 and 'comments' or 'comment'
else:
- comment_url = CFG_SITE_URL + '/record/' + str(bibrec_id) + '/reviews'
+ comment_url = CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(bibrec_id) + '/reviews'
str_comment = int(total_comments) > 1 and 'reviews' or 'review'
out += """
<li> %(content)s <br/> <span class="moreinfo"> <a class="moreinfo" href=%(comment_url)s> %(total_comments)s
%(str_comment)s</a>
(%(total_users)s %(user)s), latest on <i> %(last_comment_date)s </i></li> </span> <br/>
""" % {'content': content,
'comment_url': comment_url ,
'total_comments': total_comments,
'str_comment': str_comment,
'total_users': total_users,
'user': int(total_users) > 1 and 'users' or 'user',
'last_comment_date': last_comment_date}
out += """</ol>"""
return out
def tmpl_admin_comments(self, ln, uid, comID, recID, comment_data, reviews, error, user_collections, collection):
"""
@param comment_data: same type of tuple as that
which is returned by webcomment.py/query_retrieve_comments_or_remarks i.e.
tuple of comment where comment is
tuple (nickname,
date_creation,
body,
id) if ranking disabled or
tuple (nickname,
date_creation,
body,
nb_votes_yes,
nb_votes_total,
star_score,
title,
id)
"""
_ = gettext_set_language(ln)
coll_form = """
<script type='text/javascript'>
function collectionChange()
{
document.collection_form.submit();
}
</script>
"""
coll_form += '<form method="get" name="collection_form" action="%s/admin/webcomment/webcommentadmin.py/comments?ln=%s&reviews=%s">' % (CFG_SITE_URL, ln, reviews)
coll_form += '<input type="hidden" name="ln" value=%s>' % ln
coll_form += '<input type="hidden" name="reviews" value=%s>' % reviews
coll_form += '<div> Filter by collection: <select name="collection" onchange="javascript:collectionChange();">'
for collection_name in user_collections:
if collection_name == collection:
coll_form += '<option "SELECTED" value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)}
else:
coll_form += '<option value="%(collection_name)s">%(collection_name)s</option>' % {'collection_name': cgi.escape(collection_name)}
coll_form += '</select></div></form><br />'
if error == 1:
coll_form += "<i>User is not authorized to view such collection.</i><br />"
return coll_form
elif error == 2:
coll_form += "<i>There are no %s for this collection.</i><br />" % (reviews and 'reviews' or 'comments')
return coll_form
comments = []
comments_info = []
checkboxes = []
users = []
for (cmt_tuple, meta_data) in comment_data:
if reviews:
comments.append(self.tmpl_get_comment_with_ranking(None,#request object
ln,
cmt_tuple[0],#nickname
cmt_tuple[1],#userid
cmt_tuple[2],#date_creation
cmt_tuple[3],#body
cmt_tuple[9],#status
0,
cmt_tuple[5],#nb_votes_total
cmt_tuple[4],#nb_votes_yes
cmt_tuple[6],#star_score
cmt_tuple[7]))#title
else:
comments.append(self.tmpl_get_comment_without_ranking(None,#request object
ln,
cmt_tuple[0],#nickname
cmt_tuple[1],#userid
cmt_tuple[2],#date_creation
cmt_tuple[3],#body
cmt_tuple[5],#status
0,
None, #reply_link
None, #report_link
None, #undelete_link
None)) #delete_links
users.append(self.tmpl_admin_user_info(ln,
meta_data[0], #nickname
meta_data[1], #uid
meta_data[2]))#email
if reviews:
status = cmt_tuple[9]
else:
status = cmt_tuple[5]
comments_info.append(self.tmpl_admin_review_info(ln,
reviews,
meta_data[5], # nb abuse reports
meta_data[3], # cmt_id
meta_data[4], # rec_id
status)) # status
checkboxes.append(self.tmpl_admin_select_comment_checkbox(meta_data[3]))
form_link = "%s/admin/webcomment/webcommentadmin.py/del_com?ln=%s" % (CFG_SITE_URL, ln)
out = """
<table class="admin_wvar" style="width:100%%;">
<thead>
<tr class="adminheaderleft">
<th>%(review_label)s</th>
<th>%(written_by_label)s</th>
<th>%(review_info_label)s</th>
<th>%(select_label)s</th>
</tr>
</thead>
<tbody>""" % {'review_label': reviews and _("Review") or _("Comment"),
'written_by_label': _("Written by"),
'review_info_label': _("General informations"),
'select_label': _("Select")}
for i in range (0, len(comments)):
out += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintd" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
</tr>""" % (comments[i], users[i], comments_info[i], checkboxes[i])
out += """
</tbody>
</table>"""
if reviews:
action_display = {
'delete': _('Delete selected reviews'),
'unreport': _('Suppress selected abuse report'),
'undelete': _('Undelete selected reviews')
}
else:
action_display = {
'undelete': _('Undelete selected comments'),
'delete': _('Delete selected comments'),
'unreport': _('Suppress selected abuse report')
}
form = self.tmpl_create_multiple_actions_form(form_name="admin_comment",
form_action=form_link,
method="post",
action_display=action_display,
action_field_name='action',
button_label=_("OK"),
button_name="okbutton",
content=out)
if uid > 0:
header = '<br />'
if reviews:
header += _("Here are the reported reviews of user %s") % uid
else:
header += _("Here are the reported comments of user %s") % uid
header += '<br /><br />'
if comID > 0 and recID <= 0 and uid <= 0:
if reviews:
header = '<br />' +_("Here is review %s")% comID + '<br /><br />'
else:
header = '<br />' +_("Here is comment %s")% comID + '<br /><br />'
if uid > 0 and comID > 0 and recID <= 0:
if reviews:
header = '<br />' + _("Here is review %(x_cmtID)s written by user %(x_user)s") % {'x_cmtID': comID, 'x_user': uid}
else:
header = '<br />' + _("Here is comment %(x_cmtID)s written by user %(x_user)s") % {'x_cmtID': comID, 'x_user': uid}
header += '<br/ ><br />'
if comID <= 0 and recID <= 0 and uid <= 0:
header = '<br />'
if reviews:
header += _("Here are all reported reviews sorted by the most reported")
else:
header += _("Here are all reported comments sorted by the most reported")
header += "<br /><br />"
elif recID > 0:
header = '<br />'
if reviews:
header += _("Here are all reviews for record %i, sorted by the most reported" % recID)
header += '<br /><a href="%s/admin/webcomment/webcommentadmin.py/delete?comid=&recid=%s&amp;reviews=0">%s</a>' % (CFG_SITE_URL, recID, _("Show comments"))
else:
header += _("Here are all comments for record %i, sorted by the most reported" % recID)
header += '<br /><a href="%s/admin/webcomment/webcommentadmin.py/delete?comid=&recid=%s&amp;reviews=1">%s</a>' % (CFG_SITE_URL, recID, _("Show reviews"))
header += "<br /><br />"
return coll_form + header + form
def tmpl_admin_del_com(self, del_res, ln=CFG_SITE_LANG):
"""
@param del_res: list of the following tuple (comment_id, was_successfully_deleted),
was_successfully_deleted is boolean (0=false, >0=true
"""
_ = gettext_set_language(ln)
table_rows = ''
for deltuple in del_res:
table_rows += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
</tr>""" % (deltuple[0], deltuple[1]>0 and _("Yes") or "<span class=\"important\">" +_("No") + "</span>")
out = """
<table class="admin_wvar">
<tr class="adminheaderleft">
<td style="padding-right:10px;">%s</td>
<td>%s</td>
</tr>%s
<table>""" % (_("comment ID"), _("successfully deleted"), table_rows)
return out
def tmpl_admin_undel_com(self, del_res, ln=CFG_SITE_LANG):
"""
@param del_res: list of the following tuple (comment_id, was_successfully_undeleted),
was_successfully_undeleted is boolean (0=false, >0=true
"""
_ = gettext_set_language(ln)
table_rows = ''
for deltuple in del_res:
table_rows += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
</tr>""" % (deltuple[0], deltuple[1]>0 and _("Yes") or "<span class=\"important\">" +_("No") + "</span>")
out = """
<table class="admin_wvar">
<tr class="adminheaderleft">
<td style="padding-right:10px;">%s</td>
<td>%s</td>
</tr>%s
<table>""" % (_("comment ID"), _("successfully undeleted"), table_rows)
return out
def tmpl_admin_suppress_abuse_report(self, del_res, ln=CFG_SITE_LANG):
"""
@param del_res: list of the following tuple (comment_id, was_successfully_deleted),
was_successfully_deleted is boolean (0=false, >0=true
"""
_ = gettext_set_language(ln)
table_rows = ''
for deltuple in del_res:
table_rows += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
</tr>""" % (deltuple[0], deltuple[1]>0 and _("Yes") or "<span class=\"important\">" +_("No") + "</span>")
out = """
<table class="admin_wvar">
<tr class="adminheaderleft">
<td style ="padding-right: 10px;">%s</td>
<td>%s</td>
</tr>%s
<table>""" % (_("comment ID"), _("successfully suppressed abuse report"), table_rows)
return out
def tmpl_mini_review(self, recID, ln=CFG_SITE_LANG, action='SUBMIT',
avg_score=0, nb_comments_total=0):
"""Display the mini version of reviews (only the grading part)"""
_ = gettext_set_language(ln)
- url = '%s/record/%s/reviews/add?ln=%s&amp;action=%s' % (CFG_SITE_URL, recID, ln, action)
+ url = '%s/%s/%s/reviews/add?ln=%s&amp;action=%s' % (CFG_SITE_URL, CFG_SITE_RECORD, recID, ln, action)
if avg_score > 0:
score = _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \
{'x_nb_score': '<b>%.1f</b>' % avg_score,
'x_nb_reviews': nb_comments_total}
else:
score = '(' +_("Not yet reviewed") + ')'
if avg_score == 5:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', 'full'
elif avg_score >= 4.5:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', 'half'
elif avg_score >= 4:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', ''
elif avg_score >= 3.5:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'half', ''
elif avg_score >= 3:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', '', ''
elif avg_score >= 2.5:
s1, s2, s3, s4, s5 = 'full', 'full', 'half', '', ''
elif avg_score >= 2:
s1, s2, s3, s4, s5 = 'full', 'full', '', '', ''
elif avg_score >= 1.5:
s1, s2, s3, s4, s5 = 'full', 'half', '', '', ''
elif avg_score == 1:
s1, s2, s3, s4, s5 = 'full', '', '', '', ''
else:
s1, s2, s3, s4, s5 = '', '', '', '', ''
out = '''
<small class="detailedRecordActions">%(rate)s:</small><br /><br />
<div style="margin:auto;width:160px;">
<span style="display:none;">Rate this document:</span>
<div class="star %(s1)s" ><a href="%(url)s&amp;score=1">1</a>
<div class="star %(s2)s" ><a href="%(url)s&amp;score=2">2</a>
<div class="star %(s3)s" ><a href="%(url)s&amp;score=3">3</a>
<div class="star %(s4)s" ><a href="%(url)s&amp;score=4">4</a>
<div class="star %(s5)s" ><a href="%(url)s&amp;score=5">5</a></div></div></div></div></div>
<div style="clear:both">&nbsp;</div>
</div>
<small>%(score)s</small>
''' % {'url': url,
'score': score,
'rate': _("Rate this document"),
's1': s1,
's2': s2,
's3': s3,
's4': s4,
's5': s5
}
return out
def tmpl_email_new_comment_header(self, recID, title, reviews,
comID, report_numbers,
can_unsubscribe=True,
ln=CFG_SITE_LANG, uid=-1):
"""
Prints the email header used to notify subscribers that a new
comment/review was added.
@param recid: the ID of the commented/reviewed record
@param title: the title of the commented/reviewed record
@param reviews: True if it is a review, else if a comment
@param comID: the comment ID
@param report_numbers: the report number(s) of the record
@param can_unsubscribe: True if user can unsubscribe from alert
@param ln: language
"""
# load the right message language
_ = gettext_set_language(ln)
user_info = collect_user_info(uid)
out = _("Hello:") + '\n\n' + \
(reviews and _("The following review was sent to %(CFG_SITE_NAME)s by %(user_nickname)s:") or \
_("The following comment was sent to %(CFG_SITE_NAME)s by %(user_nickname)s:")) % \
{'CFG_SITE_NAME': CFG_SITE_NAME,
'user_nickname': user_info['nickname']}
- out += '\n(<%s>)' % (CFG_SITE_URL + '/record/' + str(recID))
+ out += '\n(<%s>)' % (CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID))
out += '\n\n\n'
return out
def tmpl_email_new_comment_footer(self, recID, title, reviews,
comID, report_numbers,
can_unsubscribe=True,
ln=CFG_SITE_LANG):
"""
Prints the email footer used to notify subscribers that a new
comment/review was added.
@param recid: the ID of the commented/reviewed record
@param title: the title of the commented/reviewed record
@param reviews: True if it is a review, else if a comment
@param comID: the comment ID
@param report_numbers: the report number(s) of the record
@param can_unsubscribe: True if user can unsubscribe from alert
@param ln: language
"""
# load the right message language
_ = gettext_set_language(ln)
out = '\n\n-- \n'
out += _("This is an automatic message, please don't reply to it.")
out += '\n'
out += _("To post another comment, go to <%(x_url)s> instead.") % \
- {'x_url': CFG_SITE_URL + '/record/' + str(recID) + \
+ {'x_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID) + \
(reviews and '/reviews' or '/comments') + '/add'}
out += '\n'
if not reviews:
out += _("To specifically reply to this comment, go to <%(x_url)s>") % \
- {'x_url': CFG_SITE_URL + '/record/' + str(recID) + \
+ {'x_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID) + \
'/comments/add?action=REPLY&comid=' + str(comID)}
out += '\n'
if can_unsubscribe:
out += _("To unsubscribe from this discussion, go to <%(x_url)s>") % \
- {'x_url': CFG_SITE_URL + '/record/' + str(recID) + \
+ {'x_url': CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID) + \
'/comments/unsubscribe'}
out += '\n'
out += _("For any question, please use <%(CFG_SITE_SUPPORT_EMAIL)s>") % \
{'CFG_SITE_SUPPORT_EMAIL': CFG_SITE_SUPPORT_EMAIL}
return out
def tmpl_email_new_comment_admin(self, recID):
"""
Prints the record information used in the email to notify the
system administrator that a new comment has been posted.
@param recID: the ID of the commented/reviewed record
"""
out = ""
title = get_fieldvalues(recID, "245__a")
authors = ', '.join(get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a"))
#res_author = ""
#res_rep_num = ""
#for author in authors:
# res_author = res_author + ' ' + author
dates = get_fieldvalues(recID, "260__c")
report_nums = get_fieldvalues(recID, "037__a")
report_nums += get_fieldvalues(recID, "088__a")
report_nums = ', '.join(report_nums)
#for rep_num in report_nums:
# res_rep_num = res_rep_num + ', ' + rep_num
out += " Title = %s \n" % (title and title[0] or "No Title")
out += " Authors = %s \n" % authors
if dates:
out += " Date = %s \n" % dates[0]
out += " Report number = %s" % report_nums
return out
def tmpl_page_do_not_leave_comment_page_js(self, ln):
"""
Code to ask user confirmation when leaving the page, so that the
comment is not lost if clicking by mistake on links.
@param ln: the user language
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''
<script language="JavaScript">
var initial_comment_value = document.forms.cmtForm.msg.value;
var user_must_confirm_before_leaving_page = true;
window.onbeforeunload = confirmExit;
function confirmExit() {
var editor_type_field = document.getElementById('%(name)seditortype');
if (editor_type_field && editor_type_field.value == 'fckeditor') {
var oEditor = FCKeditorAPI.GetInstance('%(name)s');
if (user_must_confirm_before_leaving_page && oEditor.IsDirty()) {
/* Might give false positives, when editor pre-loaded
with content. But is better than the opposite */
return "%(message)s";
}
} else {
if (user_must_confirm_before_leaving_page && document.forms.cmtForm.msg.value != initial_comment_value){
return "%(message)s";
}
}
}
</script>
''' % {'message': _('Your comment will be lost.').replace('"', '\\"'),
'name': 'msg'}
return out
diff --git a/modules/webcomment/lib/webcomment_webinterface.py b/modules/webcomment/lib/webcomment_webinterface.py
index 2134d7566..547cf1a4f 100644
--- a/modules/webcomment/lib/webcomment_webinterface.py
+++ b/modules/webcomment/lib/webcomment_webinterface.py
@@ -1,813 +1,815 @@
# -*- coding: utf-8 -*-
## Comments and reviews for records.
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Comments and reviews for records: web interface """
__lastupdated__ = """$Date$"""
__revision__ = """$Id$"""
import cgi
from invenio.webcomment import check_recID_is_in_range, \
perform_request_display_comments_or_remarks, \
perform_request_add_comment_or_remark, \
perform_request_vote, \
perform_request_report, \
subscribe_user_to_discussion, \
unsubscribe_user_from_discussion, \
get_user_subscription_to_discussion, \
check_user_can_attach_file_to_comments, \
check_user_can_view_comments, \
check_user_can_send_comments, \
check_user_can_view_comment, \
query_get_comment
from invenio.config import \
CFG_TMPDIR, \
CFG_SITE_LANG, \
CFG_SITE_URL, \
CFG_PREFIX, \
CFG_WEBCOMMENT_ALLOW_COMMENTS,\
CFG_WEBCOMMENT_ALLOW_REVIEWS, \
- CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS
+ CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS, \
+ CFG_SITE_RECORD
from invenio.webuser import getUid, page_not_authorized, isGuestUser, collect_user_info
from invenio.webpage import page, pageheaderonly, pagefooteronly
from invenio.search_engine import create_navtrail_links, \
guess_primary_collection_of_a_record, \
get_colID
from invenio.urlutils import redirect_to_url, \
make_canonical_urlargd
from invenio.htmlutils import get_mathjax_header
from invenio.errorlib import register_exception
from invenio.messages import gettext_set_language
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import \
mail_cookie_create_authorize_action, \
mail_cookie_create_common, \
mail_cookie_check_common, \
InvenioWebAccessMailCookieDeletedError, \
InvenioWebAccessMailCookieError
from invenio.webcomment_config import \
CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE, \
CFG_WEBCOMMENT_MAX_ATTACHED_FILES
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
websearch_templates = invenio.template.load('websearch')
try:
from invenio.fckeditor_invenio_connector import FCKeditorConnectorInvenio
fckeditor_available = True
except ImportError, e:
fckeditor_available = False
import os
from invenio import webinterface_handler_config as apache
from invenio.bibdocfile import \
stream_file, \
decompose_file, \
propose_next_docname
class WebInterfaceCommentsPages(WebInterfaceDirectory):
"""Defines the set of /comments pages."""
_exports = ['', 'display', 'add', 'vote', 'report', 'index', 'attachments',
'subscribe', 'unsubscribe']
def __init__(self, recid=-1, reviews=0):
self.recid = recid
self.discussion = reviews # 0:comments, 1:reviews
self.attachments = WebInterfaceCommentsFiles(recid, reviews)
def index(self, req, form):
"""
Redirects to display function
"""
return self.display(req, form)
def display(self, req, form):
"""
Display comments (reviews if enabled) associated with record having id recid where recid>0.
This function can also be used to display remarks associated with basket having id recid where recid<-99.
@param ln: language
@param recid: record id, integer
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param voted: boolean, active if user voted for a review, see vote function
@param reported: int, active if user reported a certain comment/review, see report function
@param reviews: boolean, enabled for reviews, disabled for comments
@param subscribed: int, 1 if user just subscribed to discussion, -1 if unsubscribed
@return the full html page.
"""
argd = wash_urlargd(form, {'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'voted': (int, -1),
'reported': (int, -1),
'subscribed': (int, 0),
'cmtgrp': (list, ["latest"]) # 'latest' is now a reserved group/round name
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
can_send_comments = False
(auth_code, auth_msg) = check_user_can_send_comments(user_info, self.recid)
if not auth_code:
can_send_comments = True
can_attach_files = False
(auth_code, auth_msg) = check_user_can_attach_file_to_comments(user_info, self.recid)
if not auth_code and (user_info['email'] != 'guest'):
can_attach_files = True
subscription = get_user_subscription_to_discussion(self.recid, uid)
if subscription == 1:
user_is_subscribed_to_discussion = True
user_can_unsubscribe_from_discussion = True
elif subscription == 2:
user_is_subscribed_to_discussion = True
user_can_unsubscribe_from_discussion = False
else:
user_is_subscribed_to_discussion = False
user_can_unsubscribe_from_discussion = False
#display_comment_rounds = [cmtgrp for cmtgrp in argd['cmtgrp'] if cmtgrp.isdigit() or cmtgrp == "all" or cmtgrp == "-1"]
display_comment_rounds = argd['cmtgrp']
check_warnings = []
(ok, problem) = check_recID_is_in_range(self.recid, check_warnings, argd['ln'])
if ok:
(body, errors, warnings) = perform_request_display_comments_or_remarks(req=req, recID=self.recid,
display_order=argd['do'],
display_since=argd['ds'],
nb_per_page=argd['nb'],
page=argd['p'],
ln=argd['ln'],
voted=argd['voted'],
reported=argd['reported'],
subscribed=argd['subscribed'],
reviews=self.discussion,
uid=uid,
can_send_comments=can_send_comments,
can_attach_files=can_attach_files,
user_is_subscribed_to_discussion=user_is_subscribed_to_discussion,
user_can_unsubscribe_from_discussion=user_can_unsubscribe_from_discussion,
display_comment_rounds=display_comment_rounds
)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x, y: cmp(x[1], y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
+ '%s/%s/%s/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, tab_id, link_ln), \
tab_id in ['comments', 'reviews'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
argd['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
argd['ln'])
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
if navtrail:
navtrail += ' &gt; '
- navtrail += '<a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
+ navtrail += '<a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
navtrail += ' &gt; <a class="navtrail">%s</a>' % (self.discussion==1 and _("Reviews") or _("Comments"))
mathjaxheader = ''
if CFG_WEBCOMMENT_USE_MATHJAX_IN_COMMENTS:
mathjaxheader = get_mathjax_header()
jqueryheader = '''
<script src="%(CFG_SITE_URL)s/js/jquery.min.js" type="text/javascript" language="javascript"></script>
<script src="%(CFG_SITE_URL)s/js/jquery.MultiFile.pack.js" type="text/javascript" language="javascript"></script>
''' % {'CFG_SITE_URL': CFG_SITE_URL}
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
metaheaderadd = mathjaxheader + jqueryheader,
req=req,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
top + body + bottom + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
else:
return page(title=_("Record Not Found"),
body=problem,
uid=uid,
verbose=1,
req=req,
language=argd['ln'],
warnings=check_warnings, errors=[],
navmenuid='search')
- # Return the same page wether we ask for /record/123 or /record/123/
+ # Return the same page wether we ask for /CFG_SITE_RECORD/123 or /CFG_SITE_RECORD/123/
__call__ = index
def add(self, req, form):
"""
Add a comment (review) to record with id recid where recid>0
Also works for adding a remark to basket with id recid where recid<-99
@param ln: languange
@param recid: record id
@param action: 'DISPLAY' to display add form
'SUBMIT' to submit comment once form is filled
'REPLY' to reply to an already existing comment
@param msg: the body of the comment/review or remark
@param score: star score of the review
@param note: title of the review
@param comid: comment id, needed for replying
@param editor_type: the type of editor used for submitting the
comment: 'textarea', 'fckeditor'.
@param subscribe: if set, subscribe user to receive email
notifications when new comment are added to
this discussion
@return the full html page.
"""
argd = wash_urlargd(form, {'action': (str, "DISPLAY"),
'msg': (str, ""),
'note': (str, ''),
'score': (int, 0),
'comid': (int, 0),
'editor_type': (str, ""),
'subscribe': (str, ""),
'cookie': (str, "")
})
_ = gettext_set_language(argd['ln'])
actions = ['DISPLAY', 'REPLY', 'SUBMIT']
uid = getUid(req)
# Is site ready to accept comments?
if uid == -1 or (not CFG_WEBCOMMENT_ALLOW_COMMENTS and not CFG_WEBCOMMENT_ALLOW_REVIEWS):
return page_not_authorized(req, "../comments/add",
navmenuid='search')
# Is user allowed to post comment?
user_info = collect_user_info(req)
(auth_code_1, auth_msg_1) = check_user_can_view_comments(user_info, self.recid)
(auth_code_2, auth_msg_2) = check_user_can_send_comments(user_info, self.recid)
if isGuestUser(uid):
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
# Save user's value in cookie, so that these "POST"
# parameters are not lost during login process
msg_cookie = mail_cookie_create_common('comment_msg',
{'msg': argd['msg'],
'note': argd['note'],
'score': argd['score'],
'editor_type': argd['editor_type'],
'subscribe': argd['subscribe']},
onetime=True)
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri'] + '&cookie=' + msg_cookie}, {})
return redirect_to_url(req, target, norobot=True)
elif (auth_code_1 or auth_code_2):
return page_not_authorized(req, "../", \
text = auth_msg_1 + auth_msg_2)
user_info = collect_user_info(req)
can_attach_files = False
(auth_code, auth_msg) = check_user_can_attach_file_to_comments(user_info, self.recid)
if not auth_code and (user_info['email'] != 'guest'):
can_attach_files = True
warning_msgs = []
added_files = {}
if can_attach_files:
# User is allowed to attach files. Process the files
file_too_big = False
formfields = form.get('commentattachment[]', [])
if not hasattr(formfields, "__getitem__"): # A single file was uploaded
formfields = [formfields]
for formfield in formfields[:CFG_WEBCOMMENT_MAX_ATTACHED_FILES]:
if hasattr(formfield, "filename") and formfield.filename:
filename = formfield.filename
dir_to_open = os.path.join(CFG_TMPDIR, 'webcomment', str(uid))
try:
assert(dir_to_open.startswith(CFG_TMPDIR))
except AssertionError:
register_exception(req=req,
prefix='User #%s tried to upload file to forbidden location: %s' \
% (uid, dir_to_open))
if not os.path.exists(dir_to_open):
try:
os.makedirs(dir_to_open)
except:
register_exception(req=req, alert_admin=True)
## Before saving the file to disc, wash the filename (in particular
## washing away UNIX and Windows (e.g. DFS) paths):
filename = os.path.basename(filename.split('\\')[-1])
filename = filename.strip()
if filename != "":
# Check that file does not already exist
n = 1
while os.path.exists(os.path.join(dir_to_open, filename)):
basedir, name, extension = decompose_file(filename)
new_name = propose_next_docname(name)
filename = new_name + extension
fp = open(os.path.join(dir_to_open, filename), "w")
# FIXME: temporary, waiting for wsgi handler to be
# fixed. Once done, read chunk by chunk
## while formfield.file:
## fp.write(formfield.file.read(10240))
fp.write(formfield.file.read())
fp.close()
# Isn't this file too big?
file_size = os.path.getsize(os.path.join(dir_to_open, filename))
if CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE > 0 and \
file_size > CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE:
os.remove(os.path.join(dir_to_open, filename))
# One file is too big: record that,
# dismiss all uploaded files and re-ask to
# upload again
file_too_big = True
warning_msgs.append(('WRN_WEBCOMMENT_MAX_FILE_SIZE_REACHED', cgi.escape(filename), str(file_size/1024) + 'KB', str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/1024) + 'KB'))
else:
added_files[filename] = os.path.join(dir_to_open, filename)
if file_too_big:
# One file was too big. Removed all uploaded filed
for filepath in added_files.items():
try:
os.remove(filepath)
except:
# File was already removed or does not exist?
pass
client_ip_address = req.remote_ip
check_warnings = []
(ok, problem) = check_recID_is_in_range(self.recid, check_warnings, argd['ln'])
if ok:
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req,
self.recid,
argd['ln'])
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid))
if navtrail:
navtrail += ' &gt; '
- navtrail += '<a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
+ navtrail += '<a class="navtrail" href="%s/%s/%s?ln=%s">'% (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
- navtrail += '&gt; <a class="navtrail" href="%s/record/%s/%s/?ln=%s">%s</a>' % (CFG_SITE_URL,
+ navtrail += '&gt; <a class="navtrail" href="%s/%s/%s/%s/?ln=%s">%s</a>' % (CFG_SITE_URL,
+ CFG_SITE_RECORD,
self.recid,
self.discussion==1 and 'reviews' or 'comments',
argd['ln'],
self.discussion==1 and _('Reviews') or _('Comments'))
if argd['action'] not in actions:
argd['action'] = 'DISPLAY'
if not argd['msg']:
# User had to login in-between, so retrieve msg
# from cookie
try:
(kind, cookie_argd) = mail_cookie_check_common(argd['cookie'],
delete=True)
argd.update(cookie_argd)
except InvenioWebAccessMailCookieDeletedError, e:
- return redirect_to_url(req, CFG_SITE_URL + '/record/' + \
+ return redirect_to_url(req, CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + \
str(self.recid) + (self.discussion==1 and \
'/reviews' or '/comments'))
except InvenioWebAccessMailCookieError, e:
# Invalid or empty cookie: continue
pass
subscribe = False
if argd['subscribe'] and \
get_user_subscription_to_discussion(self.recid, uid) == 0:
# User is not already subscribed, and asked to subscribe
subscribe = True
(body, errors, warnings) = perform_request_add_comment_or_remark(recID=self.recid,
ln=argd['ln'],
uid=uid,
action=argd['action'],
msg=argd['msg'],
note=argd['note'],
score=argd['score'],
reviews=self.discussion,
comID=argd['comid'],
client_ip_address=client_ip_address,
editor_type=argd['editor_type'],
can_attach_files=can_attach_files,
subscribe=subscribe,
req=req,
attached_files=added_files,
warnings=warning_msgs)
if self.discussion:
title = _("Add Review")
else:
title = _("Add Comment")
jqueryheader = '''
<script src="%(CFG_SITE_URL)s/js/jquery.min.js" type="text/javascript" language="javascript"></script>
<script src="%(CFG_SITE_URL)s/js/jquery.MultiFile.pack.js" type="text/javascript" language="javascript"></script>
''' % {'CFG_SITE_URL': CFG_SITE_URL}
return page(title=title,
body=body,
navtrail=navtrail,
uid=uid,
language=CFG_SITE_LANG,
verbose=1,
errors=errors,
warnings=warnings,
req=req,
navmenuid='search',
metaheaderadd=jqueryheader)
# id not in range
else:
return page(title=_("Record Not Found"),
body=problem,
uid=uid,
verbose=1,
req=req,
warnings=check_warnings, errors=[],
navmenuid='search')
def vote(self, req, form):
"""
Vote positively or negatively for a comment/review.
@param comid: comment/review id
@param com_value: +1 to vote positively
-1 to vote negatively
@param recid: the id of the record the comment/review is associated with
@param ln: language
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param referer: http address of the calling function to redirect to (refresh)
@param reviews: boolean, enabled for reviews, disabled for comments
"""
argd = wash_urlargd(form, {'comid': (int, -1),
'com_value': (int, 0),
'recid': (int, -1),
'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'referer': (str, None)
})
client_ip_address = req.remote_ip
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
success = perform_request_vote(argd['comid'], client_ip_address, argd['com_value'], uid)
if argd['referer']:
argd['referer'] += "?ln=%s&amp;do=%s&amp;ds=%s&amp;nb=%s&amp;p=%s&amp;voted=%s&amp;" % (
argd['ln'], argd['do'], argd['ds'], argd['nb'], argd['p'], success)
redirect_to_url(req, argd['referer'])
else:
#Note: sent to comments display
- referer = "%s/record/%s/%s?&amp;ln=%s&amp;voted=1"
- referer %= (CFG_SITE_URL, self.recid, self.discussion == 1 and 'reviews' or 'comments', argd['ln'])
+ referer = "%s/%s/%s/%s?&amp;ln=%s&amp;voted=1"
+ referer %= (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, self.discussion == 1 and 'reviews' or 'comments', argd['ln'])
redirect_to_url(req, referer)
def report(self, req, form):
"""
Report a comment/review for inappropriate content
@param comid: comment/review id
@param recid: the id of the record the comment/review is associated with
@param ln: language
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param referer: http address of the calling function to redirect to (refresh)
@param reviews: boolean, enabled for reviews, disabled for comments
"""
argd = wash_urlargd(form, {'comid': (int, -1),
'recid': (int, -1),
'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'referer': (str, None)
})
client_ip_address = req.remote_ip
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
if auth_code or user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
success = perform_request_report(argd['comid'], client_ip_address, uid)
if argd['referer']:
argd['referer'] += "?ln=%s&amp;do=%s&amp;ds=%s&amp;nb=%s&amp;p=%s&amp;reported=%s&amp;" % (argd['ln'], argd['do'], argd['ds'], argd['nb'], argd['p'], str(success))
redirect_to_url(req, argd['referer'])
else:
#Note: sent to comments display
- referer = "%s/record/%s/%s/display?ln=%s&amp;voted=1"
- referer %= (CFG_SITE_URL, self.recid, self.discussion==1 and 'reviews' or 'comments', argd['ln'])
+ referer = "%s/%s/%s/%s/display?ln=%s&amp;voted=1"
+ referer %= (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, self.discussion==1 and 'reviews' or 'comments', argd['ln'])
redirect_to_url(req, referer)
def subscribe(self, req, form):
"""
Subscribe current user to receive email notification when new
comments are added to current discussion.
"""
argd = wash_urlargd(form, {'referer': (str, None)})
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
if isGuestUser(uid):
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
success = subscribe_user_to_discussion(self.recid, uid)
- display_url = "%s/record/%s/comments/display?subscribed=%s&ln=%s" % \
- (CFG_SITE_URL, self.recid, str(success), argd['ln'])
+ display_url = "%s/%s/%s/comments/display?subscribed=%s&ln=%s" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, str(success), argd['ln'])
redirect_to_url(req, display_url)
def unsubscribe(self, req, form):
"""
Unsubscribe current user from current discussion.
"""
argd = wash_urlargd(form, {'referer': (str, None)})
user_info = collect_user_info(req)
uid = getUid(req)
if isGuestUser(uid):
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
success = unsubscribe_user_from_discussion(self.recid, uid)
- display_url = "%s/record/%s/comments/display?subscribed=%s&ln=%s" % \
- (CFG_SITE_URL, self.recid, str(-success), argd['ln'])
+ display_url = "%s/%s/%s/comments/display?subscribed=%s&ln=%s" % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, str(-success), argd['ln'])
redirect_to_url(req, display_url)
class WebInterfaceCommentsFiles(WebInterfaceDirectory):
"""Handle <strike>upload and </strike> access to files for comments.
<strike>The upload is currently only available through the FCKeditor.</strike>
"""
#_exports = ['put'] # 'get' is handled by _lookup(..)
def __init__(self, recid=-1, reviews=0):
self.recid = recid
self.discussion = reviews # 0:comments, 1:reviews
def _lookup(self, component, path):
""" This handler is invoked for the dynamic URLs (for getting
<strike>and putting attachments</strike>) Eg:
- CFG_SITE_URL/record/5953/comments/attachments/get/652/myfile.pdf
+ CFG_SITE_URL/CFG_SITE_RECORD/5953/comments/attachments/get/652/myfile.pdf
"""
if component == 'get' and len(path) > 1:
comid = path[0] # comment ID
file_name = '/'.join(path[1:]) # the filename
def answer_get(req, form):
"""Accessing files attached to comments."""
form['file'] = file_name
form['comid'] = comid
return self._get(req, form)
return answer_get, []
# All other cases: file not found
return None, []
def _get(self, req, form):
"""
Returns a file attached to a comment.
Example:
- CFG_SITE_URL/record/5953/comments/attachments/get/652/myfile.pdf
+ CFG_SITE_URL/CFG_SITE_RECORD/5953/comments/attachments/get/652/myfile.pdf
where 652 is the comment ID
"""
argd = wash_urlargd(form, {'file': (str, None),
'comid': (int, 0)})
_ = gettext_set_language(argd['ln'])
# Can user view this record, i.e. can user access its
# attachments?
uid = getUid(req)
user_info = collect_user_info(req)
# Check that user can view record, and its comments (protected
# with action "viewcomment")
(auth_code, auth_msg) = check_user_can_view_comments(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
# Does comment exist?
if not query_get_comment(argd['comid']):
req.status = apache.HTTP_NOT_FOUND
return page(title=_("Page Not Found"),
body=_('The requested comment could not be found'),
req=req)
# Check that user can view this particular comment, protected
# using its own restriction
(auth_code, auth_msg) = check_user_can_view_comment(user_info, argd['comid'])
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,
ln=argd['ln'])
if not argd['file'] is None:
# Prepare path to file on disk. Normalize the path so that
# ../ and other dangerous components are removed.
path = os.path.abspath(CFG_PREFIX + '/var/data/comments/' + \
str(self.recid) + '/' + str(argd['comid']) + \
'/' + argd['file'])
# Check that we are really accessing attachements
# directory, for the declared record.
if path.startswith(CFG_PREFIX + '/var/data/comments/' + \
str(self.recid)) and \
os.path.exists(path):
return stream_file(req, path)
# Send error 404 in all other cases
req.status = apache.HTTP_NOT_FOUND
return page(title=_("Page Not Found"),
body=_('The requested file could not be found'),
req=req,
language=argd['ln'])
## def put(self, req, form):
## """
## Process requests received from FCKeditor to upload files, etc.
## """
## if not fckeditor_available:
## return
## uid = getUid(req)
## # URL where the file can be fetched after upload
-## user_files_path = '%(CFG_SITE_URL)s/record/%(recid)i/comments/attachments/get/%(uid)s' % \
+## user_files_path = '%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)i/comments/attachments/get/%(uid)s' % \
## {'uid': uid,
## 'recid': self.recid,
## 'CFG_SITE_URL': CFG_SITE_URL}
## # Path to directory where uploaded files are saved
## user_files_absolute_path = '%(CFG_PREFIX)s/var/data/comments/%(recid)s/%(uid)s' % \
## {'uid': uid,
## 'recid': self.recid,
## 'CFG_PREFIX': CFG_PREFIX}
## # Create a Connector instance to handle the request
## conn = FCKeditorConnectorInvenio(form, recid=self.recid, uid=uid,
## allowed_commands=['QuickUpload'],
## allowed_types = ['File', 'Image', 'Flash', 'Media'],
## user_files_path = user_files_path,
## user_files_absolute_path = user_files_absolute_path)
## # Check that user can upload attachments for comments.
## user_info = collect_user_info(req)
## (auth_code, auth_msg) = check_user_can_attach_file_to_comments(user_info, self.recid)
## if user_info['email'] == 'guest':
## # User is guest: must login prior to upload
## data = conn.sendUploadResults(1, '', '', 'Please login before uploading file.')
## elif auth_code:
## # User cannot submit
## data = conn.sendUploadResults(1, '', '', 'Sorry, you are not allowed to submit files.')
## else:
## # Process the upload and get the response
## data = conn.doResponse()
## # Transform the headers into something ok for mod_python
## for header in conn.headers:
## if not header is None:
## if header[0] == 'Content-Type':
## req.content_type = header[1]
## else:
## req.headers_out[header[0]] = header[1]
## # Send our response
## req.send_http_header()
## req.write(data)
diff --git a/modules/webhelp/web/admin/admin.webdoc b/modules/webhelp/web/admin/admin.webdoc
index 6d42755e4..447d57ac5 100644
--- a/modules/webhelp/web/admin/admin.webdoc
+++ b/modules/webhelp/web/admin/admin.webdoc
@@ -1,577 +1,577 @@
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(Admin Area)_ -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>Welcome to the Admin Area of the <CFG_SITE_NAME>. You'll find here
pointers to the available runtime admin-level interfaces and
admin-level guides on how to configure and run the Invenio
system.</p>
<p>Invenio comes as a suite of several more or less independent
modules. You'll find brief descriptions for each admin module below.
(More background information on each module may be read in the <a
href="<CFG_SITE_URL>/help/hacking/modules-overview">modules overview</a> article.)
</p>
<h3>Admin HOWTO guides</h3>
<p><a href="<CFG_SITE_URL>/help/admin/howto">Admin HOWTO Guides</a> give you
you both short and not-so-short recipes and thoughts on some of the
most frequently encountered administrative tasks. They tend to answer
various admin-level questions of a rather general level. The specific
tasks are better addressed by module-specific guides and interfaces
presented below.
</p>
<h3>Data acquisition related modules</h3>
<p>The metadata input into a running Invenio system can be done in two
ways: <em>(i) admin-oriented batch mode</em>,
i.e. <strong>OAI Harvest</strong> to get data from OAI repositories,
<strong>BibConvert</strong> to convert any input data into XML MARC,
and <strong>BibUpload</strong> to upload XML MARC files into Invenio;
and <em>(ii) author-oriented interactive mode</em>,
i.e. <strong>WebSubmit</strong> to submit documents via Web. Once the
data are uploaded in Invenio, you may want to modify them via
<strong>BibEdit</strong> to edit the metadata.
</p>
<table border="1" cellpadding="2">
<tr>
<th class="searchboxheader">Admin Module</th>
<th class="searchboxheader">Admin Description</th>
<th class="searchboxheader">Admin Interface</th>
<th class="searchboxheader">Admin Guide</th>
</tr>
<tr>
<td>
<strong>OAI Harvest Admin</strong>
</td>
<td>
Enables you to configure OAI metadata harvestor for eventual
periodical batch upload of data. For example, you can define from
where to harvest, with what periodicity, how to transform data
before uploading them into Invenio, etc. See also
<a href="<CFG_SITE_URL>/admin/bibharvest/oairepositoryadmin.py">OAI Repository Admin</a>
to expose your data to other harvesters.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/bibharvest/oaiharvestadmin.py">OAI Harvest Admin Interface</a>
</td>
<td>
<a href="oai-admin-guide">OAI Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibConvert Admin</strong>
</td>
<td>
Explains how to use bibliographic data convertor. Useful for batch
upload of data. For example, when migrating the metadata from
your old system, or when integrating metadata acquisitions from
non-OAI sources, or just about any line-based
not-so-well-structured metadata.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="bibconvert-admin-guide">BibConvert Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibMatch Admin</strong>
</td>
<td>
Tools for matching XML MARC files against the repository content.
Useful when importing third-party metadata files.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="bibmatch-admin-guide">BibMatch Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibUpload Admin</strong>
</td>
<td>
Enables you to configure eventual local special operations to be
done on the data being uploaded.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="bibupload-admin-guide">BibUpload Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebSubmit Admin</strong>
</td>
<td>
Enables you to configure the submit interface and logic for various document types.
For example, you can define which metadata fields should be submitted for various
doctypes, what to do with the inputted values before uploading,
possible peer review and approval strategy, etc.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py">WebSubmit Admin Interface</a>
</td>
<td>
<a href="websubmit-admin-guide">WebSubmit Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>ElmSubmit Admin</strong>
</td>
<td>
Enables you to configure the submission of documents by electronic mail.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="elmsubmit-admin-guide">ElmSubmit Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibEdit Admin</strong>
</td>
<td>
Enables you to directly manipulate bibliographic data, edit a single
record, do global replacements, and other cataloguing tasks.
</td>
<td>
- <a href="<CFG_SITE_URL>/record/edit/">BibEdit Admin Interface</a>
+ <a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/edit/">BibEdit Admin Interface</a>
</td>
<td>
<a href="bibedit-admin-guide">BibEdit Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibCheck Admin</strong>
</td>
<td>
Enables you to manage BibCheck configuration files. BibCheck is used to verify and
correct records.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/bibcheck/bibcheckadmin.py">BibCheck Admin Interface</a>
</td>
<td>
<a href="bibcheck-admin-guide">BibCheck Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>Publiline Admin</strong>
</td>
<td>
Enables you to approve documents by using a complex approval workflow.
</td>
<td>
None
</td>
<td>
<a href="publiline-admin-guide">Publiline Admin Guide</a>
</td>
</tr>
</table>
<h3>Data provision related modules</h3>
<p>The metadata output from a running Invenio system to the end-user
is covered by several modules: <strong>BibIndex</strong> to index the
metadata, <strong>BibRank</strong> to eventually rank them,
<strong>BibFormat</strong> to format them for the output,
<strong>WebSearch</strong> to provide search interfaces and search
engine.
</p>
<table border="1">
<tr>
<th class="searchboxheader">Admin Module</th>
<th class="searchboxheader">Admin Description</th>
<th class="searchboxheader">Admin Interface</th>
<th class="searchboxheader">Admin Guide</th>
</tr>
<tr>
<td>
<strong>BibIndex Admin</strong>
</td>
<td>
Enables you to configure "word files", i.e. to define which
bibliographic fields are indexed into which word indexes. The word
indexes are then used by the search interface. For example, you can
define that the logical author index is constructed from physical
<code>100 $a</code> and <code>700 $a</code> bibliographic tags, you
can force reindexing of the fulltext index, etc.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/bibindex/bibindexadmin.py">Manage indexes</a>
<p>
<a href="<CFG_SITE_URL>/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a>
</td>
<td>
<a href="bibindex-admin-guide">BibIndex Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibRank Admin</strong>
</td>
<td>
Enables you to configure various ranking methods to be used by the search engine.
You can rebalance existing ranking sets, create new ranking methods, etc.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/bibrank/bibrankadmin.py">BibRank Admin Interface</a>
</td>
<td>
<a href="bibrank-admin-guide">BibRank Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibClassify Admin</strong>
</td>
<td>
Enables you to automatically classify documents according to
keyword taxonomies and thesauri.
</td>
<td>
<small class="note">command-line configuration</small>
</td>
<td>
<a href="bibclassify-admin-guide">BibClassify Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibFormat Admin</strong>
</td>
<td>
Enables you to specify how the bibliographic data is presented to
the end user in the search interface. You can decide that titles should be
presented in bold font, that for each author an automatic link to
author's home page should be created according to some receipt, etc.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/bibformat/bibformatadmin.py">BibFormat Admin Interface</a>
</td>
<td>
<a href="bibformat-admin-guide">BibFormat Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibSword Client Admin</strong>
</td>
<td>
Enables you to consult and refresh the status of the forwared
record to any SWORD Remote Server. It also gives information about
the SWORD configuration and credential of the Remote Server.
Finally, this function allows admin to forward record from
Invenio to any configured Remote Server.
</td>
<td>
<a href="<CFG_SITE_URL>/bibsword/">BibSword Client Admin Interface</a>
</td>
<td>
<a href="bibsword-client-admin-guide">BibSword Client Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>OAI Repository Admin</strong>
</td>
<td>
Enables you to define which records should be tagged to be exposed
via the OAI Repository gateway, so that other other repositories
can harvest your records. See also
<a href="<CFG_SITE_URL>/admin/bibharvest/oaiharvestadmin.py">OAI Harvest Admin</a>
to import data into your repository.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/bibharvest/oairepositoryadmin.py">OAI repository Admin Interface</a>
</td>
<td>
<a href="oai-admin-guide">OAI Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebSearch Admin</strong>
</td>
<td>
Enables you to configure the search interface for various metadata
collections. You can define new collections and organize them in
the tree, you can define various portalboxes that would appear on
the screen, you can define search options and search fields to
present, etc.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/websearch/websearchadmin.py">WebSearch Admin Interface</a>
</td>
<td>
<a href="websearch-admin-guide">WebSearch Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebStat Admin</strong>
</td>
<td>
Enables you to configure the usage statistics reporting system.
</td>
<td>
<small class="note">command-line configuration</small>
</td>
<td>
<a href="webstat-admin-guide">WebStat Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebJournal Admin</strong>
</td>
<td> Enables you to configure and run an online journal hosted on your
Invenio server, using the same tools and concepts at those you
use to manage bibliographic data. </td>
<td>
<a href="<CFG_SITE_URL>/admin/webjournal/webjournaladmin.py">WebJournal Admin Interface</a>
</td>
<td>
<a href="webjournal-admin-guide">WebJournal Admin Guide</a><br/><br/>
<a href="webjournal-editor-guide">WebJournal Editor Guide</a>
</td>
</tr>
</table>
<h3>Personalization related modules</h3>
<p>Invenio interface can be personalized to suit different needs
of different end-users. This functionality is covered by several
modules: <strong>WebSession</strong> to identify users and their
personal configurations, <strong>WebBasket</strong> to provide
personal baskets or document carts, and <strong>WebAlert</strong> to
set up personal email notification alerts.
</p>
<table border="1">
<tr>
<th class="searchboxheader">Admin Module</th>
<th class="searchboxheader">Admin Description</th>
<th class="searchboxheader">Admin Interface</th>
<th class="searchboxheader">Admin Guide</th>
</tr>
<tr>
<td>
<strong>WebSession Admin</strong>
</td>
<td>
Enables you to inspect the status of guest sessions and to expire
them; the status and details on registered users, etc.
</td>
<td>
<small class="note">not available, but see the guide for the command-line way</small>
</td>
<td>
<a href="websession-admin-guide">WebSession Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebBasket Admin</strong>
</td>
<td>
Enables you to inspect and manipulate user baskets set up on the
system, to make them public/private, etc.
</td>
<td>
<small class="note">not available, but see the guide for the command-line way</small>
</td>
<td>
<a href="webbasket-admin-guide">WebBasket Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebAlert Admin</strong>
</td>
<td>
Enables you to inspect and manipulate user alerts set up on the
system, to run the alert engine, etc.
</td>
<td>
<small class="note">not available, but see the guide for the command-line way</small>
</td>
<td>
<a href="webalert-admin-guide">WebAlert Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebComment Admin</strong>
</td>
<td>
Enables you to manipulate readers comments and reviews,
see which ones were reported as abuse/spam, delete them, etc.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/webcomment/webcommentadmin.py">WebComment Admin Interface</a>
</td>
<td>
<a href="webcomment-admin-guide">WebComment Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebMessage Admin</strong>
</td>
<td>
Enables you to configure the messaging system.
</td>
<td>
<small class="note">command-line configuration</small>
</td>
<td>
<a href="webmessage-admin-guide">WebMessage Admin Guide</a>
</td>
</tr>
</table>
<h3>System glue modules</h3>
<p>Modules that provide the necessary glue for those presented above
are: <strong>BibSched</strong> to manage and schedule bibliographic
tasks, <strong>WebAccess</strong> to define role-based access control
system to all Invenio services, and <strong>WebStyle</strong> to
define a common look and feel of Invenio web pages.
</p>
<table border="1">
<tr>
<th class="searchboxheader">Admin Module</th>
<th class="searchboxheader">Admin Description</th>
<th class="searchboxheader">Admin Interface</th>
<th class="searchboxheader">Admin Guide</th>
</tr>
<tr>
<td>
<strong>BibSched Admin</strong>
</td>
<td>
Enables you to inspect bibliographic task queue, to postpone or
reschedule jobs, to make priorities, to run periodical tasks, etc.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="bibsched-admin-guide">BibSched Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebAccess Admin</strong>
</td>
<td>
Enables you to define who has got access or admin rights on various Invenio modules.
For example, you can define that John is the bibliographic
data manager, that Jim can modify the search interface pages, that
Jill is the submission approval editor, etc.
</td>
<td>
<a href="<CFG_SITE_URL>/admin/webaccess/webaccessadmin.py">WebAccess Admin Interface - Full functionality</a>
<br /><small>Full interface. Manage, grant, revoke any right.</small>
<p>
<a href="<CFG_SITE_URL>/admin/webaccess/webaccessadmin.py/delegate_startarea">Delegate Rights - With Restrictions</a>
<br /><small>Delegate your rights for some roles.</small>
<p>
<a href="<CFG_SITE_URL>/admin/webaccess/webaccessadmin.py/manageaccounts">Manage Accounts</a>
<br /><small>Enable, disable, and modify accounts.</small>
</td>
<td>
<a href="webaccess-admin-guide">WebAccess Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebStyle Admin</strong>
</td>
<td>
Enables you to customize default Invenio page style and the CSS style sheet.
</td>
<td>
<small class="note">not available, but see the guide for the command-line way</small>
</td>
<td>
<a href="webstyle-admin-guide">WebStyle Admin Guide</a>
</td>
</tr>
</table>
diff --git a/modules/webhelp/web/admin/howto/howto-fulltext.webdoc b/modules/webhelp/web/admin/howto/howto-fulltext.webdoc
index 14ba50cb7..9f3348ad8 100644
--- a/modules/webhelp/web/admin/howto/howto-fulltext.webdoc
+++ b/modules/webhelp/web/admin/howto/howto-fulltext.webdoc
@@ -1,329 +1,329 @@
## This file is part of Invenio.
## Copyright (C) 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: HOWTO Manage Fulltext Files -->
<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">Admin Area</a> &gt; <a class="navtrail" href="howto">Admin HOWTOs</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>How to manage your fulltext files through BibDocFile</h2>
<protect><pre>
Usage: /opt/invenio/bin/bibdocfile [options]
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-D, --debug
-H, --human-readable print sizes in human readable format (e.g., 1KB 234MB
2GB)
Query options:
-r RECIDS, --recids=RECIDS
matches records by recids, e.g.: --recids=1-3,5-7
-d DOCIDS, --docids=DOCIDS
matches documents by docids, e.g.: --docids=1-3,5-7
-a, --all Select all the records
--with-deleted-recs=yes/no/only
'Yes' to also match deleted records, 'no' to exclude
them, 'only' to match only deleted ones
--with-deleted-docs=yes/no/only
'Yes' to also match deleted documents, 'no' to exclude
them, 'only' to match only deleted ones (e.g. for
undeletion)
--with-empty-recs=yes/no/only
'Yes' to also match records without attached
documents, 'no' to exclude them, 'only' to consider
only such records (e.g. for statistics)
--with-empty-docs=yes/no/only
'Yes' to also match documents without attached files,
'no' to exclude them, 'only' to consider only such
documents (e.g. for sanity checking)
--with-record-modification-date=date1,date2
matches records modified date1 and date2; dates can be
expressed relatively, e.g.:"-5m,2030-2-23 04:40" #
matches records modified since 5 minutes ago until the
2030...
--with-record-creation-date=date1,date2
matches records created between date1 and date2; dates
can be expressed relatively
--with-document-modification-date=date1,date2
matches documents modified between date1 and date2;
dates can be expressed relatively
--with-document-creation-date=date1,date2
matches documents created between date1 and date2;
dates can be expressed relatively
--url=URL matches the document referred by the URL, e.g. "http:/
- /pcsk.cern.ch/record/1/files/foobar.pdf?version=2"
+ /pcsk.cern.ch/<CFG_SITE_RECORD>/1/files/foobar.pdf?version=2"
--path=PATH matches the document referred by the internal
filesystem path, e.g. /opt/cds-
dev/var/data/files/g0/1/foobar.pdf\;1
--with-docname=DOCNAME
matches documents with the given docname (accept
wildcards)
--with-doctype=DOCTYPE
matches documents with the given doctype
-p PATTERN, --pattern=PATTERN
matches records by pattern
-c COLLECTION, --collection=COLLECTION
matches records by collection
--force force an action even when it's not necessary e.g.
textify on an already textified bibdoc.
Actions for getting information:
--get-info print all the informations about the matched
record/documents
--get-disk-usage print disk usage statistics of the matched documents
--get-history print the matched documents history
Actions for setting information:
--set-doctype=doctype
specify the new doctype
--set-description=description
specify a description
--set-comment=comment
specify a comment
--set-restriction=restriction
specify a restriction tag
--set-docname=docname
specifies a new docname for renaming
--unset-comment remove any comment
--unset-descriptions
remove any description
--unset-restrictions
remove any restriction
--hide hides matched documents and revisions
--unhide hides matched documents and revisions
Action for revising content:
--append=PATH/URL specify the URL/path of the file that will appended to
the bibdoc
--revise=PATH/URL specify the URL/path of the file that will revise the
bibdoc
--revert reverts a document to the specified version
--delete soft-delete the matched documents (applies to all
revisions and formats)
--hard-delete hard-delete the matched documents (applies to matched
revisions and formats)
--undelete undelete previosuly soft-deleted documents (applies to
all revisions and formats)
--purge purge (i.e. hard-delete previous versions) the matched
documents
--expunge expunge (i.e. hard-delete any version and formats) the
matched documents
--with-versions=VERSION
specifies the version(s) to be used with hard-delete,
hide, revert, e.g.: 1-2,3 or all
--with-format=FORMAT
to specify a format when
appending/revising/deleting/reverting a document, e.g.
"pdf"
--with-hide-previous
when revising, hides previous versions
Actions for housekeeping:
--check-md5 check md5 checksum validity of files
--check-format check if any format-related inconsistences exists
--check-duplicate-docnames
check for duplicate docnames associated with the same
record
--update-md5 update md5 checksum of files
--fix-all fix inconsistences in filesystem vs database vs MARC
--fix-marc synchronize MARC after filesystem/database
--fix-format fix format related inconsistences
--fix-duplicate-docnames
fix duplicate docnames associated with the same record
Experimental options (do not expect to find them in the next release):
--set-icon=URL/PATH
attache the specified icon to the matched documents
--unset-icon remove any icon on the matched documents
--textify extract text from matched documents and store it for
later indexing
--with-ocr=yes/no/always
when used with --textify, wether to perform OCR (yes
will perform it only if necessary, based on an
heuristic)
Examples:
$ bibdocfile --append foo.tar.gz --recid=1
$ bibdocfile --revise http://foo.com?search=123 --with-docname='sam'
--format=pdf --recid=3 --set-docname='pippo' # revise for record 3
# the document sam, renaming it to pippo.
$ bibdocfile --delete *sam --all # delete all documents starting ending
# with "sam"
$ bibdocfile --undelete -c "Test Collection" # undelete documents for
# the collection
$ bibdocfile --get-info --recids=1-4,6-8 # obtain informations
$ bibdocfile -r 1 --with-docname=foo --set-docname=bar # Rename a document
</pre></protect>
<h2>How to convert your fulltext files among different formats</h2>
<protect><pre>
Usage: python /opt/invenio/lib/python/invenio/websubmit_file_converter.py [options]
Options:
-h, --help show this help message and exit
-c FILE, --convert=FILE
convert the specified FILE
-d, --debug Enable debug information
--special-pdf2hocr2pdf=FILE
convert the given scanned PDF into a PDF with OCRed
text
-f FORMAT, --format=FORMAT
the desired output format
-o OUTPUT_NAME, --output=OUTPUT_NAME
the desired output FILE (if not specified a new file
will be generated with the desired output format)
--without-pdfa don't force creation of PDF/A PDFs
--without-pdfopt don't force optimization of PDFs files
--without-ocr don't force OCR
--can-convert=FORMAT display all the possible format that is possible to
generate from the given format
--is-ocr-needed=FILE check if OCR is needed for the FILE specified
-t TITLE, --title=TITLE
specify the title (used when creating PDFs)
-l LN, --language=LN specify the language (used when performing OCR, e.g.
en, it, fr...)
Examples:
python /opt/invenio/lib/python/invenio/websubmit_file_converter.py \
--convert=foo.docx -f pdf
## The previous command will generate a PDF/A out of a Microsoft Office
## Word document
python /opt/invenio/lib/python/invenio/websubmit_file_converter.py \
--special-pdf2hocr2pdf=scanned-foo.pdf --output=ocred-foo.pdf
## The previous command will generate a new PDF with a text stream
## extracted via an OCR engine.
</pre></protect>
<p>This module is used internally by Invenio to convert from one format to
another</p>
<p>It can also be invoked manually by a system administrator to obtain the
same level of conversion quality offered by Invenio.</p>
<h2>How to create icons</h2>
<protect><pre>
Usage:
python /opt/invenio/lib/python/invenio/websubmit_icon_creator.py \
[options] input-file.jpg
websubmit_icon_creator.py is used to create an icon for an image.
Options:
-h, --help Print this help.
-V, --version Print version information.
-v, --verbose=LEVEL Verbose level (0=min, 1=default, 9=max).
[NOT IMPLEMENTED]
-s, --icon-scale
Scaling information for the icon that is to
be created. Must be an integer. Defaults to
180.
-m, --multipage-icon
A flag to indicate that the icon should
consist of multiple pages. Will only be
respected if the requested icon type is GIF
and the input file is a PS or PDF consisting
of several pages.
-d, --multipage-icon-delay=VAL
If the icon consists of several pages and is
an animated GIF, a delay between frames can
be specified. Must be an integer. Defaults
to 100.
-f, --icon-file-format=FORMAT
The file format of the icon to be created.
Must be one of:
[pdf, gif, jpg, jpeg, ps, png, bmp]
Defaults to gif.
-o, --icon-name=XYZ
The optional name to be given to the created
icon file. If this is omitted, the icon file
will be given the same name as the input
file, but will be prefixed by "icon-";
Examples:
python /opt/invenio/lib/python/invenio/websubmit_icon_creator.py \
--icon-scale=200 \
--icon-name=test-icon \
--icon-file-format=jpg \
test-image.jpg
python /opt/invenio/lib/python/invenio/websubmit_icon_creator.py \
--icon-scale=200 \
--icon-name=test-icon2 \
--icon-file-format=gif \
--multipage-icon \
--multipage-icon-delay=50 \
test-image2.pdf
</pre></protect>
<h2>How to stamp PDFs</h2>
<protect><pre>
Usage:
python /opt/invenio/lib/python/invenio/websubmit_file_stamper.py \
[options] input-file.pdf
websubmit_file_stamper.py is used to add a "stamp" to a PDF file.
A LaTeX template is used to create the stamp and this stamp is then
concatenated with the original PDF file.
The stamp can take the form of either a separate "cover page" that is
appended to the document; or a "mark" that is applied somewhere either
on the document's first page or on all of its pages.
Options:
-h, --help Print this help.
-V, --version Print version information.
-v, --verbose=LEVEL Verbose level (0=min, 1=default, 9=max).
[NOT IMPLEMENTED]
-t, --latex-template=PATH
Path to the LaTeX template file that should be used
for the creation of the PDF stamp. (Note, if it's
just a basename, it will be sought first in the
current working directory, and then in the invenio
file-stamper templates directory; If there is a
qualifying path to the template name, it will be
sought only in that location);
-c, --latex-template-var='VARNAME=VALUE'
A variable that should be replaced in the LaTeX
template file with its corresponding value. Of the
following format:
VARNAME=VALUE
This option is repeatable - one for each template
variable;
-s, --stamp=STAMP-TYPE
The type of stamp to be applied to the subject
file. Must be one of 3 values:
+ "first" - stamp only the first page;
+ "all" - stamp all pages;
+ "coverpage" - add a cover page to the
document;
The default value is "first";
-o, --output-file=XYZ
The optional name to be given to the finished
(stamped) file. If this is omitted, the stamped
file will be given the same name as the input
file, but will be prefixed by "stamped-";
Example:
python /opt/invenio/lib/python/invenio/websubmit_file_stamper.py \
--latex-template=demo-stamp-left.tex \
--latex-template-var='REPORTNUMBER=TEST-THESIS-2008-019' \
--latex-template-var='DATE=27/02/2008' \
--stamp='first' \
--output-file=testfile_stamped.pdf \
testfile.pdf
</pre></protect>
diff --git a/modules/webhelp/web/hacking/fckeditor.webdoc b/modules/webhelp/web/hacking/fckeditor.webdoc
index e02150b3a..64853c21c 100644
--- a/modules/webhelp/web/hacking/fckeditor.webdoc
+++ b/modules/webhelp/web/hacking/fckeditor.webdoc
@@ -1,263 +1,263 @@
## -*- mode: html; coding: utf-8; -*-
## This file is part of Invenio.
## Copyright (C) 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: FCKeditor Integration -->
<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<ul style="list-style-type:None">
<li><strong>1. <a href="#1.">About FCKeditor</a></strong></li>
<li><strong>2. <a href="#2.">Maintenance</a></strong>
<ul style="list-style-type:None">
<li>2.1&nbsp;&nbsp;<a href="#2.1">Installation</a></li>
<li>2.2&nbsp;&nbsp;<a href="#2.2">Upgrade</a></li>
<li>2.3&nbsp;&nbsp;<a href="#2.3">Configuration</a></li>
<li>2.4&nbsp;&nbsp;<a href="#2.4">Javscript vs Python Integration</a></li>
</ul>
</li>
<li><strong>3. <a href="#3.">APIs</a></strong>
<ul style="list-style-type:None">
<li>3.1&nbsp;&nbsp;<a href="#3.1">Basic Integration</a></li>
<li>3.2&nbsp;&nbsp;<a href="#3.2">File Upload Support</a></li>
</ul>
</li>
</ul>
<p>This documentation is <strong>developer-oriented</strong>, and
provides maintenance information about the FCKeditor integration with
Invenio. <br />
Read the Invenio INSTALL file to learn how to install FCKeditor on
your Invenio instance.</p>
<p>Also note that a major revision of FCKeditor is planned for version
3 (as well as a renaming to "CKEditor"). As a consequence, links from
this page to the FCKeditor documentation might be broken or outdated,
and the integration might need to be reworked. This documentation
should help in this respect.</p>
<h2><a name="1.">1. About FCKeditor</a></h2>
<p><a href="http://www.fckeditor.net/">FCKeditor</a> is a GPL WYSWYG
javascript-based HTML editor. It is currently used in the following
modules of Invenio:</p>
<ul>
<li>WebComment: HTML-formatted comments/reviews, and file
attachment.</li>
</ul>
<p>It can optionally (and rather easily) be integrated into WebSubmit
Response Elements too, but there is no WebSubmit Core Element using
FCKeditor for the moment.</p>
<h2><a name="2.">Maintenance</a></h2>
<h3><a name="2.1">Installation</a></h3>
<p>Read the Invenio INSTALL file to learn <em>how</em> to deploy FCKeditor on your installation.</p>
The <code>invenio/Makefile.am::install-fckeditor-plugin</code> installs the necessary files
for the user:
<ul>
<li><strong>Editor:</strong> The editor itself and all the necessary
files (HTML and Javascript) are installed in
<code>/opt/invenio/var/www/fckeditor/</code></li>
<li><strong>Server-side integration:</strong> the class to embed the
editor using Python (instead of Javascript) goes here:
<code>/opt/invenio/lib/python/invenio/fckeditor/fckeditor.py</code>
</li>
<li><strong>Server-side connector:</strong> the base Python classes
for the server-side connection (to support files upload/browsing) are
copied into
<code>/opt/invenio/lib/python/invenio/fckeditor/editor/filemanager/connectors/py/</code>,
keeping the source hierarchy of directories</li>
</ul>
<p>Usually, only the necessary files are copied (<a href="http://docs.fckeditor.net/FCKeditor_2.x/Developers_Guide/Deployment">Check which files need to be deployed</a>) and none are modified.</p>
Additional files from Invenio are needed to support the editor
(these files might already be installed): <ul>
<li><strong><code>invenio/modules/webstyle/etc/invenio-fckeditor-config.js</code>:</strong>
custom configuration file. Installed in
<code>/opt/invenio/var/www/fckeditor/</code>.</li>
<li><strong><code>invenio/modules/miscutil/lib/htmlutils.py</code>:</strong>
contains function <code>get_html_text_editor(..)</code> to wrap the
initialization of the editor. Should one change the way the editor is
integrated, this function only would need to be changed. This file is
always installed, even if "<code>make install-fckeditor-plugin</code>"
has never run.</li>
<li><strong><code>invenio/modules/webstyle/lib/fckeditor_invenio_connector.py</code>:</strong>
subclasses the fckeditor connector for server-side integration, to
support files upload. This file is always installed, even if
"<code>make install-fckeditor-plugin</code>" has not never run.</li>
</ul>
<h3><a name="2.2">Upgrade</a></h3>
<p>Since the integration modifies no file of the editor, it should be
straightforward to upgrade to a newer version of the editor,
especially with minor revisions.</p>
<p>First check the FCKeditor release note, and read tips
<a href="http://docs.fckeditor.net/FCKeditor_2.x/Developers_Guide/Installation/Upgrading">how to upgrade the editor</a>
to ensure that the way <code>invenio/Makefile.am</code> installs
the files is ok.</p>
<p>The easiest to test an upgrade is to increase the version number in
<code>invenio/Makefile.am</code>, variable <code>FCKV</code> and run
"<code>Make install</code>". Make sure that the archive can still be
downloaded from the usual URL.</p>
<p>What should be specifically checked are
<code>htmlutils.get_html_text_editor(..)</code>,
<code>invenio/modules/webstyle/etc/invenio-fckeditor-config.js</code> and
<code>invenio/modules/webstyle/lib/fckeditor_invenio_connector.py</code>:
they are basically the only files that interface with the FCKeditor,
and must adapt to modifications of FCKeditor APIs.</p>
<h3><a name="2.3">Configuration</a></h3>
<p>The configuration of FCKeditor (colors, size, behaviour) can be
done when instantiating the editor ("inline", in
<code>htmlutils.get_html_text_editor(..)</code> function) or via a
Javascript config file placed in a web accessible location. Read
FCKeditor documentation to learn more about these options.<br/>
The current solution is to have a maximum of the configuration made in
<code>htmlutils.get_html_text_editor(..)</code>, such that it is easy
to customize the editor directly from the source code, without having
to change any Javascript config file.</p>
<p>For the moment a single Javascript file
(<code>invenio-fckeditor-config.js</code>) is used, mainly to define
the toolbar sets, that cannot be defined "inline".</p>
<p><strong>It is to be thought if it would not be better to have the
configuration for each call of the function (or each Invenio
module) in different config files. That would make the customization
of each instance possible by admin users.</strong></p>
<h3><a name="2.4">Javscript vs Python Integration</a></h3>
<p>FCKeditor can be integrated into pages either via Javascript, or
using the Python wrapper. The current way of doing is to use the
Python wrapper.</p>
<strong>Pro and cons of using the Python wrapper:</strong>
<ul style="list-style-type:none;">
<li><strong>+</strong> easier to read and maintain</li>
<li><strong>+</strong> can be pylinted</li>
<li><strong>+</strong> faster on client-side?</li>
<li><strong>+</strong> can partly hide changes in FCKeditor APIs?</li>
<li><strong>-</strong> cannot check if client has enabled Javascript
(can only check
user-agent for compatibility)<strong><a href="#aboutCheckingClientJavascript">*</a></strong></li>
<li><strong>-</strong> might be dropped at some point?</li>
</ul>
<a name="aboutCheckingClientJavascript"></a><strong>*</strong> A trick is applied to check if client has enabled Javascript when editor is integrated via Python: the complete instantiation code is written via <code>document.write(..)</code> (in Javascript) and a <code>&lt;noscript&gt;</code> tag is used to fall back on a regular <code>&lt;textarea&gt;</code>.
<h2><a name="3.">APIs</a></h2>
<h3><a name="3.1">Basic Integration</a></h3>
<p>To integrate the FCKeditor, please exclusively use the following method:</p>
<pre>
from htmlutils import get_html_text_editor
[...]
out += get_html_text_editor('myeditor')
</pre>
<p>Refer to <code>htmlutils.py</code> for more information about the
function and its parameters.</p>
<p>It is wise to always use the above method and never directly rely
on any FCKeditor file. You have to expect that the editor is not
always installed on the server, or that the client might not support
it (eg. Javascript disabled). In these cases, a basic
<code>&lt;textarea/&gt;</code> is used instead.<br/>
If you need to know what type of input form (<code>textarea</code> or
FCKeditor) was used by the client, you can catch the value of the form
variable <code>editor_type</code>, which is submitted at the same time
as other elements of your form.</p>
<h3><a name="3.2">File Upload Support</a></h3>
<p>In order to support file upload rigth from FCKeditor, you must call
<code>get_html_text_editor(..)</code> with its <code>file_upload_url</code>
parameter set to the URL to which the file will be uploaded.</p>
<p>The second step is to implement the URL handler
<code>file_upload_url</code> so that that it understands the
"commands" sent by FCKeditor, does something with the file (eg. moves
it to a chosen location) and sends a correct reply.</p>
<p>To do so, the easiest is to instantiate an
<code>FCKeditorConnectorInvenio</code> object with the input
parameters, and sends back the value returned by its
<code>doResponse()</code> function. Note that you have to correctly
set the response headers by reading the object <code>headers</code>
member and <strong>implement yourself restrictions checking in your
code</strong>, as this is not managed by the FCKeditorConnectorInvenio
class </p>
<p>You can use the following parameters when instantiating the connector:
<dl>
<dt><strong><code>user_files_absolute_path</code></strong></dt>
<dd>the base path where the files should be
saved. Eg:<code>%(CFG_PREFIX)s/var/data/comments/%(recid)s/%(uid)s</code></dd>
<dt><strong><code>user_files_path</code></strong></dt>
<dd>the base URL where the files can be accessed from the web, if
<code>user_files_absolute_path</code> is not a web accessible
folder. Eg:
-<code>%(CFG_SITE_URL)s/record/%(recid)i/comments/attachments/get/%(uid)s</code></dd>
+<code>%(CFG_SITE_URL)s/<CFG_SITE_RECORD>/%(recid)i/comments/attachments/get/%(uid)s</code></dd>
</dl>
</p>
<p>Note that if you set <code>user_files_path</code>, you have to
implement your own handler to stream the files from the directory
<code>user_files_absolute_path</code>. <br/>
Also note that whatever value you choose for the above parameters,
FCKeditor appends <em>sub-paths</em> automatically, such as
<code>/file/</code> for regular files, or <code>/image/</code> for
images.</p>
<p>Check
<code>invenio/modules/webcomment/webcomment_webinterface::WebInterfaceCommentsFiles</code>
URL handler to see how it works</p>
<p><em>There is currently no implementation for server files browsing.</em></p>
diff --git a/modules/webjournal/lib/elements/bfe_webjournal_article_admin_links.py b/modules/webjournal/lib/elements/bfe_webjournal_article_admin_links.py
index e4b92ee5e..6c933afc4 100644
--- a/modules/webjournal/lib/elements/bfe_webjournal_article_admin_links.py
+++ b/modules/webjournal/lib/elements/bfe_webjournal_article_admin_links.py
@@ -1,84 +1,86 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
WebJournal Element - Display admin links
"""
from invenio.config import \
CFG_SITE_URL, \
CFG_SITE_NAME, \
- CFG_SITE_NAME_INTL
+ CFG_SITE_NAME_INTL, \
+ CFG_SITE_RECORD
from invenio.access_control_engine import acc_authorize_action
from invenio.webjournal_utils import \
parse_url_string, \
get_journal_submission_params
def format_element(bfo):
"""
Display administration links for this articles when user is an
editor of the journal
"""
out = ''
if bfo.user_info['uri'].startswith('/journal'):
# Print editing links
args = parse_url_string(bfo.user_info['uri'])
journal_name = args["journal_name"]
editor = False
if acc_authorize_action(bfo.user_info['uid'], 'cfgwebjournal',
name="%s" % journal_name)[0] == 0:
editor = True
issue_number = args["issue"]
if editor:
recid = bfo.control_field('001')
(doctype, identifier_element, identifier_field) = \
get_journal_submission_params(journal_name)
if identifier_field.startswith('00'):
identifier = bfo.control_field(identifier_field)
else:
identifier = bfo.field(identifier_field)
out += '''
<div style="float:right;margin-left:5px;font-weight:700;">
<p>
<a href="%(CFG_SITE_URL)s/submit/direct?%(identifier_element)s=%(identifier)s&amp;sub=MBI%(doctype)s" target="_blank"> >> edit article</a>
</p>
<p>
- <a href="%(CFG_SITE_URL)s/record/%(recid)s" target="_blank"> >> record in %(CFG_SITE_NAME_INTL)s</a>
+ <a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)s" target="_blank"> >> record in %(CFG_SITE_NAME_INTL)s</a>
</p>
<p>
<a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/regenerate?journal_name=%(journal_name)s&amp;issue=%(issue_number)s"> >> publish changes</a>
</p>
</div>''' % {'CFG_SITE_URL': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'identifier': identifier,
'recid': recid,
'journal_name': journal_name,
'issue_number': issue_number,
'doctype': doctype,
'identifier_element': identifier_element,
'CFG_SITE_NAME_INTL': CFG_SITE_NAME_INTL.get(bfo.lang,
CFG_SITE_NAME)}
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/webjournal/lib/webjournal_templates.py b/modules/webjournal/lib/webjournal_templates.py
index 779999135..d8dee84a9 100644
--- a/modules/webjournal/lib/webjournal_templates.py
+++ b/modules/webjournal/lib/webjournal_templates.py
@@ -1,712 +1,714 @@
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
WebJournal templates - Defines the look of various parts of the
WebJournal modules. Most customizations will however be done through
BibFormat format templates files.
"""
import os
from invenio.config import \
CFG_SITE_SUPPORT_EMAIL, \
CFG_ETCDIR, \
CFG_SITE_URL, \
- CFG_SITE_LANG
+ CFG_SITE_LANG, \
+ CFG_SITE_RECORD
from invenio.messages import gettext_set_language
from invenio.webpage import page
from invenio.webjournal_utils import \
get_number_of_articles_for_issue, \
get_release_datetime, \
get_announcement_datetime, \
get_issue_number_display
class Template:
"""Templating class, refer to bibformat.py for examples of call"""
def tmpl_webjournal_missing_info_box(self, req, ln, title, msg_title, msg):
"""
returns a box indicating that the given journal was not found on the
server, leaving the opportunity to select an existing journal from a list.
"""
_ = gettext_set_language(ln)
box_title = msg_title
box_text = msg
box_list_title = _("Available Journals")
# todo: move to DB call
find_journals = lambda path: [entry for entry in os.listdir(str(path)) \
if os.path.isdir(str(path)+str(entry))]
try:
all_journals = find_journals('%s/webjournal/' % CFG_ETCDIR)
except:
all_journals = []
mail_msg = _("Contact %(x_url_open)sthe administrator%(x_url_close)s") % \
{'x_url_open' :
'<a href="mailto:%s">' % CFG_SITE_SUPPORT_EMAIL,
'x_url_close' : '</a>'}
box = '''
<div style="text-align: center;">
<fieldset style="width:400px; margin-left: auto; margin-right:auto">
<legend style="color:#a70509;background-color:#fff;">
<i>%s</i>
</legend>
<p style="text-align:center;">%s</p>
<h2 style="color:#0D2B88;">%s</h2>
<ul class="webjournalBoxList">
%s
</ul>
<br/>
<div style="text-align:right;">
%s
</div>
</fieldset>
</div>
''' % (box_title,
box_text,
box_list_title,
"".join(['<li><a href="%s/journal/?name=%s">%s</a></li>'
% (CFG_SITE_URL,
journal,
journal) for journal in all_journals]),
mail_msg)
return page(req=req, title=title, body=box)
def tmpl_webjournal_error_box(self, req, ln, title, title_msg, msg):
"""
returns an error box for webjournal errors.
"""
_ = gettext_set_language(ln)
title = _(title)
title_msg = _(title_msg)
msg = _(msg)
mail_msg = _("Contact %(x_url_open)sthe administrator%(x_url_close)s") % \
{'x_url_open' :
'<a href="mailto:%s">' % CFG_SITE_SUPPORT_EMAIL,
'x_url_close' : '</a>'}
box = '''
<div style="text-align: center;">
<fieldset style="width:400px; margin-left: auto; margin-right: auto;">
<legend style="color:#a70509;background-color:#fff;">
<i>%s</i>
</legend>
<p style="text-align:center;">%s</p>
<br/>
<div style="text-align:right;">
%s
</div>
</fieldset>
</div>
''' % (title_msg, msg, mail_msg)
return page(req=req, title=title, body=box)
def tmpl_admin_regenerate_success(self, ln, journal_name, issue):
"""
Success message if a user applied the "regenerate" link. Links back to
the regenerated journal.
"""
_ = gettext_set_language(ln)
out = '''
The issue number %(issue)s for the %(journal_name)s journal has been successfully
regenerated. <br/>
Look at your changes: >> <a href="%(CFG_SITE_URL)s/journal/%(journal_name)s/%(issue_year)s/%(issue_number)s"> %(journal_name)s </a> <br/> or go back to this journal <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">administration interface</a>.
''' % {'issue': issue,
'journal_name': journal_name,
'CFG_SITE_URL': CFG_SITE_URL,
'issue_year': issue.split('/')[1],
'issue_number': issue.split('/')[0]}
return out
def tmpl_admin_regenerate_error(self, ln, journal_name, issue):
"""
Failure message for a regeneration try.
"""
_ = gettext_set_language(ln)
return page(
title=_("Regeneration Error"),
body = _("The issue could not be correctly regenerated. "
"Please contact your administrator."))
def tmpl_admin_feature_record(self, journal_name,
featured_records=[],
ln=CFG_SITE_LANG,
msg=None):
"""
Display an interface form to feature a specific record from Invenio.
"""
_ = gettext_set_language(ln)
out = ''
out += '''<table class="admin_wvar">
<tr><th colspan="5" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="administrate?journal_name=%(journal_name)s">Administrate</a></small>&nbsp;</td>
<td>1.&nbsp;<small>Feature a Record</small>&nbsp;</td>
<td>2.&nbsp;<small><a href="configure?action=edit&amp;journal_name=%(journal_name)s">Edit Configuration</a></small>&nbsp;</td>
<td>3.&nbsp;<small><a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">Go to the Journal</a></small>&nbsp;</td>
</tr>
</table><br/>''' % {'journal_name': journal_name,
'menu': _("Menu"),
'CFG_SITE_URL': CFG_SITE_URL}
if msg is not None:
out += msg
out += '<br/><br/>'
out += '''<table class="admin_wvar" cellspacing="0" width="400px">
<tr>
<th colspan="3" class="adminheader">Featured records</th>
</tr>'''
color = "fff"
for (recid, img_url) in featured_records:
out += '''<tr style="background-color:#%(color)s">
<td class="admintd"><img src="%(img_url)s" alt="" height="40px"/></td>
- <td class="admintdleft"><a href="%(CFG_SITE_URL)s/record/%(recid)s">Record %(recid)s</a></td>
+ <td class="admintdleft"><a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)s">Record %(recid)s</a></td>
<td class="admintdright"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/feature_record?journal_name=%(journal_name)s&amp;action=askremove&amp;recid=%(recid)s">remove</a></td>
</tr>''' % {'color': color,
'journal_name': journal_name,
'recid': recid,
'img_url': img_url,
- 'CFG_SITE_URL': CFG_SITE_URL}
+ 'CFG_SITE_URL': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD}
if color == 'fff':
color = 'EBF7FF'
else:
color = 'fff'
if len(featured_records) == 0:
out += '<tr><td colspan="3" class="admintd"><em>No record featured for the moment. Add one using the form below.</em></td></tr>'
out += '</table>'
out += '''
<br/><br/><br/>
<form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/feature_record" method="post">
<input type="hidden" name="action" value="add" />
<input type="hidden" name="journal_name" value="%(journal_name)s"/>
<table class="admin_wvar" cellspacing="0">
<tr>
<th colspan="2" class="adminheaderleft">Add a new featured record:</th>
</tr>
<tr>
<td class="admintdright"><label for="recordid"><span style="white-space: nowrap;">Featured Record ID</span></label>:&nbsp;</td>
<td><input tabindex="1" type="text" name="recid" value="" id="recordid"/></td>
</tr>
<tr>
<td class="admintdright"><label for="image_url"><span style="white-space: nowrap;">Icon URL</span></label>:&nbsp;</td>
<td><input tabindex="2" type="text" name="img_url" value="" id="image_url" size="60"/><em><br/><small>Image displayed along the featured record</small></em></td>
</tr>
<tr>
<td colspan="2" align="right"><input tabindex="3" class="adminbutton" type="submit" value="Add"/></td>
</tr>
</table>
</form>
''' % {'CFG_SITE_URL': CFG_SITE_URL,
'journal_name': journal_name}
return out
def tmpl_admin_alert_plain_text(self, journal_name, ln, issue):
"""
Default plain text message for email alert of journal updates.
This will be used to pre-fill the content of the mail alert, that
can be modified by the admin.
Customize this function to return different default texts
based on journal name and language,
"""
current_publication = get_issue_number_display(issue, journal_name, ln)
plain_text = u'''Dear Subscriber,
The latest issue of %(journal_name)s, no. %(current_publication)s, has been released.
You can access it at the following URL:
%(CFG_SITE_URL)s/journal/%(journal_name)s/
Best Wishes,
%(journal_name)s team
----
Cher Abonné,
Le nouveau numéro de %(journal_name)s, no. %(current_publication)s, vient de paraître.
Vous pouvez y accéder à cette adresse :
%(CFG_SITE_URL)s/journal/%(journal_name)s/?ln=fr
Bonne lecture,
L'équipe de %(journal_name)s
''' % {'journal_name': journal_name,
'current_publication': current_publication,
'CFG_SITE_URL': CFG_SITE_URL}
return plain_text
# '
def tmpl_admin_alert_header_html(self, journal_name, ln, issue):
"""
Returns HTML header to be inserted into the HTML alert
@param journal_name: the journal name
@param ln: the current language
@param issue: the issue for wich the alert is sent
"""
_ = gettext_set_language(ln)
journal_url = '%(CFG_SITE_URL)s/journal/%(journal_name)s/%(year)s/%(number)s' % \
{'CFG_SITE_URL': CFG_SITE_URL,
'journal_name': journal_name,
'year': issue.split('/')[1],
'number': issue.split('/')[0]}
journal_link = '<a href="%(journal_url)s">%(journal_url)s</a>' % \
{'journal_url': journal_url}
return '<p class="htmlalertheader">' + \
_('If you cannot read this email please go to %(x_journal_link)s') % {'x_journal_link': journal_link} + \
'</p>'
def tmpl_admin_alert_subject(self, journal_name, ln, issue):
"""
Default subject for email alert of journal updates.
Customize this function to return different default texts
based on journal name and language,
"""
return "%s %s released" % (journal_name, \
get_issue_number_display(issue,
journal_name,
ln))
def tmpl_admin_alert_interface(self, ln, journal_name, default_subject,
default_msg, default_recipients, alert_ln):
"""
Alert email interface.
"""
_ = gettext_set_language(ln)
interface = '''
<table>
<tr>
<td valign="top">
<form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/alert" name="alert" method="post">
<input type="hidden" name="journal_name" value="%(journal_name)s"/>
<p>Recipients:</p>
<input type="text" name="recipients" value="%(default_recipients)s" size="60" />
<p>Subject:</p>
<input type="text" name="subject" value="%(subject)s" size="60" />
<p>Plain Text Message:</p>
<textarea name="plainText" wrap="soft" rows="25" cols="80">%(plain_text)s</textarea>
<p> <input type="checkbox" name="htmlMail" id="htmlMail" value="html" checked="checked" />
<label for="htmlMail">Send journal front-page <small>(<em>HTML newsletter</em>)</small></label>
</p>
<br/>
<input class="formbutton" type="submit" value="Send Alert" name="sent"/>
</form>
</td><td valign="top">
<p>HTML newsletter preview:</p>
<iframe id="htmlMailPreview" src="%(CFG_SITE_URL)s/journal/%(journal_name)s?ln=%(alert_ln)s" height="600" width="600"></iframe>
</tr>
</table>
''' % {'CFG_SITE_URL': CFG_SITE_URL,
'journal_name': journal_name,
'subject': default_subject,
'plain_text': default_msg,
'default_recipients': default_recipients,
'alert_ln': alert_ln}
return interface
def tmpl_admin_alert_was_already_sent(self, ln, journal_name,
subject, plain_text, recipients,
html_mail, issue):
"""
"""
_ = gettext_set_language(ln)
out = '''
<form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/alert" name="alert" method="post">
<input type="hidden" name="journal_name" value="%(journal_name)s"/>
<input type="hidden" name="recipients" value="%(recipients)s" />
<input type="hidden" name="subject" value="%(subject)s" />
<input type="hidden" name="plainText" value="%(plain_text)s" />
<input type="hidden" name="htmlMail" value="%(html_mail)s" />
<input type="hidden" name="force" value="True" />
<p><em>WARNING! </em>The email alert for the issue %(issue)s has already been
sent. Are you absolutely sure you want to send it again?</p>
<p>Maybe you forgot to release an update issue? If so, please do this
first <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/issue_control?journal_name=%(journal_name)s&amp;issue=%(issue)s">here</a>.</p>
<p>Proceed with caution, or your subscribers will receive the alert a second time.</p>
<br/>
<input class="formbutton" type="submit" value="I really want this!" name="sent"/>
</form>
''' % {'CFG_SITE_URL': CFG_SITE_URL,
'journal_name': journal_name,
'recipients': recipients,
'subject': subject,
'plain_text': plain_text,
'html_mail': html_mail,
'issue': issue}
return out
def tmpl_admin_alert_unreleased_issue(self, ln, journal_name):
"""
Tried to announce an unreleased issue
"""
_ = gettext_set_language(ln)
out = '''<p style="color:#f00">An alert cannot be send for this issue!</p>
You tried to send an alert for an issue that has not yet been released.
Release it first and retry.<br/>
Go back to the <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">administration interface</a>.
''' % {'CFG_SITE_URL': CFG_SITE_URL,
'journal_name': journal_name}
return out
def tmpl_admin_alert_success_msg(self, ln, journal_name):
"""
Success messge for the alert system.
"""
_ = gettext_set_language(ln)
out = '''<p style="color:#0f0">Alert sent successfully!</p>
Return to your journal here: >> \
<a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">%(journal_name)s</a> <br/>
or go back to the <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">administration interface</a>''' % {'CFG_SITE_URL': CFG_SITE_URL,
'journal_name': journal_name}
return out
def tmpl_admin_control_issue(self, ln, journal_name,
active_issues):
"""
Display the interface allowing to set the current issue.
"""
_ = gettext_set_language(ln)
out = '''
<p>This interface gives you the possibility to create your
current webjournal publication. Every checked issue number
will be in the current publication. Once you have made your
selection you can publish the new issue by clicking the %(publish)s
button at the end.
</p>
<form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/issue_control" name="publish">
<input type="hidden" name="journal_name" value="%(journal_name)s"/>
Issue Numbers to publish:
<ul>
%(issues_list)s
</ul>
<br/>
<p>Add a higher issue number by clicking "%(add)s"</p>
<input class="formbutton" type="submit" value="%(add)s" name="action"/>
<p>.. or add a custom issue number by typing it here and pressing "%(refresh)s"</p>
<input type="text" value="ww/YYYY" name="issue"/>
<input class="formbutton" type="submit" value="%(refresh)s" name="action"/>
<br/>
<br/>
<p>If all issues you want to publish are correctly checked, proceed \
by clicking "%(publish)s".</p>
<input class="formbutton" type="submit" value="%(publish)s" name="action"/>
</form>
''' % {'CFG_SITE_URL': CFG_SITE_URL,
'journal_name': journal_name,
'issues_list': "".join(['<li><input type="checkbox" name="issue" value="%s" CHECKED>&nbsp;%s</input></li>'
% (issue, issue) for issue in active_issues]),
'add' : _("Add"),
'publish' : _("Publish"),
'refresh' : _("Refresh")
}
return out
def tmpl_admin_control_issue_success_msg(self, ln,
active_issues, journal_name):
"""
An issue was successfully published
"""
_ = gettext_set_language(ln)
issue_string = "".join([" - %s" % issue for issue in active_issues])
title = '<h2>Issue(s) %s created successfully!</h2>' % issue_string
body = '''<p>Now you can:</p>
<p>Return to your journal here: >>
<a href="%s/journal/%s"> %s </a>
</p>
<p>Make additional publications here: >>
<a href="%s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%s">Publishing Interface</a>
</p>
<p>Send an alert email here: >>
<a href="%s/admin/webjournal/webjournaladmin.py/alert?journal_name=%s"> Send an alert</a>
</p>''' % (CFG_SITE_URL, journal_name,
journal_name, CFG_SITE_URL,
journal_name, CFG_SITE_URL, journal_name)
return title + body
def tmpl_admin_update_issue(self, ln, journal_name, next_issue,
current_issue):
"""
A form that lets a user make an update to an issue number.
"""
_ = gettext_set_language(ln)
current_articles = get_number_of_articles_for_issue(current_issue,
journal_name,
ln)
next_articles = get_number_of_articles_for_issue(next_issue,
journal_name,
ln)
html = '''
<p>The Issue that was released on week %(current_issue)s has pending updates scheduled. The
next update for this issue is %(next_issue)s.</p>
<p><em>Note: If you want to make a new release, please click through all the
pending updates first.</em></p>
<p>Do you want to release the update from issue: <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>%(current_issue)s</em> (%(current_articles)s) <br/>
to issue: <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>%(next_issue)s</em> (%(next_articles)s) <br/>
now?</p>
<form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/issue_control" name="publish">
<input type="hidden" name="journal_name" value="%(journal_name)s"/>
<input type="hidden" name="issue" value="%(next_issue)s"/>
<input class="formbutton" type="submit" value="%(update)s" name="action"/>
</form>
''' % {'current_issue': current_issue,
'next_issue' : next_issue,
'current_articles': ",".join(["%s : %s" % (item[0], item[1]) \
for item in current_articles.iteritems()]),
'next_articles': ",".join(["%s : %s" % (item[0], item[1]) \
for item in next_articles.iteritems()]),
'CFG_SITE_URL' : CFG_SITE_URL,
'journal_name': journal_name,
'update': _("Update")}
return html
def tmpl_admin_updated_issue_msg(self, ln, update_issue, journal_name):
"""
Prints a success message for the Update release of a journal.
"""
_ = gettext_set_language(ln)
title = '<h2>Journal update %s published successfully!</h2>' % update_issue
body = '''<p>Now you can:</p>
<p>Return to your journal here: >>
<a href="%s/journal/%s"> %s </a>
</p>
<p>Go back to the publishing interface: >>
<a href="%s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%s">Issue Interface</a>
</p>
<p>Send an alert email here: >>
<a href="%s/journal/alert?name=%s"> Send an alert</a>
</p>''' % (CFG_SITE_URL, journal_name, journal_name,
CFG_SITE_URL, journal_name, CFG_SITE_URL, journal_name)
return title + body
def tmpl_admin_administrate(self, journal_name, current_issue,
current_publication, issue_list,
next_issue_number, ln=CFG_SITE_LANG,
as_editor=True):
"""
Returns an administration interface that shows the current publication and
supports links to all important actions.
@param as_editor: True if can make changes to the configuration. Else read-only mode.
"""
_ = gettext_set_language(ln)
out = ''
if as_editor:
admin_menu = '''<table class="admin_wvar">
<tr><th colspan="5" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small>Administrate</small>&nbsp;</td>
<td>1.&nbsp;<small><a href="feature_record?journal_name=%(journal_name)s">Feature a Record</a></small>&nbsp;</td>
<td>2.&nbsp;<small><a href="configure?action=edit&amp;journal_name=%(journal_name)s">Edit Configuration</a></small>&nbsp;</td>
<td>3.&nbsp;<small><a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">Go to the Journal</a></small>&nbsp;</td>
</tr>
</table><br/>'''
else:
admin_menu = '''<table class="admin_wvar">
<tr><th colspan="5" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small>Administrate</small>&nbsp;</td>
<td>1.&nbsp;<small><a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">Go to the Journal</a></small>&nbsp;</td>
</tr>
</table><br/>'''
out += admin_menu % {'journal_name': journal_name,
'menu': _("Menu"),
'CFG_SITE_URL': CFG_SITE_URL}
# format the issues
issue_boxes = []
issue_list.append(next_issue_number)
for issue in issue_list:
articles = get_number_of_articles_for_issue(issue,
journal_name,
ln)
released_on = get_release_datetime(issue, journal_name, ln)
announced_on = get_announcement_datetime(issue, journal_name, ln)
issue_box = '''
<tr style="%s">
<td class="admintdright" style="vertical-align: middle;"></td>
<td class="admintdleft" style="white-space: nowrap; vertical-align: middle;">
<p>Issue: %s</p>
<p>Publication: %s</p>
</td>
<td class="admintdright" style="vertical-align: middle;">
%s
</td>
<td class="admintdright" style="vertical-align: middle;">
<p>%s</p>
<p>%s</p>
</td>
<td class="admintdright" style="vertical-align: middle;">
<p><a href="%s/admin/webjournal/webjournaladmin.py/regenerate?journal_name=%s&amp;issue=%s&amp;ln=%s">&gt;regenerate</a></p>
</td>
<tr>
''' % ((issue==current_issue) and "background:#00FF00;" or "background:#F1F1F1;",
issue, (issue==next_issue_number) and "?" or current_publication,
"\n".join(['<p>%s : %s <a href="%s/journal/%s/%s/%s/%s">&gt;view</a></p>' %
(item[0], item[1],
CFG_SITE_URL, journal_name,
issue.split('/')[1], issue.split('/')[0], item[0]) \
for item in articles.iteritems()]),
(not released_on) and
('<em>not released</em>' + (as_editor and '<br/><a href="%s/admin/webjournal/webjournaladmin.py/issue_control?journal_name=%s">&gt;release now</a>' % (CFG_SITE_URL, journal_name) or '')) or
'released on: %s' % released_on.strftime("%d.%m.%Y"),
(not announced_on)
and ('<em>not announced</em>' + (as_editor and '<br/><a href="%s/admin/webjournal/webjournaladmin.py/alert?journal_name=%s&issue=%s">&gt;announce now</a>' % (CFG_SITE_URL, journal_name, issue) or '')) or
'announced on: %s <br/><a href="%s/admin/webjournal/webjournaladmin.py/alert?journal_name=%s&issue=%s">&gt;re-announce</a>' % (announced_on.strftime("%d.%m.%Y"), CFG_SITE_URL, journal_name, issue),
CFG_SITE_URL, journal_name, issue, ln
)
issue_boxes.append(issue_box)
out += '''
<table class="admin_wvar" width="80%%" cellspacing="0">
<tbody>
<tr>
<th class="adminheaderleft"></th>
<th class="adminheaderleft">Issue / Publication</th>
<th class="adminheader">Articles</th>
<th class="adminheaderleft">Release / Announcement</th>
<th class="adminheaderleft">Cache Status</th>
<tr>
%s
</tbody>
</table>
''' % ("\n".join([issue_box for issue_box in issue_boxes]))
return out
def tmpl_admin_index(self, ln, journals, msg=None):
"""
Returns the admin index page content.
Lists the journals, and offers options to edit them, delete them
or add new journal
params:
ln - ln
journals - list of tuples (journal_info dict, as_editor)
msg - message to be displayed
"""
out = ""
if msg is not None:
out += msg
out += '''
<p>Choose the journal you want to administrate.</p>
<table class="admin_wvar" cellspacing="0">
<tr>
<th class="adminheader">Journals</th>
<th colspan="2" class="adminheader">&nbsp;</th>
</tr>
'''
color = "fff"
for journal_info, as_editor in journals:
row = '''<tr style="background-color:#%(color)s">
<td class="admintdleft"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">%(journal_name)s</a></td>
<td class="admintdright"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">edit</a></td>'''
if as_editor:
row += '<td class="admintdright"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/index?journal_name=%(journal_name)s&action=askDelete">delete</a></td>'
row += '</tr>'
out += row % {'color': color,
'journal_name': journal_info['journal_name'],
'journal_id': journal_info['journal_id'],
'CFG_SITE_URL': CFG_SITE_URL}
if color == 'fff':
color = 'EBF7FF'
else:
color = 'fff'
out += '''<tr style="background-color:#%(color)s">
<td class="admintdleft" colspan="3" style="padding: 5px 10px;"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/configure?action=add">Add new journal</a></td>
</tr>''' % {'color': color,
'CFG_SITE_URL': CFG_SITE_URL}
out += '</table>'
return out
def tmpl_admin_configure_journal(self, ln, journal_name='', xml_config=None,
action='edit', msg=None):
"""
Display a page to change the settings of a journal. Also used to
add a new journal.
"""
out = ''
_ = gettext_set_language(ln)
journal_name_readonly = 'readonly="readonly" disabled="disabled"'
journal_name_note = ''
submit_button_label = _('Apply')
if action == 'add':
journal_name = ''
journal_name_readonly = ''
journal_name_note = 'Used in URLs. Choose it short and meaningful. This cannot be changed later'
submit_button_label = _('Add')
elif action in ['edit', 'editDone']:
# Display navigation menu
out += '''<table class="admin_wvar">
<tr><th colspan="5" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="administrate?journal_name=%(journal_name)s">Administrate</a></small>&nbsp;</td>
<td>1.&nbsp;<small><a href="feature_record?journal_name=%(journal_name)s">Feature a Record</a></small>&nbsp;</td>
<td>2.&nbsp;<small>Edit Configuration</small>&nbsp;</td>
<td>3.&nbsp;<small><a href="%(CFG_SITE_URL)s/journal/%(journal_name)s">Go to the Journal</a></small>&nbsp;</td>
</tr>
</table><br/>''' % {'journal_name': journal_name,
'menu': _("Menu"),
'CFG_SITE_URL': CFG_SITE_URL}
if msg is not None:
out += msg
out += '<br/><br/>'
out += '''
<form action="configure" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="action" value="addDone" />
<table class="admin_wvar" cellspacing="0" style="width:90%%">
<tr>
<th colspan="2" class="adminheaderleft">
Journal settings</th>
</tr>
<tr>
<td class="admintdright" width="100px"><label for="journal_name">Name</label>:&nbsp;</td>
<td><input tabindex="0" name="journal_name" type="text" id="journal_name" maxlength="50" size="15" value="%(journal_name)s" %(readonly)s %(journal_name_readonly)s /><small>%(journal_name_note)s</small></td>
</tr>
<tr>
<td class="admintdright"><label for="xml_config">Config</label>:&nbsp;</td>
<td><textarea wrap="soft" rows="25" style="width:100%%" tabindex="3" name="xml_config" id="xml_config" size="25" %(readonly)s>%(xml_config)s</textarea></td>
</tr>
<td colspan="2" align="right"><input type="submit" class="adminbutton" value="%(submit_button_label)s"></td>
</tr>
</table>
</form>
''' % {'journal_name': journal_name,
'ln': ln,
'readonly': '',
'disabled': '',
'xml_config': xml_config.encode('utf-8'),
'journal_name_note': journal_name_note,
'submit_button_label': submit_button_label,
'journal_name_readonly': journal_name_readonly}
return out
diff --git a/modules/webjournal/lib/webjournaladminlib.py b/modules/webjournal/lib/webjournaladminlib.py
index acab5a632..38b7ffef4 100644
--- a/modules/webjournal/lib/webjournaladminlib.py
+++ b/modules/webjournal/lib/webjournaladminlib.py
@@ -1,916 +1,921 @@
## This file is part of Invenio.
## Copyright (C) 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0301
"""Invenio WebJournal Administration Interface."""
__revision__ = "$Id$"
import sys
import cPickle
import re
import os
from urllib2 import urlopen
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
from invenio.errorlib import register_exception
from invenio.config import \
CFG_SITE_URL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_ETCDIR, \
CFG_CACHEDIR, \
CFG_TMPDIR, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.messages import gettext_set_language
from invenio.mailutils import send_email
from invenio.access_control_engine import acc_authorize_action
from invenio.webjournal_config import \
InvenioWebJournalJournalIdNotFoundDBError, \
InvenioWebJournalReleaseUpdateError, \
InvenioWebJournalNoJournalOnServerError
from invenio.webjournal_utils import \
get_journals_ids_and_names, \
guess_journal_name, \
get_current_issue, \
get_issue_number_display, \
get_featured_records, \
add_featured_record, \
remove_featured_record, \
clear_cache_for_issue, \
get_next_journal_issues, \
get_release_datetime, \
get_journal_id, \
compare_issues, \
get_journal_info_path, \
get_journal_css_url, \
get_journal_alert_sender_email, \
get_journal_alert_recipient_email, \
get_journal_draft_keyword_to_remove, \
get_journal_categories, \
get_journal_articles, \
get_grouped_issues, \
get_journal_issue_grouping, \
get_journal_languages, \
get_journal_collection_to_refresh_on_release
from invenio.dbquery import run_sql
from invenio.bibrecord import \
create_record, \
print_rec
from invenio.bibformat import format_record
from invenio.bibtask import task_low_level_submission
from invenio.search_engine import get_all_collections_of_a_record
import invenio.template
wjt = invenio.template.load('webjournal')
def getnavtrail(previous = ''):
"""Get the navtrail"""
navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (CFG_SITE_URL,)
navtrail = navtrail + previous
return navtrail
def perform_index(ln=CFG_SITE_LANG, journal_name=None, action=None, uid=None):
"""
Index page
Lists the journals, and offers options to edit them, delete them
or add new journal.
Parameters:
journal_name - the journal affected by action, if any
action - one of ['', 'askDelete', _('Delete'), _('Cancel')]
ln - language
uid - user id
"""
_ = gettext_set_language(ln)
msg = None
if action == 'askDelete' and journal_name is not None:
msg = '''<fieldset style="display:inline;margin-left:auto;margin-right:auto;">
<legend>Delete Journal Configuration</legend><span style="color:#f00">Are you sure you want to delete the configuration of %(journal_name)s?
<form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py">
<input type="hidden" name="journal_name" value="%(journal_name)s" />
<input class="formbutton" type="submit" name="action" value="%(delete)s" />
<input class="formbutton" type="submit" name="action" value="%(cancel)s" />
</form></span></fieldset>''' % {'CFG_SITE_URL': CFG_SITE_URL,
'journal_name': journal_name,
'delete': _("Delete"),
'cancel': _("Cancel")}
if action == _("Delete") and journal_name is not None:
# User confirmed and clicked on "Delete" button
remove_journal(journal_name)
journals = get_journals_ids_and_names()
# Only keep journal that user can view or edit
journals = [(journal_info, acc_authorize_action(uid,
'cfgwebjournal',
name=journal_info['journal_name'],
with_editor_rights='yes')[0] == 0) \
for journal_info in journals \
if acc_authorize_action(uid,
'cfgwebjournal',
name=journal_info['journal_name'])[0] == 0]
return wjt.tmpl_admin_index(ln=ln,
journals=journals,
msg=msg)
def perform_administrate(ln=CFG_SITE_LANG, journal_name=None,
as_editor=True):
"""
Administration of a journal
Show the current and next issues/publications, and display links
to more specific administrative pages.
Parameters:
journal_name - the journal to be administrated
ln - language
with_editor_rights - True if can edit configuration. Read-only mode otherwise
"""
if journal_name is None:
try:
journal_name = guess_journal_name(ln)
except InvenioWebJournalNoJournalOnServerError, e:
return e.user_box()
if not can_read_xml_config(journal_name):
return '<span style="color:#f00">Configuration could not be read. Please check that %s/webjournal/%s/%s-config.xml exists and can be read by the server.</span><br/>' % (CFG_ETCDIR, journal_name, journal_name)
current_issue = get_current_issue(ln, journal_name)
current_publication = get_issue_number_display(current_issue,
journal_name,
ln)
issue_list = get_grouped_issues(journal_name, current_issue)
next_issue_number = get_next_journal_issues(issue_list[-1], journal_name, 1)
return wjt.tmpl_admin_administrate(journal_name,
current_issue,
current_publication,
issue_list,
next_issue_number[0],
ln,
as_editor=as_editor)
def perform_feature_record(journal_name,
recid,
img_url='',
action='',
ln=CFG_SITE_LANG):
"""
Interface to feature a record
Used to list, add and remove featured records of the journal.
Parameters:
journal_name - the journal for which the article is featured
recid - the record affected by 'action'
img_url - the URL to image displayed with given record
(only when action == 'add')
action - One of ['', 'add', 'askremove', _('Remove'), _('Cancel')]
ln - language
"""
_ = gettext_set_language(ln)
if action == 'add':
result = add_featured_record(journal_name, recid, img_url)
if result == 0:
msg ='''<span style="color:#0f0">Successfully featured
- <a href="%(CFG_SITE_URL)s/record/%(recid)s">record %(recid)s</a>.
+ <a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)s">record %(recid)s</a>.
Go to the <a href="%(CFG_SITE_URL)s/journal/%(name)s">%(name)s journal</a> to
see the result.</span>''' % {'CFG_SITE_URL': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'name': journal_name,
'recid': recid}
elif result == 1:
- msg = '''<span style="color:#f00"><a href="%(CFG_SITE_URL)s/record/%(recid)s">record %(recid)s</a> is already featured. Choose another one or remove it first.</span>''' % \
+ msg = '''<span style="color:#f00"><a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)s">record %(recid)s</a> is already featured. Choose another one or remove it first.</span>''' % \
{'CFG_SITE_URL': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'recid': recid}
else:
msg = '''<span style="color:#f00">Record could not be featured. Check file permission.</span>'''
featured_records = get_featured_records(journal_name)
return wjt.tmpl_admin_feature_record(ln=ln,
journal_name=journal_name,
featured_records=featured_records,
msg=msg)
elif action == 'askremove':
msg = '''<fieldset style="display:inline;margin-left:auto;margin-right:auto;">
- <legend>Remove featured record</legend><span style="color:#f00">Are you sure you want to remove <a href="%(CFG_SITE_URL)s/record/%(recid)s">record %(recid)s</a> from the list of featured record?
+ <legend>Remove featured record</legend><span style="color:#f00">Are you sure you want to remove <a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)s">record %(recid)s</a> from the list of featured record?
<form action="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/feature_record">
<input type="hidden" name="journal_name" value="%(name)s" />
<input type="hidden" name="recid" value="%(recid)s" />
<input class="formbutton" type="submit" name="action" value="%(remove)s" />
<input class="formbutton" type="submit" name="action" value="%(cancel)s" />
</form></span></fieldset>''' % \
{'CFG_SITE_URL': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'name': journal_name,
'recid': recid,
'cancel': _("Cancel"),
'remove': _("Remove")}
featured_records = get_featured_records(journal_name)
return wjt.tmpl_admin_feature_record(ln=ln,
journal_name=journal_name,
featured_records=featured_records,
msg=msg)
elif action == _("Remove"):
result = remove_featured_record(journal_name, recid)
- msg = '''<span style="color:#f00"><a href="%(CFG_SITE_URL)s/record/%(recid)s">Record %(recid)s</a>
+ msg = '''<span style="color:#f00"><a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)s">Record %(recid)s</a>
has been removed.</span>''' % \
{'CFG_SITE_URL': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'recid': recid}
featured_records = get_featured_records(journal_name)
return wjt.tmpl_admin_feature_record(ln=ln,
journal_name=journal_name,
featured_records=featured_records,
msg=msg)
else:
msg = '''Here you can choose which records from the %s should
be featured on the journal webpage.''' % CFG_SITE_NAME
featured_records = get_featured_records(journal_name)
return wjt.tmpl_admin_feature_record(ln=ln,
journal_name=journal_name,
featured_records=featured_records,
msg=msg)
def perform_regenerate_issue(issue,
journal_name,
ln=CFG_SITE_LANG):
"""
Clears the cache for the given issue.
Parameters:
journal_name - the journal for which the cache should be
deleted
issue - the issue for which the cache should be deleted
ln - language
"""
success = clear_cache_for_issue(journal_name,
issue)
if success:
return wjt.tmpl_admin_regenerate_success(ln,
journal_name,
issue)
else:
return wjt.tmpl_admin_regenerate_error(ln,
journal_name,
issue)
def perform_request_issue_control(journal_name, issues,
action, ln=CFG_SITE_LANG):
"""
Central logic for issue control.
Regenerates the flat files 'current_issue' and 'issue_group' of
the journal that control which issue is currently active for the
journal.
Parameters:
journal_name - the journal affected by 'action'
issues - list of issues affected by 'action' TODO: check
action - One of ['cfg', _('Add'), _('Refresh'),
_('Publish'), _('Update')]
ln - language
"""
_ = gettext_set_language(ln)
out = ''
if action == "cfg" or action == _("Refresh") or action == _("Add"):
# find out if we are in update or release
current_issue = get_current_issue(ln, journal_name)
grouped_issues = get_grouped_issues(journal_name, current_issue)
if current_issue != grouped_issues[-1]:
# The current issue has "pending updates", i.e. is grouped
# with unreleased issues. Propose to update these issues
next_issue = grouped_issues[grouped_issues.index(current_issue) + 1]
out = wjt.tmpl_admin_update_issue(ln,
journal_name,
next_issue,
current_issue)
else:
# Propose a release
next_issues = get_next_journal_issues(current_issue,
journal_name,
n=get_journal_issue_grouping(journal_name))
if action == _("Refresh"):
next_issues += issues
next_issues = list(set(next_issues))# avoid double entries
elif action == _("Add"):
next_issues += issues
next_issues = list(set(next_issues))# avoid double entries
next_issues.sort(compare_issues)
highest_issue_so_far = next_issues[-1]
one_more_issue = get_next_journal_issues(highest_issue_so_far,
journal_name,
1)
next_issues += one_more_issue
next_issues = list(set(next_issues)) # avoid double entries
else:
# get the next issue numbers to publish
next_issues = get_next_journal_issues(current_issue,
journal_name,
n=get_journal_issue_grouping(journal_name))
next_issues.sort(compare_issues)
out = wjt.tmpl_admin_control_issue(ln,
journal_name,
next_issues)
elif action == _("Publish"):
# Publish the given issues (mark them as current issues)
publish_issues = issues
publish_issues = list(set(publish_issues)) # avoid double entries
publish_issues.sort(compare_issues)
if len(publish_issues) == 0:
# User did not select an issue
current_issue = get_current_issue(ln, journal_name)
next_issues = get_next_journal_issues(current_issue,
journal_name,
n=get_journal_issue_grouping(journal_name))
out = '<p style="color:#f00;text-align:center">' + \
_('Please select an issue') + '</p>'
out += wjt.tmpl_admin_control_issue(ln,
journal_name,
next_issues)
return out
try:
release_journal_issue(publish_issues, journal_name, ln)
except InvenioWebJournalJournalIdNotFoundDBError, e:
register_exception(req=None)
return e.user_box()
out = wjt.tmpl_admin_control_issue_success_msg(ln,
publish_issues,
journal_name)
elif action == _("Update"):
try:
try:
update_issue = issues[0]
except:
raise InvenioWebJournalReleaseUpdateError(ln, journal_name)
except InvenioWebJournalReleaseUpdateError, e:
register_exception(req=None)
return e.user_box()
try:
release_journal_update(update_issue, journal_name, ln)
except InvenioWebJournalJournalIdNotFoundDBError, e:
register_exception(req=None)
return e.user_box()
out = wjt.tmpl_admin_updated_issue_msg(ln,
update_issue,
journal_name)
return out
def perform_request_alert(journal_name, issue,
sent, plain_text, subject, recipients,
html_mail, force, ln=CFG_SITE_LANG):
"""
All the logic for alert emails.
Display a form to edit email/recipients and options to send the
email. Sent in HTML/PlainText or only PlainText if wished so.
Also prevent mistake of sending the alert more than one for a
particular issue.
Parameters:
journal_name - the journal for which the alert is sent
issue - the issue for which the alert is sent
sent - Display interface to edit email if "False"
(string). Else send the email.
plain_text - the text of the mail
subject - the subject of the mail
recipients - the recipients of the mail (string with
comma-separated emails)
html_mail - if 'html', also send email as HTML (copying
from the current issue on the web)
force - if different than "False", the email is sent
even if it has already been sent.
ln - language
"""
# FIXME: more flexible options to choose the language of the alert
languages = get_journal_languages(journal_name)
if languages:
alert_ln = languages[0]
else:
alert_ln = CFG_SITE_LANG
if not get_release_datetime(issue, journal_name, ln):
# Trying to send an alert for an unreleased issue
return wjt.tmpl_admin_alert_unreleased_issue(ln,
journal_name)
if sent == "False":
# Retrieve default message, subject and recipients, and
# display email editor
subject = wjt.tmpl_admin_alert_subject(journal_name,
alert_ln,
issue)
plain_text = wjt.tmpl_admin_alert_plain_text(journal_name,
alert_ln,
issue)
plain_text = plain_text.encode('utf-8')
recipients = get_journal_alert_recipient_email(journal_name)
return wjt.tmpl_admin_alert_interface(ln,
journal_name,
subject,
plain_text,
recipients,
alert_ln)
else:
# User asked to send the mail
if was_alert_sent_for_issue(issue,
journal_name,
ln) != False and force == "False":
# Mmh, email already sent before for this issue. Ask
# confirmation
return wjt.tmpl_admin_alert_was_already_sent(ln,
journal_name,
subject,
plain_text,
recipients,
html_mail,
issue)
html_string = None
if html_mail == "html":
# Also send as HTML: retrieve from current issue
html_file = urlopen('%s/journal/%s?ln=%s'
% (CFG_SITE_URL, journal_name, alert_ln))
html_string = html_file.read()
html_file.close()
html_string = put_css_in_file(html_string, journal_name)
html_string = insert_journal_link(html_string, journal_name, issue, ln)
sender_email = get_journal_alert_sender_email(journal_name)
send_email(sender_email, recipients, subject, plain_text,
html_string, header='', footer='', html_header='',
html_footer='', charset='utf-8')
update_DB_for_alert(issue, journal_name, ln)
return wjt.tmpl_admin_alert_success_msg(ln,
journal_name)
def perform_request_configure(journal_name, xml_config, action, ln=CFG_SITE_LANG):
"""
Add a new journal or configure the settings of an existing journal.
Parameters:
journal_name - the journal to configure, or name of the new journal
xml_config - the xml configuration of the journal (string)
action - One of ['edit', 'editDone', 'add', 'addDone']
ln - language
"""
msg = None
if action == 'edit':
# Read existing config
if journal_name is not None:
if not can_read_xml_config(journal_name):
return '<span style="color:#f00">Configuration could not be read. Please check that %s/webjournal/%s/%s-config.xml exists and can be read by the server.</span><br/>' % (CFG_ETCDIR, journal_name, journal_name)
config_path = '%s/webjournal/%s/%s-config.xml' % (CFG_ETCDIR, journal_name, journal_name)
xml_config = file(config_path).read()
else:
# cannot edit unknown journal...
return '<span style="color:#f00">You must specify a journal name</span>'
if action in ['editDone', 'addDone']:
# Save config
if action == 'addDone':
res = add_journal(journal_name, xml_config)
if res == -1:
msg = '<span style="color:#f00">A journal with that name already exists. Please choose another name.</span>'
action = 'add'
elif res == -2:
msg = '<span style="color:#f00">Configuration could not be written (no permission). Please manually copy your config to %s/webjournal/%s/%s-config.xml</span><br/>' % (CFG_ETCDIR, journal_name, journal_name)
action = 'edit'
elif res == -4:
msg = '<span style="color:#f00">Cache file could not be written (no permission). Please manually create directory %s/webjournal/%s/ and make it writable for your Apache user</span><br/>' % (CFG_CACHEDIR, journal_name)
action = 'edit'
elif res > 0:
msg = '<span style="color:#0f0">Journal successfully added.</span>'
action = 'edit'
else:
msg = '<span style="color:#f00">An error occurred. The journal could not be added</span>'
action = 'edit'
if action == 'add':
# Display a sample config.
xml_config = '''<?xml version="1.0" encoding="UTF-8"?>
<webjournal name="AtlantisTimes">
<view>
<niceName>Atlantis Times</niceName>
<niceURL>%(CFG_SITE_URL)s</niceURL>
<css>
<screen>/img/AtlantisTimes.css</screen>
<print>/img/AtlantisTimes.css</print>
</css>
<format_template>
<index>AtlantisTimes_Index.bft</index>
<detailed>AtlantisTimes_Detailed.bft</detailed>
<search>AtlantisTimes_Search.bft</search>
<popup>AtlantisTimes_Popup.bft</popup>
<contact>AtlantisTimes_Contact.bft</contact>
</format_template>
</view>
<model>
<record>
<rule>News, 980__a:ATLANTISTIMESNEWS or 980__a:ATLANTISTIMESNEWSDRAFT</rule>
<rule>Science, 980__a:ATLANTISTIMESSCIENCE or 980__a:ATLANTISTIMESSCIENCEDRAFT</rule>
<rule>Arts, 980__a:ATLANTISTIMESARTS or 980__a:ATLANTISTIMESARTSDRAFT</rule>
</record>
</model>
<controller>
<issue_grouping>2</issue_grouping>
<issues_per_year>52</issues_per_year>
<hide_unreleased_issues>all</hide_unreleased_issues>
<marc_tags>
<issue_number>773__n</issue_number>
<order_number>773__c</order_number>
</marc_tags>
<alert_sender>%(CFG_SITE_SUPPORT_EMAIL)s</alert_sender>
<alert_recipients>recipients@atlantis.atl</alert_recipients>
<languages>en,fr</languages>
<submission>
<doctype>DEMOJRN</doctype>
<report_number_field>DEMOJRN_RN</report_number_field>
</submission>
<first_issue>02/2009</first_issue>
<draft_keyword>DRAFT</draft_keyword>
</controller>
</webjournal>''' % {'CFG_SITE_URL': CFG_SITE_URL,
'CFG_SITE_SUPPORT_EMAIL': CFG_SITE_SUPPORT_EMAIL}
out = wjt.tmpl_admin_configure_journal(ln=ln,
journal_name=journal_name,
xml_config=xml_config,
action=action,
msg=msg)
return out
######################## ADDING/REMOVING JOURNALS ###############################
def add_journal(journal_name, xml_config):
"""
Add a new journal to the DB. Also create the configuration file
Parameters:
journal_name - the name (used in URLs) of the new journal
xml_config - the xml configuration of the journal (string)
Returns:
the id of the journal if successfully added
-1 if could not be added because journal name already exists
-2 if config could not be saved
-3 if could not be added for other reasons
-4 if database cache could not be added
"""
try:
get_journal_id(journal_name)
except InvenioWebJournalJournalIdNotFoundDBError:
# Perfect, journal does not exist
res = run_sql("INSERT INTO jrnJOURNAL (name) VALUES(%s)", (journal_name,))
# Also save xml_config
config_dir = '%s/webjournal/%s/' % (CFG_ETCDIR, journal_name)
try:
if not os.path.exists(config_dir):
os.makedirs(config_dir)
xml_config_file = file(config_dir + journal_name + '-config.xml', 'w')
xml_config_file.write(xml_config)
xml_config_file.close()
except Exception:
res = -2
# And save some info in file in case database is down
journal_info_path = get_journal_info_path(journal_name)
journal_info_dir = os.path.dirname(journal_info_path)
if not os.path.exists(journal_info_dir):
try:
os.makedirs(journal_info_dir)
except Exception:
if res <= 0:
res = -4
journal_info_file = open(journal_info_path, 'w')
cPickle.dump({'journal_id': res,
'journal_name': journal_name,
'current_issue':'01/2000'}, journal_info_file)
return res
return -1
def remove_journal(journal_name):
"""
Remove a journal from the DB. Does not completely remove
everything, in case it was an error from the editor..
Parameters:
journal_name - the journal to remove
Returns:
the id of the journal if successfully removed or
-1 if could not be removed because journal name does not exist or
-2 if could not be removed for other reasons
"""
run_sql("DELETE FROM jrnJOURNAL WHERE name=%s", (journal_name,))
######################## TIME / ISSUE FUNCTIONS ###############################
def release_journal_issue(publish_issues, journal_name, ln=CFG_SITE_LANG):
"""
Releases a new issue.
This sets the current issue in the database to 'publish_issues' for
given 'journal_name'
Parameters:
journal_name - the journal for which we release a new issue
publish_issues - the list of issues that will be considered as
current (there can be several)
ln - language
"""
journal_id = get_journal_id(journal_name, ln)
if len(publish_issues) > 1:
publish_issues.sort(compare_issues)
low_bound = publish_issues[0]
high_bound = publish_issues[-1]
issue_display = '%s-%s/%s' % (low_bound.split("/")[0],
high_bound.split("/")[0],
high_bound.split("/")[1])
# remember convention: if we are going over a new year, take the higher
else:
issue_display = publish_issues[0]
# produce the DB lines
for publish_issue in publish_issues:
move_drafts_articles_to_ready(journal_name, publish_issue)
run_sql("INSERT INTO jrnISSUE (id_jrnJOURNAL, issue_number, issue_display) \
VALUES(%s, %s, %s)", (journal_id,
publish_issue,
issue_display))
# set first issue to published
release_journal_update(publish_issues[0], journal_name, ln)
# update information in file (in case DB is down)
journal_info_path = get_journal_info_path(journal_name)
journal_info_file = open(journal_info_path, 'w')
cPickle.dump({'journal_id': journal_id,
'journal_name': journal_name,
'current_issue': get_current_issue(ln, journal_name)},
journal_info_file)
def delete_journal_issue(issue, journal_name, ln=CFG_SITE_LANG):
"""
Deletes an issue from the DB.
(Not currently used)
"""
journal_id = get_journal_id(journal_name, ln)
run_sql("DELETE FROM jrnISSUE WHERE issue_number=%s \
AND id_jrnJOURNAL=%s",(issue, journal_id))
# update information in file (in case DB is down)
journal_info_path = get_journal_info_path(journal_name)
journal_info_file = open(journal_info_path, 'w')
cPickle.dump({'journal_id': journal_id,
'journal_name': journal_name,
'current_issue': get_current_issue(ln, journal_name)},
journal_info_file)
def was_alert_sent_for_issue(issue, journal_name, ln):
"""
Returns False if alert has not already been sent for given journal and
issue, else returns time of last alert, as time tuple
Parameters:
journal_name - the journal for which we want to check last alert
issue - the issue for which we want to check last alert
ln - language
Returns:
time tuple or False. Eg: (2008, 4, 25, 7, 58, 37, 4, 116, -1)
"""
journal_id = get_journal_id(journal_name, ln)
date_announced = run_sql("SELECT date_announced FROM jrnISSUE \
WHERE issue_number=%s \
AND id_jrnJOURNAL=%s", (issue, journal_id))[0][0]
if date_announced == None:
return False
else:
return date_announced.timetuple()
def update_DB_for_alert(issue, journal_name, ln):
"""
Update the 'last sent alert' timestamp for the given journal and
issue.
Parameters:
journal_name - the journal for which we want to update the time
of last alert
issue - the issue for which we want to update the time
of last alert
ln - language
"""
journal_id = get_journal_id(journal_name, ln)
run_sql("UPDATE jrnISSUE set date_announced=NOW() \
WHERE issue_number=%s \
AND id_jrnJOURNAL=%s", (issue,
journal_id))
def release_journal_update(update_issue, journal_name, ln=CFG_SITE_LANG):
"""
Releases an update to a journal.
"""
move_drafts_articles_to_ready(journal_name, update_issue)
journal_id = get_journal_id(journal_name, ln)
run_sql("UPDATE jrnISSUE set date_released=NOW() \
WHERE issue_number=%s \
AND id_jrnJOURNAL=%s", (update_issue,
journal_id))
def move_drafts_articles_to_ready(journal_name, issue):
"""
Move draft articles to their final "collection".
To do so we rely on the convention that an admin-chosen keyword
must be removed from the metadata
"""
protected_datafields = ['100', '245', '246', '520', '590', '700']
keyword_to_remove = get_journal_draft_keyword_to_remove(journal_name)
collections_to_refresh = {}
categories = get_journal_categories(journal_name, issue)
for category in categories:
articles = get_journal_articles(journal_name, issue, category)
for order, recids in articles.iteritems():
for recid in recids:
record_xml = format_record(recid, of='xm')
if not record_xml:
continue
new_record_xml_path = os.path.join(CFG_TMPDIR,
'webjournal_publish_' + \
str(recid) + '.xml')
if os.path.exists(new_record_xml_path):
# Do not modify twice
continue
record_struc = create_record(record_xml)
record = record_struc[0]
new_record = update_draft_record_metadata(record,
protected_datafields,
keyword_to_remove)
new_record_xml = print_rec(new_record)
if new_record_xml.find(keyword_to_remove) >= 0:
new_record_xml = new_record_xml.replace(keyword_to_remove, '')
# Write to file
new_record_xml_file = file(new_record_xml_path, 'w')
new_record_xml_file.write(new_record_xml)
new_record_xml_file.close()
# Submit
task_low_level_submission('bibupload',
'WebJournal',
'-c', new_record_xml_path)
task_low_level_submission('bibindex',
'WebJournal',
'-i', str(recid))
for collection in get_all_collections_of_a_record(recid):
collections_to_refresh[collection] = ''
# Refresh collections
collections_to_refresh.update([(c, '') for c in get_journal_collection_to_refresh_on_release(journal_name)])
for collection in collections_to_refresh.keys():
task_low_level_submission('webcoll',
'WebJournal',
'-f', '-p', '2','-c', collection)
def update_draft_record_metadata(record, protected_datafields, keyword_to_remove):
"""
Returns a new record with fields that should be modified in order
for this draft record to be considered as 'ready': keep only
controlfield 001 and non-protected fields that contains the
'keyword_to_remove'
Parameters:
record - a single recored (as BibRecord structure)
protected_datafields - *list* tags that should not be part of the
returned record
keyword_to_remove - *str* keyword that should be considered
when checking if a field should be part of
the returned record.
"""
new_record = {}
for tag, field in record.iteritems():
if tag in protected_datafields:
continue
elif not keyword_to_remove in str(field) and \
not tag == '001':
continue
else:
# Keep
new_record[tag] = field
return new_record
######################## XML CONFIG ###############################
def can_read_xml_config(journal_name):
"""
Check that configuration xml for given journal name is exists and
can be read.
"""
config_path = '%s/webjournal/%s/%s-config.xml' % \
(CFG_ETCDIR, journal_name, journal_name)
try:
file(config_path).read()
except IOError:
return False
return True
######################## EMAIL HELPER FUNCTIONS ###############################
def insert_journal_link(html_string, journal_name, issue, ln):
"""
Insert a warning regarding HTML formatting inside mail client and
link to journal page just after the body of the page.
@param html_string: the HTML newsletter
@param journal_name: the journal name
@param issue: journal issue for which the alert is sent (in the form number/year)
@param ln: language
"""
def replace_body(match_obj):
"Replace body with itself + header message"
header = wjt.tmpl_admin_alert_header_html(journal_name, ln, issue)
return match_obj.group() + header
return re.sub('<body.*?>', replace_body, html_string, 1)
def put_css_in_file(html_message, journal_name):
"""
Retrieve the CSS of the journal and insert/inline it in the <head>
section of the given html_message. (Used for HTML alert emails)
Parameters:
journal_name - the journal name
html_message - the html message (string) in which the CSS
should be inserted
Returns:
the HTML message with its CSS inlined
"""
css_path = get_journal_css_url(journal_name)
if not css_path:
return
css_file = urlopen(css_path)
css = css_file.read()
css = make_full_paths_in_css(css, journal_name)
html_parted = html_message.split("</head>")
if len(html_parted) > 1:
html = '%s<style type="text/css">%s</style></head>%s' % (html_parted[0],
css,
html_parted[1])
else:
html_parted = html_message.split("<html>")
if len(html_parted) > 1:
html = '%s<html><head><style type="text/css">%s</style></head>%s' % (html_parted[0],
css,
html_parted[1])
else:
return
return html
def make_full_paths_in_css(css, journal_name):
"""
Update the URLs in a CSS from relative to absolute URLs, so that the
URLs are accessible from anywhere (Used for HTML alert emails)
Parameters:
journal_name - the journal name
css - a cascading stylesheet (string)
Returns:
(str) the given css with relative paths converted to absolute paths
"""
url_pattern = re.compile('''url\(["']?\s*(?P<url>\S*)\s*["']?\)''',
re.DOTALL)
url_iter = url_pattern.finditer(css)
rel_to_full_path = {}
for url in url_iter:
url_string = url.group("url")
url_string = url_string.replace('"', "")
url_string = url_string.replace("'", "")
if url_string[:6] != "http://":
rel_to_full_path[url_string] = '"%s/img/webjournal_%s/%s"' % \
(CFG_SITE_URL,
journal_name,
url_string)
for url in rel_to_full_path.keys():
css = css.replace(url, rel_to_full_path[url])
return css
diff --git a/modules/webjournal/lib/widgets/bfe_webjournal_widget_featureRecord.py b/modules/webjournal/lib/widgets/bfe_webjournal_widget_featureRecord.py
index 8336273e5..51453280a 100644
--- a/modules/webjournal/lib/widgets/bfe_webjournal_widget_featureRecord.py
+++ b/modules/webjournal/lib/widgets/bfe_webjournal_widget_featureRecord.py
@@ -1,61 +1,61 @@
# -*- coding: utf-8 -*-
## $Id: bfe_webjournal_widget_forTheEyes.py,v 1.7 2008/06/03 10:04:16 jerome Exp $
##
## This file is part of Invenio.
## Copyright (C) 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
WebJournal widget - List the featured records
"""
from invenio.bibformat_engine import BibFormatObject
-from invenio.config import CFG_SITE_URL
+from invenio.config import CFG_SITE_URL, CFG_SITE_RECORD
from invenio.webjournal_utils import \
parse_url_string, \
get_featured_records
def format_element(bfo):
"""
List the 'featured' records
"""
args = parse_url_string(bfo.user_info['uri'])
journal_name = args["journal_name"]
featured_records = get_featured_records(journal_name)
lines = []
for (recid, img_url) in featured_records:
featured_record = BibFormatObject(recid)
if bfo.lang == 'fr':
title = featured_record.field('246_1a')
if title == '':
# No French translation, get it in English
title = featured_record.field('245__a')
else:
title = featured_record.field('245__a')
lines.append('''
- <a href="%s/record/%s?ln=%s" style="display:block">
+ <a href="%s/%s/%s?ln=%s" style="display:block">
<img src="%s" alt="" width="100" class="phr" />
%s
</a>
- ''' % (CFG_SITE_URL, recid, bfo.lang, img_url, title))
+ ''' % (CFG_SITE_URL, CFG_SITE_RECORD, recid, bfo.lang, img_url, title))
return '<br/><br/>'.join(lines)
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/webjournal/lib/widgets/bfe_webjournal_widget_latestPhoto.py b/modules/webjournal/lib/widgets/bfe_webjournal_widget_latestPhoto.py
index 87e969fed..4143f4705 100644
--- a/modules/webjournal/lib/widgets/bfe_webjournal_widget_latestPhoto.py
+++ b/modules/webjournal/lib/widgets/bfe_webjournal_widget_latestPhoto.py
@@ -1,103 +1,103 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
WebJournal widget - display photos from given collections
"""
from invenio.bibformat_engine import BibFormatObject
from invenio.search_engine import perform_request_search
-from invenio.config import CFG_CERN_SITE, CFG_SITE_URL
+from invenio.config import CFG_CERN_SITE, CFG_SITE_URL, CFG_SITE_RECORD
def format_element(bfo, collections, max_photos="3", separator="<br/>"):
"""
Display the latest pictures from the given collection(s)
@param collections: comma-separated list of collection form which photos have to be fetched
@param max_photos: maximum number of photos to display
@param separator: separator between photos
"""
try:
int_max_photos = int(max_photos)
except:
int_max_photos = 0
try:
collections_list = [coll.strip() for coll in collections.split(',')]
except:
collections_list = []
out = get_widget_html(bfo.lang, int_max_photos,
collections_list, separator, bfo.lang)
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
def get_widget_html(language, max_photos, collections, separator, ln):
"""
Returns the content of the widget
"""
latest_photo_ids = perform_request_search(c=collections,
rg=max_photos,
of='id')
images_urls = []
for recid in latest_photo_ids[:max_photos]:
try:
photo_record = BibFormatObject(recid)
except:
# todo: Exception, no photo in this selection
continue
if language == "fr":
try:
title = photo_record.fields('246_1a', escape=1)[0]
except KeyError:
title = ""
else:
try:
title = photo_record.fields('245__a', escape=1)[0]
except KeyError:
# todo: exception, picture with no title
title = ""
if CFG_CERN_SITE and photo_record.fields('8567_'):
# Get from 8567_
dfs_images = photo_record.fields('8567_')
for image_block in dfs_images:
if image_block.get("y", '') == "Icon":
if image_block.get("u", '').startswith("http://"):
images_urls.append((recid, image_block["u"], title))
break # Just one image per record
else:
# Get from 8564_
images = photo_record.fields('8564_')
for image_block in images:
if image_block.get("x", '').lower() == "icon":
if image_block.get("q", '').startswith("http://"):
images_urls.append((recid, image_block["q"], title))
break # Just one image per record
# Build output
- html_out = separator.join(['<a href="%s/record/%i?ln=%s"><img class="phr" width="100" height="67" src="%s"/>%s</a>' % (CFG_SITE_URL, recid, ln, photo_url, title) for (recid, photo_url, title) in images_urls])
+ html_out = separator.join(['<a href="%s/%s/%i?ln=%s"><img class="phr" width="100" height="67" src="%s"/>%s</a>' % (CFG_SITE_URL, CFG_SITE_RECORD, recid, ln, photo_url, title) for (recid, photo_url, title) in images_urls])
return html_out
diff --git a/modules/websearch/doc/admin/websearch-admin-guide.webdoc b/modules/websearch/doc/admin/websearch-admin-guide.webdoc
index ecf4fcbd1..721e889c4 100644
--- a/modules/websearch/doc/admin/websearch-admin-guide.webdoc
+++ b/modules/websearch/doc/admin/websearch-admin-guide.webdoc
@@ -1,734 +1,734 @@
## -*- mode: html; coding: utf-8; -*-
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(WebSearch Admin Guide)_ -->
<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p><table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
WARNING: THIS ADMIN GUIDE IS NOT FULLY COMPLETED
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
This Admin Guide is not yet completed. Moreover, some
admin-level functionality for this module exists only in the form of
manual recipes. We are in the process of developing both the
guide as well as the web admin interface. If you are interested
in seeing some specific things implemented with high priority,
please contact us at <CFG_SITE_SUPPORT_EMAIL>. Thanks for your interest!
</td>
</tr>
</tbody>
</table>
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
<strong>2. <a href="#2">Edit Collection Tree</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.1 <a href="#2.1">Add new collection</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.2 <a href="#2.2">Add collection to tree</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.3 <a href="#2.3">Modify existing tree</a><br />
<strong>3. <a href="#3">Edit Collection Parameters</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1. <a href="#3.1">Modify collection query</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2. <a href="#3.2">Modify access restrictions</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3. <a href="#3.3">Modify translations</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4. <a href="#3.4">Delete collection</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.5. <a href="#3.5">Modify portalboxes</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.6. <a href="#3.6">Modify search fields</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.7. <a href="#3.7">Modify search options</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.8. <a href="#3.8">Modify sort options</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.9. <a href="#3.9">Modify rank options</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.10. <a href="#3.10">Modify output formats</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.11. <a href="#3.11">Configuration of related external collections</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.12. <a href="#3.12">Detailed record page options</a><br />
<strong>4. <a href="#4">External and Hosted Collections</a></strong><br />
<strong>5. <a href="#5">Webcoll Status</a></strong><br />
<strong>6. <a href="#6">Collections Status</a></strong><br />
<strong>7. <a href="#7">Check External Collections</a></strong><br />
<strong>8. <a href="#8">Edit Search Engine Parameters</a></strong><br />
<strong>9. <a href="#9">Search Engine Cache</a></strong><br />
<strong>10. <a href="#10">Additional Information</a></strong><br />
<a name="1"></a><h2>1. Overview</h2>
<p>WebSearch Admin interface will help you to configure the search
collections that the end-users see. The WebSearch Admin functionality
can be basically separated into several parts: (i) how to organize
collections into <a href="#2">collection tree</a>; (ii) how to define
and edit <a href="#3">collection parameters</a>; (iii) how to update
collection cache via the <a href="#4">webcoll daemon</a>; and (iv) how
to influence the search engine behaviour and set various <a
href="#5">search engine parameters</a>. These issues will be
subsequently described in the rest of this guide.
<a name="2"></a><h2>2. Edit Collection Tree</h2>
<p>Metadata corpus in Invenio is organized into collections. The
collections are organized in a tree. The collection tree is what the
end-users see when they start to navigate at <a
href="<CFG_SITE_URL>"><CFG_SITE_NAME></a>. The collection tree is similar to what
other sites call Web Directories that organize Web into topical
categories, such as <a href="http://www.google.com/dirhp">Google
Directory</a>.
<p>Note that Invenio permits every collection in the tree to have
either "regular" or "virtual" sons. In other words, every node in the
collection tree may see either regular or virtual branches growing out
of it. This permits to create a tree with very complex, multi-level,
nested structures of regular and virtual branches, if needed, with the
aim to ease navigation to end-users from one branch to another. The
difference between a regular and a virtual branch will be explained in
detail further below in the <a href="#2.2">section 2.2</a>.
<a name="2.1"></a><h3>2.1 Add new collection</h3>
<p>To add a new collection, enter its default name in the default
language of the installation and click on the ADD button
to add it. There are two important actions that you have to perform
after adding a collection:
<ul>
<li>You have to define the set of records that belong to this
collection. This is done by defining a search engine query
that would return all records belonging to this collection.
See hints on <a href="#3.1">modify collection query</a> below.
<li>In order for the collection to appear in the collection
navigation tree, you will have to attach it to some existing
collection in the tree. See hints on <a href="#2.2">add
collection to tree</a> below.
</ul>
<p>After you edit these two things, the collection is fully usable for
the search interface. It will appear in the search interface after
the next run of the <a href="#4">WebColl Daemon</a>.
<p>However, you will probably want to customize further things, like
define collection name translation in various languages, define
collection web page portalboxes, define search options, etc, as
explained in this guide under the section <a href="#3">Edit
Collection Parameters</a>.
<a name="2.2"></a><h3>2.2 Add collection to tree</h3>
<p>To attach a collection to the tree, choose first which collection
do you want to attach, then choose the father collection to attach to,
and then choose the fathership relation type between them (regular,
virtual).
<p>The difference between the regular and the virtual relationship
goes as follows:
<ul>
<li><strong>regular relationship</strong>: If collection A is
composed of B and C, in a way that every document belonging to
A is either B or C, then this schema corresponds to the
regular type of relationship. For example, let A equals to
"Multimedia" and B and C to "Photos" and "Videos",
respectively. The latter collections would then be declared
as regular sons of "Multimedia" and they would appear in the
left-hand-side regular navigation tree entitled "Narrow by
Collection" in the collection tree.
<li><strong>virtual relationship</strong>: In addition to the
regular decomposition of "Multimedia" into "Photos" and
"Videos", it may be advantageous to present a different,
orthogonal point of view on "Multimedia", based not on the
document type as seen above, but rather on the document
creator information. Let us consider that some (large) part
of the multimedia material was created by the "University
Multimedia Service" and some (small) part by an external TV
company such as BBC. It may be advantageous to advertize this
point of view to the end users too, so they they would be able
to easily navigate down to the kind of multimedia material
they are looking for. We can create two more collections
named "University Multimedia Service" and "BBC Pictures and
Videos" and declare them as virtual sons of the "Multimedia"
collection. These collections would then appear in the
right-hand-side virtual navigation tree entitled "Focus on" in
the collection tree.
</ul>
The example presented above would then give us the following picture:
<blockquote>
<pre>
M u l t i m e d i a
Narrow by Collection: Focus on:
-------------------- ---------
[ ] Photos University Multimedia Service
[ ] Videos BBC Pictures and Videos
</pre>
</blockquote>
<p>It is important to note that if a collection A is composed of B and
C as its regular sons, and offers X and Y as its virtual sons, then
every document belonging to A must also belong to either B or C. This
requirement does not apply for X and Y, because X and Y offer only a
"focus-on" orthogonal view on a (possibly small) part of the document
corpus of A. If end-users search the collection A, then they are
actually searching inside B and C, not X and Y. If they want to
search inside X or Y, they have to click upon X or Y first. One can
consider virtual branches as a sort of non-essential searching aid to
the end-user that is activated only when users are interested in a
particular "focus-on" relationship, provided that this "virtual" point
of view on A interests her.
<a name="2.3"></a><h3>2.3 Modify existing tree</h3>
<p>To modify existing tree by WebSearch Admin Interface, click on
icons displayed next to collections. The meaning of icons is as
follows:
<table border="1">
<tr>
<td>
<img border="0" src="<CFG_SITE_URL>/img/iconcross.gif">
</td>
<td>
Remove chosen collection with its subcollections from the collection tree,
but do not delete the collection itself.
(For full deletion of a collection, see <a href="#3.4">section 3.4</a>.)
</td>
</tr>
<tr>
<td>
<img border="0" src="<CFG_SITE_URL>/img/arrow_up.gif"> &nbsp;
<img border="0" src="<CFG_SITE_URL>/img/arrow_down.gif">
</td>
<td>
Move chosen collection up or down among its brothers and sisters, i.e.
change the order of collections inside the same level of the tree.
</td>
</tr>
<tr>
<td>
<img border="0" src="<CFG_SITE_URL>/img/move_from.gif">
<img border="0" src="<CFG_SITE_URL>/img/move_to.gif">
</td>
<td>
Move chosen collection among branches of the tree.
Press the first icon (<img border="0" src="<CFG_SITE_URL>/img/move_from.gif">)
to choose a collection to move, and the second icon
(<img border="0" src="<CFG_SITE_URL>/img/move_to.gif">)
to select a new father collection that the chosen collection should be attached to.
</td>
</tr>
</table>
<a name="3"></a><h2>3. Edit Collection Parameters</h2>
<p>To finalize setting up of a collection, you could and should edit
many parameters, such as define list of records belonging to a
collection, define search fields, define search interface page
portalboxes, etc. In this section we will subsequently describe all
the various possibilities as they are presented in the <a
href="<CFG_SITE_URL>/admin/websearch/websearchadmin.py/editcollection?colID=1">Edit
Collection</a> pages of the WebSearch Admin Interface.
<a name="3.1"></a><h3>3.1 Modify collection query</h3>
<p>The <em>collection query</em> defines which documents belong to the
given collection. It is equal to the search term that retrieves all
documents belonging to the given collection, exactly as you would have
typed it into the search interface. For example, to define a
collection of all papers written by Ellis, you could set up your
collection query to be <code>author:Ellis</code>.
<p>Usually, the collection query is chosen on the basis of the
collection identifier that we store in MARC tag 980. This tag is
indexed in a logical field called <code>collection</code> so that a
collection of Theses could be defined via
<code>collection:THESIS</code>, supposing that every thesis metadata
record has got the text <code>THESIS</code> in MARC tag 980.
(Nitpick: we use the term `collection' in two contexts here: once as a
collection of metadata documents, but also and as a logical field
name. We should have probably called the latter
<code>collectionidentifier</code> or somesuch instead, but we hope the
difference is clear from... the context.)
<p>If a collection does not have any collection query defined, then
its content is defined by means of the content of its descendants
(subcollections). This is the case for composed collections. For
example, the composed collection <em>Articles & Preprints</em> (no
query defined) will be defined as a father of <em>Articles</em>
(query: <code>collection:ARTICLE</code>) and <em>Preprints</em>
(query: <code>collection:PREPRINT</code>). In this case the
collection query for <em>Articles & Preprints</em> can stay empty.
<p>Note that you should avoid defining non-empty collection query in
cases the collection has descendants, since it will prevail and the
descendants may not be taken into account. In the same way, if a
collection doesn't have any query nor any descendants defined, then
its contents will be empty.
<p>To define an external hosted collection set up the query to begin with
<code>hostedcollection:</code> (for more detailed information see <a href="#4">section 4</a>)
<p>To remove the collection query, set the parameter empty.
<a name="3.2"></a><h3>3.2 Modify access restrictions</h3>
<p>Until <em>Invenio-0.92.1</em> there was the possibility to directly
restrict a collection by specifying an Apache group. Users who had an
Apache user and password belonging to the given group would have been able
to access the restricted collection.</p>
<p>Collection restriction managament is now integrated with the wider
<a href="webaccess-admin-guide">Role Based Access Control</a>
facility of Invenio.</p>
<p>In order to restrict access to a collection you just have to create
at least an authorization for the action <code>viewrestrcoll</code>
specifying the name of the collection as the parameter</p>
<p>If you have just upgraded your installation from <em>CDS
Invenio-0.92.1</em> you probably have run
<code>collection_restrictions_migration_kit.py</code> tool in order
to migrate to the new framework. For every Apache Group with access to a
restricted collection a <em>role</em> will be created, with proper
authorization to access the restricted collections. Each role will have
a <em>FireRole</em> definition that specifies to allow for the given
Apache group. Trough the WebAccess admin interface you will then be able
to change these definition in order to softly migrate your restriction
to whatever is your need.</p>
<a name="3.3"></a><h3>3.3 Modify translations</h3>
<p>You may define translations of collection names into the languages
of your Invenio installation. Moreover, a collection name may be
different in different contexts (e.g. long name, short name, etc), so
that prior to modifying translations you will be asked to select which
name type you want to change.
<p>The translations aren't mandatory to define. If a translation does
not exist in a language chosen by the end user, the end user will be
shown the collection name in the default language of this
installation.
<p>Note also that the list of available languages depends on the
compile-time configuration (see the general <code>invenio.conf</code>
file).
<a name="3.4"></a><h3>3.4 Delete collection</h3>
<p>The collection to be deleted must be first removed from the
collection tree. Any metametadata associated with the collection
(such as association to portalboxes, association to records belonging
to this collection, etc) will be lost, but the metadata itself will be
preserved (such as portalboxes themselves, records themselves, etc).
In total, association to records, output formats, translations, search
options, sort options, search fields, ranking method, and access
restriction will be lost. Use with care!
<p>It may be a good idea only to remove the collection from the end
users interface, but to keep it "hidden" in a corner they don't see
and that they can't search when they search from Home. To achieve
this, do not delete the collection but simply remove it from the
collection tree so that it won't be attached to any father collection.
In this case the search interface page for this collection will stay
updated, but won't be neither shown in the tree nor searchable from
Home page. It will only be accessible via bookmarked URL, for
example.
<a name="3.5"></a><h3>3.5 Modify portalboxes</h3>
<p>The search interface HTML page for a given collection may be
customized by what we call <em>portalboxes</em>. Portalboxes are used
to show various kinds of information to the end user, such as a text
box with some inline help information about the given collection, an
illustrative picture, etc.
<p>To create a new portalbox, a title and a body must be given, where
the body can contain HTML if necessary.
<p>To add a portalbox to the collection, you must choose an existing
portalbox, the language for which the portalbox should be shown, the
position of the portalbox on the screen, and the ordering score of
portalboxes.
<ul>
<li>The <em>language</em> could be chosen depending on the language
used in the portalbox body. Since a portalbox is not necessarily
bound to one particular language, one portalbox may be reused for
several languages, which is particularly suitable for portalboxes
containing language-independent content such as images.
<li>The <em>position</em> of the portalbox on the screen is chosen
from several predefined positions, such as right-top, before-title,
after-title, before-narrow-by-collection-box, etc. You may present
several portalboxes on the same position in the same language, in
which case they will be shown by the order of decreasing score.
<li>The <em>score</em> defines the order of portalboxes that are to be
presented in the same position and in the same language context.
</ul>
<a name="3.6"></a><h3>3.6 Modify search fields</h3>
<p>The <em>search field</em> is a logical field (such as author,
title, etc) that will be proposed to the end users in Simple and
Advanced Search interface pages. If you do not set any search fields
for a collection, then a default list (author, title, year, etc) will
be shown.
<p>Note that if you want to add a new logical field or modify existing
physical MARC tags for a logical field, you have to use the <a
href="<CFG_SITE_URL>/admin/bibindex/bibindexadmin.py">BibIndex Admin</a> interface.
<a name="3.7"></a><h3>3.7 Modify search options</h3>
<p>The <em>search option</em> is like <a href="#3.6">search field</a>
in a way that it permits the end user to narrow down his search to
some logical field such as "subject", but unlike with the search field
the user is not required to type his query in a free text form;
rather, the search interface proposes to the end user several
interesting predefined values prepared by the administrators that the
end user may choose from. For example, an "author search" concept is
a good example of search field usage, since there is plenty of author
names to be matched, so that the end users would usually type the name
they wish to find in free text form; while a "subject search" concept
is a good example for search option usage, since usually there is a
limited number of subjects in the system given by local subject
classification scheme, that the end users do not necessarily know
about and that they are free to choose from a list. As a rule of
thumb, the search field concept denotes the case of unlimited number
possibilites of distinct values to be matched in a given field
(e.g. author, title, keyword); while the search option concept denotes
the case of only a handful or so distinct values to be matched in a
given field (e.g. subject, division, year).
<p>Search options are shown in the "Advanced Search" interfaces only,
while search fields are shown both in "Simple Search" and "Advanced
Search" interface. (Although if you want to add a search option to
the "Simple Search" interface, you can achieve it by creating
appropriate HTML code in a <a href="#3.5">portalbox</a>.) The search
options order, as well as the order of search option values, may be
defined by means of 'move' arrows in the WebSearch Admin interface.
<p>To add a new search option, a field name must first be chosen (for
example "subject") and then a list of possible field values must be
entered (for example "Mathematics", "Physics", "Chemistry", "Biology",
etc). Note that if you want to add a new logical field or modify
existing physical MARC tags for a logical field, you have to use the
<a href="<CFG_SITE_URL>/admin/bibindex/bibindexadmin.py">BibIndex Admin</a> interface.
<a name="3.8"></a><h3>3.8 Modify sort options</h3>
<p>You may define a list of logical fields that the end users will be
able to choose for the sorting purposes. For example, "first author"
or "year". If you don't select anything, a default list (author,
title, year, etc) will be shown.
<p>Note that if you want to add a new logical field or modify existing
physical MARC tags for a logical field, you have to use the <a
href="<CFG_SITE_URL>/admin/bibindex/bibindexadmin.py">BibIndex Admin</a> interface.
<a name="3.9"></a><h3>3.9 Modify rank options</h3>
<p>To enable a certain rank method for a collection, select the method
from the "enable rank method" box and add it. The documents in this
collection will then be included in the ranking sets the next time the
BibRank daemon will run. To disable a method the process is the same,
but select the method from the 'disable rank method' box.
<p>Note that if you want to add new ranking method or modify existing
ranking method, you have to use the <a
href="<CFG_SITE_URL>/admin/bibrank/bibrankadmin.py">BibRank Admin</a> interface.
<a name="3.10"></a><h3>3.10 Modify output formats</h3>
<p>Each collection may have several output formats defined. The end
users will be able to choose a format they want to see their search
results list in. Most formats like HTML brief or XML Dublin Core are
interesting for each collection, but some formats like HTML portfolio
are only interesting for Photographs collection, not for Articles
collection. The interface will permit you to choose the formats
appropriate for a given collection. The order of formats can be
changed using the 'move' arrows.
<p>Note that if you want to add new output format ('behaviour') or
modify existing output format, you have to use the <a
href="<CFG_SITE_URL>/admin/bibformat/bibformatadmin.py">BibFormat Admin</a> interface.
<a name="3.11"></a><h3>3.11 Configuration of related external collections</h3>
<p>You can customize each collection to provide your users an
additional source of information external to your repository: in a
<i>book</i> collection you might want for example to provide a link to
<i>Amazon</i> items corresponding to the user's query. Futhermore, for
some external services only, you can set the collection to display the
results directly in Invenio search results page.
<p>The following settings are available:
<dl>
<dt>Disabled</dt>
<dd>The external collection is not shown to the user.<dd>
<dt>See also</dt>
<dd>A link to the external collection listing the items corresponding to user's query is displayed (only once a query has been performed).</dd>
<dt>External search</dt>
<dd>User can ask to perform a search in parallel on your repository and on the external collection. Results are shown in the Invenio search results page. Not available for all external collections.<dd>
<dt>External search checked</dt>
<dd>Same as above, but the external collection is searched by default. Not available for all external collections.</dd>
<dl>
<p>You can also apply the settings to sub-collections, by checking the
"<i>Apply also to daughter collections</i>" checkboxes when you apply
your modifications.
<p>Note that in case you have defined an external hosted collection and you are
in fact configuring its related external collections there is no restriction on
setting even itself as "<em>See also</em>", "<em>External search</em>" or
"<em>External search checked</em>"; directly or recursively via the "<i>Apply
also to daughter collections</i>" option. It is up entirely to the admin to
keep a clean and consistent installation (for more detailed information see <a
href="#4">section 4</a>).
<a name="3.12"></a><h3>3.12 Detailed record page options</h3>
<p>These settings let you define how the detailed view (such as <a
-href="<CFG_SITE_URL>/record/1"><CFG_SITE_URL>/record/1</a>) of records in this
+href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/1"><CFG_SITE_URL>/<CFG_SITE_RECORD>/1</a>) of records in this
collection will look like. <br/>
More details are available in the <a
href="<CFG_SITE_URL>/help/admin/webstyle-admin-guide#det_page">WebStyle admin
guide</a>.
<p> Please note that since a record might belong to several
collections, conflicts between collection settings might occur. This
is especially true in the case of <i>virtual</i> collections. It is
therefore the settings of the <i>primary collection</i> of the record
which are applied.
<a name="4"></a><h2>4. External and Hosted Collections</h2>
<p>External and hosted collections are a way to provide your users with
additional sources of information. The simplest option is the
"<em>See also</em>" one: it provides a link to the external collection listing
the items corresponding to the user's query. Another option is to set up the
external collection an "<em>External search [checked]</em>". This option implies
a parser implemented for that external collection and allows the user to perform
a parallel search on your repository and on that of the external collection. Read
more on how to set up the above options in section <a href="#3.11">section 3.11</a>.
Also please note that some external resourses might be under copyright restrictions.
<p>Another, more advanced option, are the external hosted collections. The purpose
of these collections is to behave just as if they were local ones. That means the
admin should set them up as local collections and attach them to the tree. These
collections however are not meant to store their records locally but rather to produce
them on the fly when asked to. Once attached to the tree an external hosted collection
appears in the search home page along with its number of records and a small graphic
(arrow in this case) to indicate their being external.
<p>The admin should define a new external collection (any of the above options)
starting with the <code>websearch_external_collections_config.py</code> file, which
consists basically of a python dictionary. Let us go through the process of defining
a new external collection, starting from the dictionary:
<ul>
<li>add a new <code>key:value</code> pair to the dictionary. The key is the
name of the external collection (eg. Amazon Books). The value is another
python dictionary with the parameters of the external collection. Let's go
through these parameters in <code>key:value</code> pairs:<br /><br />
<ul>
<li><code>'engine':the_name_of_engine</code><br />
The name of the search engine (no spaces or special characters allowed
and its implemented python class (eg. for the 'AmazonBooks' engine the
corresponding class should be named AmazonBooksSearchEngine). If not
defined the default ExternalSearchEngine class will be used.
<li><code>'base_url':the_base_url_of_the_external_collection</code><br />
The base url of the external collection, used to create actual hyper
references to the external collection (eg. 'http://books.amazon.com/' ,
'http://www.amazon.com/books/').
<li><code>'search_url':the_search_url_of_the_external_collection</code><br />
The search url of the external collection, to which the search terms
will be later appended and therefore looked up (eg.
'http://books.amazon.com/search.php?title=' ,
'http://www.amazon.com/books/lookup.asp?book=').
<li><code>'parser_params':dictionary_of_the_parameters_of_the_parser</code><br />
The parameters to be passed to the parser. This way a parser can be
dynamically reused for different external collections upon defining
different settings. Let's go through the various parameters:<br /><br />
<ul>
<li><code>'host':the_host_of_the_external_collection</code><br />
The host of the external collection is used to correct the urls
when printing out its results (eg. 'books.amazon.com',
'www.amazon.com').
<li><code>'path':the_path_on_the_host_of_the_external_collection</code><br />
The path, along with the host of the external collection, is used
to correct the urls when printing out its results (eg. '',
'books/').
<li><code>'parser':the_actual_parser_class</code><br />
The actual parser class to be used by the external collection engine.
It should be imported at the beggining of this configuration file
(eg. AmazonBooksExternalCollectionResultsParser,
AmazonExternalCollectionResultsParser).
<li><code>'fetch_format':the_format_to_be_used_to_fetch_data</code><br />
Usually an abbreviated string that defines the format in which
the data should be fetched. The parser must be able to parse this
format (eg. 'hb', 'xm').
<li><code>'num_results_regex_str':the_regular_expression_for_the_number_of_results</code><br />
The regular expression used to calculate the returned number of
results when the external collection is queried (eg.
r'<strong>([0-9,]+?)</strong> records found'). Should preferably
be a python raw string.
<li><code>'num_results_regex_str':the_regular_expression_for_the_total_number_of_records</code><br />
The regular expression used to calculate the total number of records
of an external collection (eg.
r'Searching <strong>([0-9,]+?)</strong> records in total'). This
is to be used by external hosted collections that present their
total number of records in the search home page. Should preferably
be a python raw string.
<li><code>'nbrecs_url':the_url_that_provides_the_total_number_of_records</code><br />
The url that provides information on the total number of records
of an external collection (eg.
'http://books.amazon.com/search.php?show_all=yes'). The regular
expression defined above will be used on the contents of this url.
Again, this is to be used by external hosted collections that
present their total number of records in the search home page.
</ul>
</ul>
</ul>
<p>Once the dictionary <code>key:value</code> pair has been added for the new
external collection the admin should implement (or simply use if already implemented)
the search engine python class defined for this external collection. For the
"<em>See also</em>" option the above steps are sufficient. If the admin wants
to enable the "<em>External search [checked]</em>" option as well a parser must
be (or have been) implemented. Finally to set up an external hosted collection
the admin also has to create a new local collection named exactly as the key of
the external hosted collection's <code>key:value</code> pair in the python
dictionary. The new local collection's query has to begin with
<code>hostedcollection:</code> (under the current configuration it is sufficient
for the query of any external hosted collection to just be defined as
<code>hostedcollection:</code>) and the collection itself has to be attached to
the tree to be visible in the search home page. Note that due to the nature of
external hosted collections their corresponding local collections cannot have any
other collections as sons; in other words they shouldn't have any other branches
growing from them.
<a name="5"></a><h2>5. Webcoll Status</h2>
<p>WebColl is the daemon that normally periodically runs via <a
href="<CFG_SITE_URL>/help/admin/bibsched-admin-guide">BibSched</a> and that updates the
collection cache with the collection parameters configured in the
previous section. Alternatively to running webcoll via BibSched, you
can also run it any time you want from the command line, either for
all collections or for selected collection only. See the --help
option.
<p>The WebSearch Admin interface has got a WebColl Status menu that
shows when the collection cache was last updated and when the next
update is scheduled. It warns in case something suspicious was
discovered.
<a name="6"></a><h2>6. Collections Status</h2>
<p>The Collection Status menu of the WebSearch Admin interface shows
the list of all collections and checks if there is anything wrong
regarding configuration of collections, together with the languages
the collection name has been translated into, etc. Here is the
detailed explanation of the functionality:
<blockquote>
<dl>
<dt><strong>ID</strong>
<dd>ID of the collection.
<dt><strong>Name</strong>
<dd>Name of the collection.
<dt><strong>Query</strong>
<dd>The collection definition query. Note that it should be empty if
a collection got subcollections. If not, then a query is needed.
<dt><strong>Subcollections</strong>
<dd>The subcollections that the collection is composed of. Note that
a collection which got defined by a query should not have any
subcollections.
<dt><strong>Restricted</strong>
<dd>A restricted collection can only be accessed by users belonging to
the Apache groups mentioned in this column.
<dt><strong>Hosted</strong>
<dd>A hosted collection is practicly an external one behaving just as if it were local.
<dt><strong>I18N</strong>
<dd>Show which languages the collection name has been translated into.
<dt><strong>Status</strong>
<dd>If no errors was found, <em>OK</em> is displayed for each
collection. If an error was found, then an error number and short
message are shown. The meaning of the error messages is the
following: <em>1:Conflict</em> means that the collection was defined
via a query but also via subcollections too; <em>2:Empty</em> means
that the collection wasn't defined neither via query nor via
subcollections.
</dl>
</blockquote>
<a name="7"></a><h2>7. Check External Collections</h2>
<p>The Check External Collections menu of the WebSearch Admin interface is a
simple tool to check and control the consistency of the external collections
the user has defined. External collections exist both in their own database
table as well in a user defined configuration file. This tool will check the
consistency between the two and report back to the user giving them the
option to fix any potential inconsistencies.
<a name="8"></a><h2>8. Edit Search Engine Parameters</h2>
<a name="9"></a><h2>9. Search Engine Cache</h2>
<a name="10"></a><h2>10. Additional Information</h2>
<a href="<CFG_SITE_URL>/help/hacking/search-engine-internals">WebSearch Internals</a>
diff --git a/modules/websearch/lib/search_engine.py b/modules/websearch/lib/search_engine.py
index e28ed2973..d4573f742 100644
--- a/modules/websearch/lib/search_engine.py
+++ b/modules/websearch/lib/search_engine.py
@@ -1,5490 +1,5488 @@
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0301
"""Invenio Search Engine in mod_python."""
__lastupdated__ = """$Date$"""
__revision__ = "$Id$"
## import general modules:
import cgi
import cStringIO
import copy
import string
import os
import re
import time
import urllib
import urlparse
import zlib
import sys
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
## import Invenio stuff:
from invenio.config import \
CFG_CERN_SITE, \
CFG_INSPIRE_SITE, \
CFG_OAI_ID_FIELD, \
CFG_WEBCOMMENT_ALLOW_REVIEWS, \
CFG_WEBSEARCH_CALL_BIBFORMAT, \
CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX, \
CFG_WEBSEARCH_FIELDS_CONVERT, \
CFG_WEBSEARCH_NB_RECORDS_TO_SORT, \
CFG_WEBSEARCH_SEARCH_CACHE_SIZE, \
CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS, \
CFG_WEBSEARCH_USE_ALEPH_SYSNOS, \
CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS, \
CFG_WEBSEARCH_FULLTEXT_SNIPPETS, \
CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE, \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG, \
CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS, \
CFG_WEBSEARCH_WILDCARD_LIMIT, \
CFG_WEBSEARCH_SYNONYM_KBRS, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_LOGDIR, \
CFG_BIBFORMAT_HIDDEN_TAGS, \
CFG_SITE_URL, \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_BIBRANK_SHOW_CITATION_LINKS, \
- CFG_SOLR_URL
+ CFG_SOLR_URL, \
+ CFG_SITE_RECORD
+
from invenio.search_engine_config import InvenioWebSearchUnknownCollectionError, InvenioWebSearchWildcardLimitError
from invenio.bibrecord import create_record, record_get_field_instances
from invenio.bibrank_record_sorter import get_bibrank_methods, rank_records, is_method_valid
from invenio.bibrank_downloads_similarity import register_page_view_event, calculate_reading_similarity_list
from invenio.bibindex_engine_stemmer import stem
from invenio.bibindex_engine_tokenizer import wash_author_name, author_name_requires_phrase_search
from invenio.bibformat import format_record, format_records, get_output_format_content_type, create_excel
from invenio.bibformat_config import CFG_BIBFORMAT_USE_OLD_BIBFORMAT
from invenio.bibrank_downloads_grapher import create_download_history_graph_and_box
from invenio.bibknowledge import get_kbr_values
from invenio.data_cacher import DataCacher
from invenio.websearch_external_collections import print_external_results_overview, perform_external_collection_search
from invenio.access_control_admin import acc_get_action_id
from invenio.access_control_config import VIEWRESTRCOLL, \
CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.intbitset import intbitset as HitSet
from invenio.dbquery import DatabaseError, deserialize_via_marshal, InvenioDbQueryWildcardLimitError
from invenio.access_control_engine import acc_authorize_action
from invenio.errorlib import register_exception
from invenio.textutils import encode_for_xml, wash_for_utf8
from invenio.htmlutils import get_mathjax_header
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
webcomment_templates = invenio.template.load('webcomment')
from invenio.bibrank_citation_searcher import get_cited_by_count, calculate_cited_by_list, \
calculate_co_cited_with_list, get_records_with_num_cites, get_self_cited_by, \
get_refersto_hitset, get_citedby_hitset
from invenio.bibrank_citation_grapher import create_citation_history_graph_and_box
from invenio.dbquery import run_sql, run_sql_with_limit, \
get_table_update_time, Error
from invenio.webuser import getUid, collect_user_info
from invenio.webpage import pageheaderonly, pagefooteronly, create_error_box
from invenio.messages import gettext_set_language
from invenio.search_engine_query_parser import SearchQueryParenthesisedParser, \
SpiresToInvenioSyntaxConverter
from invenio import webinterface_handler_config as apache
from invenio.solrutils import solr_get_bitset
try:
import invenio.template
websearch_templates = invenio.template.load('websearch')
except:
pass
from invenio.websearch_external_collections import calculate_hosted_collections_results, do_calculate_hosted_collections_results
from invenio.websearch_external_collections_config import CFG_HOSTED_COLLECTION_TIMEOUT_ANTE_SEARCH
from invenio.websearch_external_collections_config import CFG_HOSTED_COLLECTION_TIMEOUT_POST_SEARCH
from invenio.websearch_external_collections_config import CFG_EXTERNAL_COLLECTION_MAXRESULTS
VIEWRESTRCOLL_ID = acc_get_action_id(VIEWRESTRCOLL)
## global vars:
cfg_nb_browse_seen_records = 100 # limit of the number of records to check when browsing certain collection
cfg_nicely_ordered_collection_list = 0 # do we propose collection list nicely ordered or alphabetical?
## precompile some often-used regexp for speed reasons:
re_word = re.compile('[\s]')
re_quotes = re.compile('[\'\"]')
re_doublequote = re.compile('\"')
re_equal = re.compile('\=')
re_logical_and = re.compile('\sand\s', re.I)
re_logical_or = re.compile('\sor\s', re.I)
re_logical_not = re.compile('\snot\s', re.I)
re_operators = re.compile(r'\s([\+\-\|])\s')
re_pattern_wildcards_after_spaces = re.compile(r'(\s)[\*\%]+')
re_pattern_single_quotes = re.compile("'(.*?)'")
re_pattern_double_quotes = re.compile("\"(.*?)\"")
re_pattern_regexp_quotes = re.compile("\/(.*?)\/")
re_pattern_spaces_after_colon = re.compile(r'(:\s+)')
re_pattern_short_words = re.compile(r'([\s\"]\w{1,3})[\*\%]+')
re_pattern_space = re.compile("__SPACE__")
re_pattern_today = re.compile("\$TODAY\$")
re_pattern_parens = re.compile(r'\([^\)]+\s+[^\)]+\)')
re_unicode_lowercase_a = re.compile(unicode(r"(?u)[áàäâãå]", "utf-8"))
re_unicode_lowercase_ae = re.compile(unicode(r"(?u)[æ]", "utf-8"))
re_unicode_lowercase_e = re.compile(unicode(r"(?u)[éèëê]", "utf-8"))
re_unicode_lowercase_i = re.compile(unicode(r"(?u)[íìïî]", "utf-8"))
re_unicode_lowercase_o = re.compile(unicode(r"(?u)[óòöôõø]", "utf-8"))
re_unicode_lowercase_u = re.compile(unicode(r"(?u)[úùüû]", "utf-8"))
re_unicode_lowercase_y = re.compile(unicode(r"(?u)[ýÿ]", "utf-8"))
re_unicode_lowercase_c = re.compile(unicode(r"(?u)[çć]", "utf-8"))
re_unicode_lowercase_n = re.compile(unicode(r"(?u)[ñ]", "utf-8"))
re_unicode_uppercase_a = re.compile(unicode(r"(?u)[ÁÀÄÂÃÅ]", "utf-8"))
re_unicode_uppercase_ae = re.compile(unicode(r"(?u)[Æ]", "utf-8"))
re_unicode_uppercase_e = re.compile(unicode(r"(?u)[ÉÈËÊ]", "utf-8"))
re_unicode_uppercase_i = re.compile(unicode(r"(?u)[ÍÌÏÎ]", "utf-8"))
re_unicode_uppercase_o = re.compile(unicode(r"(?u)[ÓÒÖÔÕØ]", "utf-8"))
re_unicode_uppercase_u = re.compile(unicode(r"(?u)[ÚÙÜÛ]", "utf-8"))
re_unicode_uppercase_y = re.compile(unicode(r"(?u)[Ý]", "utf-8"))
re_unicode_uppercase_c = re.compile(unicode(r"(?u)[ÇĆ]", "utf-8"))
re_unicode_uppercase_n = re.compile(unicode(r"(?u)[Ñ]", "utf-8"))
re_latex_lowercase_a = re.compile("\\\\[\"H'`~^vu=k]\{?a\}?")
re_latex_lowercase_ae = re.compile("\\\\ae\\{\\}?")
re_latex_lowercase_e = re.compile("\\\\[\"H'`~^vu=k]\\{?e\\}?")
re_latex_lowercase_i = re.compile("\\\\[\"H'`~^vu=k]\\{?i\\}?")
re_latex_lowercase_o = re.compile("\\\\[\"H'`~^vu=k]\\{?o\\}?")
re_latex_lowercase_u = re.compile("\\\\[\"H'`~^vu=k]\\{?u\\}?")
re_latex_lowercase_y = re.compile("\\\\[\"']\\{?y\\}?")
re_latex_lowercase_c = re.compile("\\\\['uc]\\{?c\\}?")
re_latex_lowercase_n = re.compile("\\\\[c'~^vu]\\{?n\\}?")
re_latex_uppercase_a = re.compile("\\\\[\"H'`~^vu=k]\\{?A\\}?")
re_latex_uppercase_ae = re.compile("\\\\AE\\{?\\}?")
re_latex_uppercase_e = re.compile("\\\\[\"H'`~^vu=k]\\{?E\\}?")
re_latex_uppercase_i = re.compile("\\\\[\"H'`~^vu=k]\\{?I\\}?")
re_latex_uppercase_o = re.compile("\\\\[\"H'`~^vu=k]\\{?O\\}?")
re_latex_uppercase_u = re.compile("\\\\[\"H'`~^vu=k]\\{?U\\}?")
re_latex_uppercase_y = re.compile("\\\\[\"']\\{?Y\\}?")
re_latex_uppercase_c = re.compile("\\\\['uc]\\{?C\\}?")
re_latex_uppercase_n = re.compile("\\\\[c'~^vu]\\{?N\\}?")
class RestrictedCollectionDataCacher(DataCacher):
def __init__(self):
def cache_filler():
ret = []
try:
res = run_sql("""SELECT DISTINCT ar.value
FROM accROLE_accACTION_accARGUMENT raa JOIN accARGUMENT ar ON raa.id_accARGUMENT = ar.id
WHERE ar.keyword = 'collection' AND raa.id_accACTION = %s""", (VIEWRESTRCOLL_ID,))
except Exception:
# database problems, return empty cache
return []
for coll in res:
ret.append(coll[0])
return ret
def timestamp_verifier():
return max(get_table_update_time('accROLE_accACTION_accARGUMENT'), get_table_update_time('accARGUMENT'))
DataCacher.__init__(self, cache_filler, timestamp_verifier)
def collection_restricted_p(collection, recreate_cache_if_needed=True):
if recreate_cache_if_needed:
restricted_collection_cache.recreate_cache_if_needed()
return collection in restricted_collection_cache.cache
try:
restricted_collection_cache.is_ok_p
except Exception:
restricted_collection_cache = RestrictedCollectionDataCacher()
def ziplist(*lists):
"""Just like zip(), but returns lists of lists instead of lists of tuples
Example:
zip([f1, f2, f3], [p1, p2, p3], [op1, op2, '']) =>
[(f1, p1, op1), (f2, p2, op2), (f3, p3, '')]
ziplist([f1, f2, f3], [p1, p2, p3], [op1, op2, '']) =>
[[f1, p1, op1], [f2, p2, op2], [f3, p3, '']]
FIXME: This is handy to have, and should live somewhere else, like
miscutil.really_useful_functions or something.
XXX: Starting in python 2.6, the same can be achieved (faster) by
using itertools.izip_longest(); when the minimum recommended Python
is bumped, we should use that instead.
"""
def l(*items):
return list(items)
return map(l, *lists)
def get_permitted_restricted_collections(user_info, recreate_cache_if_needed=True):
"""Return a list of collection that are restricted but for which the user
is authorized."""
if recreate_cache_if_needed:
restricted_collection_cache.recreate_cache_if_needed()
ret = []
for collection in restricted_collection_cache.cache:
if acc_authorize_action(user_info, 'viewrestrcoll', collection=collection)[0] == 0:
ret.append(collection)
return ret
def get_restricted_collections_for_recid(recid, recreate_cache_if_needed=True):
"""
Return the list of restricted collection names to which recid belongs.
"""
if recreate_cache_if_needed:
restricted_collection_cache.recreate_cache_if_needed()
collection_reclist_cache.recreate_cache_if_needed()
return [collection for collection in restricted_collection_cache.cache if recid in get_collection_reclist(collection, recreate_cache_if_needed=False)]
def is_user_owner_of_record(user_info, recid):
"""
Check if the user is owner of the record, i.e. he is the submitter
and/or belongs to a owner-like group authorized to 'see' the record.
@param user_info: the user_info dictionary that describe the user.
@type user_info: user_info dictionary
@param recid: the record identifier.
@type recid: positive integer
@return: True if the user is 'owner' of the record; False otherwise
@rtype: bool
"""
authorized_emails_or_group = []
for tag in CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS:
authorized_emails_or_group.extend(get_fieldvalues(recid, tag))
for email_or_group in authorized_emails_or_group:
if email_or_group in user_info['group']:
return True
email = email_or_group.strip().lower()
if user_info['email'].strip().lower() == email:
return True
return False
def check_user_can_view_record(user_info, recid):
"""
Check if the user is authorized to view the given recid. The function
grants access in two cases: either user has author rights on this
record, or he has view rights to the primary collection this record
belongs to.
@param user_info: the user_info dictionary that describe the user.
@type user_info: user_info dictionary
@param recid: the record identifier.
@type recid: positive integer
@return: (0, ''), when authorization is granted, (>0, 'message') when
authorization is not granted
@rtype: (int, string)
"""
if record_public_p(recid):
## The record is already known to be public.
return (0, '')
## At this point, either webcoll has not yet run or there are some
## restricted collections. Let's see first if the user own the record.
if is_user_owner_of_record(user_info, recid):
## Perfect! It's authorized then!
return (0, '')
restricted_collections = get_restricted_collections_for_recid(recid, recreate_cache_if_needed=False)
if restricted_collections:
## If there are restricted collections the user must be authorized to all of them
for collection in get_restricted_collections_for_recid(recid, recreate_cache_if_needed=False):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=collection)
if auth_code:
## Ouch! the user is not authorized to this collection
return (auth_code, auth_msg)
## OK! The user is authorized.
return (0, '')
if is_record_in_any_collection(recid, recreate_cache_if_needed=False):
## the record is not in any restricted collection
return (0, '')
elif record_exists(recid) > 0:
## We are in the case where webcoll has not run.
## Let's authorize SUPERADMIN
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=None)
if auth_code == 0:
return (0, '')
else:
## Too bad. Let's print a nice message:
return (1, """The record you are trying to access has just been
submitted to the system and needs to be assigned to the
proper collections. It is currently restricted for security reasons
until the assignment will be fully completed. Please come back later to
properly access this record.""")
else:
## The record either does not exists or has been deleted.
## Let's handle these situations outside of this code.
return (0, '')
class IndexStemmingDataCacher(DataCacher):
"""
Provides cache for stemming information for word/phrase indexes.
This class is not to be used directly; use function
get_index_stemming_language() instead.
"""
def __init__(self):
def cache_filler():
try:
res = run_sql("""SELECT id, stemming_language FROM idxINDEX""")
except DatabaseError:
# database problems, return empty cache
return {}
return dict(res)
def timestamp_verifier():
return get_table_update_time('idxINDEX')
DataCacher.__init__(self, cache_filler, timestamp_verifier)
try:
index_stemming_cache.is_ok_p
except Exception:
index_stemming_cache = IndexStemmingDataCacher()
def get_index_stemming_language(index_id, recreate_cache_if_needed=True):
"""Return stemming langugage for given index."""
if recreate_cache_if_needed:
index_stemming_cache.recreate_cache_if_needed()
return index_stemming_cache.cache[index_id]
class CollectionRecListDataCacher(DataCacher):
"""
Provides cache for collection reclist hitsets. This class is not
to be used directly; use function get_collection_reclist() instead.
"""
def __init__(self):
def cache_filler():
ret = {}
try:
res = run_sql("SELECT name,reclist FROM collection")
except Exception:
# database problems, return empty cache
return {}
for name, reclist in res:
ret[name] = None # this will be filled later during runtime by calling get_collection_reclist(coll)
return ret
def timestamp_verifier():
return get_table_update_time('collection')
DataCacher.__init__(self, cache_filler, timestamp_verifier)
try:
if not collection_reclist_cache.is_ok_p:
raise Exception
except Exception:
collection_reclist_cache = CollectionRecListDataCacher()
def get_collection_reclist(coll, recreate_cache_if_needed=True):
"""Return hitset of recIDs that belong to the collection 'coll'."""
if recreate_cache_if_needed:
collection_reclist_cache.recreate_cache_if_needed()
if not collection_reclist_cache.cache[coll]:
# not yet it the cache, so calculate it and fill the cache:
set = HitSet()
query = "SELECT nbrecs,reclist FROM collection WHERE name=%s"
res = run_sql(query, (coll, ), 1)
if res:
try:
set = HitSet(res[0][1])
except:
pass
collection_reclist_cache.cache[coll] = set
# finally, return reclist:
return collection_reclist_cache.cache[coll]
class SearchResultsCache(DataCacher):
"""
Provides temporary lazy cache for Search Results.
Useful when users click on `next page'.
"""
def __init__(self):
def cache_filler():
return {}
def timestamp_verifier():
return '1970-01-01 00:00:00' # lazy cache is always okay;
# its filling is governed by
# CFG_WEBSEARCH_SEARCH_CACHE_SIZE
DataCacher.__init__(self, cache_filler, timestamp_verifier)
try:
if not search_results_cache.is_ok_p:
raise Exception
except Exception:
search_results_cache = SearchResultsCache()
class CollectionI18nNameDataCacher(DataCacher):
"""
Provides cache for I18N collection names. This class is not to be
used directly; use function get_coll_i18nname() instead.
"""
def __init__(self):
def cache_filler():
ret = {}
try:
res = run_sql("SELECT c.name,cn.ln,cn.value FROM collectionname AS cn, collection AS c WHERE cn.id_collection=c.id AND cn.type='ln'") # ln=long name
except Exception:
# database problems
return {}
for c, ln, i18nname in res:
if i18nname:
if not ret.has_key(c):
ret[c] = {}
ret[c][ln] = i18nname
return ret
def timestamp_verifier():
return get_table_update_time('collectionname')
DataCacher.__init__(self, cache_filler, timestamp_verifier)
try:
if not collection_i18nname_cache.is_ok_p:
raise Exception
except Exception:
collection_i18nname_cache = CollectionI18nNameDataCacher()
def get_coll_i18nname(c, ln=CFG_SITE_LANG, verify_cache_timestamp=True):
"""
Return nicely formatted collection name (of the name type `ln'
(=long name)) for collection C in language LN.
This function uses collection_i18nname_cache, but it verifies
whether the cache is up-to-date first by default. This
verification step is performed by checking the DB table update
time. So, if you call this function 1000 times, it can get very
slow because it will do 1000 table update time verifications, even
though collection names change not that often.
Hence the parameter VERIFY_CACHE_TIMESTAMP which, when set to
False, will assume the cache is already up-to-date. This is
useful namely in the generation of collection lists for the search
results page.
"""
if verify_cache_timestamp:
collection_i18nname_cache.recreate_cache_if_needed()
out = c
try:
out = collection_i18nname_cache.cache[c][ln]
except KeyError:
pass # translation in LN does not exist
return out
class FieldI18nNameDataCacher(DataCacher):
"""
Provides cache for I18N field names. This class is not to be used
directly; use function get_field_i18nname() instead.
"""
def __init__(self):
def cache_filler():
ret = {}
try:
res = run_sql("SELECT f.name,fn.ln,fn.value FROM fieldname AS fn, field AS f WHERE fn.id_field=f.id AND fn.type='ln'") # ln=long name
except Exception:
# database problems, return empty cache
return {}
for f, ln, i18nname in res:
if i18nname:
if not ret.has_key(f):
ret[f] = {}
ret[f][ln] = i18nname
return ret
def timestamp_verifier():
return get_table_update_time('fieldname')
DataCacher.__init__(self, cache_filler, timestamp_verifier)
try:
if not field_i18nname_cache.is_ok_p:
raise Exception
except Exception:
field_i18nname_cache = FieldI18nNameDataCacher()
def get_field_i18nname(f, ln=CFG_SITE_LANG, verify_cache_timestamp=True):
"""
Return nicely formatted field name (of type 'ln', 'long name') for
field F in language LN.
If VERIFY_CACHE_TIMESTAMP is set to True, then verify DB timestamp
and field I18N name cache timestamp and refresh cache from the DB
if needed. Otherwise don't bother checking DB timestamp and
return the cached value. (This is useful when get_field_i18nname
is called inside a loop.)
"""
if verify_cache_timestamp:
field_i18nname_cache.recreate_cache_if_needed()
out = f
try:
out = field_i18nname_cache.cache[f][ln]
except KeyError:
pass # translation in LN does not exist
return out
def get_alphabetically_ordered_collection_list(level=0, ln=CFG_SITE_LANG):
"""Returns nicely ordered (score respected) list of collections, more exactly list of tuples
(collection name, printable collection name).
Suitable for create_search_box()."""
out = []
res = run_sql("SELECT id,name FROM collection ORDER BY name ASC")
for c_id, c_name in res:
# make a nice printable name (e.g. truncate c_printable for
# long collection names in given language):
c_printable_fullname = get_coll_i18nname(c_name, ln, False)
c_printable = wash_index_term(c_printable_fullname, 30, False)
if c_printable != c_printable_fullname:
c_printable = c_printable + "..."
if level:
c_printable = " " + level * '-' + " " + c_printable
out.append([c_name, c_printable])
return out
def get_nicely_ordered_collection_list(collid=1, level=0, ln=CFG_SITE_LANG):
"""Returns nicely ordered (score respected) list of collections, more exactly list of tuples
(collection name, printable collection name).
Suitable for create_search_box()."""
colls_nicely_ordered = []
res = run_sql("""SELECT c.name,cc.id_son FROM collection_collection AS cc, collection AS c
WHERE c.id=cc.id_son AND cc.id_dad=%s ORDER BY score DESC""", (collid, ))
for c, cid in res:
# make a nice printable name (e.g. truncate c_printable for
# long collection names in given language):
c_printable_fullname = get_coll_i18nname(c, ln, False)
c_printable = wash_index_term(c_printable_fullname, 30, False)
if c_printable != c_printable_fullname:
c_printable = c_printable + "..."
if level:
c_printable = " " + level * '-' + " " + c_printable
colls_nicely_ordered.append([c, c_printable])
colls_nicely_ordered = colls_nicely_ordered + get_nicely_ordered_collection_list(cid, level+1, ln=ln)
return colls_nicely_ordered
def get_index_id_from_field(field):
"""
Return index id with name corresponding to FIELD, or the first
index id where the logical field code named FIELD is indexed.
Return zero in case there is no index defined for this field.
Example: field='author', output=4.
"""
out = 0
if field == '':
field = 'global' # empty string field means 'global' index (field 'anyfield')
# first look in the index table:
res = run_sql("""SELECT id FROM idxINDEX WHERE name=%s""", (field,))
if res:
out = res[0][0]
return out
# not found in the index table, now look in the field table:
res = run_sql("""SELECT w.id FROM idxINDEX AS w, idxINDEX_field AS wf, field AS f
WHERE f.code=%s AND wf.id_field=f.id AND w.id=wf.id_idxINDEX
LIMIT 1""", (field,))
if res:
out = res[0][0]
return out
def get_words_from_pattern(pattern):
"Returns list of whitespace-separated words from pattern."
words = {}
for word in string.split(pattern):
if not words.has_key(word):
words[word] = 1
return words.keys()
def create_basic_search_units(req, p, f, m=None, of='hb'):
"""Splits search pattern and search field into a list of independently searchable units.
- A search unit consists of '(operator, pattern, field, type, hitset)' tuples where
'operator' is set union (|), set intersection (+) or set exclusion (-);
'pattern' is either a word (e.g. muon*) or a phrase (e.g. 'nuclear physics');
'field' is either a code like 'title' or MARC tag like '100__a';
'type' is the search type ('w' for word file search, 'a' for access file search).
- Optionally, the function accepts the match type argument 'm'.
If it is set (e.g. from advanced search interface), then it
performs this kind of matching. If it is not set, then a guess is made.
'm' can have values: 'a'='all of the words', 'o'='any of the words',
'p'='phrase/substring', 'r'='regular expression',
'e'='exact value'.
- Warnings are printed on req (when not None) in case of HTML output formats."""
opfts = [] # will hold (o,p,f,t,h) units
# FIXME: quick hack for the journal index
if f == 'journal':
opfts.append(['+', p, f, 'w'])
return opfts
## check arguments: is desired matching type set?
if m:
## A - matching type is known; good!
if m == 'e':
# A1 - exact value:
opfts.append(['+', p, f, 'a']) # '+' since we have only one unit
elif m == 'p':
# A2 - phrase/substring:
opfts.append(['+', "%" + p + "%", f, 'a']) # '+' since we have only one unit
elif m == 'r':
# A3 - regular expression:
opfts.append(['+', p, f, 'r']) # '+' since we have only one unit
elif m == 'a' or m == 'w':
# A4 - all of the words:
p = strip_accents(p) # strip accents for 'w' mode, FIXME: delete when not needed
for word in get_words_from_pattern(p):
opfts.append(['+', word, f, 'w']) # '+' in all units
elif m == 'o':
# A5 - any of the words:
p = strip_accents(p) # strip accents for 'w' mode, FIXME: delete when not needed
for word in get_words_from_pattern(p):
if len(opfts)==0:
opfts.append(['+', word, f, 'w']) # '+' in the first unit
else:
opfts.append(['|', word, f, 'w']) # '|' in further units
else:
if of.startswith("h"):
print_warning(req, "Matching type '%s' is not implemented yet." % cgi.escape(m), "Warning")
opfts.append(['+', "%" + p + "%", f, 'w'])
else:
## B - matching type is not known: let us try to determine it by some heuristics
if f and p[0] == '"' and p[-1] == '"':
## B0 - does 'p' start and end by double quote, and is 'f' defined? => doing ACC search
opfts.append(['+', p[1:-1], f, 'a'])
elif f in ('author', 'firstauthor', 'exactauthor') and author_name_requires_phrase_search(p):
## B1 - do we search in author, and does 'p' contain space/comma/dot/etc?
## => doing washed ACC search
opfts.append(['+', p, f, 'a'])
elif f and p[0] == "'" and p[-1] == "'":
## B0bis - does 'p' start and end by single quote, and is 'f' defined? => doing ACC search
opfts.append(['+', '%' + p[1:-1] + '%', f, 'a'])
elif f and p[0] == "/" and p[-1] == "/":
## B0ter - does 'p' start and end by a slash, and is 'f' defined? => doing regexp search
opfts.append(['+', p[1:-1], f, 'r'])
elif f and string.find(p, ',') >= 0:
## B1 - does 'p' contain comma, and is 'f' defined? => doing ACC search
opfts.append(['+', p, f, 'a'])
elif f and str(f[0:2]).isdigit():
## B2 - does 'f' exist and starts by two digits? => doing ACC search
opfts.append(['+', p, f, 'a'])
else:
## B3 - doing WRD search, but maybe ACC too
# search units are separated by spaces unless the space is within single or double quotes
# so, let us replace temporarily any space within quotes by '__SPACE__'
p = re_pattern_single_quotes.sub(lambda x: "'"+string.replace(x.group(1), ' ', '__SPACE__')+"'", p)
p = re_pattern_double_quotes.sub(lambda x: "\""+string.replace(x.group(1), ' ', '__SPACE__')+"\"", p)
p = re_pattern_regexp_quotes.sub(lambda x: "/"+string.replace(x.group(1), ' ', '__SPACE__')+"/", p)
# and spaces after colon as well:
p = re_pattern_spaces_after_colon.sub(lambda x: string.replace(x.group(1), ' ', '__SPACE__'), p)
# wash argument:
p = re_equal.sub(":", p)
p = re_logical_and.sub(" ", p)
p = re_logical_or.sub(" |", p)
p = re_logical_not.sub(" -", p)
p = re_operators.sub(r' \1', p)
for pi in string.split(p): # iterate through separated units (or items, as "pi" stands for "p item")
pi = re_pattern_space.sub(" ", pi) # replace back '__SPACE__' by ' '
# firstly, determine set operator
if pi[0] == '+' or pi[0] == '-' or pi[0] == '|':
oi = pi[0]
pi = pi[1:]
else:
# okay, there is no operator, so let us decide what to do by default
oi = '+' # by default we are doing set intersection...
# secondly, determine search pattern and field:
if string.find(pi, ":") > 0:
fi, pi = string.split(pi, ":", 1)
fi = wash_field(fi)
# test whether fi is a real index code or a MARC-tag defined code:
if fi in get_fieldcodes() or '00' <= fi[:2] <= '99':
pass
else:
# it is not, so join it back:
fi, pi = f, fi + ":" + pi
else:
fi, pi = f, pi
# wash 'fi' argument:
fi = wash_field(fi)
# wash 'pi' argument:
pi = pi.strip() # strip eventual spaces
if re_quotes.match(pi):
# B3a - quotes are found => do ACC search (phrase search)
if pi[0] == '"' and pi[-1] == '"':
pi = string.replace(pi, '"', '') # remove quote signs
opfts.append([oi, pi, fi, 'a'])
elif pi[0] == "'" and pi[-1] == "'":
pi = string.replace(pi, "'", "") # remove quote signs
opfts.append([oi, "%" + pi + "%", fi, 'a'])
else: # unbalanced quotes, so fall back to WRD query:
opfts.append([oi, pi, fi, 'w'])
elif pi.startswith('/') and pi.endswith('/'):
# B3b - pi has slashes around => do regexp search
opfts.append([oi, pi[1:-1], fi, 'r'])
elif fi and str(fi[0]).isdigit() and str(fi[0]).isdigit():
# B3c - fi exists and starts by two digits => do ACC search
opfts.append([oi, pi, fi, 'a'])
elif fi and not get_index_id_from_field(fi) and get_field_name(fi):
# B3d - logical field fi exists but there is no WRD index for fi => try ACC search
opfts.append([oi, pi, fi, 'a'])
else:
# B3e - general case => do WRD search
pi = strip_accents(pi) # strip accents for 'w' mode, FIXME: delete when not needed
for pii in get_words_from_pattern(pi):
opfts.append([oi, pii, fi, 'w'])
## sanity check:
for i in range(0, len(opfts)):
try:
pi = opfts[i][1]
if pi == '*':
if of.startswith("h"):
print_warning(req, "Ignoring standalone wildcard word.", "Warning")
del opfts[i]
if pi == '' or pi == ' ':
fi = opfts[i][2]
if fi:
if of.startswith("h"):
print_warning(req, "Ignoring empty <em>%s</em> search term." % fi, "Warning")
del opfts[i]
except:
pass
## replace old logical field names if applicable:
if CFG_WEBSEARCH_FIELDS_CONVERT:
opfts = [[o,p,wash_field(f),t] for o,p,f,t in opfts]
## return search units:
return opfts
def page_start(req, of, cc, aas, ln, uid, title_message=None,
description='', keywords='', recID=-1, tab='', p=''):
"Start page according to given output format."
_ = gettext_set_language(ln)
if not req or isinstance(req, cStringIO.OutputType):
return # we were called from CLI
if not title_message:
title_message = _("Search Results")
content_type = get_output_format_content_type(of)
if of.startswith('x'):
if of == 'xr':
# we are doing RSS output
req.content_type = "application/rss+xml"
req.send_http_header()
req.write("""<?xml version="1.0" encoding="UTF-8"?>\n""")
else:
# we are doing XML output:
req.content_type = "text/xml"
req.send_http_header()
req.write("""<?xml version="1.0" encoding="UTF-8"?>\n""")
elif of.startswith('t') or str(of[0:3]).isdigit():
# we are doing plain text output:
req.content_type = "text/plain"
req.send_http_header()
elif of == "id":
pass # nothing to do, we shall only return list of recIDs
elif content_type == 'text/html':
# we are doing HTML output:
req.content_type = "text/html"
req.send_http_header()
if not description:
description = "%s %s." % (cc, _("Search Results"))
if not keywords:
keywords = "%s, WebSearch, %s" % (get_coll_i18nname(CFG_SITE_NAME, ln, False), get_coll_i18nname(cc, ln, False))
## generate RSS URL:
argd = {}
if req.args:
argd = cgi.parse_qs(req.args)
rssurl = websearch_templates.build_rss_url(argd)
## add MathJax if displaying single records (FIXME: find
## eventual better place to this code)
if of.lower() in CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS:
metaheaderadd = get_mathjax_header()
else:
metaheaderadd = ''
## generate navtrail:
navtrail = create_navtrail_links(cc, aas, ln)
if navtrail != '':
navtrail += ' &gt; '
if (tab != '' or ((of != '' or of.lower() != 'hd') and of != 'hb')) and \
recID != -1:
# If we are not in information tab in HD format, customize
# the nav. trail to have a link back to main record. (Due
# to the way perform_request_search() works, hb
# (lowercase) is equal to hd)
- navtrail += ' <a class="navtrail" href="%s/record/%s">%s</a>' % \
- (CFG_SITE_URL, recID, title_message)
+ navtrail += ' <a class="navtrail" href="%s/%s/%s">%s</a>' % \
+ (CFG_SITE_URL, CFG_SITE_RECORD, recID, title_message)
if (of != '' or of.lower() != 'hd') and of != 'hb':
# Export
format_name = of
query = "SELECT name FROM format WHERE code=%s"
res = run_sql(query, (of,))
if res:
format_name = res[0][0]
navtrail += ' &gt; ' + format_name
else:
# Discussion, citations, etc. tabs
tab_label = get_detailed_page_tabs(cc, ln=ln)[tab]['label']
navtrail += ' &gt; ' + _(tab_label)
else:
navtrail += title_message
if p:
# we are serving search/browse results pages, so insert pattern:
navtrail += ": " + cgi.escape(p)
title_message = cgi.escape(p) + " - " + title_message
## finally, print page header:
req.write(pageheaderonly(req=req, title=title_message,
navtrail=navtrail,
description=description,
keywords=keywords,
metaheaderadd=metaheaderadd,
uid=uid,
language=ln,
navmenuid='search',
navtrail_append_title_p=0,
rssurl=rssurl))
req.write(websearch_templates.tmpl_search_pagestart(ln=ln))
#else:
# req.send_http_header()
def page_end(req, of="hb", ln=CFG_SITE_LANG):
"End page according to given output format: e.g. close XML tags, add HTML footer, etc."
if of == "id":
return [] # empty recID list
if not req:
return # we were called from CLI
if of.startswith('h'):
req.write(websearch_templates.tmpl_search_pageend(ln = ln)) # pagebody end
req.write(pagefooteronly(lastupdated=__lastupdated__, language=ln, req=req))
return
def create_page_title_search_pattern_info(p, p1, p2, p3):
"""Create the search pattern bit for the page <title> web page
HTML header. Basically combine p and (p1,p2,p3) together so that
the page header may be filled whether we are in the Simple Search
or Advanced Search interface contexts."""
out = ""
if p:
out = p
else:
out = p1
if p2:
out += ' ' + p2
if p3:
out += ' ' + p3
return out
def create_inputdate_box(name="d1", selected_year=0, selected_month=0, selected_day=0, ln=CFG_SITE_LANG):
"Produces 'From Date', 'Until Date' kind of selection box. Suitable for search options."
_ = gettext_set_language(ln)
box = ""
# day
box += """<select name="%sd">""" % name
box += """<option value="">%s""" % _("any day")
for day in range(1, 32):
box += """<option value="%02d"%s>%02d""" % (day, is_selected(day, selected_day), day)
box += """</select>"""
# month
box += """<select name="%sm">""" % name
box += """<option value="">%s""" % _("any month")
for mm, month in [(1, _("January")), (2, _("February")), (3, _("March")), (4, _("April")), \
(5, _("May")), (6, _("June")), (7, _("July")), (8, _("August")), \
(9, _("September")), (10, _("October")), (11, _("November")), (12, _("December"))]:
box += """<option value="%02d"%s>%s""" % (mm, is_selected(mm, selected_month), month)
box += """</select>"""
# year
box += """<select name="%sy">""" % name
box += """<option value="">%s""" % _("any year")
this_year = int(time.strftime("%Y", time.localtime()))
for year in range(this_year-20, this_year+1):
box += """<option value="%d"%s>%d""" % (year, is_selected(year, selected_year), year)
box += """</select>"""
return box
def create_search_box(cc, colls, p, f, rg, sf, so, sp, rm, of, ot, aas,
ln, p1, f1, m1, op1, p2, f2, m2, op2, p3, f3,
m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec,
action=""):
"""Create search box for 'search again in the results page' functionality."""
# load the right message language
_ = gettext_set_language(ln)
# some computations
cc_intl = get_coll_i18nname(cc, ln, False)
cc_colID = get_colID(cc)
colls_nicely_ordered = []
if cfg_nicely_ordered_collection_list:
colls_nicely_ordered = get_nicely_ordered_collection_list(ln=ln)
else:
colls_nicely_ordered = get_alphabetically_ordered_collection_list(ln=ln)
colls_nice = []
for (cx, cx_printable) in colls_nicely_ordered:
if not cx.startswith("Unnamed collection"):
colls_nice.append({ 'value' : cx,
'text' : cx_printable
})
coll_selects = []
if colls and colls[0] != CFG_SITE_NAME:
# some collections are defined, so print these first, and only then print 'add another collection' heading:
for c in colls:
if c:
temp = []
temp.append({ 'value' : CFG_SITE_NAME,
'text' : '*** %s ***' % _("any public collection")
})
# this field is used to remove the current collection from the ones to be searched.
temp.append({ 'value' : '',
'text' : '*** %s ***' % _("remove this collection")
})
for val in colls_nice:
# print collection:
if not cx.startswith("Unnamed collection"):
temp.append({ 'value' : val['value'],
'text' : val['text'],
'selected' : (c == re.sub("^[\s\-]*","", val['value']))
})
coll_selects.append(temp)
coll_selects.append([{ 'value' : '',
'text' : '*** %s ***' % _("add another collection")
}] + colls_nice)
else: # we searched in CFG_SITE_NAME, so print 'any public collection' heading
coll_selects.append([{ 'value' : CFG_SITE_NAME,
'text' : '*** %s ***' % _("any public collection")
}] + colls_nice)
## ranking methods
ranks = [{
'value' : '',
'text' : "- %s %s -" % (_("OR").lower (), _("rank by")),
}]
for (code, name) in get_bibrank_methods(cc_colID, ln):
# propose found rank methods:
ranks.append({
'value' : code,
'text' : name,
})
formats = []
query = """SELECT code,name FROM format WHERE visibility='1' ORDER BY name ASC"""
res = run_sql(query)
if res:
# propose found formats:
for code, name in res:
formats.append({ 'value' : code,
'text' : name
})
else:
formats.append({'value' : 'hb',
'text' : _("HTML brief")
})
# show collections in the search box? (not if there is only one
# collection defined, and not if we are in light search)
show_colls = True
show_title = True
if len(collection_reclist_cache.cache.keys()) == 1 or \
aas == -1:
show_colls = False
show_title = False
if cc == CFG_SITE_NAME:
show_title = False
return websearch_templates.tmpl_search_box(
ln = ln,
aas = aas,
cc_intl = cc_intl,
cc = cc,
ot = ot,
sp = sp,
action = action,
fieldslist = get_searchwithin_fields(ln=ln, colID=cc_colID),
f1 = f1,
f2 = f2,
f3 = f3,
m1 = m1,
m2 = m2,
m3 = m3,
p1 = p1,
p2 = p2,
p3 = p3,
op1 = op1,
op2 = op2,
rm = rm,
p = p,
f = f,
coll_selects = coll_selects,
d1y = d1y, d2y = d2y, d1m = d1m, d2m = d2m, d1d = d1d, d2d = d2d,
dt = dt,
sort_fields = get_sortby_fields(ln=ln, colID=cc_colID),
sf = sf,
so = so,
ranks = ranks,
sc = sc,
rg = rg,
formats = formats,
of = of,
pl = pl,
jrec = jrec,
ec = ec,
show_colls = show_colls,
show_title = show_title,
)
def create_navtrail_links(cc=CFG_SITE_NAME, aas=0, ln=CFG_SITE_LANG, self_p=1, tab=''):
"""Creates navigation trail links, i.e. links to collection
ancestors (except Home collection). If aas==1, then links to
Advanced Search interfaces; otherwise Simple Search.
"""
dads = []
for dad in get_coll_ancestors(cc):
if dad != CFG_SITE_NAME: # exclude Home collection
dads.append ((dad, get_coll_i18nname(dad, ln, False)))
if self_p and cc != CFG_SITE_NAME:
dads.append((cc, get_coll_i18nname(cc, ln, False)))
return websearch_templates.tmpl_navtrail_links(
aas=aas, ln=ln, dads=dads)
def get_searchwithin_fields(ln='en', colID=None):
"""Retrieves the fields name used in the 'search within' selection box for the collection ID colID."""
res = None
if colID:
res = run_sql("""SELECT f.code,f.name FROM field AS f, collection_field_fieldvalue AS cff
WHERE cff.type='sew' AND cff.id_collection=%s AND cff.id_field=f.id
ORDER BY cff.score DESC, f.name ASC""", (colID,))
if not res:
res = run_sql("SELECT code,name FROM field ORDER BY name ASC")
fields = [{
'value' : '',
'text' : get_field_i18nname("any field", ln, False)
}]
for field_code, field_name in res:
if field_code and field_code != "anyfield":
fields.append({ 'value' : field_code,
'text' : get_field_i18nname(field_name, ln, False)
})
return fields
def get_sortby_fields(ln='en', colID=None):
"""Retrieves the fields name used in the 'sort by' selection box for the collection ID colID."""
_ = gettext_set_language(ln)
res = None
if colID:
res = run_sql("""SELECT DISTINCT(f.code),f.name FROM field AS f, collection_field_fieldvalue AS cff
WHERE cff.type='soo' AND cff.id_collection=%s AND cff.id_field=f.id
ORDER BY cff.score DESC, f.name ASC""", (colID,))
if not res:
# no sort fields defined for this colID, try to take Home collection:
res = run_sql("""SELECT DISTINCT(f.code),f.name FROM field AS f, collection_field_fieldvalue AS cff
WHERE cff.type='soo' AND cff.id_collection=%s AND cff.id_field=f.id
ORDER BY cff.score DESC, f.name ASC""", (1,))
if not res:
# no sort fields defined for the Home collection, take all sort fields defined wherever they are:
res = run_sql("""SELECT DISTINCT(f.code),f.name FROM field AS f, collection_field_fieldvalue AS cff
WHERE cff.type='soo' AND cff.id_field=f.id
ORDER BY cff.score DESC, f.name ASC""",)
fields = [{
'value' : '',
'text' : _("latest first")
}]
for field_code, field_name in res:
if field_code and field_code != "anyfield":
fields.append({ 'value' : field_code,
'text' : get_field_i18nname(field_name, ln, False)
})
return fields
def create_andornot_box(name='op', value='', ln='en'):
"Returns HTML code for the AND/OR/NOT selection box."
_ = gettext_set_language(ln)
out = """
<select name="%s">
<option value="a"%s>%s
<option value="o"%s>%s
<option value="n"%s>%s
</select>
""" % (name,
is_selected('a', value), _("AND"),
is_selected('o', value), _("OR"),
is_selected('n', value), _("AND NOT"))
return out
def create_matchtype_box(name='m', value='', ln='en'):
"Returns HTML code for the 'match type' selection box."
_ = gettext_set_language(ln)
out = """
<select name="%s">
<option value="a"%s>%s
<option value="o"%s>%s
<option value="e"%s>%s
<option value="p"%s>%s
<option value="r"%s>%s
</select>
""" % (name,
is_selected('a', value), _("All of the words:"),
is_selected('o', value), _("Any of the words:"),
is_selected('e', value), _("Exact phrase:"),
is_selected('p', value), _("Partial phrase:"),
is_selected('r', value), _("Regular expression:"))
return out
def is_selected(var, fld):
"Checks if the two are equal, and if yes, returns ' selected'. Useful for select boxes."
if type(var) is int and type(fld) is int:
if var == fld:
return " selected"
elif str(var) == str(fld):
return " selected"
elif fld and len(fld)==3 and fld[0] == "w" and var == fld[1:]:
return " selected"
return ""
def wash_colls(cc, c, split_colls=0, verbose=0):
"""Wash collection list by checking whether user has deselected
anything under 'Narrow search'. Checks also if cc is a list or not.
Return list of cc, colls_to_display, colls_to_search since the list
of collections to display is different from that to search in.
This is because users might have chosen 'split by collection'
functionality.
The behaviour of "collections to display" depends solely whether
user has deselected a particular collection: e.g. if it started
from 'Articles and Preprints' page, and deselected 'Preprints',
then collection to display is 'Articles'. If he did not deselect
anything, then collection to display is 'Articles & Preprints'.
The behaviour of "collections to search in" depends on the
'split_colls' parameter:
* if is equal to 1, then we can wash the colls list down
and search solely in the collection the user started from;
* if is equal to 0, then we are splitting to the first level
of collections, i.e. collections as they appear on the page
we started to search from;
The function raises exception
InvenioWebSearchUnknownCollectionError
if cc or one of c collections is not known.
"""
colls_out = []
colls_out_for_display = []
# list to hold the hosted collections to be searched and displayed
hosted_colls_out = []
debug = ""
if verbose:
debug += "<br />"
debug += "<br />1) --- initial parameters ---"
debug += "<br />cc : %s" % cc
debug += "<br />c : %s" % c
debug += "<br />"
# check what type is 'cc':
if type(cc) is list:
for ci in cc:
if collection_reclist_cache.cache.has_key(ci):
# yes this collection is real, so use it:
cc = ci
break
else:
# check once if cc is real:
if not collection_reclist_cache.cache.has_key(cc):
if cc:
raise InvenioWebSearchUnknownCollectionError(cc)
else:
cc = CFG_SITE_NAME # cc is not set, so replace it with Home collection
# check type of 'c' argument:
if type(c) is list:
colls = c
else:
colls = [c]
if verbose:
debug += "<br />2) --- after check for the integrity of cc and the being or not c a list ---"
debug += "<br />cc : %s" % cc
debug += "<br />c : %s" % c
debug += "<br />"
# remove all 'unreal' collections:
colls_real = []
for coll in colls:
if collection_reclist_cache.cache.has_key(coll):
colls_real.append(coll)
else:
if coll:
raise InvenioWebSearchUnknownCollectionError(coll)
colls = colls_real
if verbose:
debug += "<br />3) --- keeping only the real colls of c ---"
debug += "<br />colls : %s" % colls
debug += "<br />"
# check if some real collections remain:
if len(colls)==0:
colls = [cc]
if verbose:
debug += "<br />4) --- in case no colls were left we use cc directly ---"
debug += "<br />colls : %s" % colls
debug += "<br />"
# then let us check the list of non-restricted "real" sons of 'cc' and compare it to 'coll':
res = run_sql("""SELECT c.name FROM collection AS c,
collection_collection AS cc,
collection AS ccc
WHERE c.id=cc.id_son AND cc.id_dad=ccc.id
AND ccc.name=%s AND cc.type='r'""", (cc,))
# list that holds all the non restricted sons of cc that are also not hosted collections
l_cc_nonrestricted_sons_and_nonhosted_colls = []
res_hosted = run_sql("""SELECT c.name FROM collection AS c,
collection_collection AS cc,
collection AS ccc
WHERE c.id=cc.id_son AND cc.id_dad=ccc.id
AND ccc.name=%s AND cc.type='r'
AND (c.dbquery NOT LIKE 'hostedcollection:%%' OR c.dbquery IS NULL)""", (cc,))
for row_hosted in res_hosted:
l_cc_nonrestricted_sons_and_nonhosted_colls.append(row_hosted[0])
l_cc_nonrestricted_sons_and_nonhosted_colls.sort()
l_cc_nonrestricted_sons = []
l_c = colls
for row in res:
if not collection_restricted_p(row[0]):
l_cc_nonrestricted_sons.append(row[0])
l_c.sort()
l_cc_nonrestricted_sons.sort()
if l_cc_nonrestricted_sons == l_c:
colls_out_for_display = [cc] # yep, washing permitted, it is sufficient to display 'cc'
# the following elif is a hack that preserves the above funcionality when we start searching from
# the frontpage with some hosted collections deselected (either by default or manually)
elif set(l_cc_nonrestricted_sons_and_nonhosted_colls).issubset(set(l_c)):
colls_out_for_display = colls
split_colls = 0
else:
colls_out_for_display = colls # nope, we need to display all 'colls' successively
# remove duplicates:
#colls_out_for_display_nondups=filter(lambda x, colls_out_for_display=colls_out_for_display: colls_out_for_display[x-1] not in colls_out_for_display[x:], range(1, len(colls_out_for_display)+1))
#colls_out_for_display = map(lambda x, colls_out_for_display=colls_out_for_display:colls_out_for_display[x-1], colls_out_for_display_nondups)
colls_out_for_display = list(set(colls_out_for_display))
if verbose:
debug += "<br />5) --- decide whether colls_out_for_diplay should be colls or is it sufficient for it to be cc; remove duplicates ---"
debug += "<br />colls_out_for_display : %s" % colls_out_for_display
debug += "<br />"
# FIXME: The below quoted part of the code has been commented out
# because it prevents searching in individual restricted daughter
# collections when both parent and all its public daughter
# collections were asked for, in addition to some restricted
# daughter collections. The removal was introduced for hosted
# collections, so we may want to double check in this context.
# the following piece of code takes care of removing collections whose ancestors are going to be searched anyway
# list to hold the collections to be removed
#colls_to_be_removed = []
# first calculate the collections that can safely be removed
#for coll in colls_out_for_display:
# for ancestor in get_coll_ancestors(coll):
# #if ancestor in colls_out_for_display: colls_to_be_removed.append(coll)
# if ancestor in colls_out_for_display and not is_hosted_collection(coll): colls_to_be_removed.append(coll)
# secondly remove the collections
#for coll in colls_to_be_removed:
# colls_out_for_display.remove(coll)
if verbose:
debug += "<br />6) --- remove collections that have ancestors about to be search, unless they are hosted ---"
debug += "<br />colls_out_for_display : %s" % colls_out_for_display
debug += "<br />"
# calculate the hosted collections to be searched.
if colls_out_for_display == [cc]:
if is_hosted_collection(cc):
hosted_colls_out.append(cc)
else:
for coll in get_coll_sons(cc):
if is_hosted_collection(coll):
hosted_colls_out.append(coll)
else:
for coll in colls_out_for_display:
if is_hosted_collection(coll):
hosted_colls_out.append(coll)
if verbose:
debug += "<br />7) --- calculate the hosted_colls_out ---"
debug += "<br />hosted_colls_out : %s" % hosted_colls_out
debug += "<br />"
# second, let us decide on collection splitting:
if split_colls == 0:
# type A - no sons are wanted
colls_out = colls_out_for_display
else:
# type B - sons (first-level descendants) are wanted
for coll in colls_out_for_display:
coll_sons = get_coll_sons(coll)
if coll_sons == []:
colls_out.append(coll)
else:
for coll_son in coll_sons:
if not is_hosted_collection(coll_son):
colls_out.append(coll_son)
#else:
# colls_out = colls_out + coll_sons
# remove duplicates:
#colls_out_nondups=filter(lambda x, colls_out=colls_out: colls_out[x-1] not in colls_out[x:], range(1, len(colls_out)+1))
#colls_out = map(lambda x, colls_out=colls_out:colls_out[x-1], colls_out_nondups)
colls_out = list(set(colls_out))
if verbose:
debug += "<br />8) --- calculate the colls_out; remove duplicates ---"
debug += "<br />colls_out : %s" % colls_out
debug += "<br />"
# remove the hosted collections from the collections to be searched
if hosted_colls_out:
for coll in hosted_colls_out:
try:
colls_out.remove(coll)
except ValueError:
# in case coll was not found in colls_out
pass
if verbose:
debug += "<br />9) --- remove the hosted_colls from the colls_out ---"
debug += "<br />colls_out : %s" % colls_out
return (cc, colls_out_for_display, colls_out, hosted_colls_out, debug)
def strip_accents(x):
"""Strip accents in the input phrase X (assumed in UTF-8) by replacing
accented characters with their unaccented cousins (e.g. é by e).
Return such a stripped X."""
x = re_latex_lowercase_a.sub("a", x)
x = re_latex_lowercase_ae.sub("ae", x)
x = re_latex_lowercase_e.sub("e", x)
x = re_latex_lowercase_i.sub("i", x)
x = re_latex_lowercase_o.sub("o", x)
x = re_latex_lowercase_u.sub("u", x)
x = re_latex_lowercase_y.sub("x", x)
x = re_latex_lowercase_c.sub("c", x)
x = re_latex_lowercase_n.sub("n", x)
x = re_latex_uppercase_a.sub("A", x)
x = re_latex_uppercase_ae.sub("AE", x)
x = re_latex_uppercase_e.sub("E", x)
x = re_latex_uppercase_i.sub("I", x)
x = re_latex_uppercase_o.sub("O", x)
x = re_latex_uppercase_u.sub("U", x)
x = re_latex_uppercase_y.sub("Y", x)
x = re_latex_uppercase_c.sub("C", x)
x = re_latex_uppercase_n.sub("N", x)
# convert input into Unicode string:
try:
y = unicode(x, "utf-8")
except:
return x # something went wrong, probably the input wasn't UTF-8
# asciify Latin-1 lowercase characters:
y = re_unicode_lowercase_a.sub("a", y)
y = re_unicode_lowercase_ae.sub("ae", y)
y = re_unicode_lowercase_e.sub("e", y)
y = re_unicode_lowercase_i.sub("i", y)
y = re_unicode_lowercase_o.sub("o", y)
y = re_unicode_lowercase_u.sub("u", y)
y = re_unicode_lowercase_y.sub("y", y)
y = re_unicode_lowercase_c.sub("c", y)
y = re_unicode_lowercase_n.sub("n", y)
# asciify Latin-1 uppercase characters:
y = re_unicode_uppercase_a.sub("A", y)
y = re_unicode_uppercase_ae.sub("AE", y)
y = re_unicode_uppercase_e.sub("E", y)
y = re_unicode_uppercase_i.sub("I", y)
y = re_unicode_uppercase_o.sub("O", y)
y = re_unicode_uppercase_u.sub("U", y)
y = re_unicode_uppercase_y.sub("Y", y)
y = re_unicode_uppercase_c.sub("C", y)
y = re_unicode_uppercase_n.sub("N", y)
# return UTF-8 representation of the Unicode string:
return y.encode("utf-8")
def wash_index_term(term, max_char_length=50, lower_term=True):
"""
Return washed form of the index term TERM that would be suitable
for storing into idxWORD* tables. I.e., lower the TERM if
LOWER_TERM is True, and truncate it safely to MAX_CHAR_LENGTH
UTF-8 characters (meaning, in principle, 4*MAX_CHAR_LENGTH bytes).
The function works by an internal conversion of TERM, when needed,
from its input Python UTF-8 binary string format into Python
Unicode format, and then truncating it safely to the given number
of UTF-8 characters, without possible mis-truncation in the middle
of a multi-byte UTF-8 character that could otherwise happen if we
would have been working with UTF-8 binary representation directly.
Note that MAX_CHAR_LENGTH corresponds to the length of the term
column in idxINDEX* tables.
"""
if lower_term:
washed_term = unicode(term, 'utf-8').lower()
else:
washed_term = unicode(term, 'utf-8')
if len(washed_term) <= max_char_length:
# no need to truncate the term, because it will fit
# nicely even if it uses four-byte UTF-8 characters
return washed_term.encode('utf-8')
else:
# truncate the term in a safe position:
return washed_term[:max_char_length].encode('utf-8')
def lower_index_term(term):
"""
Return safely lowered index term TERM. This is done by converting
to UTF-8 first, because standard Python lower() function is not
UTF-8 safe. To be called by both the search engine and the
indexer when appropriate (e.g. before stemming).
In case of problems with UTF-8 compliance, this function raises
UnicodeDecodeError, so the client code may want to catch it.
"""
return unicode(term, 'utf-8').lower().encode('utf-8')
def get_synonym_terms(term, kbr_name, match_type):
"""
Return list of synonyms for TERM by looking in KBR_NAME in
MATCH_TYPE style.
@param term: search-time term or index-time term
@type term: str
@param kbr_name: knowledge base name
@type kbr_name: str
@param match_type: specifies how the term matches against the KBR
before doing the lookup. Could be `exact' (default),
'leading_to_comma', `leading_to_number'.
@type match_type: str
@return: list of term synonyms
@rtype: list of strings
"""
dterms = {}
## exact match is default:
term_for_lookup = term
term_remainder = ''
## but maybe match different term:
if match_type == 'leading_to_comma':
mmm = re.match(r'^(.*?)(\s*,.*)$', term)
if mmm:
term_for_lookup = mmm.group(1)
term_remainder = mmm.group(2)
elif match_type == 'leading_to_number':
mmm = re.match(r'^(.*?)(\s*\d.*)$', term)
if mmm:
term_for_lookup = mmm.group(1)
term_remainder = mmm.group(2)
## FIXME: workaround: escaping SQL wild-card signs, since KBR's
## exact search is doing LIKE query, so would match everything:
term_for_lookup = term_for_lookup.replace('%', '\%')
## OK, now find synonyms:
for kbr_values in get_kbr_values(kbr_name,
searchkey=term_for_lookup,
searchtype='e'):
for kbr_value in kbr_values:
dterms[kbr_value + term_remainder] = 1
## return list of term synonyms:
return dterms.keys()
def wash_output_format(format):
"""Wash output format FORMAT. Currently only prevents input like
'of=9' for backwards-compatible format that prints certain fields
only. (for this task, 'of=tm' is preferred)"""
if str(format[0:3]).isdigit() and len(format) != 6:
# asked to print MARC tags, but not enough digits,
# so let's switch back to HTML brief default
return 'hb'
else:
return format
def wash_pattern(p):
"""Wash pattern passed by URL. Check for sanity of the wildcard by
removing wildcards if they are appended to extremely short words
(1-3 letters). TODO: instead of this approximative treatment, it
will be much better to introduce a temporal limit, e.g. to kill a
query if it does not finish in 10 seconds."""
# strip accents:
# p = strip_accents(p) # FIXME: when available, strip accents all the time
# add leading/trailing whitespace for the two following wildcard-sanity checking regexps:
p = " " + p + " "
# replace spaces within quotes by __SPACE__ temporarily:
p = re_pattern_single_quotes.sub(lambda x: "'"+string.replace(x.group(1), ' ', '__SPACE__')+"'", p)
p = re_pattern_double_quotes.sub(lambda x: "\""+string.replace(x.group(1), ' ', '__SPACE__')+"\"", p)
p = re_pattern_regexp_quotes.sub(lambda x: "/"+string.replace(x.group(1), ' ', '__SPACE__')+"/", p)
# get rid of unquoted wildcards after spaces:
p = re_pattern_wildcards_after_spaces.sub("\\1", p)
# get rid of extremely short words (1-3 letters with wildcards):
#p = re_pattern_short_words.sub("\\1", p)
# replace back __SPACE__ by spaces:
p = re_pattern_space.sub(" ", p)
# replace special terms:
p = re_pattern_today.sub(time.strftime("%Y-%m-%d", time.localtime()), p)
# remove unnecessary whitespace:
p = string.strip(p)
# remove potentially wrong UTF-8 characters:
p = wash_for_utf8(p)
return p
def wash_field(f):
"""Wash field passed by URL."""
if f:
# get rid of unnecessary whitespace and make it lowercase
# (e.g. Author -> author) to better suit iPhone etc input
# mode:
f = f.strip().lower()
# wash legacy 'f' field names, e.g. replace 'wau' or `au' by
# 'author', if applicable:
if CFG_WEBSEARCH_FIELDS_CONVERT:
f = CFG_WEBSEARCH_FIELDS_CONVERT.get(f, f)
return f
def wash_dates(d1="", d1y=0, d1m=0, d1d=0, d2="", d2y=0, d2m=0, d2d=0):
"""
Take user-submitted date arguments D1 (full datetime string) or
(D1Y, D1M, D1Y) year, month, day tuple and D2 or (D2Y, D2M, D2Y)
and return (YYY1-M1-D2 H1:M1:S2, YYY2-M2-D2 H2:M2:S2) datetime
strings in the YYYY-MM-DD HH:MM:SS format suitable for time
restricted searching.
Note that when both D1 and (D1Y, D1M, D1D) parameters are present,
the precedence goes to D1. Ditto for D2*.
Note that when (D1Y, D1M, D1D) are taken into account, some values
may be missing and are completed e.g. to 01 or 12 according to
whether it is the starting or the ending date.
"""
datetext1, datetext2 = "", ""
# sanity checking:
if d1 == "" and d1y == 0 and d1m == 0 and d1d == 0 and d2 == "" and d2y == 0 and d2m == 0 and d2d == 0:
return ("", "") # nothing selected, so return empty values
# wash first (starting) date:
if d1:
# full datetime string takes precedence:
datetext1 = d1
else:
# okay, first date passed as (year,month,day):
if d1y:
datetext1 += "%04d" % d1y
else:
datetext1 += "0000"
if d1m:
datetext1 += "-%02d" % d1m
else:
datetext1 += "-01"
if d1d:
datetext1 += "-%02d" % d1d
else:
datetext1 += "-01"
datetext1 += " 00:00:00"
# wash second (ending) date:
if d2:
# full datetime string takes precedence:
datetext2 = d2
else:
# okay, second date passed as (year,month,day):
if d2y:
datetext2 += "%04d" % d2y
else:
datetext2 += "9999"
if d2m:
datetext2 += "-%02d" % d2m
else:
datetext2 += "-12"
if d2d:
datetext2 += "-%02d" % d2d
else:
datetext2 += "-31" # NOTE: perhaps we should add max(datenumber) in
# given month, but for our quering it's not
# needed, 31 will always do
datetext2 += " 00:00:00"
# okay, return constructed YYYY-MM-DD HH:MM:SS datetexts:
return (datetext1, datetext2)
def is_hosted_collection(coll):
"""Check if the given collection is a hosted one; i.e. its dbquery starts with hostedcollection:
Returns True if it is, False if it's not or if the result is empty or if the query failed"""
res = run_sql("SELECT dbquery FROM collection WHERE name=%s", (coll, ))
try:
return res[0][0].startswith("hostedcollection:")
except:
return False
def get_colID(c):
"Return collection ID for collection name C. Return None if no match found."
colID = None
res = run_sql("SELECT id FROM collection WHERE name=%s", (c,), 1)
if res:
colID = res[0][0]
return colID
def get_coll_normalised_name(c):
"""Returns normalised collection name (case sensitive) for collection name
C (case insensitive).
Returns None if no match found."""
try:
return run_sql("SELECT name FROM collection WHERE name=%s", (c,))[0][0]
except:
return None
def get_coll_ancestors(coll):
"Returns a list of ancestors for collection 'coll'."
coll_ancestors = []
coll_ancestor = coll
while 1:
res = run_sql("""SELECT c.name FROM collection AS c
LEFT JOIN collection_collection AS cc ON c.id=cc.id_dad
LEFT JOIN collection AS ccc ON ccc.id=cc.id_son
WHERE ccc.name=%s ORDER BY cc.id_dad ASC LIMIT 1""",
(coll_ancestor,))
if res:
coll_name = res[0][0]
coll_ancestors.append(coll_name)
coll_ancestor = coll_name
else:
break
# ancestors found, return reversed list:
coll_ancestors.reverse()
return coll_ancestors
def get_coll_sons(coll, type='r', public_only=1):
"""Return a list of sons (first-level descendants) of type 'type' for collection 'coll'.
If public_only, then return only non-restricted son collections.
"""
coll_sons = []
query = "SELECT c.name FROM collection AS c "\
"LEFT JOIN collection_collection AS cc ON c.id=cc.id_son "\
"LEFT JOIN collection AS ccc ON ccc.id=cc.id_dad "\
"WHERE cc.type=%s AND ccc.name=%s"
query += " ORDER BY cc.score DESC"
res = run_sql(query, (type, coll))
for name in res:
if not public_only or not collection_restricted_p(name[0]):
coll_sons.append(name[0])
return coll_sons
def get_coll_real_descendants(coll, type='_', get_hosted_colls=True):
"""Return a list of all descendants of collection 'coll' that are defined by a 'dbquery'.
IOW, we need to decompose compound collections like "A & B" into "A" and "B" provided
that "A & B" has no associated database query defined.
"""
coll_sons = []
res = run_sql("""SELECT c.name,c.dbquery FROM collection AS c
LEFT JOIN collection_collection AS cc ON c.id=cc.id_son
LEFT JOIN collection AS ccc ON ccc.id=cc.id_dad
WHERE ccc.name=%s AND cc.type LIKE %s ORDER BY cc.score DESC""",
(coll, type,))
for name, dbquery in res:
if dbquery: # this is 'real' collection, so return it:
if get_hosted_colls:
coll_sons.append(name)
else:
if not dbquery.startswith("hostedcollection:"):
coll_sons.append(name)
else: # this is 'composed' collection, so recurse:
coll_sons.extend(get_coll_real_descendants(name))
return coll_sons
def browse_pattern(req, colls, p, f, rg, ln=CFG_SITE_LANG):
"""Browse either biliographic phrases or words indexes, and display it."""
# load the right message language
_ = gettext_set_language(ln)
## is p enclosed in quotes? (coming from exact search)
if p.startswith('"') and p.endswith('"'):
p = p[1:-1]
p_orig = p
## okay, "real browse" follows:
## FIXME: the maths in the get_nearest_terms_in_bibxxx is just a test
if not f and string.find(p, ":") > 0: # does 'p' contain ':'?
f, p = string.split(p, ":", 1)
## do we search in words indexes?
if not f:
return browse_in_bibwords(req, p, f)
index_id = get_index_id_from_field(f)
if index_id != 0:
coll = HitSet()
for coll_name in colls:
coll |= get_collection_reclist(coll_name)
browsed_phrases_in_colls = get_nearest_terms_in_idxphrase_with_collection(p, index_id, rg/2, rg/2, coll)
else:
browsed_phrases = get_nearest_terms_in_bibxxx(p, f, (rg+1)/2+1, (rg-1)/2+1)
while not browsed_phrases:
# try again and again with shorter and shorter pattern:
try:
p = p[:-1]
browsed_phrases = get_nearest_terms_in_bibxxx(p, f, (rg+1)/2+1, (rg-1)/2+1)
except:
# probably there are no hits at all:
req.write(_("No values found."))
return
## try to check hits in these particular collection selection:
browsed_phrases_in_colls = []
if 0:
for phrase in browsed_phrases:
phrase_hitset = HitSet()
phrase_hitsets = search_pattern("", phrase, f, 'e')
for coll in colls:
phrase_hitset.union_update(phrase_hitsets[coll])
if len(phrase_hitset) > 0:
# okay, this phrase has some hits in colls, so add it:
browsed_phrases_in_colls.append([phrase, len(phrase_hitset)])
## were there hits in collections?
if browsed_phrases_in_colls == []:
if browsed_phrases != []:
#print_warning(req, """<p>No match close to <em>%s</em> found in given collections.
#Please try different term.<p>Displaying matches in any collection...""" % p_orig)
## try to get nbhits for these phrases in any collection:
for phrase in browsed_phrases:
browsed_phrases_in_colls.append([phrase, get_nbhits_in_bibxxx(phrase, f)])
## display results now:
out = websearch_templates.tmpl_browse_pattern(
f=f,
fn=get_field_i18nname(get_field_name(f) or f, ln, False),
ln=ln,
browsed_phrases_in_colls=browsed_phrases_in_colls,
colls=colls,
rg=rg,
)
req.write(out)
return
def browse_in_bibwords(req, p, f, ln=CFG_SITE_LANG):
"""Browse inside words indexes."""
if not p:
return
_ = gettext_set_language(ln)
urlargd = {}
urlargd.update(req.argd)
urlargd['action'] = 'search'
nearest_box = create_nearest_terms_box(urlargd, p, f, 'w', ln=ln, intro_text_p=0)
req.write(websearch_templates.tmpl_search_in_bibwords(
p = p,
f = f,
ln = ln,
nearest_box = nearest_box
))
return
def search_pattern(req=None, p=None, f=None, m=None, ap=0, of="id", verbose=0, ln=CFG_SITE_LANG, display_nearest_terms_box=True, wl=0):
"""Search for complex pattern 'p' within field 'f' according to
matching type 'm'. Return hitset of recIDs.
The function uses multi-stage searching algorithm in case of no
exact match found. See the Search Internals document for
detailed description.
The 'ap' argument governs whether an alternative patterns are to
be used in case there is no direct hit for (p,f,m). For
example, whether to replace non-alphanumeric characters by
spaces if it would give some hits. See the Search Internals
document for detailed description. (ap=0 forbits the
alternative pattern usage, ap=1 permits it.)
The 'of' argument governs whether to print or not some
information to the user in case of no match found. (Usually it
prints the information in case of HTML formats, otherwise it's
silent).
The 'verbose' argument controls the level of debugging information
to be printed (0=least, 9=most).
All the parameters are assumed to have been previously washed.
This function is suitable as a mid-level API.
"""
_ = gettext_set_language(ln)
hitset_empty = HitSet()
# sanity check:
if not p:
hitset_full = HitSet(trailing_bits=1)
hitset_full.discard(0)
# no pattern, so return all universe
return hitset_full
# search stage 1: break up arguments into basic search units:
if verbose and of.startswith("h"):
t1 = os.times()[4]
basic_search_units = create_basic_search_units(req, p, f, m, of)
if verbose and of.startswith("h"):
t2 = os.times()[4]
print_warning(req, "Search stage 1: basic search units are: %s" % cgi.escape(repr(basic_search_units)))
print_warning(req, "Search stage 1: execution took %.2f seconds." % (t2 - t1))
# search stage 2: do search for each search unit and verify hit presence:
if verbose and of.startswith("h"):
t1 = os.times()[4]
basic_search_units_hitsets = []
#prepare hiddenfield-related..
myhiddens = CFG_BIBFORMAT_HIDDEN_TAGS
can_see_hidden = False
if req:
user_info = collect_user_info(req)
can_see_hidden = (acc_authorize_action(user_info, 'runbibedit')[0] == 0)
if can_see_hidden:
myhiddens = []
if CFG_INSPIRE_SITE and of.startswith('h'):
# fulltext/caption search warnings for INSPIRE:
fields_to_be_searched = [f for o,p,f,m in basic_search_units]
if 'fulltext' in fields_to_be_searched:
print_warning(req, _("Warning: full-text search is only available for a subset of papers mostly from 2006-2011."))
elif 'caption' in fields_to_be_searched:
print_warning(req, _("Warning: figure caption search is only available for a subset of papers mostly from 2008-2011."))
for idx_unit in xrange(len(basic_search_units)):
bsu_o, bsu_p, bsu_f, bsu_m = basic_search_units[idx_unit]
try:
basic_search_unit_hitset = search_unit(bsu_p, bsu_f, bsu_m, wl)
except InvenioWebSearchWildcardLimitError, excp:
basic_search_unit_hitset = excp.res
if of.startswith("h"):
print_warning(req, "Search term too generic, displaying only partial results...")
# FIXME: print warning if we use native full-text indexing
if bsu_f == 'fulltext' and bsu_m != 'w' and of.startswith('h') and not CFG_SOLR_URL:
print_warning(req, _("No phrase index available for fulltext yet, looking for word combination..."))
#check that the user is allowed to search with this tag
#if he/she tries it
if bsu_f and len(bsu_f) > 1 and bsu_f[0].isdigit() and bsu_f[1].isdigit():
for htag in myhiddens:
ltag = len(htag)
samelenfield = bsu_f[0:ltag]
if samelenfield == htag: #user searches by a hidden tag
#we won't show you anything..
basic_search_unit_hitset = HitSet()
if verbose >= 9 and of.startswith("h"):
print_warning(req, "Pattern %s hitlist omitted since \
it queries in a hidden tag %s" %
(repr(bsu_p), repr(myhiddens)))
display_nearest_terms_box=False #..and stop spying, too.
if verbose >= 9 and of.startswith("h"):
print_warning(req, "Search stage 1: pattern %s gave hitlist %s" % (cgi.escape(bsu_p), basic_search_unit_hitset))
if len(basic_search_unit_hitset) > 0 or \
ap==0 or \
bsu_o=="|" or \
((idx_unit+1)<len(basic_search_units) and basic_search_units[idx_unit+1][0]=="|"):
# stage 2-1: this basic search unit is retained, since
# either the hitset is non-empty, or the approximate
# pattern treatment is switched off, or the search unit
# was joined by an OR operator to preceding/following
# units so we do not require that it exists
basic_search_units_hitsets.append(basic_search_unit_hitset)
else:
# stage 2-2: no hits found for this search unit, try to replace non-alphanumeric chars inside pattern:
if re.search(r'[^a-zA-Z0-9\s\:]', bsu_p) and bsu_f != 'refersto' and bsu_f != 'citedby':
if bsu_p.startswith('"') and bsu_p.endswith('"'): # is it ACC query?
bsu_pn = re.sub(r'[^a-zA-Z0-9\s\:]+', "*", bsu_p)
else: # it is WRD query
bsu_pn = re.sub(r'[^a-zA-Z0-9\s\:]+', " ", bsu_p)
if verbose and of.startswith('h') and req:
print_warning(req, "Trying (%s,%s,%s)" % (cgi.escape(bsu_pn), cgi.escape(bsu_f), cgi.escape(bsu_m)))
basic_search_unit_hitset = search_pattern(req=None, p=bsu_pn, f=bsu_f, m=bsu_m, of="id", ln=ln, wl=wl)
if len(basic_search_unit_hitset) > 0:
# we retain the new unit instead
if of.startswith('h'):
print_warning(req, _("No exact match found for %(x_query1)s, using %(x_query2)s instead...") % \
{'x_query1': "<em>" + cgi.escape(bsu_p) + "</em>",
'x_query2': "<em>" + cgi.escape(bsu_pn) + "</em>"})
basic_search_units[idx_unit][1] = bsu_pn
basic_search_units_hitsets.append(basic_search_unit_hitset)
else:
# stage 2-3: no hits found either, propose nearest indexed terms:
if of.startswith('h') and display_nearest_terms_box:
if req:
if bsu_f == "recid":
print_warning(req, _("Requested record does not seem to exist."))
else:
print_warning(req, create_nearest_terms_box(req.argd, bsu_p, bsu_f, bsu_m, ln=ln))
return hitset_empty
else:
# stage 2-3: no hits found either, propose nearest indexed terms:
if of.startswith('h') and display_nearest_terms_box:
if req:
if bsu_f == "recid":
print_warning(req, _("Requested record does not seem to exist."))
else:
print_warning(req, create_nearest_terms_box(req.argd, bsu_p, bsu_f, bsu_m, ln=ln))
return hitset_empty
if verbose and of.startswith("h"):
t2 = os.times()[4]
for idx_unit in range(0, len(basic_search_units)):
print_warning(req, "Search stage 2: basic search unit %s gave %d hits." %
(basic_search_units[idx_unit][1:], len(basic_search_units_hitsets[idx_unit])))
print_warning(req, "Search stage 2: execution took %.2f seconds." % (t2 - t1))
# search stage 3: apply boolean query for each search unit:
if verbose and of.startswith("h"):
t1 = os.times()[4]
# let the initial set be the complete universe:
hitset_in_any_collection = HitSet(trailing_bits=1)
hitset_in_any_collection.discard(0)
for idx_unit in xrange(len(basic_search_units)):
this_unit_operation = basic_search_units[idx_unit][0]
this_unit_hitset = basic_search_units_hitsets[idx_unit]
if this_unit_operation == '+':
hitset_in_any_collection.intersection_update(this_unit_hitset)
elif this_unit_operation == '-':
hitset_in_any_collection.difference_update(this_unit_hitset)
elif this_unit_operation == '|':
hitset_in_any_collection.union_update(this_unit_hitset)
else:
if of.startswith("h"):
print_warning(req, "Invalid set operation %s." % cgi.escape(this_unit_operation), "Error")
if len(hitset_in_any_collection) == 0:
# no hits found, propose alternative boolean query:
if of.startswith('h') and display_nearest_terms_box:
nearestterms = []
for idx_unit in range(0, len(basic_search_units)):
bsu_o, bsu_p, bsu_f, bsu_m = basic_search_units[idx_unit]
if bsu_p.startswith("%") and bsu_p.endswith("%"):
bsu_p = "'" + bsu_p[1:-1] + "'"
bsu_nbhits = len(basic_search_units_hitsets[idx_unit])
# create a similar query, but with the basic search unit only
argd = {}
argd.update(req.argd)
argd['p'] = bsu_p
argd['f'] = bsu_f
nearestterms.append((bsu_p, bsu_nbhits, argd))
text = websearch_templates.tmpl_search_no_boolean_hits(
ln=ln, nearestterms=nearestterms)
print_warning(req, text)
if verbose and of.startswith("h"):
t2 = os.times()[4]
print_warning(req, "Search stage 3: boolean query gave %d hits." % len(hitset_in_any_collection))
print_warning(req, "Search stage 3: execution took %.2f seconds." % (t2 - t1))
return hitset_in_any_collection
def search_pattern_parenthesised(req=None, p=None, f=None, m=None, ap=0, of="id", verbose=0, ln=CFG_SITE_LANG, display_nearest_terms_box=True, wl=0):
"""Search for complex pattern 'p' containing parenthesis within field 'f' according to
matching type 'm'. Return hitset of recIDs.
For more details on the parameters see 'search_pattern'
"""
_ = gettext_set_language(ln)
spires_syntax_converter = SpiresToInvenioSyntaxConverter()
spires_syntax_query = False
# if the pattern uses SPIRES search syntax, convert it to Invenio syntax
if spires_syntax_converter.is_applicable(p):
spires_syntax_query = True
p = spires_syntax_converter.convert_query(p)
# sanity check: do not call parenthesised parser for search terms
# like U(1):
if not re_pattern_parens.search(p):
return search_pattern(req, p, f, m, ap, of, verbose, ln, display_nearest_terms_box=display_nearest_terms_box, wl=wl)
# Try searching with parentheses
try:
parser = SearchQueryParenthesisedParser()
# get a hitset with all recids
result_hitset = HitSet(trailing_bits=1)
# parse the query. The result is list of [op1, expr1, op2, expr2, ..., opN, exprN]
parsing_result = parser.parse_query(p)
if verbose and of.startswith("h"):
print_warning(req, "Search stage 1: search_pattern_parenthesised() returned %s." % repr(parsing_result))
# go through every pattern
# calculate hitset for it
# combine pattern's hitset with the result using the corresponding operator
for index in xrange(0, len(parsing_result)-1, 2 ):
current_operator = parsing_result[index]
current_pattern = parsing_result[index+1]
if CFG_INSPIRE_SITE and spires_syntax_query:
# setting ap=0 to turn off approximate matching for 0 results.
# Doesn't work well in combinations.
# FIXME: The right fix involves collecting statuses for each
# hitset, then showing a nearest terms box exactly once,
# outside this loop.
ap = 0
display_nearest_terms_box=False
# obtain a hitset for the current pattern
current_hitset = search_pattern(req, current_pattern, f, m, ap, of, verbose, ln, display_nearest_terms_box=display_nearest_terms_box, wl=wl)
# combine the current hitset with resulting hitset using the current operator
if current_operator == '+':
result_hitset = result_hitset & current_hitset
elif current_operator == '-':
result_hitset = result_hitset - current_hitset
elif current_operator == '|':
result_hitset = result_hitset | current_hitset
else:
assert False, "Unknown operator in search_pattern_parenthesised()"
return result_hitset
# If searching with parenteses fails, perform search ignoring parentheses
except SyntaxError:
print_warning(req, _("Search syntax misunderstood. Ignoring all parentheses in the query. If this doesn't help, please check your search and try again."))
# remove the parentheses in the query. Current implementation removes all the parentheses,
# but it could be improved to romove only these that are not inside quotes
p = p.replace('(', ' ')
p = p.replace(')', ' ')
return search_pattern(req, p, f, m, ap, of, verbose, ln, display_nearest_terms_box=display_nearest_terms_box, wl=wl)
def search_unit(p, f=None, m=None, wl=0):
"""Search for basic search unit defined by pattern 'p' and field
'f' and matching type 'm'. Return hitset of recIDs.
All the parameters are assumed to have been previously washed.
'p' is assumed to be already a ``basic search unit'' so that it
is searched as such and is not broken up in any way. Only
wildcard and span queries are being detected inside 'p'.
If CFG_WEBSEARCH_SYNONYM_KBRS is set and we are searching in
one of the indexes that has defined runtime synonym knowledge
base, then look up there and automatically enrich search
results with results for synonyms.
In case the wildcard limit (wl) is greater than 0 and this limit
is reached an InvenioWebSearchWildcardLimitError will be raised.
In case you want to call this function with no limit for the
wildcard queries, wl should be 0.
This function is suitable as a low-level API.
"""
## create empty output results set:
hitset = HitSet()
if not p: # sanity checking
return hitset
## eventually look up runtime synonyms:
hitset_synonyms = HitSet()
if CFG_WEBSEARCH_SYNONYM_KBRS.has_key(f):
for p_synonym in get_synonym_terms(p,
CFG_WEBSEARCH_SYNONYM_KBRS[f][0],
CFG_WEBSEARCH_SYNONYM_KBRS[f][1]):
if p_synonym != p:
hitset_synonyms |= search_unit(p_synonym, f, m, wl)
## look up hits:
if CFG_SOLR_URL and f == 'fulltext':
# redirect to Solr/Lucene
return search_unit_in_solr(p, f, m)
if f == 'datecreated':
hitset = search_unit_in_bibrec(p, p, 'c')
elif f == 'datemodified':
hitset = search_unit_in_bibrec(p, p, 'm')
elif f == 'refersto':
# we are doing search by the citation count
hitset = search_unit_refersto(p)
elif f == 'citedby':
# we are doing search by the citation count
hitset = search_unit_citedby(p)
elif m == 'a' or m == 'r':
# we are doing either phrase search or regexp search
if f == 'fulltext':
# FIXME: workaround for not having phrase index yet
return search_pattern(None, p, f, 'w')
index_id = get_index_id_from_field(f)
if index_id != 0:
hitset = search_unit_in_idxphrases(p, f, m, wl)
else:
hitset = search_unit_in_bibxxx(p, f, m, wl)
elif p.startswith("cited:"):
# we are doing search by the citation count
hitset = search_unit_by_times_cited(p[6:])
else:
# we are doing bibwords search by default
hitset = search_unit_in_bibwords(p, f, m, wl=wl)
## merge synonym results and return total:
hitset |= hitset_synonyms
return hitset
def search_unit_in_bibwords(word, f, m=None, decompress=zlib.decompress, wl=0):
"""Searches for 'word' inside bibwordsX table for field 'f' and returns hitset of recIDs."""
set = HitSet() # will hold output result set
set_used = 0 # not-yet-used flag, to be able to circumvent set operations
limit_reached = 0 # flag for knowing if the query limit has been reached
# deduce into which bibwordsX table we will search:
stemming_language = get_index_stemming_language(get_index_id_from_field("anyfield"))
bibwordsX = "idxWORD%02dF" % get_index_id_from_field("anyfield")
if f:
index_id = get_index_id_from_field(f)
if index_id:
bibwordsX = "idxWORD%02dF" % index_id
stemming_language = get_index_stemming_language(index_id)
else:
return HitSet() # word index f does not exist
# wash 'word' argument and run query:
word = string.replace(word, '*', '%') # we now use '*' as the truncation character
words = string.split(word, "->", 1) # check for span query
if len(words) == 2:
word0 = re_word.sub('', words[0])
word1 = re_word.sub('', words[1])
if stemming_language:
word0 = lower_index_term(word0)
word1 = lower_index_term(word1)
word0 = stem(word0, stemming_language)
word1 = stem(word1, stemming_language)
try:
res = run_sql_with_limit("SELECT term,hitlist FROM %s WHERE term BETWEEN %%s AND %%s" % bibwordsX,
(wash_index_term(word0), wash_index_term(word1)), wildcard_limit = wl)
except InvenioDbQueryWildcardLimitError, excp:
res = excp.res
limit_reached = 1 # set the limit reached flag to true
else:
if f == 'journal':
pass # FIXME: quick hack for the journal index
else:
word = re_word.sub('', word)
if stemming_language:
word = lower_index_term(word)
word = stem(word, stemming_language)
if string.find(word, '%') >= 0: # do we have wildcard in the word?
if f == 'journal':
# FIXME: quick hack for the journal index
# FIXME: we can run a sanity check here for all indexes
res = ()
else:
try:
res = run_sql_with_limit("SELECT term,hitlist FROM %s WHERE term LIKE %%s" % bibwordsX,
(wash_index_term(word),), wildcard_limit = wl)
except InvenioDbQueryWildcardLimitError, excp:
res = excp.res
limit_reached = 1 # set the limit reached flag to true
else:
res = run_sql("SELECT term,hitlist FROM %s WHERE term=%%s" % bibwordsX,
(wash_index_term(word),))
# fill the result set:
for word, hitlist in res:
hitset_bibwrd = HitSet(hitlist)
# add the results:
if set_used:
set.union_update(hitset_bibwrd)
else:
set = hitset_bibwrd
set_used = 1
#check to see if the query limit was reached
if limit_reached:
#raise an exception, so we can print a nice message to the user
raise InvenioWebSearchWildcardLimitError(set)
# okay, return result set:
return set
def search_unit_in_idxphrases(p, f, type, wl=0):
"""Searches for phrase 'p' inside idxPHRASE*F table for field 'f' and returns hitset of recIDs found.
The search type is defined by 'type' (e.g. equals to 'r' for a regexp search)."""
set = HitSet() # will hold output result set
set_used = 0 # not-yet-used flag, to be able to circumvent set operations
limit_reached = 0 # flag for knowing if the query limit has been reached
use_query_limit = False # flag for knowing if to limit the query results or not
# deduce in which idxPHRASE table we will search:
idxphraseX = "idxPHRASE%02dF" % get_index_id_from_field("anyfield")
if f:
index_id = get_index_id_from_field(f)
if index_id:
idxphraseX = "idxPHRASE%02dF" % index_id
else:
return HitSet() # phrase index f does not exist
# detect query type (exact phrase, partial phrase, regexp):
if type == 'r':
query_addons = "REGEXP %s"
query_params = (p,)
use_query_limit = True
else:
p = string.replace(p, '*', '%') # we now use '*' as the truncation character
ps = string.split(p, "->", 1) # check for span query:
if len(ps) == 2 and not (ps[0].endswith(' ') or ps[1].startswith(' ')):
query_addons = "BETWEEN %s AND %s"
query_params = (ps[0], ps[1])
use_query_limit = True
else:
if string.find(p, '%') > -1:
query_addons = "LIKE %s"
query_params = (p,)
use_query_limit = True
else:
query_addons = "= %s"
query_params = (p,)
# special washing for fuzzy author index:
if f in ('author', 'firstauthor', 'exactauthor'):
query_params_washed = ()
for query_param in query_params:
query_params_washed += (wash_author_name(query_param),)
query_params = query_params_washed
# perform search:
if use_query_limit:
try:
res = run_sql_with_limit("SELECT term,hitlist FROM %s WHERE term %s" % (idxphraseX, query_addons),
query_params, wildcard_limit=wl)
except InvenioDbQueryWildcardLimitError, excp:
res = excp.res
limit_reached = 1 # set the limit reached flag to true
else:
res = run_sql("SELECT term,hitlist FROM %s WHERE term %s" % (idxphraseX, query_addons), query_params)
# fill the result set:
for word, hitlist in res:
hitset_bibphrase = HitSet(hitlist)
# add the results:
if set_used:
set.union_update(hitset_bibphrase)
else:
set = hitset_bibphrase
set_used = 1
#check to see if the query limit was reached
if limit_reached:
#raise an exception, so we can print a nice message to the user
raise InvenioWebSearchWildcardLimitError(set)
# okay, return result set:
return set
def search_unit_in_bibxxx(p, f, type, wl=0):
"""Searches for pattern 'p' inside bibxxx tables for field 'f' and returns hitset of recIDs found.
The search type is defined by 'type' (e.g. equals to 'r' for a regexp search)."""
# FIXME: quick hack for the journal index
if f == 'journal':
return search_unit_in_bibwords(p, f, wl=wl)
p_orig = p # saving for eventual future 'no match' reporting
limit_reached = 0 # flag for knowing if the query limit has been reached
use_query_limit = False # flag for knowing if to limit the query results or not
query_addons = "" # will hold additional SQL code for the query
query_params = () # will hold parameters for the query (their number may vary depending on TYPE argument)
# wash arguments:
f = string.replace(f, '*', '%') # replace truncation char '*' in field definition
if type == 'r':
query_addons = "REGEXP %s"
query_params = (p,)
use_query_limit = True
else:
p = string.replace(p, '*', '%') # we now use '*' as the truncation character
ps = string.split(p, "->", 1) # check for span query:
if len(ps) == 2 and not (ps[0].endswith(' ') or ps[1].startswith(' ')):
query_addons = "BETWEEN %s AND %s"
query_params = (ps[0], ps[1])
use_query_limit = True
else:
if string.find(p, '%') > -1:
query_addons = "LIKE %s"
query_params = (p,)
use_query_limit = True
else:
query_addons = "= %s"
query_params = (p,)
# construct 'tl' which defines the tag list (MARC tags) to search in:
tl = []
if str(f[0]).isdigit() and str(f[1]).isdigit():
tl.append(f) # 'f' seems to be okay as it starts by two digits
else:
# deduce desired MARC tags on the basis of chosen 'f'
tl = get_field_tags(f)
if not tl:
# f index does not exist, nevermind
pass
# okay, start search:
l = [] # will hold list of recID that matched
for t in tl:
# deduce into which bibxxx table we will search:
digit1, digit2 = int(t[0]), int(t[1])
bx = "bib%d%dx" % (digit1, digit2)
bibx = "bibrec_bib%d%dx" % (digit1, digit2)
# construct and run query:
if t == "001":
if query_addons.find('BETWEEN') > -1 or query_addons.find('=') > -1:
# verify that the params are integers (to avoid returning record 123 when searching for 123foo)
try:
query_params = tuple(int(param) for param in query_params)
except ValueError:
return HitSet()
if use_query_limit:
try:
res = run_sql_with_limit("SELECT id FROM bibrec WHERE id %s" % query_addons,
query_params, wildcard_limit=wl)
except InvenioDbQueryWildcardLimitError, excp:
res = excp.res
limit_reached = 1 # set the limit reached flag to true
else:
res = run_sql("SELECT id FROM bibrec WHERE id %s" % query_addons,
query_params)
else:
query = "SELECT bibx.id_bibrec FROM %s AS bx LEFT JOIN %s AS bibx ON bx.id=bibx.id_bibxxx WHERE bx.value %s" % \
(bx, bibx, query_addons)
if len(t) != 6 or t[-1:]=='%':
# wildcard query, or only the beginning of field 't'
# is defined, so add wildcard character:
query += " AND bx.tag LIKE %s"
query_params = query_params + (t + '%',)
else:
# exact query for 't':
query += " AND bx.tag=%s"
query_params = query_params + (t,)
if use_query_limit:
try:
res = run_sql_with_limit(query, query_params, wildcard_limit=wl)
except InvenioDbQueryWildcardLimitError, excp:
res = excp.res
limit_reached = 1 # set the limit reached flag to true
else:
res = run_sql(query, query_params)
# fill the result set:
for id_bibrec in res:
if id_bibrec[0]:
l.append(id_bibrec[0])
# check no of hits found:
nb_hits = len(l)
# okay, return result set:
set = HitSet(l)
#check to see if the query limit was reached
if limit_reached:
#raise an exception, so we can print a nice message to the user
raise InvenioWebSearchWildcardLimitError(set)
return set
def search_unit_in_solr(p, f=None, m=None):
"""
Query the Solr full-text index and return an intbitset corresponding
to the result. Parameters (p,f,m) are usual search unit ones.
"""
if m and (m == 'a' or m == 'r'): # phrase/regexp query
if p.startswith('%') and p.endswith('%'):
p = p[1:-1] # fix for partial phrase
p = '"' + p + '"'
return solr_get_bitset(p, CFG_SOLR_URL)
def search_unit_in_bibrec(datetext1, datetext2, type='c'):
"""
Return hitset of recIDs found that were either created or modified
(according to 'type' arg being 'c' or 'm') from datetext1 until datetext2, inclusive.
Does not pay attention to pattern, collection, anything. Useful
to intersect later on with the 'real' query.
"""
set = HitSet()
if type.startswith("m"):
type = "modification_date"
else:
type = "creation_date" # by default we are searching for creation dates
parts = datetext1.split('->')
if len(parts) > 1 and datetext1 == datetext2:
datetext1 = parts[0]
datetext2 = parts[1]
if datetext1 == datetext2:
res = run_sql("SELECT id FROM bibrec WHERE %s LIKE %%s" % (type,),
(datetext1 + '%',))
else:
res = run_sql("SELECT id FROM bibrec WHERE %s>=%%s AND %s<=%%s" % (type, type),
(datetext1, datetext2))
for row in res:
set += row[0]
return set
def search_unit_by_times_cited(p):
"""
Return histset of recIDs found that are cited P times.
Usually P looks like '10->23'.
"""
numstr = '"'+p+'"'
#this is sort of stupid but since we may need to
#get the records that do _not_ have cites, we have to
#know the ids of all records, too
#but this is needed only if bsu_p is 0 or 0 or 0->0
allrecs = []
if p == 0 or p == "0" or \
p.startswith("0->") or p.endswith("->0"):
allrecs = HitSet(run_sql("SELECT id FROM bibrec"))
return get_records_with_num_cites(numstr, allrecs)
def search_unit_refersto(query):
"""
Search for records satisfying the query (e.g. author:ellis) and
return list of records referred to by these records.
"""
if query:
ahitset = search_pattern(p=query)
if ahitset:
return get_refersto_hitset(ahitset)
else:
return HitSet([])
else:
return HitSet([])
def search_unit_citedby(query):
"""
Search for records satisfying the query (e.g. author:ellis) and
return list of records cited by these records.
"""
if query:
ahitset = search_pattern(p=query)
if ahitset:
return get_citedby_hitset(ahitset)
else:
return HitSet([])
else:
return HitSet([])
def intersect_results_with_collrecs(req, hitset_in_any_collection, colls, ap=0, of="hb", verbose=0, ln=CFG_SITE_LANG, display_nearest_terms_box=True):
"""Return dict of hitsets given by intersection of hitset with the collection universes."""
_ = gettext_set_language(ln)
# search stage 4: intersect with the collection universe:
if verbose and of.startswith("h"):
t1 = os.times()[4]
results = {}
results_nbhits = 0
for coll in colls:
results[coll] = hitset_in_any_collection & get_collection_reclist(coll)
results_nbhits += len(results[coll])
if results_nbhits == 0:
# no hits found, try to search in Home:
results_in_Home = hitset_in_any_collection & get_collection_reclist(CFG_SITE_NAME)
if len(results_in_Home) > 0:
# some hits found in Home, so propose this search:
if of.startswith("h") and display_nearest_terms_box:
url = websearch_templates.build_search_url(req.argd, cc=CFG_SITE_NAME, c=[])
print_warning(req, _("No match found in collection %(x_collection)s. Other public collections gave %(x_url_open)s%(x_nb_hits)d hits%(x_url_close)s.") %\
{'x_collection': '<em>' + string.join([get_coll_i18nname(coll, ln, False) for coll in colls], ', ') + '</em>',
'x_url_open': '<a class="nearestterms" href="%s">' % (url),
'x_nb_hits': len(results_in_Home),
'x_url_close': '</a>'})
results = {}
else:
# no hits found in Home, recommend different search terms:
if of.startswith("h") and display_nearest_terms_box:
print_warning(req, _("No public collection matched your query. "
"If you were looking for a non-public document, please choose "
"the desired restricted collection first."))
results = {}
if verbose and of.startswith("h"):
t2 = os.times()[4]
print_warning(req, "Search stage 4: intersecting with collection universe gave %d hits." % results_nbhits)
print_warning(req, "Search stage 4: execution took %.2f seconds." % (t2 - t1))
return results
def intersect_results_with_hitset(req, results, hitset, ap=0, aptext="", of="hb"):
"""Return intersection of search 'results' (a dict of hitsets
with collection as key) with the 'hitset', i.e. apply
'hitset' intersection to each collection within search
'results'.
If the final 'results' set is to be empty, and 'ap'
(approximate pattern) is true, and then print the `warningtext'
and return the original 'results' set unchanged. If 'ap' is
false, then return empty results set.
"""
if ap:
results_ap = copy.deepcopy(results)
else:
results_ap = {} # will return empty dict in case of no hits found
nb_total = 0
for coll in results.keys():
results[coll].intersection_update(hitset)
nb_total += len(results[coll])
if nb_total == 0:
if of.startswith("h"):
print_warning(req, aptext)
results = results_ap
return results
def create_similarly_named_authors_link_box(author_name, ln=CFG_SITE_LANG):
"""Return a box similar to ``Not satisfied...'' one by proposing
author searches for similar names. Namely, take AUTHOR_NAME
and the first initial of the firstame (after comma) and look
into author index whether authors with e.g. middle names exist.
Useful mainly for CERN Library that sometimes contains name
forms like Ellis-N, Ellis-Nick, Ellis-Nicolas all denoting the
same person. The box isn't proposed if no similarly named
authors are found to exist.
"""
# return nothing if not configured:
if CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX == 0:
return ""
# return empty box if there is no initial:
if re.match(r'[^ ,]+, [^ ]', author_name) is None:
return ""
# firstly find name comma initial:
author_name_to_search = re.sub(r'^([^ ,]+, +[^ ,]).*$', '\\1', author_name)
# secondly search for similar name forms:
similar_author_names = {}
for name in author_name_to_search, strip_accents(author_name_to_search):
for tag in get_field_tags("author"):
# deduce into which bibxxx table we will search:
digit1, digit2 = int(tag[0]), int(tag[1])
bx = "bib%d%dx" % (digit1, digit2)
bibx = "bibrec_bib%d%dx" % (digit1, digit2)
if len(tag) != 6 or tag[-1:]=='%':
# only the beginning of field 't' is defined, so add wildcard character:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value LIKE %%s AND bx.tag LIKE %%s""" % bx,
(name + "%", tag + "%"))
else:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value LIKE %%s AND bx.tag=%%s""" % bx,
(name + "%", tag))
for row in res:
similar_author_names[row[0]] = 1
# remove the original name and sort the list:
try:
del similar_author_names[author_name]
except KeyError:
pass
# thirdly print the box:
out = ""
if similar_author_names:
out_authors = similar_author_names.keys()
out_authors.sort()
tmp_authors = []
for out_author in out_authors:
nbhits = get_nbhits_in_bibxxx(out_author, "author")
if nbhits:
tmp_authors.append((out_author, nbhits))
out += websearch_templates.tmpl_similar_author_names(
authors=tmp_authors, ln=ln)
return out
def create_nearest_terms_box(urlargd, p, f, t='w', n=5, ln=CFG_SITE_LANG, intro_text_p=True):
"""Return text box containing list of 'n' nearest terms above/below 'p'
for the field 'f' for matching type 't' (words/phrases) in
language 'ln'.
Propose new searches according to `urlargs' with the new words.
If `intro_text_p' is true, then display the introductory message,
otherwise print only the nearest terms in the box content.
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
nearest_terms = []
if not p: # sanity check
p = "."
if p.startswith('%') and p.endswith('%'):
p = p[1:-1] # fix for partial phrase
index_id = get_index_id_from_field(f)
if f == 'fulltext':
if CFG_SOLR_URL:
return _("No match found, please enter different search terms.")
else:
# FIXME: workaround for not having native phrase index yet
t = 'w'
# special indexes:
if f == 'refersto':
return _("There are no records referring to %s.") % cgi.escape(p)
if f == 'citedby':
return _("There are no records cited by %s.") % cgi.escape(p)
# look for nearest terms:
if t == 'w':
nearest_terms = get_nearest_terms_in_bibwords(p, f, n, n)
if not nearest_terms:
return _("No word index is available for %s.") % \
('<em>' + cgi.escape(get_field_i18nname(get_field_name(f) or f, ln, False)) + '</em>')
else:
nearest_terms = []
if index_id:
nearest_terms = get_nearest_terms_in_idxphrase(p, index_id, n, n)
if f == 'datecreated' or f == 'datemodified':
nearest_terms = get_nearest_terms_in_bibrec(p, f, n, n)
if not nearest_terms:
nearest_terms = get_nearest_terms_in_bibxxx(p, f, n, n)
if not nearest_terms:
return _("No phrase index is available for %s.") % \
('<em>' + cgi.escape(get_field_i18nname(get_field_name(f) or f, ln, False)) + '</em>')
terminfo = []
for term in nearest_terms:
if t == 'w':
hits = get_nbhits_in_bibwords(term, f)
else:
if index_id:
hits = get_nbhits_in_idxphrases(term, f)
elif f == 'datecreated' or f == 'datemodified':
hits = get_nbhits_in_bibrec(term, f)
else:
hits = get_nbhits_in_bibxxx(term, f)
argd = {}
argd.update(urlargd)
# check which fields contained the requested parameter, and replace it.
for (px, fx) in ('p', 'f'), ('p1', 'f1'), ('p2', 'f2'), ('p3', 'f3'):
if px in argd:
argd_px = argd[px]
if t == 'w':
# p was stripped of accents, to do the same:
argd_px = strip_accents(argd_px)
if f == argd[fx] or f == "anyfield" or f == "":
if string.find(argd_px, p) > -1:
argd[px] = string.replace(argd_px, p, term)
break
else:
if string.find(argd_px, f+':'+p) > -1:
argd[px] = string.replace(argd_px, f+':'+p, f+':'+term)
break
elif string.find(argd_px, f+':"'+p+'"') > -1:
argd[px] = string.replace(argd_px, f+':"'+p+'"', f+':"'+term+'"')
break
elif string.find(argd_px, f+':\''+p+'\'') > -1:
argd[px] = string.replace(argd_px, f+':\''+p+'\'', f+':\''+term+'\'')
break
terminfo.append((term, hits, argd))
intro = ""
if intro_text_p: # add full leading introductory text
if f:
intro = _("Search term %(x_term)s inside index %(x_index)s did not match any record. Nearest terms in any collection are:") % \
{'x_term': "<em>" + cgi.escape(p.startswith("%") and p.endswith("%") and p[1:-1] or p) + "</em>",
'x_index': "<em>" + cgi.escape(get_field_i18nname(get_field_name(f) or f, ln, False)) + "</em>"}
else:
intro = _("Search term %s did not match any record. Nearest terms in any collection are:") % \
("<em>" + cgi.escape(p.startswith("%") and p.endswith("%") and p[1:-1] or p) + "</em>")
return websearch_templates.tmpl_nearest_term_box(p=p, ln=ln, f=f, terminfo=terminfo,
intro=intro)
def get_nearest_terms_in_bibwords(p, f, n_below, n_above):
"""Return list of +n -n nearest terms to word `p' in index for field `f'."""
nearest_words = [] # will hold the (sorted) list of nearest words to return
# deduce into which bibwordsX table we will search:
bibwordsX = "idxWORD%02dF" % get_index_id_from_field("anyfield")
if f:
index_id = get_index_id_from_field(f)
if index_id:
bibwordsX = "idxWORD%02dF" % index_id
else:
return nearest_words
# firstly try to get `n' closest words above `p':
res = run_sql("SELECT term FROM %s WHERE term<%%s ORDER BY term DESC LIMIT %%s" % bibwordsX,
(p, n_above))
for row in res:
nearest_words.append(row[0])
nearest_words.reverse()
# secondly insert given word `p':
nearest_words.append(p)
# finally try to get `n' closest words below `p':
res = run_sql("SELECT term FROM %s WHERE term>%%s ORDER BY term ASC LIMIT %%s" % bibwordsX,
(p, n_below))
for row in res:
nearest_words.append(row[0])
return nearest_words
def get_nearest_terms_in_idxphrase(p, index_id, n_below, n_above):
"""Browse (-n_above, +n_below) closest bibliographic phrases
for the given pattern p in the given field idxPHRASE table,
regardless of collection.
Return list of [phrase1, phrase2, ... , phrase_n]."""
if CFG_INSPIRE_SITE and index_id in (3, 15): # FIXME: workaround due to new fuzzy index
return [p,]
idxphraseX = "idxPHRASE%02dF" % index_id
res_above = run_sql("SELECT term FROM %s WHERE term<%%s ORDER BY term DESC LIMIT %%s" % idxphraseX, (p, n_above))
res_above = map(lambda x: x[0], res_above)
res_above.reverse()
res_below = run_sql("SELECT term FROM %s WHERE term>=%%s ORDER BY term ASC LIMIT %%s" % idxphraseX, (p, n_below))
res_below = map(lambda x: x[0], res_below)
return res_above + res_below
def get_nearest_terms_in_idxphrase_with_collection(p, index_id, n_below, n_above, collection):
"""Browse (-n_above, +n_below) closest bibliographic phrases
for the given pattern p in the given field idxPHRASE table,
considering the collection (HitSet).
Return list of [(phrase1, hitset), (phrase2, hitset), ... , (phrase_n, hitset)]."""
idxphraseX = "idxPHRASE%02dF" % index_id
res_above = run_sql("SELECT term,hitlist FROM %s WHERE term<%%s ORDER BY term DESC LIMIT %%s" % idxphraseX, (p, n_above * 3))
res_above = [(term, HitSet(hitlist) & collection) for term, hitlist in res_above]
res_above = [(term, len(hitlist)) for term, hitlist in res_above if hitlist]
res_below = run_sql("SELECT term,hitlist FROM %s WHERE term>=%%s ORDER BY term ASC LIMIT %%s" % idxphraseX, (p, n_below * 3))
res_below = [(term, HitSet(hitlist) & collection) for term, hitlist in res_below]
res_below = [(term, len(hitlist)) for term, hitlist in res_below if hitlist]
res_above.reverse()
return res_above[-n_above:] + res_below[:n_below]
def get_nearest_terms_in_bibxxx(p, f, n_below, n_above):
"""Browse (-n_above, +n_below) closest bibliographic phrases
for the given pattern p in the given field f, regardless
of collection.
Return list of [phrase1, phrase2, ... , phrase_n]."""
## determine browse field:
if not f and string.find(p, ":") > 0: # does 'p' contain ':'?
f, p = string.split(p, ":", 1)
# FIXME: quick hack for the journal index
if f == 'journal':
return get_nearest_terms_in_bibwords(p, f, n_below, n_above)
## We are going to take max(n_below, n_above) as the number of
## values to ferch from bibXXx. This is needed to work around
## MySQL UTF-8 sorting troubles in 4.0.x. Proper solution is to
## use MySQL 4.1.x or our own idxPHRASE in the future.
index_id = get_index_id_from_field(f)
if index_id:
return get_nearest_terms_in_idxphrase(p, index_id, n_below, n_above)
n_fetch = 2*max(n_below, n_above)
## construct 'tl' which defines the tag list (MARC tags) to search in:
tl = []
if str(f[0]).isdigit() and str(f[1]).isdigit():
tl.append(f) # 'f' seems to be okay as it starts by two digits
else:
# deduce desired MARC tags on the basis of chosen 'f'
tl = get_field_tags(f)
## start browsing to fetch list of hits:
browsed_phrases = {} # will hold {phrase1: 1, phrase2: 1, ..., phraseN: 1} dict of browsed phrases (to make them unique)
# always add self to the results set:
browsed_phrases[p.startswith("%") and p.endswith("%") and p[1:-1] or p] = 1
for t in tl:
# deduce into which bibxxx table we will search:
digit1, digit2 = int(t[0]), int(t[1])
bx = "bib%d%dx" % (digit1, digit2)
bibx = "bibrec_bib%d%dx" % (digit1, digit2)
# firstly try to get `n' closest phrases above `p':
if len(t) != 6 or t[-1:]=='%': # only the beginning of field 't' is defined, so add wildcard character:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value<%%s AND bx.tag LIKE %%s
ORDER BY bx.value DESC LIMIT %%s""" % bx,
(p, t + "%", n_fetch))
else:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value<%%s AND bx.tag=%%s
ORDER BY bx.value DESC LIMIT %%s""" % bx,
(p, t, n_fetch))
for row in res:
browsed_phrases[row[0]] = 1
# secondly try to get `n' closest phrases equal to or below `p':
if len(t) != 6 or t[-1:]=='%': # only the beginning of field 't' is defined, so add wildcard character:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value>=%%s AND bx.tag LIKE %%s
ORDER BY bx.value ASC LIMIT %%s""" % bx,
(p, t + "%", n_fetch))
else:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value>=%%s AND bx.tag=%%s
ORDER BY bx.value ASC LIMIT %%s""" % bx,
(p, t, n_fetch))
for row in res:
browsed_phrases[row[0]] = 1
# select first n words only: (this is needed as we were searching
# in many different tables and so aren't sure we have more than n
# words right; this of course won't be needed when we shall have
# one ACC table only for given field):
phrases_out = browsed_phrases.keys()
phrases_out.sort(lambda x, y: cmp(string.lower(strip_accents(x)),
string.lower(strip_accents(y))))
# find position of self:
try:
idx_p = phrases_out.index(p)
except:
idx_p = len(phrases_out)/2
# return n_above and n_below:
return phrases_out[max(0, idx_p-n_above):idx_p+n_below]
def get_nearest_terms_in_bibrec(p, f, n_below, n_above):
"""Return list of nearest terms and counts from bibrec table.
p is usually a date, and f either datecreated or datemodified.
Note: below/above count is very approximative, not really respected.
"""
col = 'creation_date'
if f == 'datemodified':
col = 'modification_date'
res_above = run_sql("""SELECT DATE_FORMAT(%s,'%%%%Y-%%%%m-%%%%d %%%%H:%%%%i:%%%%s')
FROM bibrec WHERE %s < %%s
ORDER BY %s DESC LIMIT %%s""" % (col, col, col),
(p, n_above))
res_below = run_sql("""SELECT DATE_FORMAT(%s,'%%%%Y-%%%%m-%%%%d %%%%H:%%%%i:%%%%s')
FROM bibrec WHERE %s > %%s
ORDER BY %s ASC LIMIT %%s""" % (col, col, col),
(p, n_below))
out = set([])
for row in res_above:
out.add(row[0])
for row in res_below:
out.add(row[0])
out_list = list(out)
out_list.sort()
return list(out_list)
def get_nbhits_in_bibrec(term, f):
"""Return number of hits in bibrec table. term is usually a date,
and f is either 'datecreated' or 'datemodified'."""
col = 'creation_date'
if f == 'datemodified':
col = 'modification_date'
res = run_sql("SELECT COUNT(*) FROM bibrec WHERE %s LIKE %%s" % (col,),
(term + '%',))
return res[0][0]
def get_nbhits_in_bibwords(word, f):
"""Return number of hits for word 'word' inside words index for field 'f'."""
out = 0
# deduce into which bibwordsX table we will search:
bibwordsX = "idxWORD%02dF" % get_index_id_from_field("anyfield")
if f:
index_id = get_index_id_from_field(f)
if index_id:
bibwordsX = "idxWORD%02dF" % index_id
else:
return 0
if word:
res = run_sql("SELECT hitlist FROM %s WHERE term=%%s" % bibwordsX,
(word,))
for hitlist in res:
out += len(HitSet(hitlist[0]))
return out
def get_nbhits_in_idxphrases(word, f):
"""Return number of hits for word 'word' inside phrase index for field 'f'."""
out = 0
# deduce into which bibwordsX table we will search:
idxphraseX = "idxPHRASE%02dF" % get_index_id_from_field("anyfield")
if f:
index_id = get_index_id_from_field(f)
if index_id:
idxphraseX = "idxPHRASE%02dF" % index_id
else:
return 0
if word:
res = run_sql("SELECT hitlist FROM %s WHERE term=%%s" % idxphraseX,
(word,))
for hitlist in res:
out += len(HitSet(hitlist[0]))
return out
def get_nbhits_in_bibxxx(p, f):
"""Return number of hits for word 'word' inside words index for field 'f'."""
## determine browse field:
if not f and string.find(p, ":") > 0: # does 'p' contain ':'?
f, p = string.split(p, ":", 1)
# FIXME: quick hack for the journal index
if f == 'journal':
return get_nbhits_in_bibwords(p, f)
## construct 'tl' which defines the tag list (MARC tags) to search in:
tl = []
if str(f[0]).isdigit() and str(f[1]).isdigit():
tl.append(f) # 'f' seems to be okay as it starts by two digits
else:
# deduce desired MARC tags on the basis of chosen 'f'
tl = get_field_tags(f)
# start searching:
recIDs = {} # will hold dict of {recID1: 1, recID2: 1, ..., } (unique recIDs, therefore)
for t in tl:
# deduce into which bibxxx table we will search:
digit1, digit2 = int(t[0]), int(t[1])
bx = "bib%d%dx" % (digit1, digit2)
bibx = "bibrec_bib%d%dx" % (digit1, digit2)
if len(t) != 6 or t[-1:]=='%': # only the beginning of field 't' is defined, so add wildcard character:
res = run_sql("""SELECT bibx.id_bibrec FROM %s AS bibx, %s AS bx
WHERE bx.value=%%s AND bx.tag LIKE %%s
AND bibx.id_bibxxx=bx.id""" % (bibx, bx),
(p, t + "%"))
else:
res = run_sql("""SELECT bibx.id_bibrec FROM %s AS bibx, %s AS bx
WHERE bx.value=%%s AND bx.tag=%%s
AND bibx.id_bibxxx=bx.id""" % (bibx, bx),
(p, t))
for row in res:
recIDs[row[0]] = 1
return len(recIDs)
def get_mysql_recid_from_aleph_sysno(sysno):
"""Returns DB's recID for ALEPH sysno passed in the argument (e.g. "002379334CER").
Returns None in case of failure."""
out = None
res = run_sql("""SELECT bb.id_bibrec FROM bibrec_bib97x AS bb, bib97x AS b
WHERE b.value=%s AND b.tag='970__a' AND bb.id_bibxxx=b.id""",
(sysno,))
if res:
out = res[0][0]
return out
def guess_primary_collection_of_a_record(recID):
"""Return primary collection name a record recid belongs to, by
testing 980 identifier.
May lead to bad guesses when a collection is defined dynamically
via dbquery.
In that case, return 'CFG_SITE_NAME'."""
out = CFG_SITE_NAME
dbcollids = get_fieldvalues(recID, "980__a")
if dbcollids:
for dbcollid in dbcollids:
dbquery = "collection:" + dbcollid
res = run_sql("SELECT name FROM collection WHERE dbquery=%s", (dbquery,))
if res:
out = res[0][0]
break
if CFG_CERN_SITE:
# dirty hack for ATLAS collections at CERN:
if out in ('ATLAS Communications', 'ATLAS Internal Notes'):
for alternative_collection in ('ATLAS Communications Physics',
'ATLAS Communications General',
'ATLAS Internal Notes Physics',
'ATLAS Internal Notes General',):
if recID in get_collection_reclist(alternative_collection):
out = alternative_collection
break
return out
_re_collection_url = re.compile('/collection/(.+)')
def guess_collection_of_a_record(recID, referer=None, recreate_cache_if_needed=True):
"""Return collection name a record recid belongs to, by first testing
the referer URL if provided and otherwise returning the
primary collection."""
if referer:
dummy, hostname, path, dummy, query, dummy = urlparse.urlparse(referer)
#requests can come from different invenio installations, with different collections
if CFG_SITE_URL.find(hostname) < 0:
return guess_primary_collection_of_a_record(recID)
g = _re_collection_url.match(path)
if g:
name = urllib.unquote_plus(g.group(1))
#check if this collection actually exist (also normalize the name if case-insensitive)
name = get_coll_normalised_name(name)
if name and recID in get_collection_reclist(name):
return name
elif path.startswith('/search'):
if recreate_cache_if_needed:
collection_reclist_cache.recreate_cache_if_needed()
query = cgi.parse_qs(query)
for name in query.get('cc', []) + query.get('c', []):
if recID in get_collection_reclist(name, recreate_cache_if_needed=False):
return name
return guess_primary_collection_of_a_record(recID)
def is_record_in_any_collection(recID, recreate_cache_if_needed=True):
"""Return True if the record belongs to at least one collection. This is a
good, although not perfect, indicator to guess if webcoll has already run
after this record has been entered into the system.
"""
if recreate_cache_if_needed:
collection_reclist_cache.recreate_cache_if_needed()
for name in collection_reclist_cache.cache.keys():
if recID in get_collection_reclist(name, recreate_cache_if_needed=False):
return True
return False
def get_all_collections_of_a_record(recID, recreate_cache_if_needed=True):
"""Return all the collection names a record belongs to.
Note this function is O(n_collections)."""
ret = []
if recreate_cache_if_needed:
collection_reclist_cache.recreate_cache_if_needed()
for name in collection_reclist_cache.cache.keys():
if recID in get_collection_reclist(name, recreate_cache_if_needed=False):
ret.append(name)
return ret
def get_tag_name(tag_value, prolog="", epilog=""):
"""Return tag name from the known tag value, by looking up the 'tag' table.
Return empty string in case of failure.
Example: input='100__%', output=first author'."""
out = ""
res = run_sql("SELECT name FROM tag WHERE value=%s", (tag_value,))
if res:
out = prolog + res[0][0] + epilog
return out
def get_fieldcodes():
"""Returns a list of field codes that may have been passed as 'search options' in URL.
Example: output=['subject','division']."""
out = []
res = run_sql("SELECT DISTINCT(code) FROM field")
for row in res:
out.append(row[0])
return out
def get_field_name(code):
"""Return the corresponding field_name given the field code.
e.g. reportnumber -> report number."""
res = run_sql("SELECT name FROM field WHERE code=%s", (code, ))
if res:
return res[0][0]
else:
return ""
def get_field_tags(field):
"""Returns a list of MARC tags for the field code 'field'.
Returns empty list in case of error.
Example: field='author', output=['100__%','700__%']."""
out = []
query = """SELECT t.value FROM tag AS t, field_tag AS ft, field AS f
WHERE f.code=%s AND ft.id_field=f.id AND t.id=ft.id_tag
ORDER BY ft.score DESC"""
res = run_sql(query, (field, ))
for val in res:
out.append(val[0])
return out
def get_fieldvalues(recIDs, tag, repetitive_values=True):
"""
Return list of field values for field TAG for the given record ID
or list of record IDs. (RECIDS can be both an integer or a list
of integers.)
If REPETITIVE_VALUES is set to True, then return all values even
if they are doubled. If set to False, then return unique values
only.
"""
out = []
- try:
- recIDs = int(recIDs)
- except:
- pass
if isinstance(recIDs, (int, long)):
recIDs =[recIDs,]
if not isinstance(recIDs, (list, tuple)):
return []
if len(recIDs) == 0:
return []
if tag == "001___":
# we have asked for tag 001 (=recID) that is not stored in bibXXx tables
out = [str(recID) for recID in recIDs]
else:
# we are going to look inside bibXXx tables
digits = tag[0:2]
try:
intdigits = int(digits)
if intdigits < 0 or intdigits > 99:
raise ValueError
except ValueError:
# invalid tag value asked for
return []
bx = "bib%sx" % digits
bibx = "bibrec_bib%sx" % digits
queryparam = []
for recID in recIDs:
queryparam.append(recID)
if not repetitive_values:
queryselect = "DISTINCT(bx.value)"
else:
queryselect = "bx.value"
query = "SELECT %s FROM %s AS bx, %s AS bibx WHERE bibx.id_bibrec IN (%s) " \
" AND bx.id=bibx.id_bibxxx AND bx.tag LIKE %%s " \
" ORDER BY bibx.field_number, bx.tag ASC" % \
(queryselect, bx, bibx, ("%s,"*len(queryparam))[:-1])
res = run_sql(query, tuple(queryparam) + (tag,))
for row in res:
out.append(row[0])
return out
def get_fieldvalues_alephseq_like(recID, tags_in, can_see_hidden=False):
"""Return buffer of ALEPH sequential-like textual format with fields found
in the list TAGS_IN for record RECID.
If can_see_hidden is True, just print everything. Otherwise hide fields
from CFG_BIBFORMAT_HIDDEN_TAGS.
"""
out = ""
if type(tags_in) is not list:
tags_in = [tags_in,]
if len(tags_in) == 1 and len(tags_in[0]) == 6:
## case A: one concrete subfield asked, so print its value if found
## (use with care: can mislead if field has multiple occurrences)
out += string.join(get_fieldvalues(recID, tags_in[0]),"\n")
else:
## case B: print our "text MARC" format; works safely all the time
# find out which tags to output:
dict_of_tags_out = {}
if not tags_in:
for i in range(0, 10):
for j in range(0, 10):
dict_of_tags_out["%d%d%%" % (i, j)] = 1
else:
for tag in tags_in:
if len(tag) == 0:
for i in range(0, 10):
for j in range(0, 10):
dict_of_tags_out["%d%d%%" % (i, j)] = 1
elif len(tag) == 1:
for j in range(0, 10):
dict_of_tags_out["%s%d%%" % (tag, j)] = 1
elif len(tag) < 5:
dict_of_tags_out["%s%%" % tag] = 1
elif tag >= 6:
dict_of_tags_out[tag[0:5]] = 1
tags_out = dict_of_tags_out.keys()
tags_out.sort()
# search all bibXXx tables as needed:
for tag in tags_out:
digits = tag[0:2]
try:
intdigits = int(digits)
if intdigits < 0 or intdigits > 99:
raise ValueError
except ValueError:
# invalid tag value asked for
continue
if tag.startswith("001") or tag.startswith("00%"):
if out:
out += "\n"
out += "%09d %s %d" % (recID, "001__", recID)
bx = "bib%sx" % digits
bibx = "bibrec_bib%sx" % digits
query = "SELECT b.tag,b.value,bb.field_number FROM %s AS b, %s AS bb "\
"WHERE bb.id_bibrec=%%s AND b.id=bb.id_bibxxx AND b.tag LIKE %%s"\
"ORDER BY bb.field_number, b.tag ASC" % (bx, bibx)
res = run_sql(query, (recID, str(tag)+'%'))
# go through fields:
field_number_old = -999
field_old = ""
for row in res:
field, value, field_number = row[0], row[1], row[2]
ind1, ind2 = field[3], field[4]
printme = True
#check the stuff in hiddenfields
if not can_see_hidden:
for htag in CFG_BIBFORMAT_HIDDEN_TAGS:
ltag = len(htag)
samelenfield = field[0:ltag]
if samelenfield == htag:
printme = False
if ind1 == "_":
ind1 = ""
if ind2 == "_":
ind2 = ""
# print field tag
if printme:
if field_number != field_number_old or field[:-1] != field_old[:-1]:
if out:
out += "\n"
out += "%09d %s " % (recID, field[:5])
field_number_old = field_number
field_old = field
# print subfield value
if field[0:2] == "00" and field[-1:] == "_":
out += value
else:
out += "$$%s%s" % (field[-1:], value)
return out
def record_exists(recID):
"""Return 1 if record RECID exists.
Return 0 if it doesn't exist.
Return -1 if it exists but is marked as deleted.
"""
out = 0
res = run_sql("SELECT id FROM bibrec WHERE id=%s", (recID,), 1)
if res:
try: # if recid is '123foo', mysql will return id=123, and we don't want that
recID = int(recID)
except ValueError:
return 0
# record exists; now check whether it isn't marked as deleted:
dbcollids = get_fieldvalues(recID, "980__%")
if ("DELETED" in dbcollids) or (CFG_CERN_SITE and "DUMMY" in dbcollids):
out = -1 # exists, but marked as deleted
else:
out = 1 # exists fine
return out
def record_empty(recID):
"""
Is this record empty, e.g. has only 001, waiting for integration?
@param recID: the record identifier.
@type recID: int
@return: 1 if the record is empty, 0 otherwise.
@rtype: int
"""
record = get_record(recID)
if record is None or len(record) < 2:
return 1
else:
return 0
def record_public_p(recID, recreate_cache_if_needed=True):
"""Return 1 if the record is public, i.e. if it can be found in the Home collection.
Return 0 otherwise.
"""
return recID in get_collection_reclist(CFG_SITE_NAME, recreate_cache_if_needed=recreate_cache_if_needed)
def get_creation_date(recID, fmt="%Y-%m-%d"):
"Returns the creation date of the record 'recID'."
out = ""
res = run_sql("SELECT DATE_FORMAT(creation_date,%s) FROM bibrec WHERE id=%s", (fmt, recID), 1)
if res:
out = res[0][0]
return out
def get_modification_date(recID, fmt="%Y-%m-%d"):
"Returns the date of last modification for the record 'recID'."
out = ""
res = run_sql("SELECT DATE_FORMAT(modification_date,%s) FROM bibrec WHERE id=%s", (fmt, recID), 1)
if res:
out = res[0][0]
return out
def print_warning(req, msg, msg_type='', prologue='<br />', epilogue='<br />'):
"Prints warning message and flushes output."
if req and msg:
req.write(websearch_templates.tmpl_print_warning(
msg = msg,
type = msg_type,
prologue = prologue,
epilogue = epilogue,
))
return
def print_search_info(p, f, sf, so, sp, rm, of, ot, collection=CFG_SITE_NAME, nb_found=-1, jrec=1, rg=10,
aas=0, ln=CFG_SITE_LANG, p1="", p2="", p3="", f1="", f2="", f3="", m1="", m2="", m3="", op1="", op2="",
sc=1, pl_in_url="",
d1y=0, d1m=0, d1d=0, d2y=0, d2m=0, d2d=0, dt="",
cpu_time=-1, middle_only=0):
"""Prints stripe with the information on 'collection' and 'nb_found' results and CPU time.
Also, prints navigation links (beg/next/prev/end) inside the results set.
If middle_only is set to 1, it will only print the middle box information (beg/netx/prev/end/etc) links.
This is suitable for displaying navigation links at the bottom of the search results page."""
# sanity check:
if jrec < 1:
jrec = 1
if jrec > nb_found:
jrec = max(nb_found-rg+1, 1)
return websearch_templates.tmpl_print_search_info(
ln = ln,
collection = collection,
aas = aas,
collection_name = get_coll_i18nname(collection, ln, False),
collection_id = get_colID(collection),
middle_only = middle_only,
rg = rg,
nb_found = nb_found,
sf = sf,
so = so,
rm = rm,
of = of,
ot = ot,
p = p,
f = f,
p1 = p1,
p2 = p2,
p3 = p3,
f1 = f1,
f2 = f2,
f3 = f3,
m1 = m1,
m2 = m2,
m3 = m3,
op1 = op1,
op2 = op2,
pl_in_url = pl_in_url,
d1y = d1y,
d1m = d1m,
d1d = d1d,
d2y = d2y,
d2m = d2m,
d2d = d2d,
dt = dt,
jrec = jrec,
sc = sc,
sp = sp,
all_fieldcodes = get_fieldcodes(),
cpu_time = cpu_time,
)
def print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, collection=CFG_SITE_NAME, nb_found=-1, jrec=1, rg=10,
aas=0, ln=CFG_SITE_LANG, p1="", p2="", p3="", f1="", f2="", f3="", m1="", m2="", m3="", op1="", op2="",
sc=1, pl_in_url="",
d1y=0, d1m=0, d1d=0, d2y=0, d2m=0, d2d=0, dt="",
cpu_time=-1, middle_only=0):
"""Prints stripe with the information on 'collection' and 'nb_found' results and CPU time.
Also, prints navigation links (beg/next/prev/end) inside the results set.
If middle_only is set to 1, it will only print the middle box information (beg/netx/prev/end/etc) links.
This is suitable for displaying navigation links at the bottom of the search results page."""
out = ""
# sanity check:
if jrec < 1:
jrec = 1
if jrec > nb_found:
jrec = max(nb_found-rg+1, 1)
return websearch_templates.tmpl_print_hosted_search_info(
ln = ln,
collection = collection,
aas = aas,
collection_name = get_coll_i18nname(collection, ln, False),
collection_id = get_colID(collection),
middle_only = middle_only,
rg = rg,
nb_found = nb_found,
sf = sf,
so = so,
rm = rm,
of = of,
ot = ot,
p = p,
f = f,
p1 = p1,
p2 = p2,
p3 = p3,
f1 = f1,
f2 = f2,
f3 = f3,
m1 = m1,
m2 = m2,
m3 = m3,
op1 = op1,
op2 = op2,
pl_in_url = pl_in_url,
d1y = d1y,
d1m = d1m,
d1d = d1d,
d2y = d2y,
d2m = d2m,
d2d = d2d,
dt = dt,
jrec = jrec,
sc = sc,
sp = sp,
all_fieldcodes = get_fieldcodes(),
cpu_time = cpu_time,
)
def print_results_overview(colls, results_final_nb_total, results_final_nb, cpu_time, ln=CFG_SITE_LANG, ec=[], hosted_colls_potential_results_p=False):
"""Prints results overview box with links to particular collections below."""
out = ""
new_colls = []
for coll in colls:
new_colls.append({
'id': get_colID(coll),
'code': coll,
'name': get_coll_i18nname(coll, ln, False),
})
return websearch_templates.tmpl_print_results_overview(
ln = ln,
results_final_nb_total = results_final_nb_total,
results_final_nb = results_final_nb,
cpu_time = cpu_time,
colls = new_colls,
ec = ec,
hosted_colls_potential_results_p = hosted_colls_potential_results_p,
)
def print_hosted_results(url_and_engine, ln=CFG_SITE_LANG, of=None, req=None, no_records_found=False, search_timed_out=False, limit=CFG_EXTERNAL_COLLECTION_MAXRESULTS):
"""Prints the full results of a hosted collection"""
if of.startswith("h"):
if no_records_found:
return "<br />No results found."
if search_timed_out:
return "<br />The search engine did not respond in time."
return websearch_templates.tmpl_print_hosted_results(
url_and_engine=url_and_engine,
ln=ln,
of=of,
req=req,
limit=limit
)
def sort_records(req, recIDs, sort_field='', sort_order='d', sort_pattern='', verbose=0, of='hb', ln=CFG_SITE_LANG):
"""Sort records in 'recIDs' list according sort field 'sort_field' in order 'sort_order'.
If more than one instance of 'sort_field' is found for a given record, try to choose that that is given by
'sort pattern', for example "sort by report number that starts by CERN-PS".
Note that 'sort_field' can be field code like 'author' or MARC tag like '100__a' directly."""
_ = gettext_set_language(ln)
## check arguments:
if not sort_field:
return recIDs
if len(recIDs) > CFG_WEBSEARCH_NB_RECORDS_TO_SORT:
if of.startswith('h'):
print_warning(req, _("Sorry, sorting is allowed on sets of up to %d records only. Using default sort order.") % CFG_WEBSEARCH_NB_RECORDS_TO_SORT, "Warning")
return recIDs
sort_fields = string.split(sort_field, ",")
recIDs_dict = {}
recIDs_out = []
## first deduce sorting MARC tag out of the 'sort_field' argument:
tags = []
for sort_field in sort_fields:
if sort_field and str(sort_field[0:2]).isdigit():
# sort_field starts by two digits, so this is probably a MARC tag already
tags.append(sort_field)
else:
# let us check the 'field' table
query = """SELECT DISTINCT(t.value) FROM tag AS t, field_tag AS ft, field AS f
WHERE f.code=%s AND ft.id_field=f.id AND t.id=ft.id_tag
ORDER BY ft.score DESC"""
res = run_sql(query, (sort_field, ))
if res:
for row in res:
tags.append(row[0])
else:
if of.startswith('h'):
print_warning(req, _("Sorry, %s does not seem to be a valid sort option. Choosing title sort instead.") % cgi.escape(sort_field), "Error")
tags.append("245__a")
if verbose >= 3:
print_warning(req, "Sorting by tags %s." % cgi.escape(repr(tags)))
if sort_pattern:
print_warning(req, "Sorting preferentially by %s." % cgi.escape(sort_pattern))
## check if we have sorting tag defined:
if tags:
# fetch the necessary field values:
for recID in recIDs:
val = "" # will hold value for recID according to which sort
vals = [] # will hold all values found in sorting tag for recID
for tag in tags:
if CFG_CERN_SITE and tag == '773__c':
# CERN hack: journal sorting
# 773__c contains page numbers, e.g. 3-13, and we want to sort by 3, and numerically:
vals.extend(["%050s" % x.split("-",1)[0] for x in get_fieldvalues(recID, tag)])
else:
vals.extend(get_fieldvalues(recID, tag))
if sort_pattern:
# try to pick that tag value that corresponds to sort pattern
bingo = 0
for v in vals:
if v.lower().startswith(sort_pattern.lower()): # bingo!
bingo = 1
val = v
break
if not bingo: # sort_pattern not present, so add other vals after spaces
val = sort_pattern + " " + string.join(vals)
else:
# no sort pattern defined, so join them all together
val = string.join(vals)
val = strip_accents(val.lower()) # sort values regardless of accents and case
if recIDs_dict.has_key(val):
recIDs_dict[val].append(recID)
else:
recIDs_dict[val] = [recID]
# sort them:
recIDs_dict_keys = recIDs_dict.keys()
recIDs_dict_keys.sort()
# now that keys are sorted, create output array:
for k in recIDs_dict_keys:
for s in recIDs_dict[k]:
recIDs_out.append(s)
# ascending or descending?
if sort_order == 'a':
recIDs_out.reverse()
# okay, we are done
return recIDs_out
else:
# good, no sort needed
return recIDs
def print_records(req, recIDs, jrec=1, rg=10, format='hb', ot='', ln=CFG_SITE_LANG, relevances=[], relevances_prologue="(", relevances_epilogue="%%)", decompress=zlib.decompress, search_pattern='', print_records_prologue_p=True, print_records_epilogue_p=True, verbose=0, tab='', sf='', so='d', sp='', rm=''):
"""
Prints list of records 'recIDs' formatted according to 'format' in
groups of 'rg' starting from 'jrec'.
Assumes that the input list 'recIDs' is sorted in reverse order,
so it counts records from tail to head.
A value of 'rg=-9999' means to print all records: to be used with care.
Print also list of RELEVANCES for each record (if defined), in
between RELEVANCE_PROLOGUE and RELEVANCE_EPILOGUE.
Print prologue and/or epilogue specific to 'format' if
'print_records_prologue_p' and/or print_records_epilogue_p' are
True.
'sf' is sort field and 'rm' is ranking method that are passed here
only for proper linking purposes: e.g. when a certain ranking
method or a certain sort field was selected, keep it selected in
any dynamic search links that may be printed.
"""
# load the right message language
_ = gettext_set_language(ln)
# sanity checking:
if req is None:
return
# get user_info (for formatting based on user)
if isinstance(req, cStringIO.OutputType):
user_info = {}
else:
user_info = collect_user_info(req)
if len(recIDs):
nb_found = len(recIDs)
if rg == -9999: # print all records
rg = nb_found
else:
rg = abs(rg)
if jrec < 1: # sanity checks
jrec = 1
if jrec > nb_found:
jrec = max(nb_found-rg+1, 1)
# will print records from irec_max to irec_min excluded:
irec_max = nb_found - jrec
irec_min = nb_found - jrec - rg
if irec_min < 0:
irec_min = -1
if irec_max >= nb_found:
irec_max = nb_found - 1
#req.write("%s:%d-%d" % (recIDs, irec_min, irec_max))
if format.startswith('x'):
# print header if needed
if print_records_prologue_p:
print_records_prologue(req, format)
# print records
recIDs_to_print = [recIDs[x] for x in range(irec_max, irec_min, -1)]
format_records(recIDs_to_print,
format,
ln=ln,
search_pattern=search_pattern,
record_separator="\n",
user_info=user_info,
req=req)
# print footer if needed
if print_records_epilogue_p:
print_records_epilogue(req, format)
elif format.startswith('t') or str(format[0:3]).isdigit():
# we are doing plain text output:
for irec in range(irec_max, irec_min, -1):
x = print_record(recIDs[irec], format, ot, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm)
req.write(x)
if x:
req.write('\n')
elif format == 'excel':
recIDs_to_print = [recIDs[x] for x in range(irec_max, irec_min, -1)]
create_excel(recIDs=recIDs_to_print, req=req, ln=ln, ot=ot)
else:
# we are doing HTML output:
if format == 'hp' or format.startswith("hb_") or format.startswith("hd_"):
# portfolio and on-the-fly formats:
for irec in range(irec_max, irec_min, -1):
req.write(print_record(recIDs[irec], format, ot, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm))
elif format.startswith("hb"):
# HTML brief format:
display_add_to_basket = True
if user_info:
if user_info['email'] == 'guest':
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS > 4:
display_add_to_basket = False
else:
if not user_info['precached_usebaskets']:
display_add_to_basket = False
req.write(websearch_templates.tmpl_record_format_htmlbrief_header(
ln = ln))
for irec in range(irec_max, irec_min, -1):
row_number = jrec+irec_max-irec
recid = recIDs[irec]
if relevances and relevances[irec]:
relevance = relevances[irec]
else:
relevance = ''
record = print_record(recIDs[irec], format, ot, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm)
req.write(websearch_templates.tmpl_record_format_htmlbrief_body(
ln = ln,
recid = recid,
row_number = row_number,
relevance = relevance,
record = record,
relevances_prologue = relevances_prologue,
relevances_epilogue = relevances_epilogue,
display_add_to_basket = display_add_to_basket
))
req.write(websearch_templates.tmpl_record_format_htmlbrief_footer(
ln = ln,
display_add_to_basket = display_add_to_basket))
elif format.startswith("hd"):
# HTML detailed format:
for irec in range(irec_max, irec_min, -1):
if record_exists(recIDs[irec]) == -1:
print_warning(req, _("The record has been deleted."))
continue
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(recIDs[irec])),
recIDs[irec], ln=ln)
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x,y: cmp(x[1],y[1]))
link_ln = ''
if ln != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
recid = recIDs[irec]
recid_to_display = recid # Record ID used to build the URL.
if CFG_WEBSEARCH_USE_ALEPH_SYSNOS:
try:
recid_to_display = get_fieldvalues(recid,
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG)[0]
except IndexError:
# No external sysno is available, keep using
# internal recid.
pass
citedbynum = 0 #num of citations, to be shown in the cit tab
references = -1 #num of references
if CFG_BIBRANK_SHOW_CITATION_LINKS:
citedbynum = get_cited_by_count(recid)
if not CFG_CERN_SITE:#FIXME:should be replaced by something like CFG_SHOW_REFERENCES
reftag = ""
reftags = get_field_tags("reference")
if reftags:
reftag = reftags[0]
tmprec = get_record(recid)
if reftag and len(reftag) > 4:
references = len(record_get_field_instances(tmprec, reftag[0:3], reftag[3], reftag[4]))
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (CFG_SITE_URL, recid_to_display, tab_id, link_ln), \
+ '%s/%s/%s/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, recid_to_display, tab_id, link_ln), \
tab_id == tab,
unordered_tabs[tab_id]['enabled']) \
for (tab_id, order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
# load content
if tab == 'usage':
req.write(webstyle_templates.detailed_record_container_top(recIDs[irec],
tabs,
ln,
citationnum=citedbynum,
referencenum=references))
r = calculate_reading_similarity_list(recIDs[irec], "downloads")
downloadsimilarity = None
downloadhistory = None
#if r:
# downloadsimilarity = r
if CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS:
downloadhistory = create_download_history_graph_and_box(recIDs[irec], ln)
r = calculate_reading_similarity_list(recIDs[irec], "pageviews")
viewsimilarity = None
if r: viewsimilarity = r
content = websearch_templates.tmpl_detailed_record_statistics(recIDs[irec],
ln,
downloadsimilarity=downloadsimilarity,
downloadhistory=downloadhistory,
viewsimilarity=viewsimilarity)
req.write(content)
req.write(webstyle_templates.detailed_record_container_bottom(recIDs[irec],
tabs,
ln))
elif tab == 'citations':
recid = recIDs[irec]
req.write(webstyle_templates.detailed_record_container_top(recid,
tabs,
ln,
citationnum=citedbynum,
referencenum=references))
req.write(websearch_templates.tmpl_detailed_record_citations_prologue(recid, ln))
# Citing
citinglist = calculate_cited_by_list(recid)
req.write(websearch_templates.tmpl_detailed_record_citations_citing_list(recid,
ln,
citinglist,
sf=sf,
so=so,
sp=sp,
rm=rm))
# Self-cited
selfcited = get_self_cited_by(recid)
req.write(websearch_templates.tmpl_detailed_record_citations_self_cited(recid,
ln, selfcited=selfcited, citinglist=citinglist))
# Co-cited
s = calculate_co_cited_with_list(recid)
cociting = None
if s:
cociting = s
req.write(websearch_templates.tmpl_detailed_record_citations_co_citing(recid,
ln,
cociting=cociting))
# Citation history, if needed
citationhistory = None
if citinglist:
citationhistory = create_citation_history_graph_and_box(recid, ln)
#debug
if verbose > 3:
print_warning(req, "Citation graph debug: " + \
str(len(citationhistory)))
req.write(websearch_templates.tmpl_detailed_record_citations_citation_history(recid, ln, citationhistory))
req.write(websearch_templates.tmpl_detailed_record_citations_epilogue(recid, ln))
req.write(webstyle_templates.detailed_record_container_bottom(recid,
tabs,
ln))
elif tab == 'references':
req.write(webstyle_templates.detailed_record_container_top(recIDs[irec],
tabs,
ln,
citationnum=citedbynum,
referencenum=references))
req.write(format_record(recIDs[irec], 'HDREF', ln=ln, user_info=user_info, verbose=verbose))
req.write(webstyle_templates.detailed_record_container_bottom(recIDs[irec],
tabs,
ln))
elif tab == 'keywords':
from invenio.bibclassify_webinterface import \
record_get_keywords, get_sorting_options, \
generate_keywords, get_keywords_body
from invenio.webinterface_handler import wash_urlargd
form = req.form
argd = wash_urlargd(form, {
'generate': (str, 'no'),
'sort': (str, 'occurrences'),
'type': (str, 'tagcloud'),
'numbering': (str, 'off'),
})
recid = recIDs[irec]
req.write(webstyle_templates.detailed_record_container_top(recid,
tabs, ln, citationnum=citedbynum, referencenum=references))
if argd['generate'] == 'yes':
# The user asked to generate the keywords.
keywords = generate_keywords(req, recid)
else:
# Get the keywords contained in the MARC.
keywords = record_get_keywords(recid, argd)
if keywords:
req.write(get_sorting_options(argd, keywords))
elif argd['sort'] == 'related' and not keywords:
req.write('You may want to run BibIndex.')
# Output the keywords or the generate button.
get_keywords_body(keywords, req, recid, argd)
req.write(webstyle_templates.detailed_record_container_bottom(recid,
tabs, ln))
elif tab == 'plots':
req.write(webstyle_templates.detailed_record_container_top(recIDs[irec],
tabs,
ln))
content = websearch_templates.tmpl_record_plots(
recID=recIDs[irec],
ln=ln)
req.write(content)
req.write(webstyle_templates.detailed_record_container_bottom(recIDs[irec],
tabs,
ln))
else:
# Metadata tab
req.write(webstyle_templates.detailed_record_container_top(recIDs[irec],
tabs,
ln,
show_short_rec_p=False,
citationnum=citedbynum, referencenum=references))
creationdate = None
modificationdate = None
if record_exists(recIDs[irec]) == 1:
creationdate = get_creation_date(recIDs[irec])
modificationdate = get_modification_date(recIDs[irec])
content = print_record(recIDs[irec], format, ot, ln,
search_pattern=search_pattern,
user_info=user_info, verbose=verbose,
sf=sf, so=so, sp=sp, rm=rm)
content = websearch_templates.tmpl_detailed_record_metadata(
recID = recIDs[irec],
ln = ln,
format = format,
creationdate = creationdate,
modificationdate = modificationdate,
content = content)
req.write(content)
req.write(webstyle_templates.detailed_record_container_bottom(recIDs[irec],
tabs,
ln,
creationdate=creationdate,
modificationdate=modificationdate,
show_short_rec_p=False))
if len(tabs) > 0:
# Add the mini box at bottom of the page
if CFG_WEBCOMMENT_ALLOW_REVIEWS:
from invenio.webcomment import get_mini_reviews
reviews = get_mini_reviews(recid = recIDs[irec], ln=ln)
else:
reviews = ''
actions = format_record(recIDs[irec], 'HDACT', ln=ln, user_info=user_info, verbose=verbose)
files = format_record(recIDs[irec], 'HDFILE', ln=ln, user_info=user_info, verbose=verbose)
req.write(webstyle_templates.detailed_record_mini_panel(recIDs[irec],
ln,
format,
files=files,
reviews=reviews,
actions=actions))
else:
# Other formats
for irec in range(irec_max, irec_min, -1):
req.write(print_record(recIDs[irec], format, ot, ln,
search_pattern=search_pattern,
user_info=user_info, verbose=verbose,
sf=sf, so=so, sp=sp, rm=rm))
else:
print_warning(req, _("Use different search terms."))
def print_records_prologue(req, format):
"""
Print the appropriate prologue for list of records in the given
format.
"""
prologue = "" # no prologue needed for HTML or Text formats
if format.startswith('xm'):
prologue = websearch_templates.tmpl_xml_marc_prologue()
elif format.startswith('xn'):
prologue = websearch_templates.tmpl_xml_nlm_prologue()
elif format.startswith('xw'):
prologue = websearch_templates.tmpl_xml_refworks_prologue()
elif format.startswith('xr'):
prologue = websearch_templates.tmpl_xml_rss_prologue()
elif format.startswith('xe'):
prologue = websearch_templates.tmpl_xml_endnote_prologue()
elif format.startswith('xo'):
prologue = websearch_templates.tmpl_xml_mods_prologue()
elif format.startswith('xp'):
prologue = websearch_templates.tmpl_xml_podcast_prologue()
elif format.startswith('x'):
prologue = websearch_templates.tmpl_xml_default_prologue()
req.write(prologue)
def print_records_epilogue(req, format):
"""
Print the appropriate epilogue for list of records in the given
format.
"""
epilogue = "" # no epilogue needed for HTML or Text formats
if format.startswith('xm'):
epilogue = websearch_templates.tmpl_xml_marc_epilogue()
elif format.startswith('xn'):
epilogue = websearch_templates.tmpl_xml_nlm_epilogue()
elif format.startswith('xw'):
epilogue = websearch_templates.tmpl_xml_refworks_epilogue()
elif format.startswith('xr'):
epilogue = websearch_templates.tmpl_xml_rss_epilogue()
elif format.startswith('xe'):
epilogue = websearch_templates.tmpl_xml_endnote_epilogue()
elif format.startswith('xo'):
epilogue = websearch_templates.tmpl_xml_mods_epilogue()
elif format.startswith('xp'):
epilogue = websearch_templates.tmpl_xml_podcast_epilogue()
elif format.startswith('x'):
epilogue = websearch_templates.tmpl_xml_default_epilogue()
req.write(epilogue)
def get_record(recid):
"""Directly the record object corresponding to the recid."""
if CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE:
value = run_sql("SELECT value FROM bibfmt WHERE id_bibrec=%s AND FORMAT='recstruct'", (recid, ))
if value:
try:
return deserialize_via_marshal(value[0][0])
except:
### In case of corruption, let's rebuild it!
pass
return create_record(print_record(recid, 'xm'))[0]
def print_record(recID, format='hb', ot='', ln=CFG_SITE_LANG, decompress=zlib.decompress,
search_pattern=None, user_info=None, verbose=0, sf='', so='d', sp='', rm=''):
"""
Prints record 'recID' formatted according to 'format'.
'sf' is sort field and 'rm' is ranking method that are passed here
only for proper linking purposes: e.g. when a certain ranking
method or a certain sort field was selected, keep it selected in
any dynamic search links that may be printed.
"""
if format == 'recstruct':
return get_record(recID)
_ = gettext_set_language(ln)
display_claim_this_paper = False
try:
display_claim_this_paper = user_info["precached_viewclaimlink"]
except (KeyError, TypeError):
display_claim_this_paper = False
#check from user information if the user has the right to see hidden fields/tags in the
#records as well
can_see_hidden = (acc_authorize_action(user_info, 'runbibedit')[0] == 0)
out = ""
# sanity check:
record_exist_p = record_exists(recID)
if record_exist_p == 0: # doesn't exist
return out
# New Python BibFormat procedure for formatting
# Old procedure follows further below
# We must still check some special formats, but these
# should disappear when BibFormat improves.
if not (CFG_BIBFORMAT_USE_OLD_BIBFORMAT \
or format.lower().startswith('t') \
or format.lower().startswith('hm') \
or str(format[0:3]).isdigit() \
or ot):
# Unspecified format is hd
if format == '':
format = 'hd'
if record_exist_p == -1 and get_output_format_content_type(format) == 'text/html':
# HTML output displays a default value for deleted records.
# Other format have to deal with it.
out += _("The record has been deleted.")
else:
out += call_bibformat(recID, format, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
# at the end of HTML brief mode, print the "Detailed record" functionality:
if format.lower().startswith('hb') and \
format.lower() != 'hb_p':
out += websearch_templates.tmpl_print_record_brief_links(ln=ln,
recID=recID,
sf=sf,
so=so,
sp=sp,
rm=rm,
display_claim_link=display_claim_this_paper)
return out
# Old PHP BibFormat procedure for formatting
# print record opening tags, if needed:
if format == "marcxml" or format == "oai_dc":
out += " <record>\n"
out += " <header>\n"
for oai_id in get_fieldvalues(recID, CFG_OAI_ID_FIELD):
out += " <identifier>%s</identifier>\n" % oai_id
out += " <datestamp>%s</datestamp>\n" % get_modification_date(recID)
out += " </header>\n"
out += " <metadata>\n"
if format.startswith("xm") or format == "marcxml":
# look for detailed format existence:
query = "SELECT value FROM bibfmt WHERE id_bibrec=%s AND format=%s"
res = run_sql(query, (recID, format), 1)
if res and record_exist_p == 1:
# record 'recID' is formatted in 'format', so print it
out += "%s" % decompress(res[0][0])
else:
# record 'recID' is not formatted in 'format' -- they are not in "bibfmt" table; so fetch all the data from "bibXXx" tables:
if format == "marcxml":
out += """ <record xmlns="http://www.loc.gov/MARC21/slim">\n"""
out += " <controlfield tag=\"001\">%d</controlfield>\n" % int(recID)
elif format.startswith("xm"):
out += """ <record>\n"""
out += " <controlfield tag=\"001\">%d</controlfield>\n" % int(recID)
if record_exist_p == -1:
# deleted record, so display only OAI ID and 980:
oai_ids = get_fieldvalues(recID, CFG_OAI_ID_FIELD)
if oai_ids:
out += "<datafield tag=\"%s\" ind1=\"%s\" ind2=\"%s\"><subfield code=\"%s\">%s</subfield></datafield>\n" % \
(CFG_OAI_ID_FIELD[0:3], CFG_OAI_ID_FIELD[3:4], CFG_OAI_ID_FIELD[4:5], CFG_OAI_ID_FIELD[5:6], oai_ids[0])
out += "<datafield tag=\"980\" ind1=\"\" ind2=\"\"><subfield code=\"c\">DELETED</subfield></datafield>\n"
else:
# controlfields
query = "SELECT b.tag,b.value,bb.field_number FROM bib00x AS b, bibrec_bib00x AS bb "\
"WHERE bb.id_bibrec=%s AND b.id=bb.id_bibxxx AND b.tag LIKE '00%%' "\
"ORDER BY bb.field_number, b.tag ASC"
res = run_sql(query, (recID, ))
for row in res:
field, value = row[0], row[1]
value = encode_for_xml(value)
out += """ <controlfield tag="%s" >%s</controlfield>\n""" % \
(encode_for_xml(field[0:3]), value)
# datafields
i = 1 # Do not process bib00x and bibrec_bib00x, as
# they are controlfields. So start at bib01x and
# bibrec_bib00x (and set i = 0 at the end of
# first loop)
for digit1 in range(0, 10):
for digit2 in range(i, 10):
bx = "bib%d%dx" % (digit1, digit2)
bibx = "bibrec_bib%d%dx" % (digit1, digit2)
query = "SELECT b.tag,b.value,bb.field_number FROM %s AS b, %s AS bb "\
"WHERE bb.id_bibrec=%%s AND b.id=bb.id_bibxxx AND b.tag LIKE %%s"\
"ORDER BY bb.field_number, b.tag ASC" % (bx, bibx)
res = run_sql(query, (recID, str(digit1)+str(digit2)+'%'))
field_number_old = -999
field_old = ""
for row in res:
field, value, field_number = row[0], row[1], row[2]
ind1, ind2 = field[3], field[4]
if ind1 == "_" or ind1 == "":
ind1 = " "
if ind2 == "_" or ind2 == "":
ind2 = " "
# print field tag, unless hidden
printme = True
if not can_see_hidden:
for htag in CFG_BIBFORMAT_HIDDEN_TAGS:
ltag = len(htag)
samelenfield = field[0:ltag]
if samelenfield == htag:
printme = False
if printme:
if field_number != field_number_old or field[:-1] != field_old[:-1]:
if field_number_old != -999:
out += """ </datafield>\n"""
out += """ <datafield tag="%s" ind1="%s" ind2="%s">\n""" % \
(encode_for_xml(field[0:3]), encode_for_xml(ind1), encode_for_xml(ind2))
field_number_old = field_number
field_old = field
# print subfield value
value = encode_for_xml(value)
out += """ <subfield code="%s">%s</subfield>\n""" % \
(encode_for_xml(field[-1:]), value)
# all fields/subfields printed in this run, so close the tag:
if field_number_old != -999:
out += """ </datafield>\n"""
i = 0 # Next loop should start looking at bib%0 and bibrec_bib00x
# we are at the end of printing the record:
out += " </record>\n"
elif format == "xd" or format == "oai_dc":
# XML Dublin Core format, possibly OAI -- select only some bibXXx fields:
out += """ <dc xmlns="http://purl.org/dc/elements/1.1/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://purl.org/dc/elements/1.1/
http://www.openarchives.org/OAI/1.1/dc.xsd">\n"""
if record_exist_p == -1:
out += ""
else:
for f in get_fieldvalues(recID, "041__a"):
out += " <language>%s</language>\n" % f
for f in get_fieldvalues(recID, "100__a"):
out += " <creator>%s</creator>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "700__a"):
out += " <creator>%s</creator>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "245__a"):
out += " <title>%s</title>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "65017a"):
out += " <subject>%s</subject>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "8564_u"):
if f.split('.') == 'png':
continue
out += " <identifier>%s</identifier>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "520__a"):
out += " <description>%s</description>\n" % encode_for_xml(f)
out += " <date>%s</date>\n" % get_creation_date(recID)
out += " </dc>\n"
elif len(format) == 6 and str(format[0:3]).isdigit():
# user has asked to print some fields only
if format == "001":
out += "<!--%s-begin-->%s<!--%s-end-->\n" % (format, recID, format)
else:
vals = get_fieldvalues(recID, format)
for val in vals:
out += "<!--%s-begin-->%s<!--%s-end-->\n" % (format, val, format)
elif format.startswith('t'):
## user directly asked for some tags to be displayed only
if record_exist_p == -1:
out += get_fieldvalues_alephseq_like(recID, ["001", CFG_OAI_ID_FIELD, "980"], can_see_hidden)
else:
out += get_fieldvalues_alephseq_like(recID, ot, can_see_hidden)
elif format == "hm":
if record_exist_p == -1:
out += "\n<pre>" + cgi.escape(get_fieldvalues_alephseq_like(recID, ["001", CFG_OAI_ID_FIELD, "980"], can_see_hidden)) + "</pre>"
else:
out += "\n<pre>" + cgi.escape(get_fieldvalues_alephseq_like(recID, ot, can_see_hidden)) + "</pre>"
elif format.startswith("h") and ot:
## user directly asked for some tags to be displayed only
if record_exist_p == -1:
out += "\n<pre>" + get_fieldvalues_alephseq_like(recID, ["001", CFG_OAI_ID_FIELD, "980"], can_see_hidden) + "</pre>"
else:
out += "\n<pre>" + get_fieldvalues_alephseq_like(recID, ot, can_see_hidden) + "</pre>"
elif format == "hd":
# HTML detailed format
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
# look for detailed format existence:
query = "SELECT value FROM bibfmt WHERE id_bibrec=%s AND format=%s"
res = run_sql(query, (recID, format), 1)
if res:
# record 'recID' is formatted in 'format', so print it
out += "%s" % decompress(res[0][0])
else:
# record 'recID' is not formatted in 'format', so try to call BibFormat on the fly or use default format:
out_record_in_format = call_bibformat(recID, format, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
if out_record_in_format:
out += out_record_in_format
else:
out += websearch_templates.tmpl_print_record_detailed(
ln = ln,
recID = recID,
)
elif format.startswith("hb_") or format.startswith("hd_"):
# underscore means that HTML brief/detailed formats should be called on-the-fly; suitable for testing formats
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
out += call_bibformat(recID, format, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
elif format.startswith("hx"):
# BibTeX format, called on the fly:
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
out += call_bibformat(recID, format, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
elif format.startswith("hs"):
# for citation/download similarity navigation links:
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
out += '<a href="%s">' % websearch_templates.build_search_url(recid=recID, ln=ln)
# firstly, title:
titles = get_fieldvalues(recID, "245__a")
if titles:
for title in titles:
out += "<strong>%s</strong>" % title
else:
# usual title not found, try conference title:
titles = get_fieldvalues(recID, "111__a")
if titles:
for title in titles:
out += "<strong>%s</strong>" % title
else:
# just print record ID:
out += "<strong>%s %d</strong>" % (get_field_i18nname("record ID", ln, False), recID)
out += "</a>"
# secondly, authors:
authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")
if authors:
out += " - %s" % authors[0]
if len(authors) > 1:
out += " <em>et al</em>"
# thirdly publication info:
publinfos = get_fieldvalues(recID, "773__s")
if not publinfos:
publinfos = get_fieldvalues(recID, "909C4s")
if not publinfos:
publinfos = get_fieldvalues(recID, "037__a")
if not publinfos:
publinfos = get_fieldvalues(recID, "088__a")
if publinfos:
out += " - %s" % publinfos[0]
else:
# fourthly publication year (if not publication info):
years = get_fieldvalues(recID, "773__y")
if not years:
years = get_fieldvalues(recID, "909C4y")
if not years:
years = get_fieldvalues(recID, "260__c")
if years:
out += " (%s)" % years[0]
else:
# HTML brief format by default
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
query = "SELECT value FROM bibfmt WHERE id_bibrec=%s AND format=%s"
res = run_sql(query, (recID, format))
if res:
# record 'recID' is formatted in 'format', so print it
out += "%s" % decompress(res[0][0])
else:
# record 'recID' is not formatted in 'format', so try to call BibFormat on the fly: or use default format:
if CFG_WEBSEARCH_CALL_BIBFORMAT:
out_record_in_format = call_bibformat(recID, format, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
if out_record_in_format:
out += out_record_in_format
else:
out += websearch_templates.tmpl_print_record_brief(
ln = ln,
recID = recID,
)
else:
out += websearch_templates.tmpl_print_record_brief(
ln = ln,
recID = recID,
)
# at the end of HTML brief mode, print the "Detailed record" functionality:
if format == 'hp' or format.startswith("hb_") or format.startswith("hd_"):
pass # do nothing for portfolio and on-the-fly formats
else:
out += websearch_templates.tmpl_print_record_brief_links(ln=ln,
recID=recID,
sf=sf,
so=so,
sp=sp,
rm=rm,
display_claim_link=display_claim_this_paper)
# print record closing tags, if needed:
if format == "marcxml" or format == "oai_dc":
out += " </metadata>\n"
out += " </record>\n"
return out
def call_bibformat(recID, format="HD", ln=CFG_SITE_LANG, search_pattern=None, user_info=None, verbose=0):
"""
Calls BibFormat and returns formatted record.
BibFormat will decide by itself if old or new BibFormat must be used.
"""
from invenio.bibformat_utils import get_pdf_snippets
keywords = []
if search_pattern is not None:
units = create_basic_search_units(None, str(search_pattern), None)
keywords = [unit[1] for unit in units if (unit[0] != '-' and unit[2] in [None, 'fulltext'])]
out = format_record(recID,
of=format,
ln=ln,
search_pattern=keywords,
user_info=user_info,
verbose=verbose)
if CFG_WEBSEARCH_FULLTEXT_SNIPPETS and user_info and \
'fulltext' in user_info['uri']:
# check snippets only if URL contains fulltext
# FIXME: make it work for CLI too, via new function arg
if keywords:
snippets = get_pdf_snippets(recID, keywords)
if snippets:
out += snippets
return out
def log_query(hostname, query_args, uid=-1):
"""
Log query into the query and user_query tables.
Return id_query or None in case of problems.
"""
id_query = None
if uid >= 0:
# log the query only if uid is reasonable
res = run_sql("SELECT id FROM query WHERE urlargs=%s", (query_args,), 1)
try:
id_query = res[0][0]
except:
id_query = run_sql("INSERT INTO query (type, urlargs) VALUES ('r', %s)", (query_args,))
if id_query:
run_sql("INSERT INTO user_query (id_user, id_query, hostname, date) VALUES (%s, %s, %s, %s)",
(uid, id_query, hostname,
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
return id_query
def log_query_info(action, p, f, colls, nb_records_found_total=-1):
"""Write some info to the log file for later analysis."""
try:
log = open(CFG_LOGDIR + "/search.log", "a")
log.write(time.strftime("%Y%m%d%H%M%S#", time.localtime()))
log.write(action+"#")
log.write(p+"#")
log.write(f+"#")
for coll in colls[:-1]:
log.write("%s," % coll)
log.write("%s#" % colls[-1])
log.write("%d" % nb_records_found_total)
log.write("\n")
log.close()
except:
pass
return
### CALLABLES
def perform_request_search(req=None, cc=CFG_SITE_NAME, c=None, p="", f="", rg=CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS, sf="", so="d", sp="", rm="", of="id", ot="", aas=0,
p1="", f1="", m1="", op1="", p2="", f2="", m2="", op2="", p3="", f3="", m3="", sc=0, jrec=0,
recid=-1, recidb=-1, sysno="", id=-1, idb=-1, sysnb="", action="", d1="",
d1y=0, d1m=0, d1d=0, d2="", d2y=0, d2m=0, d2d=0, dt="", verbose=0, ap=0, ln=CFG_SITE_LANG, ec=None, tab="", wl=CFG_WEBSEARCH_WILDCARD_LIMIT):
"""Perform search or browse request, without checking for
authentication. Return list of recIDs found, if of=id.
Otherwise create web page.
The arguments are as follows:
req - mod_python Request class instance.
cc - current collection (e.g. "ATLAS"). The collection the
user started to search/browse from.
c - collection list (e.g. ["Theses", "Books"]). The
collections user may have selected/deselected when
starting to search from 'cc'.
p - pattern to search for (e.g. "ellis and muon or kaon").
f - field to search within (e.g. "author").
rg - records in groups of (e.g. "10"). Defines how many hits
per collection in the search results page are
displayed.
sf - sort field (e.g. "title").
so - sort order ("a"=ascending, "d"=descending).
sp - sort pattern (e.g. "CERN-") -- in case there are more
values in a sort field, this argument tells which one
to prefer
rm - ranking method (e.g. "jif"). Defines whether results
should be ranked by some known ranking method.
of - output format (e.g. "hb"). Usually starting "h" means
HTML output (and "hb" for HTML brief, "hd" for HTML
detailed), "x" means XML output, "t" means plain text
output, "id" means no output at all but to return list
of recIDs found. (Suitable for high-level API.)
ot - output only these MARC tags (e.g. "100,700,909C0b").
Useful if only some fields are to be shown in the
output, e.g. for library to control some fields.
aas - advanced search ("0" means no, "1" means yes). Whether
search was called from within the advanced search
interface.
p1 - first pattern to search for in the advanced search
interface. Much like 'p'.
f1 - first field to search within in the advanced search
interface. Much like 'f'.
m1 - first matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
op1 - first operator, to join the first and the second unit
in the advanced search interface. ("a" add, "o" or,
"n" not).
p2 - second pattern to search for in the advanced search
interface. Much like 'p'.
f2 - second field to search within in the advanced search
interface. Much like 'f'.
m2 - second matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
op2 - second operator, to join the second and the third unit
in the advanced search interface. ("a" add, "o" or,
"n" not).
p3 - third pattern to search for in the advanced search
interface. Much like 'p'.
f3 - third field to search within in the advanced search
interface. Much like 'f'.
m3 - third matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
sc - split by collection ("0" no, "1" yes). Governs whether
we want to present the results in a single huge list,
or splitted by collection.
jrec - jump to record (e.g. "234"). Used for navigation
inside the search results.
recid - display record ID (e.g. "20000"). Do not
search/browse but go straight away to the Detailed
record page for the given recID.
recidb - display record ID bis (e.g. "20010"). If greater than
'recid', then display records from recid to recidb.
Useful for example for dumping records from the
database for reformatting.
sysno - display old system SYS number (e.g. ""). If you
migrate to Invenio from another system, and store your
old SYS call numbers, you can use them instead of recid
if you wish so.
id - the same as recid, in case recid is not set. For
backwards compatibility.
idb - the same as recid, in case recidb is not set. For
backwards compatibility.
sysnb - the same as sysno, in case sysno is not set. For
backwards compatibility.
action - action to do. "SEARCH" for searching, "Browse" for
browsing. Default is to search.
d1 - first datetime in full YYYY-mm-dd HH:MM:DD format
(e.g. "1998-08-23 12:34:56"). Useful for search limits
on creation/modification date (see 'dt' argument
below). Note that 'd1' takes precedence over d1y, d1m,
d1d if these are defined.
d1y - first date's year (e.g. "1998"). Useful for search
limits on creation/modification date.
d1m - first date's month (e.g. "08"). Useful for search
limits on creation/modification date.
d1d - first date's day (e.g. "23"). Useful for search
limits on creation/modification date.
d2 - second datetime in full YYYY-mm-dd HH:MM:DD format
(e.g. "1998-09-02 12:34:56"). Useful for search limits
on creation/modification date (see 'dt' argument
below). Note that 'd2' takes precedence over d2y, d2m,
d2d if these are defined.
d2y - second date's year (e.g. "1998"). Useful for search
limits on creation/modification date.
d2m - second date's month (e.g. "09"). Useful for search
limits on creation/modification date.
d2d - second date's day (e.g. "02"). Useful for search
limits on creation/modification date.
dt - first and second date's type (e.g. "c"). Specifies
whether to search in creation dates ("c") or in
modification dates ("m"). When dt is not set and d1*
and d2* are set, the default is "c".
verbose - verbose level (0=min, 9=max). Useful to print some
internal information on the searching process in case
something goes wrong.
ap - alternative patterns (0=no, 1=yes). In case no exact
match is found, the search engine can try alternative
patterns e.g. to replace non-alphanumeric characters by
a boolean query. ap defines if this is wanted.
ln - language of the search interface (e.g. "en"). Useful
for internationalization.
ec - list of external search engines to search as well
(e.g. "SPIRES HEP").
wl - wildcard limit (ex: 100) the wildcard queries will be
limited at 100 results
"""
selected_external_collections_infos = None
# wash output format:
of = wash_output_format(of)
# raise an exception when trying to print out html from the cli
if of.startswith("h"):
assert req
# for every search engine request asking for an HTML output, we
# first regenerate cache of collection and field I18N names if
# needed; so that later we won't bother checking timestamps for
# I18N names at all:
if of.startswith("h"):
collection_i18nname_cache.recreate_cache_if_needed()
field_i18nname_cache.recreate_cache_if_needed()
# wash all arguments requiring special care
try:
(cc, colls_to_display, colls_to_search, hosted_colls, wash_colls_debug) = wash_colls(cc, c, sc, verbose) # which colls to search and to display?
except InvenioWebSearchUnknownCollectionError, exc:
colname = exc.colname
if of.startswith("h"):
page_start(req, of, cc, aas, ln, getUid(req),
websearch_templates.tmpl_collection_not_found_page_title(colname, ln))
req.write(websearch_templates.tmpl_collection_not_found_page_body(colname, ln))
return page_end(req, of, ln)
elif of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
else:
return page_end(req, of, ln)
p = wash_pattern(p)
f = wash_field(f)
p1 = wash_pattern(p1)
f1 = wash_field(f1)
p2 = wash_pattern(p2)
f2 = wash_field(f2)
p3 = wash_pattern(p3)
f3 = wash_field(f3)
datetext1, datetext2 = wash_dates(d1, d1y, d1m, d1d, d2, d2y, d2m, d2d)
# wash ranking method:
if not is_method_valid(None, rm):
rm = ""
_ = gettext_set_language(ln)
# backwards compatibility: id, idb, sysnb -> recid, recidb, sysno (if applicable)
if sysnb != "" and sysno == "":
sysno = sysnb
if id > 0 and recid == -1:
recid = id
if idb > 0 and recidb == -1:
recidb = idb
# TODO deduce passed search limiting criterias (if applicable)
pl, pl_in_url = "", "" # no limits by default
if action != "browse" and req and not isinstance(req, cStringIO.OutputType) \
and req.args: # we do not want to add options while browsing or while calling via command-line
fieldargs = cgi.parse_qs(req.args)
for fieldcode in get_fieldcodes():
if fieldargs.has_key(fieldcode):
for val in fieldargs[fieldcode]:
pl += "+%s:\"%s\" " % (fieldcode, val)
pl_in_url += "&amp;%s=%s" % (urllib.quote(fieldcode), urllib.quote(val))
# deduce recid from sysno argument (if applicable):
if sysno: # ALEPH SYS number was passed, so deduce DB recID for the record:
recid = get_mysql_recid_from_aleph_sysno(sysno)
if recid is None:
recid = 0 # use recid 0 to indicate that this sysno does not exist
# deduce collection we are in (if applicable):
if recid > 0:
referer = None
if req:
referer = req.headers_in.get('Referer')
cc = guess_collection_of_a_record(recid, referer)
# deduce user id (if applicable):
try:
uid = getUid(req)
except:
uid = 0
## 0 - start output
if recid >= 0: # recid can be 0 if deduced from sysno and if such sysno does not exist
## 1 - detailed record display
title, description, keywords = \
websearch_templates.tmpl_record_page_header_content(req, recid, ln)
if req is not None and not req.header_only:
page_start(req, of, cc, aas, ln, uid, title, description, keywords, recid, tab)
# Default format is hb but we are in detailed -> change 'of'
if of == "hb":
of = "hd"
if record_exists(recid):
if recidb <= recid: # sanity check
recidb = recid + 1
if of == "id":
return [recidx for recidx in range(recid, recidb) if record_exists(recidx)]
else:
print_records(req, range(recid, recidb), -1, -9999, of, ot, ln, search_pattern=p, verbose=verbose, tab=tab, sf=sf, so=so, sp=sp, rm=rm)
if req and of.startswith("h"): # register detailed record page view event
client_ip_address = str(req.remote_ip)
register_page_view_event(recid, uid, client_ip_address)
else: # record does not exist
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
elif of.startswith("h"):
if req.header_only:
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
else:
print_warning(req, _("Requested record does not seem to exist."))
elif action == "browse":
## 2 - browse needed
of = 'hb'
page_start(req, of, cc, aas, ln, uid, _("Browse"), p=create_page_title_search_pattern_info(p, p1, p2, p3))
req.write(create_search_box(cc, colls_to_display, p, f, rg, sf, so, sp, rm, of, ot, aas, ln, p1, f1, m1, op1,
p2, f2, m2, op2, p3, f3, m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec, action))
try:
if aas == 1 or (p1 or p2 or p3):
browse_pattern(req, colls_to_search, p1, f1, rg, ln)
browse_pattern(req, colls_to_search, p2, f2, rg, ln)
browse_pattern(req, colls_to_search, p3, f3, rg, ln)
else:
browse_pattern(req, colls_to_search, p, f, rg, ln)
except:
register_exception(req=req, alert_admin=True)
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
elif rm and p.startswith("recid:"):
## 3-ter - similarity search (or old-style citation search) needed
if req and not req.header_only:
page_start(req, of, cc, aas, ln, uid, _("Search Results"), p=create_page_title_search_pattern_info(p, p1, p2, p3))
if of.startswith("h"):
req.write(create_search_box(cc, colls_to_display, p, f, rg, sf, so, sp, rm, of, ot, aas, ln, p1, f1, m1, op1,
p2, f2, m2, op2, p3, f3, m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec, action))
if record_exists(p[6:]) != 1:
# record does not exist
if of.startswith("h"):
if req.header_only:
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
else:
print_warning(req, _("Requested record does not seem to exist."))
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
# record well exists, so find similar ones to it
t1 = os.times()[4]
results_similar_recIDs, results_similar_relevances, results_similar_relevances_prologue, results_similar_relevances_epilogue, results_similar_comments = \
rank_records(rm, 0, get_collection_reclist(cc), string.split(p), verbose)
if results_similar_recIDs:
t2 = os.times()[4]
cpu_time = t2 - t1
if of.startswith("h"):
req.write(print_search_info(p, f, sf, so, sp, rm, of, ot, cc, len(results_similar_recIDs),
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
print_warning(req, results_similar_comments)
print_records(req, results_similar_recIDs, jrec, rg, of, ot, ln,
results_similar_relevances, results_similar_relevances_prologue, results_similar_relevances_epilogue, search_pattern=p, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm)
elif of=="id":
return results_similar_recIDs
elif of.startswith("x"):
print_records(req, results_similar_recIDs, jrec, rg, of, ot, ln,
results_similar_relevances, results_similar_relevances_prologue, results_similar_relevances_epilogue, search_pattern=p, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm)
else:
# rank_records failed and returned some error message to display:
if of.startswith("h"):
print_warning(req, results_similar_relevances_prologue)
print_warning(req, results_similar_relevances_epilogue)
print_warning(req, results_similar_comments)
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
elif p.startswith("cocitedwith:"): #WAS EXPERIMENTAL
## 3-terter - cited by search needed
page_start(req, of, cc, aas, ln, uid, _("Search Results"), p=create_page_title_search_pattern_info(p, p1, p2, p3))
if of.startswith("h"):
req.write(create_search_box(cc, colls_to_display, p, f, rg, sf, so, sp, rm, of, ot, aas, ln, p1, f1, m1, op1,
p2, f2, m2, op2, p3, f3, m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec, action))
recID = p[12:]
if record_exists(recID) != 1:
# record does not exist
if of.startswith("h"):
print_warning(req, _("Requested record does not seem to exist."))
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
# record well exists, so find co-cited ones:
t1 = os.times()[4]
results_cocited_recIDs = map(lambda x: x[0], calculate_co_cited_with_list(int(recID)))
if results_cocited_recIDs:
t2 = os.times()[4]
cpu_time = t2 - t1
if of.startswith("h"):
req.write(print_search_info(p, f, sf, so, sp, rm, of, ot, CFG_SITE_NAME, len(results_cocited_recIDs),
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
print_records(req, results_cocited_recIDs, jrec, rg, of, ot, ln, search_pattern=p, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm)
elif of=="id":
return results_cocited_recIDs
elif of.startswith("x"):
print_records(req, results_cocited_recIDs, jrec, rg, of, ot, ln, search_pattern=p, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm)
else:
# cited rank_records failed and returned some error message to display:
if of.startswith("h"):
print_warning(req, "nothing found")
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
## 3 - common search needed
query_in_cache = False
query_representation_in_cache = repr((p,f,colls_to_search, wl))
page_start(req, of, cc, aas, ln, uid, p=create_page_title_search_pattern_info(p, p1, p2, p3))
if of.startswith("h") and verbose and wash_colls_debug:
print_warning(req, "wash_colls debugging info : %s" % wash_colls_debug)
# search into the hosted collections only if the output format is html or xml
if hosted_colls and (of.startswith("h") or of.startswith("x")) and not p.startswith("recid:"):
# hosted_colls_results : the hosted collections' searches that did not timeout
# hosted_colls_timeouts : the hosted collections' searches that timed out and will be searched later on again
(hosted_colls_results, hosted_colls_timeouts) = calculate_hosted_collections_results(req, [p, p1, p2, p3], f, hosted_colls, verbose, ln, CFG_HOSTED_COLLECTION_TIMEOUT_ANTE_SEARCH)
# successful searches
if hosted_colls_results:
hosted_colls_true_results = []
for result in hosted_colls_results:
# if the number of results is None or 0 (or False) then just do nothing
if result[1] == None or result[1] == False:
# these are the searches the returned no or zero results
if verbose:
print_warning(req, "Hosted collections (perform_search_request): %s returned no results" % result[0][1].name)
else:
# these are the searches that actually returned results on time
hosted_colls_true_results.append(result)
if verbose:
print_warning(req, "Hosted collections (perform_search_request): %s returned %s results in %s seconds" % (result[0][1].name, result[1], result[2]))
else:
if verbose:
print_warning(req, "Hosted collections (perform_search_request): there were no hosted collections results to be printed at this time")
if hosted_colls_timeouts:
if verbose:
for timeout in hosted_colls_timeouts:
print_warning(req, "Hosted collections (perform_search_request): %s timed out and will be searched again later" % timeout[0][1].name)
# we need to know for later use if there were any hosted collections to be searched even if they weren't in the end
elif hosted_colls and ((not (of.startswith("h") or of.startswith("x"))) or p.startswith("recid:")):
(hosted_colls_results, hosted_colls_timeouts) = (None, None)
else:
if verbose:
print_warning(req, "Hosted collections (perform_search_request): there were no hosted collections to be searched")
## let's define some useful boolean variables:
# True means there are actual or potential hosted collections results to be printed
hosted_colls_actual_or_potential_results_p = not (not hosted_colls or not ((hosted_colls_results and hosted_colls_true_results) or hosted_colls_timeouts))
# True means there are hosted collections timeouts to take care of later
# (useful for more accurate printing of results later)
hosted_colls_potential_results_p = not (not hosted_colls or not hosted_colls_timeouts)
# True means we only have hosted collections to deal with
only_hosted_colls_actual_or_potential_results_p = not colls_to_search and hosted_colls_actual_or_potential_results_p
if of.startswith("h"):
req.write(create_search_box(cc, colls_to_display, p, f, rg, sf, so, sp, rm, of, ot, aas, ln, p1, f1, m1, op1,
p2, f2, m2, op2, p3, f3, m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec, action))
t1 = os.times()[4]
results_in_any_collection = HitSet()
if aas == 1 or (p1 or p2 or p3):
## 3A - advanced search
try:
results_in_any_collection = search_pattern_parenthesised(req, p1, f1, m1, ap=ap, of=of, verbose=verbose, ln=ln, wl=wl)
if len(results_in_any_collection) == 0:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
if p2:
results_tmp = search_pattern_parenthesised(req, p2, f2, m2, ap=ap, of=of, verbose=verbose, ln=ln, wl=wl)
if op1 == "a": # add
results_in_any_collection.intersection_update(results_tmp)
elif op1 == "o": # or
results_in_any_collection.union_update(results_tmp)
elif op1 == "n": # not
results_in_any_collection.difference_update(results_tmp)
else:
if of.startswith("h"):
print_warning(req, "Invalid set operation %s." % cgi.escape(op1), "Error")
if len(results_in_any_collection) == 0:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
if p3:
results_tmp = search_pattern_parenthesised(req, p3, f3, m3, ap=ap, of=of, verbose=verbose, ln=ln, wl=wl)
if op2 == "a": # add
results_in_any_collection.intersection_update(results_tmp)
elif op2 == "o": # or
results_in_any_collection.union_update(results_tmp)
elif op2 == "n": # not
results_in_any_collection.difference_update(results_tmp)
else:
if of.startswith("h"):
print_warning(req, "Invalid set operation %s." % cgi.escape(op2), "Error")
except:
register_exception(req=req, alert_admin=True)
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
else:
## 3B - simple search
if search_results_cache.cache.has_key(query_representation_in_cache):
# query is not in the cache already, so reuse it:
query_in_cache = True
results_in_any_collection = search_results_cache.cache[query_representation_in_cache]
if verbose and of.startswith("h"):
print_warning(req, "Search stage 0: query found in cache, reusing cached results.")
else:
try:
# added the display_nearest_terms_box parameter to avoid printing out the "Nearest terms in any collection"
# recommendations when there are results only in the hosted collections. Also added the if clause to avoid
# searching in case we know we only have actual or potential hosted collections results
if not only_hosted_colls_actual_or_potential_results_p:
results_in_any_collection = search_pattern_parenthesised(req, p, f, ap=ap, of=of, verbose=verbose, ln=ln, display_nearest_terms_box=not hosted_colls_actual_or_potential_results_p, wl=wl)
except:
register_exception(req=req, alert_admin=True)
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if len(results_in_any_collection) == 0 and not hosted_colls_actual_or_potential_results_p:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
# store this search query results into search results cache if needed:
if CFG_WEBSEARCH_SEARCH_CACHE_SIZE and not query_in_cache:
if len(search_results_cache.cache) > CFG_WEBSEARCH_SEARCH_CACHE_SIZE:
search_results_cache.clear()
search_results_cache.cache[query_representation_in_cache] = results_in_any_collection
if verbose and of.startswith("h"):
print_warning(req, "Search stage 3: storing query results in cache.")
# search stage 4: intersection with collection universe:
try:
# added the display_nearest_terms_box parameter to avoid printing out the "Nearest terms in any collection"
# recommendations when there results only in the hosted collections. Also added the if clause to avoid
# searching in case we know since the last stage that we have no results in any collection
if len(results_in_any_collection) != 0:
results_final = intersect_results_with_collrecs(req, results_in_any_collection, colls_to_search, ap, of, verbose, ln, display_nearest_terms_box=not hosted_colls_actual_or_potential_results_p)
else:
results_final = {}
except:
register_exception(req=req, alert_admin=True)
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if results_final == {} and not hosted_colls_actual_or_potential_results_p:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
if of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
# search stage 5: apply search option limits and restrictions:
if datetext1 != "" and results_final != {}:
if verbose and of.startswith("h"):
print_warning(req, "Search stage 5: applying time etc limits, from %s until %s..." % (datetext1, datetext2))
try:
results_final = intersect_results_with_hitset(req,
results_final,
search_unit_in_bibrec(datetext1, datetext2, dt),
ap,
aptext= _("No match within your time limits, "
"discarding this condition..."),
of=of)
except:
register_exception(req=req, alert_admin=True)
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if results_final == {} and not hosted_colls_actual_or_potential_results_p:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
#if of.startswith("x"):
# # Print empty, but valid XML
# print_records_prologue(req, of)
# print_records_epilogue(req, of)
return page_end(req, of, ln)
if pl and results_final != {}:
pl = wash_pattern(pl)
if verbose and of.startswith("h"):
print_warning(req, "Search stage 5: applying search pattern limit %s..." % cgi.escape(pl))
try:
results_final = intersect_results_with_hitset(req,
results_final,
search_pattern_parenthesised(req, pl, ap=0, ln=ln, wl=wl),
ap,
aptext=_("No match within your search limits, "
"discarding this condition..."),
of=of)
except:
register_exception(req=req, alert_admin=True)
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if results_final == {} and not hosted_colls_actual_or_potential_results_p:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
if of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
t2 = os.times()[4]
cpu_time = t2 - t1
## search stage 6: display results:
results_final_nb_total = 0
results_final_nb = {} # will hold number of records found in each collection
# (in simple dict to display overview more easily)
for coll in results_final.keys():
results_final_nb[coll] = len(results_final[coll])
#results_final_nb_total += results_final_nb[coll]
# Now let us calculate results_final_nb_total more precisely,
# in order to get the total number of "distinct" hits across
# searched collections; this is useful because a record might
# have been attributed to more than one primary collection; so
# we have to avoid counting it multiple times. The price to
# pay for this accuracy of results_final_nb_total is somewhat
# increased CPU time.
if results_final.keys() == 1:
# only one collection; no need to union them
results_final_for_all_selected_colls = results_final.values()[0]
results_final_nb_total = results_final_nb.values()[0]
else:
# okay, some work ahead to union hits across collections:
results_final_for_all_selected_colls = HitSet()
for coll in results_final.keys():
results_final_for_all_selected_colls.union_update(results_final[coll])
results_final_nb_total = len(results_final_for_all_selected_colls)
#if hosted_colls and (of.startswith("h") or of.startswith("x")):
if hosted_colls_actual_or_potential_results_p:
if hosted_colls_results:
for result in hosted_colls_true_results:
colls_to_search.append(result[0][1].name)
results_final_nb[result[0][1].name] = result[1]
results_final_nb_total += result[1]
cpu_time += result[2]
if hosted_colls_timeouts:
for timeout in hosted_colls_timeouts:
colls_to_search.append(timeout[1].name)
# use -963 as a special number to identify the collections that timed out
results_final_nb[timeout[1].name] = -963
# we continue past this point only if there is a hosted collection that has timed out and might offer potential results
if results_final_nb_total ==0 and not hosted_colls_potential_results_p:
if of.startswith("h"):
print_warning(req, "No match found, please enter different search terms.")
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
# yes, some hits found: good!
# collection list may have changed due to not-exact-match-found policy so check it out:
for coll in results_final.keys():
if coll not in colls_to_search:
colls_to_search.append(coll)
# print results overview:
if of == "id":
# we have been asked to return list of recIDs
recIDs = list(results_final_for_all_selected_colls)
if sf: # do we have to sort?
recIDs = sort_records(req, recIDs, sf, so, sp, verbose, of)
elif rm: # do we have to rank?
results_final_for_all_colls_rank_records_output = rank_records(rm, 0, results_final_for_all_selected_colls,
string.split(p) + string.split(p1) +
string.split(p2) + string.split(p3), verbose)
if results_final_for_all_colls_rank_records_output[0]:
recIDs = results_final_for_all_colls_rank_records_output[0]
return recIDs
elif of.startswith("h"):
if of not in ['hcs']:
# added the hosted_colls_potential_results_p parameter to help print out the overview more accurately
req.write(print_results_overview(colls_to_search, results_final_nb_total, results_final_nb, cpu_time, ln, ec, hosted_colls_potential_results_p=hosted_colls_potential_results_p))
selected_external_collections_infos = print_external_results_overview(req, cc, [p, p1, p2, p3], f, ec, verbose, ln)
# print number of hits found for XML outputs:
if of.startswith("x"):
req.write("<!-- Search-Engine-Total-Number-Of-Results: %s -->\n" % results_final_nb_total)
# print records:
if of in ['hcs']:
# feed the current search to be summarized:
from invenio.search_engine_summarizer import summarize_records
search_p = p
search_f = f
if not p and (aas == 1 or p1 or p2 or p3):
op_d = {'n': ' and not ', 'a': ' and ', 'o': ' or ', '': ''}
triples = ziplist([f1, f2, f3], [p1, p2, p3], [op1, op2, ''])
triples_len = len(triples)
for i in range(triples_len):
fi, pi, oi = triples[i] # e.g.:
if i < triples_len-1 and not triples[i+1][1]: # if p2 empty
triples[i+1][0] = '' # f2 must be too
oi = '' # and o1
if ' ' in pi:
pi = '"'+pi+'"'
if fi:
fi = fi + ':'
search_p += fi + pi + op_d[oi]
search_f = ''
summarize_records(results_final_for_all_selected_colls, 'hcs', ln, search_p, search_f, req)
else:
if len(colls_to_search)>1:
cpu_time = -1 # we do not want to have search time printed on each collection
print_records_prologue(req, of)
for coll in colls_to_search:
if results_final.has_key(coll) and len(results_final[coll]):
if of.startswith("h"):
req.write(print_search_info(p, f, sf, so, sp, rm, of, ot, coll, results_final_nb[coll],
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
results_final_recIDs = list(results_final[coll])
results_final_relevances = []
results_final_relevances_prologue = ""
results_final_relevances_epilogue = ""
if sf: # do we have to sort?
results_final_recIDs = sort_records(req, results_final_recIDs, sf, so, sp, verbose, of)
elif rm: # do we have to rank?
results_final_recIDs_ranked, results_final_relevances, results_final_relevances_prologue, results_final_relevances_epilogue, results_final_comments = \
rank_records(rm, 0, results_final[coll],
string.split(p) + string.split(p1) +
string.split(p2) + string.split(p3), verbose)
if of.startswith("h"):
print_warning(req, results_final_comments)
if results_final_recIDs_ranked:
results_final_recIDs = results_final_recIDs_ranked
else:
# rank_records failed and returned some error message to display:
print_warning(req, results_final_relevances_prologue)
print_warning(req, results_final_relevances_epilogue)
print_records(req, results_final_recIDs, jrec, rg, of, ot, ln,
results_final_relevances,
results_final_relevances_prologue,
results_final_relevances_epilogue,
search_pattern=p,
print_records_prologue_p=False,
print_records_epilogue_p=False,
verbose=verbose,
sf=sf,
so=so,
sp=sp,
rm=rm)
if of.startswith("h"):
req.write(print_search_info(p, f, sf, so, sp, rm, of, ot, coll, results_final_nb[coll],
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time, 1))
#if hosted_colls and (of.startswith("h") or of.startswith("x")):
if hosted_colls_actual_or_potential_results_p:
if hosted_colls_results:
# TODO: add a verbose message here
for result in hosted_colls_true_results:
if of.startswith("h"):
req.write(print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, result[0][1].name, results_final_nb[result[0][1].name],
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
req.write(print_hosted_results(url_and_engine=result[0], ln=ln, of=of, req=req, limit=rg))
if of.startswith("h"):
req.write(print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, result[0][1].name, results_final_nb[result[0][1].name],
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time, 1))
if hosted_colls_timeouts:
# TODO: add a verbose message here
# TODO: check if verbose messages still work when dealing with (re)calculations of timeouts
(hosted_colls_timeouts_results, hosted_colls_timeouts_timeouts) = do_calculate_hosted_collections_results(req, ln, None, verbose, None, hosted_colls_timeouts, CFG_HOSTED_COLLECTION_TIMEOUT_POST_SEARCH)
if hosted_colls_timeouts_results:
for result in hosted_colls_timeouts_results:
if result[1] == None or result[1] == False:
## these are the searches the returned no or zero results
## also print a nearest terms box, in case this is the only
## collection being searched and it returns no results?
if of.startswith("h"):
req.write(print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, result[0][1].name, -963,
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
req.write(print_hosted_results(url_and_engine=result[0], ln=ln, of=of, req=req, no_records_found=True, limit=rg))
req.write(print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, result[0][1].name, -963,
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time, 1))
else:
# these are the searches that actually returned results on time
if of.startswith("h"):
req.write(print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, result[0][1].name, result[1],
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
req.write(print_hosted_results(url_and_engine=result[0], ln=ln, of=of, req=req, limit=rg))
if of.startswith("h"):
req.write(print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, result[0][1].name, result[1],
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time, 1))
if hosted_colls_timeouts_timeouts:
for timeout in hosted_colls_timeouts_timeouts:
if of.startswith("h"):
req.write(print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, timeout[1].name, -963,
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
req.write(print_hosted_results(url_and_engine=timeout[0], ln=ln, of=of, req=req, search_timed_out=True, limit=rg))
req.write(print_hosted_search_info(p, f, sf, so, sp, rm, of, ot, timeout[1].name, -963,
jrec, rg, aas, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time, 1))
print_records_epilogue(req, of)
if f == "author" and of.startswith("h"):
req.write(create_similarly_named_authors_link_box(p, ln))
# log query:
try:
id_query = log_query(req.remote_host, req.args, uid)
if of.startswith("h") and id_query:
if not of in ['hcs']:
# display alert/RSS teaser for non-summary formats:
user_info = collect_user_info(req)
display_email_alert_part = True
if user_info:
if user_info['email'] == 'guest':
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS > 4:
display_email_alert_part = False
else:
if not user_info['precached_usealerts']:
display_email_alert_part = False
req.write(websearch_templates.tmpl_alert_rss_teaser_box_for_query(id_query, \
ln=ln, display_email_alert_part=display_email_alert_part))
except:
# do not log query if req is None (used by CLI interface)
pass
log_query_info("ss", p, f, colls_to_search, results_final_nb_total)
# External searches
if of.startswith("h"):
if not of in ['hcs']:
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
def perform_request_cache(req, action="show"):
"""Manipulates the search engine cache."""
req.content_type = "text/html"
req.send_http_header()
req.write("<html>")
out = ""
out += "<h1>Search Cache</h1>"
# clear cache if requested:
if action == "clear":
search_results_cache.clear()
req.write(out)
# show collection reclist cache:
out = "<h3>Collection reclist cache</h3>"
out += "- collection table last updated: %s" % get_table_update_time('collection')
out += "<br />- reclist cache timestamp: %s" % collection_reclist_cache.timestamp
out += "<br />- reclist cache contents:"
out += "<blockquote>"
for coll in collection_reclist_cache.cache.keys():
if collection_reclist_cache.cache[coll]:
out += "%s (%d)<br />" % (coll, len(collection_reclist_cache.cache[coll]))
out += "</blockquote>"
req.write(out)
# show search results cache:
out = "<h3>Search Cache</h3>"
out += "- search cache usage: %d queries cached (max. ~%d)" % \
(len(search_results_cache.cache), CFG_WEBSEARCH_SEARCH_CACHE_SIZE)
if len(search_results_cache.cache):
out += "<br />- search cache contents:"
out += "<blockquote>"
for query, hitset in search_results_cache.cache.items():
out += "<br />%s ... %s" % (query, hitset)
out += """<p><a href="%s/search/cache?action=clear">clear search results cache</a>""" % CFG_SITE_URL
out += "</blockquote>"
req.write(out)
# show field i18nname cache:
out = "<h3>Field I18N names cache</h3>"
out += "- fieldname table last updated: %s" % get_table_update_time('fieldname')
out += "<br />- i18nname cache timestamp: %s" % field_i18nname_cache.timestamp
out += "<br />- i18nname cache contents:"
out += "<blockquote>"
for field in field_i18nname_cache.cache.keys():
for ln in field_i18nname_cache.cache[field].keys():
out += "%s, %s = %s<br />" % (field, ln, field_i18nname_cache.cache[field][ln])
out += "</blockquote>"
req.write(out)
# show collection i18nname cache:
out = "<h3>Collection I18N names cache</h3>"
out += "- collectionname table last updated: %s" % get_table_update_time('collectionname')
out += "<br />- i18nname cache timestamp: %s" % collection_i18nname_cache.timestamp
out += "<br />- i18nname cache contents:"
out += "<blockquote>"
for coll in collection_i18nname_cache.cache.keys():
for ln in collection_i18nname_cache.cache[coll].keys():
out += "%s, %s = %s<br />" % (coll, ln, collection_i18nname_cache.cache[coll][ln])
out += "</blockquote>"
req.write(out)
req.write("</html>")
return "\n"
def perform_request_log(req, date=""):
"""Display search log information for given date."""
req.content_type = "text/html"
req.send_http_header()
req.write("<html>")
req.write("<h1>Search Log</h1>")
if date: # case A: display stats for a day
yyyymmdd = string.atoi(date)
req.write("<p><big><strong>Date: %d</strong></big><p>" % yyyymmdd)
req.write("""<table border="1">""")
req.write("<tr><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td></tr>" % ("No.", "Time", "Pattern", "Field", "Collection", "Number of Hits"))
# read file:
p = os.popen("grep ^%d %s/search.log" % (yyyymmdd, CFG_LOGDIR), 'r')
lines = p.readlines()
p.close()
# process lines:
i = 0
for line in lines:
try:
datetime, dummy_aas, p, f, c, nbhits = string.split(line,"#")
i += 1
req.write("<tr><td align=\"right\">#%d</td><td>%s:%s:%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" \
% (i, datetime[8:10], datetime[10:12], datetime[12:], p, f, c, nbhits))
except:
pass # ignore eventual wrong log lines
req.write("</table>")
else: # case B: display summary stats per day
yyyymm01 = int(time.strftime("%Y%m01", time.localtime()))
yyyymmdd = int(time.strftime("%Y%m%d", time.localtime()))
req.write("""<table border="1">""")
req.write("<tr><td><strong>%s</strong></td><td><strong>%s</strong></tr>" % ("Day", "Number of Queries"))
for day in range(yyyymm01, yyyymmdd + 1):
p = os.popen("grep -c ^%d %s/search.log" % (day, CFG_LOGDIR), 'r')
for line in p.readlines():
req.write("""<tr><td>%s</td><td align="right"><a href="%s/search/log?date=%d">%s</a></td></tr>""" % \
(day, CFG_SITE_URL, day, line))
p.close()
req.write("</table>")
req.write("</html>")
return "\n"
def get_most_popular_field_values(recids, tags, exclude_values=None, count_repetitive_values=True):
"""
Analyze RECIDS and look for TAGS and return most popular values
and the frequency with which they occur sorted according to
descending frequency.
If a value is found in EXCLUDE_VALUES, then do not count it.
If COUNT_REPETITIVE_VALUES is True, then we count every occurrence
of value in the tags. If False, then we count the value only once
regardless of the number of times it may appear in a record.
(But, if the same value occurs in another record, we count it, of
course.)
Example:
>>> get_most_popular_field_values(range(11,20), '980__a')
(('PREPRINT', 10), ('THESIS', 7), ...)
>>> get_most_popular_field_values(range(11,20), ('100__a', '700__a'))
(('Ellis, J', 10), ('Ellis, N', 7), ...)
>>> get_most_popular_field_values(range(11,20), ('100__a', '700__a'), ('Ellis, J'))
(('Ellis, N', 7), ...)
"""
def _get_most_popular_field_values_helper_sorter(val1, val2):
"Compare VAL1 and VAL2 according to, firstly, frequency, then secondly, alphabetically."
compared_via_frequencies = cmp(valuefreqdict[val2], valuefreqdict[val1])
if compared_via_frequencies == 0:
return cmp(val1.lower(), val2.lower())
else:
return compared_via_frequencies
valuefreqdict = {}
## sanity check:
if not exclude_values:
exclude_values = []
if isinstance(tags, str):
tags = (tags,)
## find values to count:
vals_to_count = []
displaytmp = {}
if count_repetitive_values:
# counting technique A: can look up many records at once: (very fast)
for tag in tags:
vals_to_count.extend(get_fieldvalues(recids, tag))
else:
# counting technique B: must count record-by-record: (slow)
for recid in recids:
vals_in_rec = []
for tag in tags:
for val in get_fieldvalues(recid, tag, False):
vals_in_rec.append(val)
# do not count repetitive values within this record
# (even across various tags, so need to unify again):
dtmp = {}
for val in vals_in_rec:
dtmp[val.lower()] = 1
displaytmp[val.lower()] = val
vals_in_rec = dtmp.keys()
vals_to_count.extend(vals_in_rec)
## are we to exclude some of found values?
for val in vals_to_count:
if val not in exclude_values:
if valuefreqdict.has_key(val):
valuefreqdict[val] += 1
else:
valuefreqdict[val] = 1
## sort by descending frequency of values:
out = ()
vals = valuefreqdict.keys()
vals.sort(_get_most_popular_field_values_helper_sorter)
for val in vals:
tmpdisplv = ''
if displaytmp.has_key(val):
tmpdisplv = displaytmp[val]
else:
tmpdisplv = val
out += (tmpdisplv, valuefreqdict[val]),
return out
def profile(p="", f="", c=CFG_SITE_NAME):
"""Profile search time."""
import profile
import pstats
profile.run("perform_request_search(p='%s',f='%s', c='%s')" % (p, f, c), "perform_request_search_profile")
p = pstats.Stats("perform_request_search_profile")
p.strip_dirs().sort_stats("cumulative").print_stats()
return 0
diff --git a/modules/websearch/lib/websearch_regression_tests.py b/modules/websearch/lib/websearch_regression_tests.py
index 488e14212..04803c991 100644
--- a/modules/websearch/lib/websearch_regression_tests.py
+++ b/modules/websearch/lib/websearch_regression_tests.py
@@ -1,1895 +1,1895 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0301
# pylint: disable=E1102
"""WebSearch module regression tests."""
__revision__ = "$Id$"
import unittest
import re
import urlparse, cgi
import sys
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
from mechanize import Browser, LinkNotFoundError
-from invenio.config import CFG_SITE_URL, CFG_SITE_NAME, CFG_SITE_LANG
+from invenio.config import CFG_SITE_URL, CFG_SITE_NAME, CFG_SITE_LANG, \
+ CFG_SITE_RECORD
from invenio.testutils import make_test_suite, \
run_test_suite, \
make_url, make_surl, test_web_page_content, \
merge_error_messages
from invenio.urlutils import same_urls_p
from invenio.search_engine import perform_request_search, \
guess_primary_collection_of_a_record, guess_collection_of_a_record, \
collection_restricted_p, get_permitted_restricted_collections, \
get_fieldvalues, search_pattern, search_unit, search_unit_in_bibrec, \
wash_colls
def parse_url(url):
parts = urlparse.urlparse(url)
query = cgi.parse_qs(parts[4], True)
return parts[2].split('/')[1:], query
class WebSearchWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebSearch web pages whether they are up or not."""
def test_search_interface_pages_availability(self):
"""websearch - availability of search interface pages"""
baseurl = CFG_SITE_URL + '/'
_exports = ['', 'collection/Poetry', 'collection/Poetry?as=1']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_search_results_pages_availability(self):
"""websearch - availability of search results pages"""
baseurl = CFG_SITE_URL + '/search'
_exports = ['', '?c=Poetry', '?p=ellis', '/cache', '/log']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_search_detailed_record_pages_availability(self):
"""websearch - availability of search detailed record pages"""
- baseurl = CFG_SITE_URL + '/record/'
+ baseurl = CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/'
_exports = ['', '1', '1/', '1/files', '1/files/']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_browse_results_pages_availability(self):
"""websearch - availability of browse results pages"""
baseurl = CFG_SITE_URL + '/search'
_exports = ['?p=ellis&f=author&action_browse=Browse']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_help_page_availability(self):
"""websearch - availability of Help Central page"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help',
expected_text="Help Central"))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/?ln=fr',
expected_text="Centre d'aide"))
def test_search_tips_page_availability(self):
"""websearch - availability of Search Tips"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/search-tips',
expected_text="Search Tips"))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/search-tips?ln=fr',
expected_text="Conseils de recherche"))
def test_search_guide_page_availability(self):
"""websearch - availability of Search Guide"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/search-guide',
expected_text="Search Guide"))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/search-guide?ln=fr',
expected_text="Guide de recherche"))
class WebSearchTestLegacyURLs(unittest.TestCase):
""" Check that the application still responds to legacy URLs for
navigating, searching and browsing."""
def test_legacy_collections(self):
""" websearch - collections handle legacy urls """
browser = Browser()
def check(legacy, new, browser=browser):
browser.open(legacy)
got = browser.geturl()
self.failUnless(same_urls_p(got, new), got)
# Use the root URL unless we need more
check(make_url('/', c=CFG_SITE_NAME),
make_url('/', ln=CFG_SITE_LANG))
# Other collections are redirected in the /collection area
check(make_url('/', c='Poetry'),
make_url('/collection/Poetry', ln=CFG_SITE_LANG))
# Drop unnecessary arguments, like ln and as (when they are
# the default value)
args = {'as': 0}
check(make_url('/', c='Poetry', **args),
make_url('/collection/Poetry', ln=CFG_SITE_LANG))
# Otherwise, keep them
args = {'as': 1, 'ln': CFG_SITE_LANG}
check(make_url('/', c='Poetry', **args),
make_url('/collection/Poetry', **args))
# Support the /index.py addressing too
check(make_url('/index.py', c='Poetry'),
make_url('/collection/Poetry', ln=CFG_SITE_LANG))
def test_legacy_search(self):
""" websearch - search queries handle legacy urls """
browser = Browser()
def check(legacy, new, browser=browser):
browser.open(legacy)
got = browser.geturl()
self.failUnless(same_urls_p(got, new), got)
# /search.py is redirected on /search
# Note that `as' is a reserved word in Python 2.5
check(make_url('/search.py', p='nuclear', ln='en') + 'as=1',
make_url('/search', p='nuclear', ln='en') + 'as=1')
- # direct recid searches are redirected to /record
+ # direct recid searches are redirected to /CFG_SITE_RECORD
check(make_url('/search.py', recid=1, ln='es'),
- make_url('/record/1', ln='es'))
+ make_url('/%s/1' % CFG_SITE_RECORD, ln='es'))
def test_legacy_search_help_link(self):
"""websearch - legacy Search Help page link"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/search/index.en.html',
expected_text="Help Central"))
def test_legacy_search_tips_link(self):
"""websearch - legacy Search Tips page link"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/search/tips.fr.html',
expected_text="Conseils de recherche"))
def test_legacy_search_guide_link(self):
"""websearch - legacy Search Guide page link"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/help/search/guide.en.html',
expected_text="Search Guide"))
class WebSearchTestRecord(unittest.TestCase):
- """ Check the interface of the /record results """
+ """ Check the interface of the /CFG_SITE_RECORD results """
def test_format_links(self):
""" websearch - check format links for records """
browser = Browser()
# We open the record in all known HTML formats
for hformat in ('hd', 'hx', 'hm'):
- browser.open(make_url('/record/1', of=hformat))
+ browser.open(make_url('/%s/1' % CFG_SITE_RECORD, of=hformat))
if hformat == 'hd':
# hd format should have a link to the following
# formats
for oformat in ('hx', 'hm', 'xm', 'xd'):
- target = make_url('/record/1/export/%s?ln=en' % oformat)
+ target = make_url('/%s/1/export/%s?ln=en' % (CFG_SITE_RECORD, oformat))
try:
browser.find_link(url=target)
except LinkNotFoundError:
self.fail('link %r should be in page' % target)
else:
# non-hd HTML formats should have a link back to
# the main detailed record
- target = make_url('/record/1')
+ target = make_url('/%s/1' % CFG_SITE_RECORD)
try:
browser.find_link(url=target)
except LinkNotFoundError:
self.fail('link %r should be in page' % target)
return
def test_exported_formats(self):
- """ websearch - check formats exported through /record/1/export/ URLs"""
+ """ websearch - check formats exported through /CFG_SITE_RECORD/1/export/ URLs"""
browser = Browser()
self.assertEqual([],
- test_web_page_content(make_url('/record/1/export/hm'),
+ test_web_page_content(make_url('/%s/1/export/hm' % CFG_SITE_RECORD),
expected_text='245__ $$aALEPH experiment'))
self.assertEqual([],
- test_web_page_content(make_url('/record/1/export/hd'),
+ test_web_page_content(make_url('/%s/1/export/hd' % CFG_SITE_RECORD),
expected_text='<strong>ALEPH experiment'))
self.assertEqual([],
- test_web_page_content(make_url('/record/1/export/xm'),
+ test_web_page_content(make_url('/%s/1/export/xm' % CFG_SITE_RECORD),
expected_text='<subfield code="a">ALEPH experiment'))
self.assertEqual([],
- test_web_page_content(make_url('/record/1/export/xd'),
+ test_web_page_content(make_url('/%s/1/export/xd' % CFG_SITE_RECORD),
expected_text='<dc:title>ALEPH experiment'))
self.assertEqual([],
- test_web_page_content(make_url('/record/1/export/hs'),
- expected_text='<a href="/record/1?ln=%s">ALEPH experiment' % \
- CFG_SITE_LANG))
+ test_web_page_content(make_url('/%s/1/export/hs' % CFG_SITE_RECORD),
+ expected_text='<a href="/%s/1?ln=%s">ALEPH experiment' % \
+ (CFG_SITE_RECORD, CFG_SITE_LANG)))
self.assertEqual([],
- test_web_page_content(make_url('/record/1/export/hx'),
+ test_web_page_content(make_url('/%s/1/export/hx' % CFG_SITE_RECORD),
expected_text='title = "ALEPH experiment'))
self.assertEqual([],
- test_web_page_content(make_url('/record/1/export/t?ot=245'),
+ test_web_page_content(make_url('/%s/1/export/t?ot=245' % CFG_SITE_RECORD),
expected_text='245__ $$aALEPH experiment'))
self.assertNotEqual([],
- test_web_page_content(make_url('/record/1/export/t?ot=245'),
+ test_web_page_content(make_url('/%s/1/export/t?ot=245' % CFG_SITE_RECORD),
expected_text='001__'))
self.assertEqual([],
- test_web_page_content(make_url('/record/1/export/h?ot=245'),
+ test_web_page_content(make_url('/%s/1/export/h?ot=245' % CFG_SITE_RECORD),
expected_text='245__ $$aALEPH experiment'))
self.assertNotEqual([],
- test_web_page_content(make_url('/record/1/export/h?ot=245'),
+ test_web_page_content(make_url('/%s/1/export/h?ot=245' % CFG_SITE_RECORD),
expected_text='001__'))
return
class WebSearchTestCollections(unittest.TestCase):
def test_traversal_links(self):
""" websearch - traverse all the publications of a collection """
browser = Browser()
try:
for aas in (0, 1):
args = {'as': aas}
browser.open(make_url('/collection/Preprints', **args))
for jrec in (11, 21, 11, 28):
args = {'jrec': jrec, 'cc': 'Preprints'}
if aas:
args['as'] = aas
url = make_url('/search', **args)
try:
browser.follow_link(url=url)
except LinkNotFoundError:
args['ln'] = CFG_SITE_LANG
url = make_url('/search', **args)
browser.follow_link(url=url)
except LinkNotFoundError:
self.fail('no link %r in %r' % (url, browser.geturl()))
def test_collections_links(self):
""" websearch - enter in collections and subcollections """
browser = Browser()
def tryfollow(url):
cur = browser.geturl()
body = browser.response().read()
try:
browser.follow_link(url=url)
except LinkNotFoundError:
print body
self.fail("in %r: could not find %r" % (
cur, url))
return
for aas in (0, 1):
if aas:
kargs = {'as': 1}
else:
kargs = {}
kargs['ln'] = CFG_SITE_LANG
# We navigate from immediate son to immediate son...
browser.open(make_url('/', **kargs))
tryfollow(make_url('/collection/Articles%20%26%20Preprints',
**kargs))
tryfollow(make_url('/collection/Articles', **kargs))
# But we can also jump to a grandson immediately
browser.back()
browser.back()
tryfollow(make_url('/collection/ALEPH', **kargs))
return
def test_records_links(self):
""" websearch - check the links toward records in leaf collections """
browser = Browser()
browser.open(make_url('/collection/Preprints'))
def harvest():
""" Parse all the links in the page, and check that for
each link to a detailed record, we also have the
corresponding link to the similar records."""
records = set()
similar = set()
for link in browser.links():
path, q = parse_url(link.url)
if not path:
continue
- if path[0] == 'record':
+ if path[0] == CFG_SITE_RECORD:
records.add(int(path[1]))
continue
if path[0] == 'search':
if not q.get('rm') == ['wrd']:
continue
recid = q['p'][0].split(':')[1]
similar.add(int(recid))
self.failUnlessEqual(records, similar)
return records
- # We must have 10 links to the corresponding /records
+ # We must have 10 links to the corresponding /CFG_SITE_RECORD
found = harvest()
self.failUnlessEqual(len(found), 10)
# When clicking on the "Search" button, we must also have
# these 10 links on the records.
browser.select_form(name="search")
browser.submit()
found = harvest()
self.failUnlessEqual(len(found), 10)
return
class WebSearchTestBrowse(unittest.TestCase):
def test_browse_field(self):
""" websearch - check that browsing works """
browser = Browser()
browser.open(make_url('/'))
browser.select_form(name='search')
browser['f'] = ['title']
browser.submit(name='action_browse')
def collect():
# We'll get a few links to search for the actual hits, plus a
# link to the following results.
res = []
for link in browser.links(url_regex=re.compile(CFG_SITE_URL +
r'/search\?')):
if link.text == 'Advanced Search':
continue
dummy, q = parse_url(link.url)
res.append((link, q))
return res
# if we follow the last link, we should get another
# batch. There is an overlap of one item.
batch_1 = collect()
browser.follow_link(link=batch_1[-1][0])
batch_2 = collect()
# FIXME: we cannot compare the whole query, as the collection
# set is not equal
self.failUnlessEqual(batch_1[-2][1]['p'], batch_2[0][1]['p'])
class WebSearchTestOpenURL(unittest.TestCase):
def test_isbn_01(self):
""" websearch - isbn query via OpenURL 0.1"""
browser = Browser()
# We do a precise search in an isolated collection
browser.open(make_url('/openurl', isbn='0387940758'))
dummy, current_q = parse_url(browser.geturl())
self.failUnlessEqual(current_q, {
'sc' : ['1'],
'p' : ['isbn:"0387940758"'],
'of' : ['hd']
})
def test_isbn_10_rft_id(self):
""" websearch - isbn query via OpenURL 1.0 - rft_id"""
browser = Browser()
# We do a precise search in an isolated collection
browser.open(make_url('/openurl', rft_id='urn:ISBN:0387940758'))
dummy, current_q = parse_url(browser.geturl())
self.failUnlessEqual(current_q, {
'sc' : ['1'],
'p' : ['isbn:"0387940758"'],
'of' : ['hd']
})
def test_isbn_10(self):
""" websearch - isbn query via OpenURL 1.0"""
browser = Browser()
# We do a precise search in an isolated collection
browser.open(make_url('/openurl?rft.isbn=0387940758'))
dummy, current_q = parse_url(browser.geturl())
self.failUnlessEqual(current_q, {
'sc' : ['1'],
'p' : ['isbn:"0387940758"'],
'of' : ['hd']
})
class WebSearchTestSearch(unittest.TestCase):
def test_hits_in_other_collection(self):
""" websearch - check extension of a query to the home collection """
browser = Browser()
# We do a precise search in an isolated collection
browser.open(make_url('/collection/ISOLDE', ln='en'))
browser.select_form(name='search')
browser['f'] = ['author']
browser['p'] = 'matsubara'
browser.submit()
dummy, current_q = parse_url(browser.geturl())
link = browser.find_link(text_regex=re.compile('.*hit', re.I))
dummy, target_q = parse_url(link.url)
# the target query should be the current query without any c
# or cc specified.
for f in ('cc', 'c', 'action_search'):
if f in current_q:
del current_q[f]
self.failUnlessEqual(current_q, target_q)
def test_nearest_terms(self):
""" websearch - provide a list of nearest terms """
browser = Browser()
browser.open(make_url(''))
# Search something weird
browser.select_form(name='search')
browser['p'] = 'gronf'
browser.submit()
dummy, original = parse_url(browser.geturl())
for to_drop in ('cc', 'action_search', 'f'):
if to_drop in original:
del original[to_drop]
if 'ln' not in original:
original['ln'] = [CFG_SITE_LANG]
# we should get a few searches back, which are identical
# except for the p field being substituted (and the cc field
# being dropped).
if 'cc' in original:
del original['cc']
for link in browser.links(url_regex=re.compile(CFG_SITE_URL + r'/search\?')):
if link.text == 'Advanced Search':
continue
dummy, target = parse_url(link.url)
if 'ln' not in target:
target['ln'] = [CFG_SITE_LANG]
original['p'] = [link.text]
self.failUnlessEqual(original, target)
return
def test_switch_to_simple_search(self):
""" websearch - switch to simple search """
browser = Browser()
args = {'as': 1}
browser.open(make_url('/collection/ISOLDE', **args))
browser.select_form(name='search')
browser['p1'] = 'tandem'
browser['f1'] = ['title']
browser.submit()
browser.follow_link(text='Simple Search')
dummy, q = parse_url(browser.geturl())
self.failUnlessEqual(q, {'cc': ['ISOLDE'],
'p': ['tandem'],
'f': ['title'],
'ln': ['en']})
def test_switch_to_advanced_search(self):
""" websearch - switch to advanced search """
browser = Browser()
browser.open(make_url('/collection/ISOLDE'))
browser.select_form(name='search')
browser['p'] = 'tandem'
browser['f'] = ['title']
browser.submit()
browser.follow_link(text='Advanced Search')
dummy, q = parse_url(browser.geturl())
self.failUnlessEqual(q, {'cc': ['ISOLDE'],
'p1': ['tandem'],
'f1': ['title'],
'as': ['1'],
'ln' : ['en']})
def test_no_boolean_hits(self):
""" websearch - check the 'no boolean hits' proposed links """
browser = Browser()
browser.open(make_url(''))
browser.select_form(name='search')
browser['p'] = 'quasinormal muon'
browser.submit()
dummy, q = parse_url(browser.geturl())
for to_drop in ('cc', 'action_search', 'f'):
if to_drop in q:
del q[to_drop]
for bsu in ('quasinormal', 'muon'):
l = browser.find_link(text=bsu)
q['p'] = bsu
if not same_urls_p(l.url, make_url('/search', **q)):
self.fail(repr((l.url, make_url('/search', **q))))
def test_similar_authors(self):
""" websearch - test similar authors box """
browser = Browser()
browser.open(make_url(''))
browser.select_form(name='search')
browser['p'] = 'Ellis, R K'
browser['f'] = ['author']
browser.submit()
l = browser.find_link(text="Ellis, R S")
self.failUnless(same_urls_p(l.url, make_url('/search',
p="Ellis, R S",
f='author',
ln='en')))
class WebSearchTestWildcardLimit(unittest.TestCase):
"""Checks if the wildcard limit is correctly passed and that
users without autorization can not exploit it"""
def test_wildcard_limit_correctly_passed_when_not_set(self):
"""websearch - wildcard limit is correctly passed when default"""
self.assertEqual(search_pattern(p='e*', f='author'),
search_pattern(p='e*', f='author', wl=1000))
def test_wildcard_limit_correctly_passed_when_set(self):
"""websearch - wildcard limit is correctly passed when set"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=e*&f=author&of=id&wl=5',
expected_text="[9, 10, 11, 17, 46, 48, 50, 51, 52, 53, 54, 67, 72, 74, 81, 88, 92, 96]"))
def test_wildcard_limit_correctly_not_active(self):
"""websearch - wildcard limit is not active when there is no wildcard query"""
self.assertEqual(search_pattern(p='ellis', f='author'),
search_pattern(p='ellis', f='author', wl=1))
def test_wildcard_limit_increased_by_authorized_users(self):
"""websearch - wildcard limit increased by authorized user"""
browser = Browser()
#try a search query, with no wildcard limit set by the user
browser.open(make_url('/search?p=a*&of=id'))
recid_list_guest_no_limit = browser.response().read() # so the limit is CGF_WEBSEARCH_WILDCARD_LIMIT
#try a search query, with a wildcard limit imposed by the user
#wl=1000000 - a very high limit,higher then what the CFG_WEBSEARCH_WILDCARD_LIMIT might be
browser.open(make_url('/search?p=a*&of=id&wl=1000000'))
recid_list_guest_with_limit = browser.response().read()
#same results should be returned for a search without the wildcard limit set by the user
#and for a search with a large limit set by the user
#in this way we know that nomatter how large the limit is, the wildcard query will be
#limitted by CFG_WEBSEARCH_WILDCARD_LIMIT (for a guest user)
self.failIf(len(recid_list_guest_no_limit.split(',')) != len(recid_list_guest_with_limit.split(',')))
##login as admin
browser.open(make_surl('/youraccount/login'))
browser.select_form(nr=0)
browser['p_un'] = 'admin'
browser['p_pw'] = ''
browser.submit()
#try a search query, with a wildcard limit imposed by an authorized user
#wl = 10000 a very high limit, higher then what the CFG_WEBSEARCH_WILDCARD_LIMIT might be
browser.open(make_surl('/search?p=a*&of=id&wl=10000'))
recid_list_authuser_with_limit = browser.response().read()
#the authorized user can set whatever limit he might wish
#so, the results returned for the auth. users should exceed the results returned for unauth. users
self.failUnless(len(recid_list_guest_no_limit.split(',')) <= len(recid_list_authuser_with_limit.split(',')))
#logout
browser.open(make_surl('/youraccount/logout'))
browser.response().read()
browser.close()
class WebSearchNearestTermsTest(unittest.TestCase):
"""Check various alternatives of searches leading to the nearest
terms box."""
def test_nearest_terms_box_in_okay_query(self):
""" websearch - no nearest terms box for a successful query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis',
expected_text="jump to record"))
def test_nearest_terms_box_in_unsuccessful_simple_query(self):
""" websearch - nearest terms box for unsuccessful simple query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellisz',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=embed",
expected_link_label='embed'))
def test_nearest_terms_box_in_unsuccessful_simple_accented_query(self):
""" websearch - nearest terms box for unsuccessful accented query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=elliszà',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=embed",
expected_link_label='embed'))
def test_nearest_terms_box_in_unsuccessful_structured_query(self):
""" websearch - nearest terms box for unsuccessful structured query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellisz&f=author',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=fabbro&f=author",
expected_link_label='fabbro'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=author%3Aellisz',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=author%3Afabbro",
expected_link_label='fabbro'))
def test_nearest_terms_box_in_unsuccessful_phrase_query(self):
""" websearch - nearest terms box for unsuccessful phrase query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=author%3A%22Ellis%2C+Z%22',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=author%3A%22Enqvist%2C+K%22",
expected_link_label='Enqvist, K'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%22ellisz%22&f=author',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=%22Enqvist%2C+K%22&f=author",
expected_link_label='Enqvist, K'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%22elliszà%22&f=author',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=%22Enqvist%2C+K%22&f=author",
expected_link_label='Enqvist, K'))
def test_nearest_terms_box_in_unsuccessful_partial_phrase_query(self):
""" websearch - nearest terms box for unsuccessful partial phrase query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=author%3A%27Ellis%2C+Z%27',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=author%3A%27Enqvist%2C+K%27",
expected_link_label='Enqvist, K'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%27ellisz%27&f=author',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=%27Enqvist%2C+K%27&f=author",
expected_link_label='Enqvist, K'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%27elliszà%27&f=author',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=%27Enqvist%2C+K%27&f=author",
expected_link_label='Enqvist, K'))
def test_nearest_terms_box_in_unsuccessful_partial_phrase_advanced_query(self):
""" websearch - nearest terms box for unsuccessful partial phrase advanced search query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p1=aaa&f1=title&m1=p&as=1',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&f1=title&as=1&p1=A+simple+functional+form+for+proton-nucleus+total+reaction+cross+sections&m1=p",
expected_link_label='A simple functional form for proton-nucleus total reaction cross sections'))
def test_nearest_terms_box_in_unsuccessful_exact_phrase_advanced_query(self):
""" websearch - nearest terms box for unsuccessful exact phrase advanced search query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p1=aaa&f1=title&m1=e&as=1',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&f1=title&as=1&p1=A+simple+functional+form+for+proton-nucleus+total+reaction+cross+sections&m1=e",
expected_link_label='A simple functional form for proton-nucleus total reaction cross sections'))
def test_nearest_terms_box_in_unsuccessful_boolean_query(self):
""" websearch - nearest terms box for unsuccessful boolean query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=title%3Aellisz+author%3Aellisz',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=title%3Aenergi+author%3Aellisz",
expected_link_label='energi'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=title%3Aenergi+author%3Aenergie',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=title%3Aenergi+author%3Aenqvist",
expected_link_label='enqvist'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?ln=en&p=title%3Aellisz+author%3Aellisz&f=keyword',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=title%3Aenergi+author%3Aellisz&f=keyword",
expected_link_label='energi'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?ln=en&p=title%3Aenergi+author%3Aenergie&f=keyword',
expected_text="Nearest terms in any collection are",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=title%3Aenergi+author%3Aenqvist&f=keyword",
expected_link_label='enqvist'))
class WebSearchBooleanQueryTest(unittest.TestCase):
"""Check various boolean queries."""
def test_successful_boolean_query(self):
""" websearch - successful boolean query """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis+muon',
expected_text="records found",
expected_link_label="Detailed record"))
def test_unsuccessful_boolean_query_where_all_individual_terms_match(self):
""" websearch - unsuccessful boolean query where all individual terms match """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis+muon+letter',
expected_text="Boolean query returned no hits. Please combine your search terms differently."))
class WebSearchAuthorQueryTest(unittest.TestCase):
"""Check various author-related queries."""
def test_propose_similar_author_names_box(self):
""" websearch - propose similar author names box """
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=Ellis%2C+R&f=author',
expected_text="See also: similar author names",
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=Ellis%2C+R+K&f=author",
expected_link_label="Ellis, R K"))
def test_do_not_propose_similar_author_names_box(self):
""" websearch - do not propose similar author names box """
errmsgs = test_web_page_content(CFG_SITE_URL + '/search?p=author%3A%22Ellis%2C+R%22',
expected_link_target=CFG_SITE_URL+"/search?ln=en&p=Ellis%2C+R+K&f=author",
expected_link_label="Ellis, R K")
if errmsgs[0].find("does not contain link to") > -1:
pass
else:
self.fail("Should not propose similar author names box.")
return
class WebSearchSearchEnginePythonAPITest(unittest.TestCase):
"""Check typical search engine Python API calls on the demo data."""
def test_search_engine_python_api_for_failed_query(self):
"""websearch - search engine Python API for failed query"""
self.assertEqual([],
perform_request_search(p='aoeuidhtns'))
def test_search_engine_python_api_for_successful_query(self):
"""websearch - search engine Python API for successful query"""
self.assertEqual([8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47],
perform_request_search(p='ellis'))
def test_search_engine_python_api_for_existing_record(self):
"""websearch - search engine Python API for existing record"""
self.assertEqual([8],
perform_request_search(recid=8))
def test_search_engine_python_api_for_nonexisting_record(self):
"""websearch - search engine Python API for non-existing record"""
self.assertEqual([],
perform_request_search(recid=1234567809))
def test_search_engine_python_api_for_nonexisting_collection(self):
"""websearch - search engine Python API for non-existing collection"""
self.assertEqual([],
perform_request_search(c='Foo'))
def test_search_engine_python_api_for_range_of_records(self):
"""websearch - search engine Python API for range of records"""
self.assertEqual([1, 2, 3, 4, 5, 6, 7, 8, 9],
perform_request_search(recid=1, recidb=10))
def test_search_engine_python_api_ranked_by_citation(self):
"""websearch - search engine Python API for citation ranking"""
self.assertEqual([82, 83, 87, 89],
perform_request_search(p='recid:81', rm='citation'))
def test_search_engine_python_api_textmarc(self):
"""websearch - search engine Python API for Text MARC output"""
# we are testing example from /help/hacking/search-engine-api
import cStringIO
tmp = cStringIO.StringIO()
perform_request_search(req=tmp, p='higgs', of='tm', ot=['100', '700'])
out = tmp.getvalue()
tmp.close()
self.assertEqual(out, """\
000000085 100__ $$aGirardello, L$$uINFN$$uUniversita di Milano-Bicocca
000000085 700__ $$aPorrati, Massimo
000000085 700__ $$aZaffaroni, A
000000001 100__ $$aPhotolab
""")
class WebSearchSearchEngineWebAPITest(unittest.TestCase):
"""Check typical search engine Web API calls on the demo data."""
def test_search_engine_web_api_for_failed_query(self):
"""websearch - search engine Web API for failed query"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=aoeuidhtns&of=id',
expected_text="[]"))
def test_search_engine_web_api_for_successful_query(self):
"""websearch - search engine Web API for successful query"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis&of=id',
expected_text="[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47]"))
def test_search_engine_web_api_for_existing_record(self):
"""websearch - search engine Web API for existing record"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?recid=8&of=id',
expected_text="[8]"))
def test_search_engine_web_api_for_nonexisting_record(self):
"""websearch - search engine Web API for non-existing record"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?recid=123456789&of=id',
expected_text="[]"))
def test_search_engine_web_api_for_nonexisting_collection(self):
"""websearch - search engine Web API for non-existing collection"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?c=Foo&of=id',
expected_text="[]"))
def test_search_engine_web_api_for_range_of_records(self):
"""websearch - search engine Web API for range of records"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?recid=1&recidb=10&of=id',
expected_text="[1, 2, 3, 4, 5, 6, 7, 8, 9]"))
class WebSearchRestrictedCollectionTest(unittest.TestCase):
"""Test of the restricted Theses collection behaviour."""
def test_restricted_collection_interface_page(self):
"""websearch - restricted collection interface page body"""
# there should be no Latest additions box for restricted collections
self.assertNotEqual([],
test_web_page_content(CFG_SITE_URL + '/collection/Theses',
expected_text="Latest additions"))
def test_restricted_search_as_anonymous_guest(self):
"""websearch - restricted collection not searchable by anonymous guest"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?c=Theses')
response = browser.response().read()
if response.find("If you think you have right to access it, please authenticate yourself.") > -1:
pass
else:
self.fail("Oops, searching restricted collection without password should have redirected to login dialog.")
return
def test_restricted_search_as_authorized_person(self):
"""websearch - restricted collection searchable by authorized person"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?c=Theses')
browser.select_form(nr=0)
browser['p_un'] = 'jekyll'
browser['p_pw'] = 'j123ekyll'
browser.submit()
if browser.response().read().find("records found") > -1:
pass
else:
self.fail("Oops, Dr. Jekyll should be able to search Theses collection.")
def test_restricted_search_as_unauthorized_person(self):
"""websearch - restricted collection not searchable by unauthorized person"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?c=Theses')
browser.select_form(nr=0)
browser['p_un'] = 'hyde'
browser['p_pw'] = 'h123yde'
browser.submit()
# Mr. Hyde should not be able to connect:
if browser.response().read().find("Authorization failure") <= -1:
# if we got here, things are broken:
self.fail("Oops, Mr.Hyde should not be able to search Theses collection.")
def test_restricted_detailed_record_page_as_anonymous_guest(self):
"""websearch - restricted detailed record page not accessible to guests"""
browser = Browser()
- browser.open(CFG_SITE_URL + '/record/35')
+ browser.open(CFG_SITE_URL + '/%s/35' % CFG_SITE_RECORD)
if browser.response().read().find("You can use your nickname or your email address to login.") > -1:
pass
else:
self.fail("Oops, searching restricted collection without password should have redirected to login dialog.")
return
def test_restricted_detailed_record_page_as_authorized_person(self):
"""websearch - restricted detailed record page accessible to authorized person"""
browser = Browser()
browser.open(CFG_SITE_URL + '/youraccount/login')
browser.select_form(nr=0)
browser['p_un'] = 'jekyll'
browser['p_pw'] = 'j123ekyll'
browser.submit()
- browser.open(CFG_SITE_URL + '/record/35')
+ browser.open(CFG_SITE_URL + '/%s/35' % CFG_SITE_RECORD)
# Dr. Jekyll should be able to connect
# (add the pw to the whole CFG_SITE_URL because we shall be
# redirected to '/reordrestricted/'):
if browser.response().read().find("A High-performance Video Browsing System") > -1:
pass
else:
self.fail("Oops, Dr. Jekyll should be able to access restricted detailed record page.")
def test_restricted_detailed_record_page_as_unauthorized_person(self):
"""websearch - restricted detailed record page not accessible to unauthorized person"""
browser = Browser()
browser.open(CFG_SITE_URL + '/youraccount/login')
browser.select_form(nr=0)
browser['p_un'] = 'hyde'
browser['p_pw'] = 'h123yde'
browser.submit()
- browser.open(CFG_SITE_URL + '/record/35')
+ browser.open(CFG_SITE_URL + '/%s/35' % CFG_SITE_RECORD)
# Mr. Hyde should not be able to connect:
if browser.response().read().find('You are not authorized') <= -1:
# if we got here, things are broken:
self.fail("Oops, Mr.Hyde should not be able to access restricted detailed record page.")
def test_collection_restricted_p(self):
"""websearch - collection_restricted_p"""
self.failUnless(collection_restricted_p('Theses'), True)
self.failIf(collection_restricted_p('Books & Reports'))
def test_get_permitted_restricted_collections(self):
"""websearch - get_permitted_restricted_collections"""
from invenio.webuser import get_uid_from_email, collect_user_info
self.assertEqual(get_permitted_restricted_collections(collect_user_info(get_uid_from_email('jekyll@cds.cern.ch'))), ['Theses'])
self.assertEqual(get_permitted_restricted_collections(collect_user_info(get_uid_from_email('hyde@cds.cern.ch'))), [])
class WebSearchRestrictedPicturesTest(unittest.TestCase):
"""
Check whether restricted pictures on the demo site can be accessed
well by people who have rights to access them.
"""
def test_restricted_pictures_guest(self):
"""websearch - restricted pictures not available to guest"""
- error_messages = test_web_page_content(CFG_SITE_URL + '/record/1/files/0106015_01.jpg',
+ error_messages = test_web_page_content(CFG_SITE_URL + '/%s/1/files/0106015_01.jpg' % CFG_SITE_RECORD,
expected_text=['This file is restricted. If you think you have right to access it, please authenticate yourself.'])
if error_messages:
self.fail(merge_error_messages(error_messages))
def test_restricted_pictures_romeo(self):
"""websearch - restricted pictures available to Romeo"""
- error_messages = test_web_page_content(CFG_SITE_URL + '/record/1/files/0106015_01.jpg',
+ error_messages = test_web_page_content(CFG_SITE_URL + '/%s/1/files/0106015_01.jpg' % CFG_SITE_RECORD,
username='romeo',
password='r123omeo',
expected_text=[],
unexpected_text=['This file is restricted',
'You are not authorized'])
if error_messages:
self.fail(merge_error_messages(error_messages))
def test_restricted_pictures_hyde(self):
"""websearch - restricted pictures not available to Mr. Hyde"""
- error_messages = test_web_page_content(CFG_SITE_URL + '/record/1/files/0106015_01.jpg',
+ error_messages = test_web_page_content(CFG_SITE_URL + '/%s/1/files/0106015_01.jpg' % CFG_SITE_RECORD,
username='hyde',
password='h123yde',
expected_text=['This file is restricted',
'You are not authorized'])
if error_messages:
self.failUnless("HTTP Error 401: Unauthorized" in merge_error_messages(error_messages))
class WebSearchRSSFeedServiceTest(unittest.TestCase):
"""Test of the RSS feed service."""
def test_rss_feed_service(self):
"""websearch - RSS feed service"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/rss',
expected_text='<rss version="2.0"'))
class WebSearchXSSVulnerabilityTest(unittest.TestCase):
"""Test possible XSS vulnerabilities of the search engine."""
def test_xss_in_collection_interface_page(self):
"""websearch - no XSS vulnerability in collection interface pages"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/?c=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='Collection &amp;lt;SCRIPT&amp;gt;alert("XSS");&amp;lt;/SCRIPT&amp;gt; Not Found'))
def test_xss_in_collection_search_page(self):
"""websearch - no XSS vulnerability in collection search pages"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?c=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='Collection &lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt; Not Found'))
def test_xss_in_simple_search(self):
"""websearch - no XSS vulnerability in simple search"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='Search term <em>&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;</em> did not match any record.'))
def test_xss_in_structured_search(self):
"""websearch - no XSS vulnerability in structured search"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='No word index is available for <em>&lt;script&gt;alert("xss");&lt;/script&gt;</em>.'))
def test_xss_in_advanced_search(self):
"""websearch - no XSS vulnerability in advanced search"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?as=1&p1=ellis&f1=author&op1=a&p2=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f2=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&m2=e',
expected_text='Search term <em>&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;</em> inside index <em>&lt;script&gt;alert("xss");&lt;/script&gt;</em> did not match any record.'))
def test_xss_in_browse(self):
"""websearch - no XSS vulnerability in browse"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&action_browse=Browse',
expected_text='&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;'))
class WebSearchResultsOverview(unittest.TestCase):
"""Test of the search results page's Results overview box and links."""
def test_results_overview_split_off(self):
"""websearch - results overview box when split by collection is off"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?p=of&sc=0')
body = browser.response().read()
if body.find("Results overview") > -1:
self.fail("Oops, when split by collection is off, "
"results overview should not be present.")
if body.find('<a name="1"></a>') == -1:
self.fail("Oops, when split by collection is off, "
"Atlantis collection should be found.")
if body.find('<a name="15"></a>') > -1:
self.fail("Oops, when split by collection is off, "
"Multimedia & Arts should not be found.")
try:
browser.find_link(url='#15')
self.fail("Oops, when split by collection is off, "
"a link to Multimedia & Arts should not be found.")
except LinkNotFoundError:
pass
def test_results_overview_split_on(self):
"""websearch - results overview box when split by collection is on"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?p=of&sc=1')
body = browser.response().read()
if body.find("Results overview") == -1:
self.fail("Oops, when split by collection is on, "
"results overview should be present.")
if body.find('<a name="Atlantis%20Institute%20of%20Fictive%20Science"></a>') > -1:
self.fail("Oops, when split by collection is on, "
"Atlantis collection should not be found.")
if body.find('<a name="15"></a>') == -1:
self.fail("Oops, when split by collection is on, "
"Multimedia & Arts should be found.")
try:
browser.find_link(url='#15')
except LinkNotFoundError:
self.fail("Oops, when split by collection is on, "
"a link to Multimedia & Arts should be found.")
class WebSearchSortResultsTest(unittest.TestCase):
"""Test of the search results page's sorting capability."""
def test_sort_results_default(self):
"""websearch - search results sorting, default method"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=of&f=title&rg=1',
expected_text="[TESLA-FEL-99-07]"))
def test_sort_results_ascending(self):
"""websearch - search results sorting, ascending field"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=of&f=title&rg=1&sf=reportnumber&so=a',
expected_text="ISOLTRAP"))
def test_sort_results_descending(self):
"""websearch - search results sorting, descending field"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=of&f=title&rg=1&sf=reportnumber&so=d',
expected_text=" [TESLA-FEL-99-07]"))
def test_sort_results_sort_pattern(self):
"""websearch - search results sorting, preferential sort pattern"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=of&f=title&rg=1&sf=reportnumber&so=d&sp=cern',
expected_text="[CERN-TH-2002-069]"))
class WebSearchSearchResultsXML(unittest.TestCase):
"""Test search results in various output"""
def test_search_results_xm_output_split_on(self):
""" websearch - check document element of search results in xm output (split by collection on)"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?sc=1&of=xm')
body = browser.response().read()
num_doc_element = body.count("<collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">")
if num_doc_element == 0:
self.fail("Oops, no document element <collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">"
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements <collection> "
"found in search results.")
num_doc_element = body.count("</collection>")
if num_doc_element == 0:
self.fail("Oops, no document element </collection> "
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements </collection> "
"found in search results.")
def test_search_results_xm_output_split_off(self):
""" websearch - check document element of search results in xm output (split by collection off)"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?sc=0&of=xm')
body = browser.response().read()
num_doc_element = body.count("<collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">")
if num_doc_element == 0:
self.fail("Oops, no document element <collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">"
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements <collection> "
"found in search results.")
num_doc_element = body.count("</collection>")
if num_doc_element == 0:
self.fail("Oops, no document element </collection> "
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements </collection> "
"found in search results.")
def test_search_results_xd_output_split_on(self):
""" websearch - check document element of search results in xd output (split by collection on)"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?sc=1&of=xd')
body = browser.response().read()
num_doc_element = body.count("<collection")
if num_doc_element == 0:
self.fail("Oops, no document element <collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">"
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements <collection> "
"found in search results.")
num_doc_element = body.count("</collection>")
if num_doc_element == 0:
self.fail("Oops, no document element </collection> "
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements </collection> "
"found in search results.")
def test_search_results_xd_output_split_off(self):
""" websearch - check document element of search results in xd output (split by collection off)"""
browser = Browser()
browser.open(CFG_SITE_URL + '/search?sc=0&of=xd')
body = browser.response().read()
num_doc_element = body.count("<collection>")
if num_doc_element == 0:
self.fail("Oops, no document element <collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">"
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements <collection> "
"found in search results.")
num_doc_element = body.count("</collection>")
if num_doc_element == 0:
self.fail("Oops, no document element </collection> "
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements </collection> "
"found in search results.")
class WebSearchUnicodeQueryTest(unittest.TestCase):
"""Test of the search results for queries containing Unicode characters."""
def test_unicode_word_query(self):
"""websearch - Unicode word query"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=title%3A%CE%99%CE%B8%CE%AC%CE%BA%CE%B7',
expected_text="[76]"))
def test_unicode_word_query_not_found_term(self):
"""websearch - Unicode word query, not found term"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=title%3A%CE%99%CE%B8',
expected_text="ιθάκη"))
def test_unicode_exact_phrase_query(self):
"""websearch - Unicode exact phrase query"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=title%3A%22%CE%99%CE%B8%CE%AC%CE%BA%CE%B7%22',
expected_text="[76]"))
def test_unicode_partial_phrase_query(self):
"""websearch - Unicode partial phrase query"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=title%3A%27%CE%B7%27',
expected_text="[76]"))
def test_unicode_regexp_query(self):
"""websearch - Unicode regexp query"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=title%3A%2F%CE%B7%2F',
expected_text="[76]"))
class WebSearchMARCQueryTest(unittest.TestCase):
"""Test of the search results for queries containing physical MARC tags."""
def test_single_marc_tag_exact_phrase_query(self):
"""websearch - single MARC tag, exact phrase query (100__a)"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=100__a%3A%22Ellis%2C+J%22',
expected_text="[9, 14, 18]"))
def test_single_marc_tag_partial_phrase_query(self):
"""websearch - single MARC tag, partial phrase query (245__b)"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=245__b%3A%27and%27',
expected_text="[28]"))
def test_many_marc_tags_partial_phrase_query(self):
"""websearch - many MARC tags, partial phrase query (245)"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=245%3A%27and%27',
expected_text="[1, 8, 9, 14, 15, 20, 22, 24, 28, 33, 47, 48, 49, 51, 53, 64, 69, 71, 79, 82, 83, 85, 91, 96]"))
def test_single_marc_tag_regexp_query(self):
"""websearch - single MARC tag, regexp query"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=245%3A%2Fand%2F',
expected_text="[1, 8, 9, 14, 15, 20, 22, 24, 28, 33, 47, 48, 49, 51, 53, 64, 69, 71, 79, 82, 83, 85, 91, 96]"))
class WebSearchExtSysnoQueryTest(unittest.TestCase):
"""Test of queries using external system numbers."""
def test_existing_sysno_html_output(self):
"""websearch - external sysno query, existing sysno, HTML output"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?sysno=000289446CER',
expected_text="The wall of the cave"))
def test_existing_sysno_id_output(self):
"""websearch - external sysno query, existing sysno, ID output"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?sysno=000289446CER&of=id',
expected_text="[95]"))
def test_nonexisting_sysno_html_output(self):
"""websearch - external sysno query, non-existing sysno, HTML output"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?sysno=000289446CERRRR',
expected_text="Requested record does not seem to exist."))
def test_nonexisting_sysno_id_output(self):
"""websearch - external sysno query, non-existing sysno, ID output"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?sysno=000289446CERRRR&of=id',
expected_text="[]"))
class WebSearchResultsRecordGroupingTest(unittest.TestCase):
"""Test search results page record grouping (rg)."""
def test_search_results_rg_guest(self):
"""websearch - search results, records in groups of, guest"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?rg=17',
expected_text="1 - 17"))
def test_search_results_rg_nonguest(self):
"""websearch - search results, records in groups of, non-guest"""
# This test used to fail due to saved user preference fetching
# not overridden by URL rg argument.
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?rg=17',
username='admin',
expected_text="1 - 17"))
class WebSearchSpecialTermsQueryTest(unittest.TestCase):
"""Test of the search results for queries containing special terms."""
def test_special_terms_u1(self):
"""websearch - query for special terms, U(1)"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=U%281%29',
expected_text="[57, 79, 80, 88]"))
def test_special_terms_u1_and_sl(self):
"""websearch - query for special terms, U(1) SL(2,Z)"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=U%281%29+SL%282%2CZ%29',
expected_text="[88]"))
def test_special_terms_u1_and_sl_or(self):
"""websearch - query for special terms, U(1) OR SL(2,Z)"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=U%281%29+OR+SL%282%2CZ%29',
expected_text="[57, 79, 80, 88]"))
def test_special_terms_u1_and_sl_or_parens(self):
"""websearch - query for special terms, (U(1) OR SL(2,Z))"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=%28U%281%29+OR+SL%282%2CZ%29%29',
expected_text="[57, 79, 80, 88]"))
class WebSearchJournalQueryTest(unittest.TestCase):
"""Test of the search results for journal pubinfo queries."""
def test_query_journal_title_only(self):
"""websearch - journal publication info query, title only"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&f=journal&p=Phys.+Lett.+B',
expected_text="[77, 78, 85, 87]"))
def test_query_journal_full_pubinfo(self):
"""websearch - journal publication info query, full reference"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&f=journal&p=Phys.+Lett.+B+531+%282002%29+301',
expected_text="[78]"))
class WebSearchStemmedIndexQueryTest(unittest.TestCase):
"""Test of the search results for queries using stemmed indexes."""
def test_query_stemmed_lowercase(self):
"""websearch - stemmed index query, lowercase"""
# note that dasse/Dasse is stemmed into dass/Dass, as expected
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=dasse',
expected_text="[25, 26]"))
def test_query_stemmed_uppercase(self):
"""websearch - stemmed index query, uppercase"""
# ... but note also that DASSE is stemmed into DASSE(!); so
# the test would fail if the search engine would not lower the
# query term. (Something that is not necessary for
# non-stemmed indexes.)
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?of=id&p=DASSE',
expected_text="[25, 26]"))
class WebSearchSummarizerTest(unittest.TestCase):
"""Test of the search results summarizer functions."""
def test_most_popular_field_values_singletag(self):
"""websearch - most popular field values, simple tag"""
from invenio.search_engine import get_most_popular_field_values
self.assertEqual((('PREPRINT', 37), ('ARTICLE', 28), ('BOOK', 14), ('THESIS', 8), ('PICTURE', 7), ('POETRY', 2), ('REPORT', 2), ('ATLANTISTIMESNEWS', 1)),
get_most_popular_field_values(range(0,100), '980__a'))
def test_most_popular_field_values_singletag_multiexclusion(self):
"""websearch - most popular field values, simple tag, multiple exclusions"""
from invenio.search_engine import get_most_popular_field_values
self.assertEqual((('PREPRINT', 37), ('ARTICLE', 28), ('BOOK', 14), ('REPORT', 2), ('ATLANTISTIMESNEWS', 1)),
get_most_popular_field_values(range(0,100), '980__a', ('THESIS', 'PICTURE', 'POETRY')))
def test_most_popular_field_values_multitag(self):
"""websearch - most popular field values, multiple tags"""
from invenio.search_engine import get_most_popular_field_values
self.assertEqual((('Ellis, J', 3), ('Enqvist, K', 1), ('Ibanez, L E', 1), ('Nanopoulos, D V', 1), ('Ross, G G', 1)),
get_most_popular_field_values((9, 14, 18), ('100__a', '700__a')))
def test_most_popular_field_values_multitag_singleexclusion(self):
"""websearch - most popular field values, multiple tags, single exclusion"""
from invenio.search_engine import get_most_popular_field_values
self.assertEqual((('Enqvist, K', 1), ('Ibanez, L E', 1), ('Nanopoulos, D V', 1), ('Ross, G G', 1)),
get_most_popular_field_values((9, 14, 18), ('100__a', '700__a'), ('Ellis, J')))
def test_most_popular_field_values_multitag_countrepetitive(self):
"""websearch - most popular field values, multiple tags, counting repetitive occurrences"""
from invenio.search_engine import get_most_popular_field_values
self.assertEqual((('THESIS', 2), ('REPORT', 1)),
get_most_popular_field_values((41,), ('690C_a', '980__a'), count_repetitive_values=True))
self.assertEqual((('REPORT', 1), ('THESIS', 1)),
get_most_popular_field_values((41,), ('690C_a', '980__a'), count_repetitive_values=False))
def test_ellis_citation_summary(self):
"""websearch - query ellis, citation summary output format"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis&of=hcs',
expected_text="Less known papers (1-9)",
expected_link_target=CFG_SITE_URL+"/search?p=ellis%20AND%20cited%3A1-%3E9&rm=citation",
expected_link_label='1'))
def test_ellis_not_quark_citation_summary_advanced(self):
"""websearch - ellis and not quark, citation summary format advanced"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?ln=en&as=1&m1=a&p1=ellis&f1=author&op1=n&m2=a&p2=quark&f2=&op2=a&m3=a&p3=&f3=&action_search=Search&sf=&so=a&rm=&rg=10&sc=1&of=hcs',
expected_text="Less known papers (1-9)",
expected_link_target=CFG_SITE_URL+'/search?p=author%3Aellis%20and%20not%20quark%20AND%20cited%3A1-%3E9&rm=citation',
expected_link_label='1'))
def test_ellis_not_quark_citation_summary_regular(self):
"""websearch - ellis and not quark, citation summary format advanced"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?ln=en&p=author%3Aellis+and+not+quark&f=&action_search=Search&sf=&so=d&rm=&rg=10&sc=0&of=hcs',
expected_text="Less known papers (1-9)",
expected_link_target=CFG_SITE_URL+'/search?p=author%3Aellis%20and%20not%20quark%20AND%20cited%3A1-%3E9&rm=citation',
expected_link_label='1'))
class WebSearchRecordCollectionGuessTest(unittest.TestCase):
"""Primary collection guessing tests."""
def test_guess_primary_collection_of_a_record(self):
"""websearch - guess_primary_collection_of_a_record"""
self.assertEqual(guess_primary_collection_of_a_record(96), 'Articles')
def test_guess_collection_of_a_record(self):
"""websearch - guess_collection_of_a_record"""
self.assertEqual(guess_collection_of_a_record(96), 'Articles')
self.assertEqual(guess_collection_of_a_record(96, '%s/collection/Theoretical Physics (TH)?ln=en' % CFG_SITE_URL), 'Articles')
self.assertEqual(guess_collection_of_a_record(12, '%s/collection/Theoretical Physics (TH)?ln=en' % CFG_SITE_URL), 'Theoretical Physics (TH)')
self.assertEqual(guess_collection_of_a_record(12, '%s/collection/Theoretical%%20Physics%%20%%28TH%%29?ln=en' % CFG_SITE_URL), 'Theoretical Physics (TH)')
class WebSearchGetFieldValuesTest(unittest.TestCase):
"""Testing get_fieldvalues() function."""
def test_get_fieldvalues_001(self):
"""websearch - get_fieldvalues() for bibxxx-agnostic tags"""
self.assertEqual(get_fieldvalues(10, '001___'), ['10'])
def test_get_fieldvalues_980(self):
"""websearch - get_fieldvalues() for bibxxx-powered tags"""
self.assertEqual(get_fieldvalues(18, '700__a'), ['Enqvist, K', 'Nanopoulos, D V'])
self.assertEqual(get_fieldvalues(18, '909C1u'), ['CERN'])
def test_get_fieldvalues_wildcard(self):
"""websearch - get_fieldvalues() for tag wildcards"""
self.assertEqual(get_fieldvalues(18, '%'), [])
self.assertEqual(get_fieldvalues(18, '7%'), [])
self.assertEqual(get_fieldvalues(18, '700%'), ['Enqvist, K', 'Nanopoulos, D V'])
self.assertEqual(get_fieldvalues(18, '909C0%'), ['1985', '13','TH'])
def test_get_fieldvalues_recIDs(self):
"""websearch - get_fieldvalues() for list of recIDs"""
self.assertEqual(get_fieldvalues([], '001___'), [])
self.assertEqual(get_fieldvalues([], '700__a'), [])
- self.assertEqual(get_fieldvalues('10', '001___'), ['10'])
self.assertEqual(get_fieldvalues([10, 13], '001___'), ['10', '13'])
self.assertEqual(get_fieldvalues([18, 13], '700__a'),
['Dawson, S', 'Ellis, R K', 'Enqvist, K', 'Nanopoulos, D V'])
def test_get_fieldvalues_repetitive(self):
"""websearch - get_fieldvalues() for repetitive values"""
self.assertEqual(get_fieldvalues([17, 18], '909C1u'),
['CERN', 'CERN'])
self.assertEqual(get_fieldvalues([17, 18], '909C1u', repetitive_values=True),
['CERN', 'CERN'])
self.assertEqual(get_fieldvalues([17, 18], '909C1u', repetitive_values=False),
['CERN'])
class WebSearchAddToBasketTest(unittest.TestCase):
"""Test of the add-to-basket presence depending on user rights."""
def test_add_to_basket_guest(self):
"""websearch - add-to-basket facility allowed for guests"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A10',
expected_text='Add to basket'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A10',
expected_text='<input name="recid" type="checkbox" value="10" />'))
def test_add_to_basket_jekyll(self):
"""websearch - add-to-basket facility allowed for Dr. Jekyll"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A10',
expected_text='Add to basket',
username='jekyll',
password='j123ekyll'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A10',
expected_text='<input name="recid" type="checkbox" value="10" />',
username='jekyll',
password='j123ekyll'))
def test_add_to_basket_hyde(self):
"""websearch - add-to-basket facility denied to Mr. Hyde"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A10',
unexpected_text='Add to basket',
username='hyde',
password='h123yde'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A10',
unexpected_text='<input name="recid" type="checkbox" value="10" />',
username='hyde',
password='h123yde'))
class WebSearchAlertTeaserTest(unittest.TestCase):
"""Test of the alert teaser presence depending on user rights."""
def test_alert_teaser_guest(self):
"""websearch - alert teaser allowed for guests"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis',
expected_link_label='email alert'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis',
expected_text='RSS feed'))
def test_alert_teaser_jekyll(self):
"""websearch - alert teaser allowed for Dr. Jekyll"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis',
expected_text='email alert',
username='jekyll',
password='j123ekyll'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis',
expected_text='RSS feed',
username='jekyll',
password='j123ekyll'))
def test_alert_teaser_hyde(self):
"""websearch - alert teaser allowed for Mr. Hyde"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis',
expected_text='email alert',
username='hyde',
password='h123yde'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=ellis',
expected_text='RSS feed',
username='hyde',
password='h123yde'))
class WebSearchSpanQueryTest(unittest.TestCase):
"""Test of span queries."""
def test_span_in_word_index(self):
"""websearch - span query in a word index"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=year%3A1992-%3E1996&of=id&ap=0',
expected_text='[17, 66, 69, 71]'))
def test_span_in_phrase_index(self):
"""websearch - span query in a phrase index"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=year%3A%221992%22-%3E%221996%22&of=id&ap=0',
expected_text='[17, 66, 69, 71]'))
def test_span_in_bibxxx(self):
"""websearch - span query in MARC tables"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=909C0y%3A%221992%22-%3E%221996%22&of=id&ap=0',
expected_text='[17, 66, 69, 71]'))
def test_span_with_spaces(self):
"""websearch - no span query when a space is around"""
# useful for reaction search
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=title%3A%27mu%20--%3E%20e%27&of=id&ap=0',
expected_text='[67]'))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=245%3A%27mu%20--%3E%20e%27&of=id&ap=0',
expected_text='[67]'))
def test_span_in_author(self):
"""websearch - span query in special author index"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=author%3A%22Ellis,%20K%22-%3E%22Ellis,%20RZ%22&of=id&ap=0',
expected_text='[8, 11, 13, 17, 47]'))
class WebSearchReferstoCitedbyTest(unittest.TestCase):
"""Test of refersto/citedby search operators."""
def test_refersto_recid(self):
'websearch - refersto:recid:84'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=refersto%3Arecid%3A84&of=id&ap=0',
expected_text='[85, 88, 91]'))
def test_refersto_repno(self):
'websearch - refersto:reportnumber:hep-th/0205061'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=refersto%3Areportnumber%3Ahep-th/0205061&of=id&ap=0',
expected_text='[91]'))
def test_refersto_author_word(self):
'websearch - refersto:author:klebanov'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=refersto%3Aauthor%3Aklebanov&of=id&ap=0',
expected_text='[85, 86, 88, 91]'))
def test_refersto_author_phrase(self):
'websearch - refersto:author:"Klebanov, I"'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=refersto%3Aauthor%3A%22Klebanov,%20I%22&of=id&ap=0',
expected_text='[85, 86, 88, 91]'))
def test_citedby_recid(self):
'websearch - citedby:recid:92'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=citedby%3Arecid%3A92&of=id&ap=0',
expected_text='[74, 91]'))
def test_citedby_repno(self):
'websearch - citedby:reportnumber:hep-th/0205061'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=citedby%3Areportnumber%3Ahep-th/0205061&of=id&ap=0',
expected_text='[78]'))
def test_citedby_author_word(self):
'websearch - citedby:author:klebanov'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=citedby%3Aauthor%3Aklebanov&of=id&ap=0',
expected_text='[95]'))
def test_citedby_author_phrase(self):
'websearch - citedby:author:"Klebanov, I"'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=citedby%3Aauthor%3A%22Klebanov,%20I%22&of=id&ap=0',
expected_text='[95]'))
class WebSearchSPIRESSyntaxTest(unittest.TestCase):
"""Test of SPIRES syntax issues"""
def test_and_not_parens(self):
'websearch - find a ellis, j and not a enqvist'
self.assertEqual([],
test_web_page_content(CFG_SITE_URL +'/search?p=find+a+ellis%2C+j+and+not+a+enqvist&of=id&ap=0',
expected_text='[9, 12, 14, 47]'))
def test_dadd_search(self):
'websearch - find da > today - 3650'
# XXX: assumes we've reinstalled our site in the last 10 years
# should return every document in the system
self.assertEqual([],
test_web_page_content(CFG_SITE_URL +'/search?ln=en&p=find+da+%3E+today+-+3650&f=&of=id',
expected_text='[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104]'))
class WebSearchDateQueryTest(unittest.TestCase):
"""Test various date queries."""
def setUp(self):
"""Establish variables we plan to re-use"""
from invenio.intbitset import intbitset as HitSet
self.empty = HitSet()
def test_search_unit_hits_for_datecreated_previous_millenia(self):
"""websearch - search_unit with datecreated returns >0 hits for docs in the last 1000 years"""
self.assertNotEqual(self.empty, search_unit('1000-01-01->9999', 'datecreated'))
def test_search_unit_hits_for_datemodified_previous_millenia(self):
"""websearch - search_unit with datemodified returns >0 hits for docs in the last 1000 years"""
self.assertNotEqual(self.empty, search_unit('1000-01-01->9999', 'datemodified'))
def test_search_unit_in_bibrec_for_datecreated_previous_millenia(self):
"""websearch - search_unit_in_bibrec with creationdate gets >0 hits for past 1000 years"""
self.assertNotEqual(self.empty, search_unit_in_bibrec("1000-01-01", "9999-12-31", 'creationdate'))
def test_search_unit_in_bibrec_for_datecreated_next_millenia(self):
"""websearch - search_unit_in_bibrec with creationdate gets 0 hits for after year 3000"""
self.assertEqual(self.empty, search_unit_in_bibrec("3000-01-01", "9999-12-31", 'creationdate'))
class WebSearchSynonymQueryTest(unittest.TestCase):
"""Test of queries using synonyms."""
def test_journal_phrvd(self):
"""websearch - search-time synonym search, journal title"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=PHRVD&f=journal&of=id',
expected_text="[66, 72]"))
def test_journal_phrvd_54_1996_4234(self):
"""websearch - search-time synonym search, journal article"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=PHRVD%2054%20%281996%29%204234&f=journal&of=id',
expected_text="[66]"))
def test_journal_beta_decay_title(self):
"""websearch - index-time synonym search, beta decay in title"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=beta+decay&f=title&of=id',
expected_text="[59]"))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%CE%B2+decay&f=title&of=id',
expected_text="[59]"))
def test_journal_beta_decay_global(self):
"""websearch - index-time synonym search, beta decay in any field"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=beta+decay&of=id',
expected_text="[52, 59]"))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%CE%B2+decay&of=id',
expected_text="[52, 59]"))
def test_journal_beta_title(self):
"""websearch - index-time synonym search, beta in title"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=beta&f=title&of=id',
expected_text="[59]"))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%CE%B2&f=title&of=id',
expected_text="[59]"))
def test_journal_beta_global(self):
"""websearch - index-time synonym search, beta in any field"""
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=beta&of=id',
expected_text="[52, 59]"))
self.assertEqual([],
test_web_page_content(CFG_SITE_URL + '/search?p=%CE%B2&of=id',
expected_text="[52, 59]"))
class WebSearchWashCollectionsTest(unittest.TestCase):
"""Test if the collection argument is washed correctly"""
def test_wash_coll_when_coll_restricted(self):
"""websearch - washing of restricted daughter collections"""
self.assertEqual(
sorted(wash_colls(cc='', c=['Books & Reports', 'Theses'])[1]),
['Books & Reports', 'Theses'])
self.assertEqual(
sorted(wash_colls(cc='', c=['Books & Reports', 'Theses'])[2]),
['Books & Reports', 'Theses'])
TEST_SUITE = make_test_suite(WebSearchWebPagesAvailabilityTest,
WebSearchTestSearch,
WebSearchTestBrowse,
WebSearchTestOpenURL,
WebSearchTestCollections,
WebSearchTestRecord,
WebSearchTestLegacyURLs,
WebSearchNearestTermsTest,
WebSearchBooleanQueryTest,
WebSearchAuthorQueryTest,
WebSearchSearchEnginePythonAPITest,
WebSearchSearchEngineWebAPITest,
WebSearchRestrictedCollectionTest,
WebSearchRestrictedPicturesTest,
WebSearchRSSFeedServiceTest,
WebSearchXSSVulnerabilityTest,
WebSearchResultsOverview,
WebSearchSortResultsTest,
WebSearchSearchResultsXML,
WebSearchUnicodeQueryTest,
WebSearchMARCQueryTest,
WebSearchExtSysnoQueryTest,
WebSearchResultsRecordGroupingTest,
WebSearchSpecialTermsQueryTest,
WebSearchJournalQueryTest,
WebSearchStemmedIndexQueryTest,
WebSearchSummarizerTest,
WebSearchRecordCollectionGuessTest,
WebSearchGetFieldValuesTest,
WebSearchAddToBasketTest,
WebSearchAlertTeaserTest,
WebSearchSpanQueryTest,
WebSearchReferstoCitedbyTest,
WebSearchSPIRESSyntaxTest,
WebSearchDateQueryTest,
WebSearchTestWildcardLimit,
WebSearchSynonymQueryTest,
WebSearchWashCollectionsTest)
if __name__ == "__main__":
run_test_suite(TEST_SUITE, warn_user=True)
diff --git a/modules/websearch/lib/websearch_templates.py b/modules/websearch/lib/websearch_templates.py
index 08906d73d..c5a77ad22 100644
--- a/modules/websearch/lib/websearch_templates.py
+++ b/modules/websearch/lib/websearch_templates.py
@@ -1,4376 +1,4377 @@
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0301
__revision__ = "$Id$"
import time
import cgi
import string
import re
import locale
from urllib import quote, urlencode
from xml.sax.saxutils import escape as xml_escape
from invenio.config import \
CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH, \
CFG_WEBSEARCH_SIMPLESEARCH_PATTERN_BOX_WIDTH, \
CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH, \
CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD, \
CFG_WEBSEARCH_USE_ALEPH_SYSNOS, \
CFG_WEBSEARCH_SPLIT_BY_COLLECTION, \
CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS, \
CFG_BIBRANK_SHOW_READING_STATS, \
CFG_BIBRANK_SHOW_DOWNLOAD_STATS, \
CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS, \
CFG_BIBRANK_SHOW_CITATION_LINKS, \
CFG_BIBRANK_SHOW_CITATION_STATS, \
CFG_BIBRANK_SHOW_CITATION_GRAPHS, \
CFG_WEBSEARCH_RSS_TTL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_VERSION, \
CFG_SITE_URL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_ADMIN_EMAIL, \
CFG_INSPIRE_SITE, \
CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE, \
CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES, \
CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS, \
CFG_BIBINDEX_CHARS_PUNCTUATION, \
CFG_WEBCOMMENT_ALLOW_COMMENTS, \
CFG_WEBCOMMENT_ALLOW_REVIEWS, \
CFG_WEBSEARCH_WILDCARD_LIMIT,\
CFG_WEBSEARCH_SHOW_COMMENT_COUNT, \
- CFG_WEBSEARCH_SHOW_REVIEW_COUNT
+ CFG_WEBSEARCH_SHOW_REVIEW_COUNT, \
+ CFG_SITE_RECORD
from invenio.dbquery import run_sql
from invenio.messages import gettext_set_language
from invenio.urlutils import make_canonical_urlargd, drop_default_urlargd, create_html_link, create_url
from invenio.htmlutils import nmtoken_from_string
from invenio.webinterface_handler import wash_urlargd
from invenio.bibrank_citation_searcher import get_cited_by_count
from invenio.intbitset import intbitset
from invenio.websearch_external_collections import external_collection_get_state, get_external_collection_engine
from invenio.websearch_external_collections_utils import get_collection_id
from invenio.websearch_external_collections_config import CFG_EXTERNAL_COLLECTION_MAXRESULTS
_RE_PUNCTUATION = re.compile(CFG_BIBINDEX_CHARS_PUNCTUATION)
_RE_SPACES = re.compile(r"\s+")
def get_fieldvalues(recID, tag):
"""Return list of field values for field TAG inside record RECID.
FIXME: should be imported commonly for search_engine too."""
out = []
if tag == "001___":
# we have asked for recID that is not stored in bibXXx tables
out.append(str(recID))
else:
# we are going to look inside bibXXx tables
digit = tag[0:2]
bx = "bib%sx" % digit
bibx = "bibrec_bib%sx" % digit
query = "SELECT bx.value FROM %s AS bx, %s AS bibx WHERE bibx.id_bibrec='%s' AND bx.id=bibx.id_bibxxx AND bx.tag LIKE '%s'" \
"ORDER BY bibx.field_number, bx.tag ASC" % (bx, bibx, recID, tag)
res = run_sql(query)
for row in res:
out.append(row[0])
return out
class Template:
# This dictionary maps Invenio language code to locale codes (ISO 639)
tmpl_localemap = {
'bg': 'bg_BG',
'ca': 'ca_ES',
'de': 'de_DE',
'el': 'el_GR',
'en': 'en_US',
'es': 'es_ES',
'pt': 'pt_BR',
'fr': 'fr_FR',
'it': 'it_IT',
'ka': 'ka_GE',
'lt': 'lt_LT',
'ro': 'ro_RO',
'ru': 'ru_RU',
'rw': 'rw_RW',
'sk': 'sk_SK',
'cs': 'cs_CZ',
'no': 'no_NO',
'sv': 'sv_SE',
'uk': 'uk_UA',
'ja': 'ja_JA',
'pl': 'pl_PL',
'hr': 'hr_HR',
'zh_CN': 'zh_CN',
'zh_TW': 'zh_TW',
'hu': 'hu_HU',
'af': 'af_ZA',
'gl': 'gl_ES'
}
tmpl_default_locale = "en_US" # which locale to use by default, useful in case of failure
# Type of the allowed parameters for the web interface for search results
search_results_default_urlargd = {
'cc': (str, CFG_SITE_NAME),
'c': (list, []),
'p': (str, ""), 'f': (str, ""),
'rg': (int, CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS),
'sf': (str, ""),
'so': (str, "d"),
'sp': (str, ""),
'rm': (str, ""),
'of': (str, "hb"),
'ot': (list, []),
'aas': (int, CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE),
'as': (int, CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE),
'p1': (str, ""), 'f1': (str, ""), 'm1': (str, ""), 'op1':(str, ""),
'p2': (str, ""), 'f2': (str, ""), 'm2': (str, ""), 'op2':(str, ""),
'p3': (str, ""), 'f3': (str, ""), 'm3': (str, ""),
'sc': (int, 0),
'jrec': (int, 0),
'recid': (int, -1), 'recidb': (int, -1), 'sysno': (str, ""),
'id': (int, -1), 'idb': (int, -1), 'sysnb': (str, ""),
'action': (str, "search"),
'action_search': (str, ""),
'action_browse': (str, ""),
'd1': (str, ""),
'd1y': (int, 0), 'd1m': (int, 0), 'd1d': (int, 0),
'd2': (str, ""),
'd2y': (int, 0), 'd2m': (int, 0), 'd2d': (int, 0),
'dt': (str, ""),
'ap': (int, 1),
'verbose': (int, 0),
'ec': (list, []),
'wl': (int, CFG_WEBSEARCH_WILDCARD_LIMIT),
}
# ...and for search interfaces
search_interface_default_urlargd = {
'aas': (int, CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE),
'as': (int, CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE),
'verbose': (int, 0)}
# ...and for RSS feeds
rss_default_urlargd = {'c' : (list, []),
'cc' : (str, ""),
'p' : (str, ""),
'f' : (str, ""),
'p1' : (str, ""),
'f1' : (str, ""),
'm1' : (str, ""),
'op1': (str, ""),
'p2' : (str, ""),
'f2' : (str, ""),
'm2' : (str, ""),
'op2': (str, ""),
'p3' : (str, ""),
'f3' : (str, ""),
'm3' : (str, "")}
tmpl_openurl_accepted_args = {
'id' : (list, []),
'genre' : (str, ''),
'aulast' : (str, ''),
'aufirst' : (str, ''),
'auinit' : (str, ''),
'auinit1' : (str, ''),
'auinitm' : (str, ''),
'issn' : (str, ''),
'eissn' : (str, ''),
'coden' : (str, ''),
'isbn' : (str, ''),
'sici' : (str, ''),
'bici' : (str, ''),
'title' : (str, ''),
'stitle' : (str, ''),
'atitle' : (str, ''),
'volume' : (str, ''),
'part' : (str, ''),
'issue' : (str, ''),
'spage' : (str, ''),
'epage' : (str, ''),
'pages' : (str, ''),
'artnum' : (str, ''),
'date' : (str, ''),
'ssn' : (str, ''),
'quarter' : (str, ''),
'url_ver' : (str, ''),
'ctx_ver' : (str, ''),
'rft_val_fmt' : (str, ''),
'rft_id' : (list, []),
'rft.atitle' : (str, ''),
'rft.title' : (str, ''),
'rft.jtitle' : (str, ''),
'rft.stitle' : (str, ''),
'rft.date' : (str, ''),
'rft.volume' : (str, ''),
'rft.issue' : (str, ''),
'rft.spage' : (str, ''),
'rft.epage' : (str, ''),
'rft.pages' : (str, ''),
'rft.artnumber' : (str, ''),
'rft.issn' : (str, ''),
'rft.eissn' : (str, ''),
'rft.aulast' : (str, ''),
'rft.aufirst' : (str, ''),
'rft.auinit' : (str, ''),
'rft.auinit1' : (str, ''),
'rft.auinitm' : (str, ''),
'rft.ausuffix' : (str, ''),
'rft.au' : (list, []),
'rft.aucorp' : (str, ''),
'rft.isbn' : (str, ''),
'rft.coden' : (str, ''),
'rft.sici' : (str, ''),
'rft.genre' : (str, 'unknown'),
'rft.chron' : (str, ''),
'rft.ssn' : (str, ''),
'rft.quarter' : (int, ''),
'rft.part' : (str, ''),
'rft.btitle' : (str, ''),
'rft.isbn' : (str, ''),
'rft.atitle' : (str, ''),
'rft.place' : (str, ''),
'rft.pub' : (str, ''),
'rft.edition' : (str, ''),
'rft.tpages' : (str, ''),
'rft.series' : (str, ''),
}
tmpl_opensearch_rss_url_syntax = "%(CFG_SITE_URL)s/rss?p={searchTerms}&amp;jrec={startIndex}&amp;rg={count}&amp;ln={language}" % {'CFG_SITE_URL': CFG_SITE_URL}
tmpl_opensearch_html_url_syntax = "%(CFG_SITE_URL)s/search?p={searchTerms}&amp;jrec={startIndex}&amp;rg={count}&amp;ln={language}" % {'CFG_SITE_URL': CFG_SITE_URL}
def tmpl_openurl2invenio(self, openurl_data):
""" Return an Invenio url corresponding to a search with the data
included in the openurl form map.
"""
def isbn_to_isbn13_isbn10(isbn):
isbn = isbn.replace(' ', '').replace('-', '')
if len(isbn) == 10 and isbn.isdigit():
## We already have isbn10
return ('', isbn)
if len(isbn) != 13 and isbn.isdigit():
return ('', '')
isbn13, isbn10 = isbn, isbn[3:-1]
checksum = 0
weight = 10
for char in isbn10:
checksum += int(char) * weight
weight -= 1
checksum = 11 - (checksum % 11)
if checksum == 10:
isbn10 += 'X'
if checksum == 11:
isbn10 += '0'
else:
isbn10 += str(checksum)
return (isbn13, isbn10)
from invenio.search_engine import perform_request_search
doi = ''
pmid = ''
bibcode = ''
oai = ''
issn = ''
isbn = ''
for elem in openurl_data['id']:
if elem.startswith('doi:'):
doi = elem[len('doi:'):]
elif elem.startswith('pmid:'):
pmid = elem[len('pmid:'):]
elif elem.startswith('bibcode:'):
bibcode = elem[len('bibcode:'):]
elif elem.startswith('oai:'):
oai = elem[len('oai:'):]
for elem in openurl_data['rft_id']:
if elem.startswith('info:doi/'):
doi = elem[len('info:doi/'):]
elif elem.startswith('info:pmid/'):
pmid = elem[len('info:pmid/'):]
elif elem.startswith('info:bibcode/'):
bibcode = elem[len('info:bibcode/'):]
elif elem.startswith('info:oai/'):
oai = elem[len('info:oai/')]
elif elem.startswith('urn:ISBN:'):
isbn = elem[len('urn:ISBN:'):]
elif elem.startswith('urn:ISSN:'):
issn = elem[len('urn:ISSN:'):]
## Building author query
aulast = openurl_data['rft.aulast'] or openurl_data['aulast']
aufirst = openurl_data['rft.aufirst'] or openurl_data['aufirst']
auinit = openurl_data['rft.auinit'] or \
openurl_data['auinit'] or \
openurl_data['rft.auinit1'] + ' ' + openurl_data['rft.auinitm'] or \
openurl_data['auinit1'] + ' ' + openurl_data['auinitm'] or aufirst[:1]
auinit = auinit.upper()
if aulast and aufirst:
author_query = 'author:"%s, %s" or author:"%s, %s"' % (aulast, aufirst, aulast, auinit)
elif aulast and auinit:
author_query = 'author:"%s, %s"' % (aulast, auinit)
else:
author_query = ''
## Building title query
title = openurl_data['rft.atitle'] or \
openurl_data['atitle'] or \
openurl_data['rft.btitle'] or \
openurl_data['rft.title'] or \
openurl_data['title']
if title:
title_query = 'title:"%s"' % title
title_query_cleaned = 'title:"%s"' % _RE_SPACES.sub(' ', _RE_PUNCTUATION.sub(' ', title))
else:
title_query = ''
## Building journal query
jtitle = openurl_data['rft.stitle'] or \
openurl_data['stitle'] or \
openurl_data['rft.jtitle'] or \
openurl_data['title']
if jtitle:
journal_query = 'journal:"%s"' % jtitle
else:
journal_query = ''
## Building isbn query
isbn = isbn or openurl_data['rft.isbn'] or \
openurl_data['isbn']
isbn13, isbn10 = isbn_to_isbn13_isbn10(isbn)
if isbn13:
isbn_query = 'isbn:"%s" or isbn:"%s"' % (isbn13, isbn10)
elif isbn10:
isbn_query = 'isbn:"%s"' % isbn10
else:
isbn_query = ''
## Building issn query
issn = issn or openurl_data['rft.eissn'] or \
openurl_data['eissn'] or \
openurl_data['rft.issn'] or \
openurl_data['issn']
if issn:
issn_query = 'issn:"%s"' % issn
else:
issn_query = ''
## Building coden query
coden = openurl_data['rft.coden'] or openurl_data['coden']
if coden:
coden_query = 'coden:"%s"' % coden
else:
coden_query = ''
## Building doi query
if False: #doi: #FIXME Temporaly disabled until doi field is properly setup
doi_query = 'doi:"%s"' % doi
else:
doi_query = ''
## Trying possible searches
if doi_query:
if perform_request_search(p=doi_query):
return '%s/search?%s' % (CFG_SITE_URL, urlencode({
'p' : doi_query,
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hd'}))
if isbn_query:
if perform_request_search(p=isbn_query):
return '%s/search?%s' % (CFG_SITE_URL, urlencode({
'p' : isbn_query,
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hd'}))
if coden_query:
if perform_request_search(p=coden_query):
return '%s/search?%s' % (CFG_SITE_URL, urlencode({
'p' : coden_query,
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hd'}))
if author_query and title_query:
if perform_request_search(p='%s and %s' % (title_query, author_query)):
return '%s/search?%s' % (CFG_SITE_URL, urlencode({
'p' : '%s and %s' % (title_query, author_query),
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hd'}))
if title_query:
result = len(perform_request_search(p=title_query))
if result == 1:
return '%s/search?%s' % (CFG_SITE_URL, urlencode({
'p' : title_query,
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hd'}))
elif result > 1:
return '%s/search?%s' % (CFG_SITE_URL, urlencode({
'p' : title_query,
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hb'}))
## Nothing worked, let's return a search that the user can improve
if author_query and title_query:
return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : '%s and %s' % (title_query_cleaned, author_query),
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hb'}, {}))
elif title_query:
return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : title_query_cleaned,
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hb'}, {}))
else:
## Mmh. Too few information provided.
return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : 'recid:-1',
'sc' : CFG_WEBSEARCH_SPLIT_BY_COLLECTION,
'of' : 'hb'}, {}))
def tmpl_opensearch_description(self, ln):
""" Returns the OpenSearch description file of this site.
"""
_ = gettext_set_language(ln)
return """<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>%(short_name)s</ShortName>
<LongName>%(long_name)s</LongName>
<Description>%(description)s</Description>
<InputEncoding>UTF-8</InputEncoding>
<OutputEncoding>UTF-8</OutputEncoding>
<Language>*</Language>
<Contact>%(CFG_SITE_ADMIN_EMAIL)s</Contact>
<Query role="example" searchTerms="a" />
<Developer>Powered by Invenio</Developer>
<Url type="text/html" indexOffset="1" rel="results" template="%(html_search_syntax)s" />
<Url type="application/rss+xml" indexOffset="1" rel="results" template="%(rss_search_syntax)s" />
<Url type="application/opensearchdescription+xml" rel="self" template="%(CFG_SITE_URL)s/opensearchdescription" />
<moz:SearchForm>%(CFG_SITE_URL)s</moz:SearchForm>
</OpenSearchDescription>""" % \
{'CFG_SITE_URL': CFG_SITE_URL,
'short_name': CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)[:16],
'long_name': CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
'description': (_("Search on %(x_CFG_SITE_NAME_INTL)s") % \
{'x_CFG_SITE_NAME_INTL': CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)})[:1024],
'CFG_SITE_ADMIN_EMAIL': CFG_SITE_ADMIN_EMAIL,
'rss_search_syntax': self.tmpl_opensearch_rss_url_syntax,
'html_search_syntax': self.tmpl_opensearch_html_url_syntax
}
def build_search_url(self, known_parameters={}, **kargs):
""" Helper for generating a canonical search
url. 'known_parameters' is the list of query parameters you
inherit from your current query. You can then pass keyword
arguments to modify this query.
build_search_url(known_parameters, of="xm")
The generated URL is absolute.
"""
parameters = {}
parameters.update(known_parameters)
parameters.update(kargs)
# Now, we only have the arguments which have _not_ their default value
parameters = drop_default_urlargd(parameters, self.search_results_default_urlargd)
# Treat `as' argument specially:
if parameters.has_key('aas'):
parameters['as'] = parameters['aas']
del parameters['aas']
- # Asking for a recid? Return a /record/<recid> URL
+ # Asking for a recid? Return a /CFG_SITE_RECORD/<recid> URL
if 'recid' in parameters:
- target = "%s/record/%s" % (CFG_SITE_URL, parameters['recid'])
+ target = "%s/%s/%s" % (CFG_SITE_URL, CFG_SITE_RECORD, parameters['recid'])
del parameters['recid']
target += make_canonical_urlargd(parameters, self.search_results_default_urlargd)
return target
return "%s/search%s" % (CFG_SITE_URL, make_canonical_urlargd(parameters, self.search_results_default_urlargd))
def build_search_interface_url(self, known_parameters={}, **kargs):
""" Helper for generating a canonical search interface URL."""
parameters = {}
parameters.update(known_parameters)
parameters.update(kargs)
c = parameters['c']
del parameters['c']
# Now, we only have the arguments which have _not_ their default value
parameters = drop_default_urlargd(parameters, self.search_results_default_urlargd)
# Treat `as' argument specially:
if parameters.has_key('aas'):
parameters['as'] = parameters['aas']
del parameters['aas']
if c and c != CFG_SITE_NAME:
base = CFG_SITE_URL + '/collection/' + quote(c)
else:
base = CFG_SITE_URL
return create_url(base, parameters)
def build_rss_url(self, known_parameters, **kargs):
"""Helper for generating a canonical RSS URL"""
parameters = {}
parameters.update(known_parameters)
parameters.update(kargs)
# Keep only interesting parameters
argd = wash_urlargd(parameters, self.rss_default_urlargd)
if argd:
# Handle 'c' differently since it is a list
c = argd.get('c', [])
del argd['c']
# Create query, and drop empty params
args = make_canonical_urlargd(argd, self.rss_default_urlargd)
if c != []:
# Add collections
c = [quote(coll) for coll in c]
if args == '':
args += '?'
else:
args += '&amp;'
args += 'c=' + '&amp;c='.join(c)
return CFG_SITE_URL + '/rss' + args
def tmpl_record_page_header_content(self, req, recid, ln):
- """ Provide extra information in the header of /record pages """
+ """ Provide extra information in the header of /CFG_SITE_RECORD pages """
_ = gettext_set_language(ln)
title = get_fieldvalues(recid, "245__a")
if title:
title = cgi.escape(title[0])
else:
title = _("Record") + ' #%d' % recid
keywords = ', '.join(get_fieldvalues(recid, "6531_a"))
description = ' '.join(get_fieldvalues(recid, "520__a"))
description += "\n"
description += '; '.join(get_fieldvalues(recid, "100__a") + get_fieldvalues(recid, "700__a"))
return [cgi.escape(x, True) for x in (title, description, keywords)]
def tmpl_navtrail_links(self, aas, ln, dads):
"""
Creates the navigation bar at top of each search page (*Home > Root collection > subcollection > ...*)
Parameters:
- 'aas' *int* - Should we display an advanced search box?
- 'ln' *string* - The language to display
- 'separator' *string* - The separator between two consecutive collections
- 'dads' *list* - A list of parent links, eachone being a dictionary of ('name', 'longname')
"""
out = []
for url, name in dads:
args = {'c': url, 'as': aas, 'ln': ln}
out.append(create_html_link(self.build_search_interface_url(**args), {}, cgi.escape(name), {'class': 'navtrail'}))
return ' &gt; '.join(out)
def tmpl_webcoll_body(self, ln, collection, te_portalbox,
searchfor, np_portalbox, narrowsearch,
focuson, instantbrowse, ne_portalbox):
""" Creates the body of the main search page.
Parameters:
- 'ln' *string* - language of the page being generated
- 'collection' - collection id of the page being generated
- 'te_portalbox' *string* - The HTML code for the portalbox on top of search
- 'searchfor' *string* - The HTML code for the search for box
- 'np_portalbox' *string* - The HTML code for the portalbox on bottom of search
- 'narrowsearch' *string* - The HTML code for the search categories (left bottom of page)
- 'focuson' *string* - The HTML code for the "focuson" categories (right bottom of page)
- 'ne_portalbox' *string* - The HTML code for the bottom of the page
"""
if not narrowsearch:
narrowsearch = instantbrowse
body = '''
<form name="search" action="%(siteurl)s/search" method="get">
%(searchfor)s
%(np_portalbox)s
<table cellspacing="0" cellpadding="0" border="0" class="narrowandfocusonsearchbox">
<tr>
<td valign="top">%(narrowsearch)s</td>
''' % {
'siteurl' : CFG_SITE_URL,
'searchfor' : searchfor,
'np_portalbox' : np_portalbox,
'narrowsearch' : narrowsearch,
}
if focuson:
body += """<td valign="top">""" + focuson + """</td>"""
body += """</tr></table>
%(ne_portalbox)s
</form>""" % {'ne_portalbox' : ne_portalbox}
return body
def tmpl_portalbox(self, title, body):
"""Creates portalboxes based on the parameters
Parameters:
- 'title' *string* - The title of the box
- 'body' *string* - The HTML code for the body of the box
"""
out = """<div class="portalbox">
<div class="portalboxheader">%(title)s</div>
<div class="portalboxbody">%(body)s</div>
</div>""" % {'title' : cgi.escape(title), 'body' : body}
return out
def tmpl_searchfor_light(self, ln, collection_id, collection_name, record_count,
example_search_queries): # EXPERIMENTAL
"""Produces light *Search for* box for the current collection.
Parameters:
- 'ln' *string* - *str* The language to display
- 'collection_id' - *str* The collection id
- 'collection_name' - *str* The collection name in current language
- 'example_search_queries' - *list* List of search queries given as example for this collection
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''
<!--create_searchfor_light()-->
'''
argd = drop_default_urlargd({'ln': ln, 'sc': CFG_WEBSEARCH_SPLIT_BY_COLLECTION},
self.search_results_default_urlargd)
# Only add non-default hidden values
for field, value in argd.items():
out += self.tmpl_input_hidden(field, value)
header = _("Search %s records for:") % \
self.tmpl_nbrecs_info(record_count, "", "")
asearchurl = self.build_search_interface_url(c=collection_id,
aas=max(CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES),
ln=ln)
# Build example of queries for this collection
example_search_queries_links = [create_html_link(self.build_search_url(p=example_query,
ln=ln,
aas= -1,
c=collection_id),
{},
cgi.escape(example_query),
{'class': 'examplequery'}) \
for example_query in example_search_queries]
example_query_html = ''
if len(example_search_queries) > 0:
example_query_link = example_search_queries_links[0]
# offers more examples if possible
more = ''
if len(example_search_queries_links) > 1:
more = '''
<script type="text/javascript">
function toggle_more_example_queries_visibility(){
var more = document.getElementById('more_example_queries');
var link = document.getElementById('link_example_queries');
var sep = document.getElementById('more_example_sep');
if (more.style.display=='none'){
more.style.display = '';
link.innerHTML = "%(show_less)s"
link.style.color = "rgb(204,0,0)";
sep.style.display = 'none';
} else {
more.style.display = 'none';
link.innerHTML = "%(show_more)s"
link.style.color = "rgb(0,0,204)";
sep.style.display = '';
}
return false;
}
</script>
<span id="more_example_queries" style="display:none;text-align:right"><br/>%(more_example_queries)s<br/></span>
<a id="link_example_queries" href="#" onclick="toggle_more_example_queries_visibility()" style="display:none"></a>
<script type="text/javascript">
var link = document.getElementById('link_example_queries');
var sep = document.getElementById('more_example_sep');
link.style.display = '';
link.innerHTML = "%(show_more)s";
sep.style.display = '';
</script>
''' % {'more_example_queries': '<br/>'.join(example_search_queries_links[1:]),
'show_less':_("less"),
'show_more':_("more")}
example_query_html += '''<p style="text-align:right;margin:0px;">
%(example)s<span id="more_example_sep" style="display:none;">&nbsp;&nbsp;::&nbsp;</span>%(more)s
</p>
''' % {'example': _("Example: %(x_sample_search_query)s") % \
{'x_sample_search_query': example_query_link},
'more': more}
# display options to search in current collection or everywhere
search_in = ''
if collection_name != CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME):
search_in += '''
<input type="radio" name="cc" value="%(collection_id)s" id="searchCollection" checked="checked"/>
<label for="searchCollection">%(search_in_collection_name)s</label>
<input type="radio" name="cc" value="%(root_collection_name)s" id="searchEverywhere" />
<label for="searchEverywhere">%(search_everywhere)s</label>
''' % {'search_in_collection_name': _("Search in %(x_collection_name)s") % \
{'x_collection_name': collection_name},
'collection_id': collection_id,
'root_collection_name': CFG_SITE_NAME,
'search_everywhere': _("Search everywhere")}
# print commentary start:
out += '''
<table class="searchbox lightsearch">
<tbody>
<tr valign="baseline">
<td class="searchboxbody" align="right"><input type="text" name="p" size="%(sizepattern)d" value="" class="lightsearchfield"/><br/>
<small><small>%(example_query_html)s</small></small>
</td>
<td class="searchboxbody" align="left">
<input class="formbutton" type="submit" name="action_search" value="%(msg_search)s" />
</td>
<td class="searchboxbody" align="left" rowspan="2" valign="top">
<small><small>
<a href="%(siteurl)s/help/search-tips%(langlink)s">%(msg_search_tips)s</a><br/>
%(asearch)s
</small></small>
</td>
</tr></table>
<!--<tr valign="baseline">
<td class="searchboxbody" colspan="2" align="left">
<small>
--><small>%(search_in)s</small><!--
</small>
</td>
</tr>
</tbody>
</table>-->
<!--/create_searchfor_light()-->
''' % {'ln' : ln,
'sizepattern' : CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'siteurl' : CFG_SITE_URL,
'asearch' : create_html_link(asearchurl, {}, _('Advanced Search')),
'header' : header,
'msg_search' : _('Search'),
'msg_browse' : _('Browse'),
'msg_search_tips' : _('Search Tips'),
'search_in': search_in,
'example_query_html': example_query_html}
return out
def tmpl_searchfor_simple(self, ln, collection_id, collection_name, record_count, middle_option):
"""Produces simple *Search for* box for the current collection.
Parameters:
- 'ln' *string* - *str* The language to display
- 'collection_id' - *str* The collection id
- 'collection_name' - *str* The collection name in current language
- 'record_count' - *str* Number of records in this collection
- 'middle_option' *string* - HTML code for the options (any field, specific fields ...)
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''
<!--create_searchfor_simple()-->
'''
argd = drop_default_urlargd({'ln': ln, 'cc': collection_id, 'sc': CFG_WEBSEARCH_SPLIT_BY_COLLECTION},
self.search_results_default_urlargd)
# Only add non-default hidden values
for field, value in argd.items():
out += self.tmpl_input_hidden(field, value)
header = _("Search %s records for:") % \
self.tmpl_nbrecs_info(record_count, "", "")
asearchurl = self.build_search_interface_url(c=collection_id,
aas=max(CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES),
ln=ln)
# print commentary start:
out += '''
<table class="searchbox simplesearch">
<thead>
<tr align="left">
<th colspan="3" class="searchboxheader">%(header)s</th>
</tr>
</thead>
<tbody>
<tr valign="baseline">
<td class="searchboxbody" align="left"><input type="text" name="p" size="%(sizepattern)d" value="" class="simplesearchfield"/></td>
<td class="searchboxbody" align="left">%(middle_option)s</td>
<td class="searchboxbody" align="left">
<input class="formbutton" type="submit" name="action_search" value="%(msg_search)s" />
<input class="formbutton" type="submit" name="action_browse" value="%(msg_browse)s" /></td>
</tr>
<tr valign="baseline">
<td class="searchboxbody" colspan="3" align="right">
<small>
<a href="%(siteurl)s/help/search-tips%(langlink)s">%(msg_search_tips)s</a> ::
%(asearch)s
</small>
</td>
</tr>
</tbody>
</table>
<!--/create_searchfor_simple()-->
''' % {'ln' : ln,
'sizepattern' : CFG_WEBSEARCH_SIMPLESEARCH_PATTERN_BOX_WIDTH,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'siteurl' : CFG_SITE_URL,
'asearch' : create_html_link(asearchurl, {}, _('Advanced Search')),
'header' : header,
'middle_option' : middle_option,
'msg_search' : _('Search'),
'msg_browse' : _('Browse'),
'msg_search_tips' : _('Search Tips')}
return out
def tmpl_searchfor_advanced(self,
ln, # current language
collection_id,
collection_name,
record_count,
middle_option_1, middle_option_2, middle_option_3,
searchoptions,
sortoptions,
rankoptions,
displayoptions,
formatoptions
):
"""
Produces advanced *Search for* box for the current collection.
Parameters:
- 'ln' *string* - The language to display
- 'middle_option_1' *string* - HTML code for the first row of options (any field, specific fields ...)
- 'middle_option_2' *string* - HTML code for the second row of options (any field, specific fields ...)
- 'middle_option_3' *string* - HTML code for the third row of options (any field, specific fields ...)
- 'searchoptions' *string* - HTML code for the search options
- 'sortoptions' *string* - HTML code for the sort options
- 'rankoptions' *string* - HTML code for the rank options
- 'displayoptions' *string* - HTML code for the display options
- 'formatoptions' *string* - HTML code for the format options
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''
<!--create_searchfor_advanced()-->
'''
argd = drop_default_urlargd({'ln': ln, 'aas': 1, 'cc': collection_id, 'sc': CFG_WEBSEARCH_SPLIT_BY_COLLECTION},
self.search_results_default_urlargd)
# Only add non-default hidden values
for field, value in argd.items():
out += self.tmpl_input_hidden(field, value)
header = _("Search %s records for") % \
self.tmpl_nbrecs_info(record_count, "", "")
header += ':'
ssearchurl = self.build_search_interface_url(c=collection_id, aas=min(CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES), ln=ln)
out += '''
<table class="searchbox advancedsearch">
<thead>
<tr>
<th class="searchboxheader" colspan="3">%(header)s</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody" style="white-space: nowrap;">
%(matchbox_m1)s<input type="text" name="p1" size="%(sizepattern)d" value="" class="advancedsearchfield"/>
</td>
<td class="searchboxbody" style="white-space: nowrap;">%(middle_option_1)s</td>
<td class="searchboxbody">%(andornot_op1)s</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody" style="white-space: nowrap;">
%(matchbox_m2)s<input type="text" name="p2" size="%(sizepattern)d" value="" class="advancedsearchfield"/>
</td>
<td class="searchboxbody">%(middle_option_2)s</td>
<td class="searchboxbody">%(andornot_op2)s</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody" style="white-space: nowrap;">
%(matchbox_m3)s<input type="text" name="p3" size="%(sizepattern)d" value="" class="advancedsearchfield"/>
</td>
<td class="searchboxbody">%(middle_option_3)s</td>
<td class="searchboxbody" style="white-space: nowrap;">
<input class="formbutton" type="submit" name="action_search" value="%(msg_search)s" />
<input class="formbutton" type="submit" name="action_browse" value="%(msg_browse)s" /></td>
</tr>
<tr valign="bottom">
<td colspan="3" class="searchboxbody" align="right">
<small>
<a href="%(siteurl)s/help/search-tips%(langlink)s">%(msg_search_tips)s</a> ::
%(ssearch)s
</small>
</td>
</tr>
</tbody>
</table>
<!-- @todo - more imports -->
''' % {'ln' : ln,
'sizepattern' : CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'siteurl' : CFG_SITE_URL,
'ssearch' : create_html_link(ssearchurl, {}, _("Simple Search")),
'header' : header,
'matchbox_m1' : self.tmpl_matchtype_box('m1', ln=ln),
'middle_option_1' : middle_option_1,
'andornot_op1' : self.tmpl_andornot_box('op1', ln=ln),
'matchbox_m2' : self.tmpl_matchtype_box('m2', ln=ln),
'middle_option_2' : middle_option_2,
'andornot_op2' : self.tmpl_andornot_box('op2', ln=ln),
'matchbox_m3' : self.tmpl_matchtype_box('m3', ln=ln),
'middle_option_3' : middle_option_3,
'msg_search' : _("Search"),
'msg_browse' : _("Browse"),
'msg_search_tips' : _("Search Tips")}
if (searchoptions):
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(searchheader)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">%(searchoptions)s</td>
</tr>
</tbody>
</table>""" % {
'searchheader' : _("Search options:"),
'searchoptions' : searchoptions
}
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(added)s
</th>
<th class="searchboxheader">
%(until)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">%(added_or_modified)s %(date_added)s</td>
<td class="searchboxbody">%(date_until)s</td>
</tr>
</tbody>
</table>
<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(msg_sort)s
</th>
<th class="searchboxheader">
%(msg_display)s
</th>
<th class="searchboxheader">
%(msg_format)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">%(sortoptions)s %(rankoptions)s</td>
<td class="searchboxbody">%(displayoptions)s</td>
<td class="searchboxbody">%(formatoptions)s</td>
</tr>
</tbody>
</table>
<!--/create_searchfor_advanced()-->
""" % {
'added' : _("Added/modified since:"),
'until' : _("until:"),
'added_or_modified': self.tmpl_inputdatetype(ln=ln),
'date_added' : self.tmpl_inputdate("d1", ln=ln),
'date_until' : self.tmpl_inputdate("d2", ln=ln),
'msg_sort' : _("Sort by:"),
'msg_display' : _("Display results:"),
'msg_format' : _("Output format:"),
'sortoptions' : sortoptions,
'rankoptions' : rankoptions,
'displayoptions' : displayoptions,
'formatoptions' : formatoptions
}
return out
def tmpl_matchtype_box(self, name='m', value='', ln='en'):
"""Returns HTML code for the 'match type' selection box.
Parameters:
- 'name' *string* - The name of the produced select
- 'value' *string* - The selected value (if any value is already selected)
- 'ln' *string* - the language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<select name="%(name)s">
<option value="a"%(sela)s>%(opta)s</option>
<option value="o"%(selo)s>%(opto)s</option>
<option value="e"%(sele)s>%(opte)s</option>
<option value="p"%(selp)s>%(optp)s</option>
<option value="r"%(selr)s>%(optr)s</option>
</select>
""" % {'name' : name,
'sela' : self.tmpl_is_selected('a', value),
'opta' : _("All of the words:"),
'selo' : self.tmpl_is_selected('o', value),
'opto' : _("Any of the words:"),
'sele' : self.tmpl_is_selected('e', value),
'opte' : _("Exact phrase:"),
'selp' : self.tmpl_is_selected('p', value),
'optp' : _("Partial phrase:"),
'selr' : self.tmpl_is_selected('r', value),
'optr' : _("Regular expression:")
}
return out
def tmpl_is_selected(self, var, fld):
"""
Checks if *var* and *fld* are equal, and if yes, returns ' selected="selected"'. Useful for select boxes.
Parameters:
- 'var' *string* - First value to compare
- 'fld' *string* - Second value to compare
"""
if var == fld:
return ' selected="selected"'
else:
return ""
def tmpl_andornot_box(self, name='op', value='', ln='en'):
"""
Returns HTML code for the AND/OR/NOT selection box.
Parameters:
- 'name' *string* - The name of the produced select
- 'value' *string* - The selected value (if any value is already selected)
- 'ln' *string* - the language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<select name="%(name)s">
<option value="a"%(sela)s>%(opta)s</option>
<option value="o"%(selo)s>%(opto)s</option>
<option value="n"%(seln)s>%(optn)s</option>
</select>
""" % {'name' : name,
'sela' : self.tmpl_is_selected('a', value), 'opta' : _("AND"),
'selo' : self.tmpl_is_selected('o', value), 'opto' : _("OR"),
'seln' : self.tmpl_is_selected('n', value), 'optn' : _("AND NOT")
}
return out
def tmpl_inputdate(self, name, ln, sy=0, sm=0, sd=0):
"""
Produces *From Date*, *Until Date* kind of selection box. Suitable for search options.
Parameters:
- 'name' *string* - The base name of the produced selects
- 'ln' *string* - the language to display
"""
# load the right message language
_ = gettext_set_language(ln)
box = """
<select name="%(name)sd">
<option value=""%(sel)s>%(any)s</option>
""" % {
'name' : name,
'any' : _("any day"),
'sel' : self.tmpl_is_selected(sd, 0)
}
for day in range(1, 32):
box += """<option value="%02d"%s>%02d</option>""" % (day, self.tmpl_is_selected(sd, day), day)
box += """</select>"""
# month
box += """
<select name="%(name)sm">
<option value=""%(sel)s>%(any)s</option>
""" % {
'name' : name,
'any' : _("any month"),
'sel' : self.tmpl_is_selected(sm, 0)
}
for mm, month in [(1, _("January")), (2, _("February")), (3, _("March")), (4, _("April")), \
(5, _("May")), (6, _("June")), (7, _("July")), (8, _("August")), \
(9, _("September")), (10, _("October")), (11, _("November")), (12, _("December"))]:
box += """<option value="%02d"%s>%s</option>""" % (mm, self.tmpl_is_selected(sm, mm), month)
box += """</select>"""
# year
box += """
<select name="%(name)sy">
<option value=""%(sel)s>%(any)s</option>
""" % {
'name' : name,
'any' : _("any year"),
'sel' : self.tmpl_is_selected(sy, 0)
}
this_year = int(time.strftime("%Y", time.localtime()))
for year in range(this_year - 20, this_year + 1):
box += """<option value="%d"%s>%d</option>""" % (year, self.tmpl_is_selected(sy, year), year)
box += """</select>"""
return box
def tmpl_inputdatetype(self, dt='', ln=CFG_SITE_LANG):
"""
Produces input date type selection box to choose
added-or-modified date search option.
Parameters:
- 'dt' *string - date type (c=created, m=modified)
- 'ln' *string* - the language to display
"""
# load the right message language
_ = gettext_set_language(ln)
box = """<select name="dt">
<option value="">%(added)s </option>
<option value="m"%(sel)s>%(modified)s </option>
</select>
""" % { 'added': _("Added since:"),
'modified': _("Modified since:"),
'sel': self.tmpl_is_selected(dt, 'm'),
}
return box
def tmpl_narrowsearch(self, aas, ln, type, father,
has_grandchildren, sons, display_grandsons,
grandsons):
"""
Creates list of collection descendants of type *type* under title *title*.
If aas==1, then links to Advanced Search interfaces; otherwise Simple Search.
Suitable for 'Narrow search' and 'Focus on' boxes.
Parameters:
- 'aas' *bool* - Should we display an advanced search box?
- 'ln' *string* - The language to display
- 'type' *string* - The type of the produced box (virtual collections or normal collections)
- 'father' *collection* - The current collection
- 'has_grandchildren' *bool* - If the current collection has grand children
- 'sons' *list* - The list of the sub-collections (first level)
- 'display_grandsons' *bool* - If the grand children collections should be displayed (2 level deep display)
- 'grandsons' *list* - The list of sub-collections (second level)
"""
# load the right message language
_ = gettext_set_language(ln)
title = {'r': _("Narrow by collection:"),
'v': _("Focus on:")}[type]
if has_grandchildren:
style_prolog = "<strong>"
style_epilog = "</strong>"
else:
style_prolog = ""
style_epilog = ""
out = """<table class="%(narrowsearchbox)s">
<thead>
<tr>
<th colspan="2" align="left" class="%(narrowsearchbox)sheader">
%(title)s
</th>
</tr>
</thead>
<tbody>""" % {'title' : title,
'narrowsearchbox': {'r': 'narrowsearchbox',
'v': 'focusonsearchbox'}[type]}
# iterate through sons:
i = 0
for son in sons:
out += """<tr><td class="%(narrowsearchbox)sbody" valign="top">""" % \
{ 'narrowsearchbox': {'r': 'narrowsearchbox',
'v': 'focusonsearchbox'}[type]}
if type == 'r':
if son.restricted_p() and son.restricted_p() != father.restricted_p():
out += """<input type="checkbox" name="c" value="%(name)s" /></td>""" % {'name' : cgi.escape(son.name) }
# hosted collections are checked by default only when configured so
elif str(son.dbquery).startswith("hostedcollection:"):
external_collection_engine = get_external_collection_engine(str(son.name))
if external_collection_engine and external_collection_engine.selected_by_default:
out += """<input type="checkbox" name="c" value="%(name)s" checked="checked" /></td>""" % {'name' : cgi.escape(son.name) }
elif external_collection_engine and not external_collection_engine.selected_by_default:
out += """<input type="checkbox" name="c" value="%(name)s" /></td>""" % {'name' : cgi.escape(son.name) }
else:
# strangely, the external collection engine was never found. In that case,
# why was the hosted collection here in the first place?
out += """<input type="checkbox" name="c" value="%(name)s" /></td>""" % {'name' : cgi.escape(son.name) }
else:
out += """<input type="checkbox" name="c" value="%(name)s" checked="checked" /></td>""" % {'name' : cgi.escape(son.name) }
else:
out += '</td>'
out += """<td valign="top">%(link)s%(recs)s """ % {
'link': create_html_link(self.build_search_interface_url(c=son.name, ln=ln, aas=aas),
{}, style_prolog + cgi.escape(son.get_name(ln)) + style_epilog),
'recs' : self.tmpl_nbrecs_info(son.nbrecs, ln=ln)}
# the following prints the "external collection" arrow just after the name and
# number of records of the hosted collection
# 1) we might want to make the arrow work as an anchor to the hosted collection as well.
# That would probably require a new separate function under invenio.urlutils
# 2) we might want to place the arrow between the name and the number of records of the hosted collection
# That would require to edit/separate the above out += ...
if type == 'r':
if str(son.dbquery).startswith("hostedcollection:"):
out += """<img src="%(siteurl)s/img/external-icon-light-8x8.gif" border="0" alt="%(name)s"/>""" % \
{ 'siteurl' : CFG_SITE_URL, 'name' : cgi.escape(son.name), }
if son.restricted_p():
out += """ <small class="warning">[%(msg)s]</small> """ % { 'msg' : _("restricted") }
if display_grandsons and len(grandsons[i]):
# iterate trough grandsons:
out += """<br />"""
for grandson in grandsons[i]:
out += """ <small>%(link)s%(nbrec)s</small> """ % {
'link': create_html_link(self.build_search_interface_url(c=grandson.name, ln=ln, aas=aas),
{},
cgi.escape(grandson.get_name(ln))),
'nbrec' : self.tmpl_nbrecs_info(grandson.nbrecs, ln=ln)}
# the following prints the "external collection" arrow just after the name and
# number of records of the hosted collection
# Some relatives comments have been made just above
if type == 'r':
if str(grandson.dbquery).startswith("hostedcollection:"):
out += """<img src="%(siteurl)s/img/external-icon-light-8x8.gif" border="0" alt="%(name)s"/>""" % \
{ 'siteurl' : CFG_SITE_URL, 'name' : cgi.escape(grandson.name), }
out += """</td></tr>"""
i += 1
out += "</tbody></table>"
return out
def tmpl_searchalso(self, ln, engines_list, collection_id):
_ = gettext_set_language(ln)
box_name = _("Search also:")
html = """<table cellspacing="0" cellpadding="0" border="0">
<tr><td valign="top"><table class="searchalsosearchbox">
<thead><tr><th colspan="2" align="left" class="searchalsosearchboxheader">%(box_name)s
</th></tr></thead><tbody>
""" % locals()
for engine in engines_list:
internal_name = engine.name
name = _(internal_name)
base_url = engine.base_url
if external_collection_get_state(engine, collection_id) == 3:
checked = ' checked="checked"'
else:
checked = ''
html += """<tr><td class="searchalsosearchboxbody" valign="top">
<input type="checkbox" name="ec" id="%(id)s" value="%(internal_name)s" %(checked)s /></td>
<td valign="top" class="searchalsosearchboxbody">
<div style="white-space: nowrap"><label for="%(id)s">%(name)s</label>
<a href="%(base_url)s">
<img src="%(siteurl)s/img/external-icon-light-8x8.gif" border="0" alt="%(name)s"/></a>
</div></td></tr>""" % \
{ 'checked': checked,
'base_url': base_url,
'internal_name': internal_name,
'name': cgi.escape(name),
'id': "extSearch" + nmtoken_from_string(name),
'siteurl': CFG_SITE_URL, }
html += """</tbody></table></td></tr></table>"""
return html
def tmpl_nbrecs_info(self, number, prolog=None, epilog=None, ln=CFG_SITE_LANG):
"""
Return information on the number of records.
Parameters:
- 'number' *string* - The number of records
- 'prolog' *string* (optional) - An HTML code to prefix the number (if **None**, will be
'<small class="nbdoccoll">(')
- 'epilog' *string* (optional) - An HTML code to append to the number (if **None**, will be
')</small>')
"""
if number is None:
number = 0
if prolog is None:
prolog = '''&nbsp;<small class="nbdoccoll">('''
if epilog is None:
epilog = ''')</small>'''
return prolog + self.tmpl_nice_number(number, ln) + epilog
def tmpl_box_restricted_content(self, ln):
"""
Displays a box containing a *restricted content* message
Parameters:
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
return _("This collection is restricted. If you are authorized to access it, please click on the Search button.")
def tmpl_box_hosted_collection(self, ln):
"""
Displays a box containing a *hosted collection* message
Parameters:
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
return _("This is a hosted external collection. Please click on the Search button to see its content.")
def tmpl_box_no_records(self, ln):
"""
Displays a box containing a *no content* message
Parameters:
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
return _("This collection does not contain any document yet.")
def tmpl_instant_browse(self, aas, ln, recids, more_link=None):
"""
Formats a list of records (given in the recids list) from the database.
Parameters:
- 'aas' *int* - Advanced Search interface or not (0 or 1)
- 'ln' *string* - The language to display
- 'recids' *list* - the list of records from the database
- 'more_link' *string* - the "More..." link for the record. If not given, will not be displayed
"""
# load the right message language
_ = gettext_set_language(ln)
body = '''<table class="latestadditionsbox">'''
for recid in recids:
body += '''
<tr>
<td class="latestadditionsboxtimebody">%(date)s</td>
<td class="latestadditionsboxrecordbody">
<abbr class="unapi-id" title="%(recid)s"></abbr>
%(body)s
</td>
</tr>''' % {
'recid': recid['id'],
'date': recid['date'],
'body': recid['body']
}
body += "</table>"
if more_link:
body += '<div align="right"><small>' + \
create_html_link(more_link, {}, '[&gt;&gt; %s]' % _("more")) + \
'</small></div>'
return '''
<table class="narrowsearchbox">
<thead>
<tr>
<th class="narrowsearchboxheader">%(header)s</th>
</tr>
</thead>
<tbody>
<tr>
<td class="narrowsearchboxbody">%(body)s</td>
</tr>
</tbody>
</table>''' % {'header' : _("Latest additions:"),
'body' : body,
}
def tmpl_searchwithin_select(self, ln, fieldname, selected, values):
"""
Produces 'search within' selection box for the current collection.
Parameters:
- 'ln' *string* - The language to display
- 'fieldname' *string* - the name of the select box produced
- 'selected' *string* - which of the values is selected
- 'values' *list* - the list of values in the select
"""
out = '<select name="%(fieldname)s">' % {'fieldname': fieldname}
if values:
for pair in values:
out += """<option value="%(value)s"%(selected)s>%(text)s</option>""" % {
'value' : cgi.escape(pair['value']),
'selected' : self.tmpl_is_selected(pair['value'], selected),
'text' : cgi.escape(pair['text'])
}
out += """</select>"""
return out
def tmpl_select(self, fieldname, values, selected=None, css_class=''):
"""
Produces a generic select box
Parameters:
- 'css_class' *string* - optional, a css class to display this select with
- 'fieldname' *list* - the name of the select box produced
- 'selected' *string* - which of the values is selected
- 'values' *list* - the list of values in the select
"""
if css_class != '':
class_field = ' class="%s"' % css_class
else:
class_field = ''
out = '<select name="%(fieldname)s"%(class)s>' % {
'fieldname' : fieldname,
'class' : class_field
}
for pair in values:
if pair.get('selected', False) or pair['value'] == selected:
flag = ' selected="selected"'
else:
flag = ''
out += '<option value="%(value)s"%(selected)s>%(text)s</option>' % {
'value' : cgi.escape(str(pair['value'])),
'selected' : flag,
'text' : cgi.escape(pair['text'])
}
out += """</select>"""
return out
def tmpl_record_links(self, recid, ln, sf='', so='d', sp='', rm=''):
"""
Displays the *More info* and *Find similar* links for a record
Parameters:
- 'ln' *string* - The language to display
- 'recid' *string* - the id of the displayed record
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''<br /><span class="moreinfo">%(detailed)s - %(similar)s</span>''' % {
'detailed': create_html_link(self.build_search_url(recid=recid, ln=ln),
{},
_("Detailed record"), {'class': "moreinfo"}),
'similar': create_html_link(self.build_search_url(p="recid:%d" % recid, rm='wrd', ln=ln),
{},
_("Similar records"),
{'class': "moreinfo"})}
if CFG_BIBRANK_SHOW_CITATION_LINKS:
num_timescited = get_cited_by_count(recid)
if num_timescited:
out += '''<span class="moreinfo"> - %s </span>''' % \
create_html_link(self.build_search_url(p='refersto:recid:%d' % recid,
sf=sf,
so=so,
sp=sp,
rm=rm,
ln=ln),
{}, _("Cited by %i records") % num_timescited, {'class': "moreinfo"})
return out
def tmpl_record_body(self, titles, authors, dates, rns, abstracts, urls_u, urls_z, ln):
"""
Displays the "HTML basic" format of a record
Parameters:
- 'authors' *list* - the authors (as strings)
- 'dates' *list* - the dates of publication
- 'rns' *list* - the quicknotes for the record
- 'abstracts' *list* - the abstracts for the record
- 'urls_u' *list* - URLs to the original versions of the record
- 'urls_z' *list* - Not used
"""
out = ""
for title in titles:
out += "<strong>%(title)s</strong> " % {
'title' : cgi.escape(title)
}
if authors:
out += " / "
for author in authors[:CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD]:
out += '%s ' % \
create_html_link(self.build_search_url(p=author, f='author', ln=ln),
{}, cgi.escape(author))
if len(authors) > CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD:
out += "<em>et al</em>"
for date in dates:
out += " %s." % cgi.escape(date)
for rn in rns:
out += """ <small class="quicknote">[%(rn)s]</small>""" % {'rn' : cgi.escape(rn)}
for abstract in abstracts:
out += "<br /><small>%(abstract)s [...]</small>" % {'abstract' : cgi.escape(abstract[:1 + string.find(abstract, '.')]) }
for idx in range(0, len(urls_u)):
out += """<br /><small class="note"><a class="note" href="%(url)s">%(name)s</a></small>""" % {
'url' : urls_u[idx],
'name' : urls_u[idx]
}
return out
def tmpl_search_in_bibwords(self, p, f, ln, nearest_box):
"""
Displays the *Words like current ones* links for a search
Parameters:
- 'p' *string* - Current search words
- 'f' *string* - the fields in which the search was done
- 'nearest_box' *string* - the HTML code for the "nearest_terms" box - most probably from a create_nearest_terms_box call
"""
# load the right message language
_ = gettext_set_language(ln)
out = '<p>'
if f:
out += _("Words nearest to %(x_word)s inside %(x_field)s in any collection are:") % {'x_word': '<em>' + cgi.escape(p) + '</em>',
'x_field': '<em>' + cgi.escape(f) + '</em>'}
else:
out += _("Words nearest to %(x_word)s in any collection are:") % {'x_word': '<em>' + cgi.escape(p) + '</em>'}
out += '<br />' + nearest_box + '</p>'
return out
def tmpl_nearest_term_box(self, p, ln, f, terminfo, intro):
"""
Displays the *Nearest search terms* box
Parameters:
- 'p' *string* - Current search words
- 'f' *string* - a collection description (if the search has been completed in a collection)
- 'ln' *string* - The language to display
- 'terminfo': tuple (term, hits, argd) for each near term
- 'intro' *string* - the intro HTML to prefix the box with
"""
out = '''<table class="nearesttermsbox" cellpadding="0" cellspacing="0" border="0">'''
for term, hits, argd in terminfo:
if hits:
hitsinfo = str(hits)
else:
hitsinfo = '-'
term = cgi.escape(term)
if term == p: # print search word for orientation:
nearesttermsboxbody_class = "nearesttermsboxbodyselected"
if hits > 0:
term = create_html_link(self.build_search_url(argd), {},
term, {'class': "nearesttermsselected"})
else:
nearesttermsboxbody_class = "nearesttermsboxbody"
term = create_html_link(self.build_search_url(argd), {},
term, {'class': "nearestterms"})
out += '''\
<tr>
<td class="%(nearesttermsboxbody_class)s" align="right">%(hits)s</td>
<td class="%(nearesttermsboxbody_class)s" width="15">&nbsp;</td>
<td class="%(nearesttermsboxbody_class)s" align="left">%(term)s</td>
</tr>
''' % {'hits': hitsinfo,
'nearesttermsboxbody_class': nearesttermsboxbody_class,
'term': term}
out += "</table>"
return intro + "<blockquote>" + out + "</blockquote>"
def tmpl_browse_pattern(self, f, fn, ln, browsed_phrases_in_colls, colls, rg):
"""
Displays the *Nearest search terms* box
Parameters:
- 'f' *string* - field (*not* i18nized)
- 'fn' *string* - field name (i18nized)
- 'ln' *string* - The language to display
- 'browsed_phrases_in_colls' *array* - the phrases to display
- 'colls' *array* - the list of collection parameters of the search (c's)
- 'rg' *int* - the number of records
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<table class="searchresultsbox">
<thead>
<tr>
<th class="searchresultsboxheader" style="text-align: right;" width="15">
%(hits)s
</th>
<th class="searchresultsboxheader" width="15">
&nbsp;
</th>
<th class="searchresultsboxheader" style="text-align: left;">
%(fn)s
</th>
</tr>
</thead>
<tbody>""" % {
'hits' : _("Hits"),
'fn' : cgi.escape(fn)
}
if len(browsed_phrases_in_colls) == 1:
# one hit only found:
phrase, nbhits = browsed_phrases_in_colls[0][0], browsed_phrases_in_colls[0][1]
query = {'c': colls,
'ln': ln,
'p': '"%s"' % phrase.replace('"', '\\"'),
'f': f,
'rg' : rg}
out += """<tr>
<td class="searchresultsboxbody" style="text-align: right;">
%(nbhits)s
</td>
<td class="searchresultsboxbody" width="15">
&nbsp;
</td>
<td class="searchresultsboxbody" style="text-align: left;">
%(link)s
</td>
</tr>""" % {'nbhits': nbhits,
'link': create_html_link(self.build_search_url(query),
{}, cgi.escape(phrase))}
elif len(browsed_phrases_in_colls) > 1:
# first display what was found but the last one:
for phrase, nbhits in browsed_phrases_in_colls[:-1]:
query = {'c': colls,
'ln': ln,
'p': '"%s"' % phrase.replace('"', '\\"'),
'f': f,
'rg' : rg}
out += """<tr>
<td class="searchresultsboxbody" style="text-align: right;">
%(nbhits)s
</td>
<td class="searchresultsboxbody" width="15">
&nbsp;
</td>
<td class="searchresultsboxbody" style="text-align: left;">
%(link)s
</td>
</tr>""" % {'nbhits' : nbhits,
'link': create_html_link(self.build_search_url(query),
{},
cgi.escape(phrase))}
# now display last hit as "previous term":
phrase, nbhits = browsed_phrases_in_colls[0]
query_previous = {'c': colls,
'ln': ln,
'p': '"%s"' % phrase.replace('"', '\\"'),
'f': f,
'rg' : rg}
# now display last hit as "next term":
phrase, nbhits = browsed_phrases_in_colls[-1]
query_next = {'c': colls,
'ln': ln,
'p': '"%s"' % phrase.replace('"', '\\"'),
'f': f,
'rg' : rg}
out += """<tr><td colspan="2" class="normal">
&nbsp;
</td>
<td class="normal">
%(link_previous)s
<img src="%(siteurl)s/img/sp.gif" alt="" border="0" />
<img src="%(siteurl)s/img/sn.gif" alt="" border="0" />
%(link_next)s
</td>
</tr>""" % {'link_previous': create_html_link(self.build_search_url(query_previous, action='browse'), {}, _("Previous")),
'link_next': create_html_link(self.build_search_url(query_next, action='browse'),
{}, _("next")),
'siteurl' : CFG_SITE_URL}
out += """</tbody>
</table>"""
return out
def tmpl_search_box(self, ln, aas, cc, cc_intl, ot, sp,
action, fieldslist, f1, f2, f3, m1, m2, m3,
p1, p2, p3, op1, op2, rm, p, f, coll_selects,
d1y, d2y, d1m, d2m, d1d, d2d, dt, sort_fields,
sf, so, ranks, sc, rg, formats, of, pl, jrec, ec,
show_colls=True, show_title=True):
"""
Displays the *Nearest search terms* box
Parameters:
- 'ln' *string* - The language to display
- 'aas' *bool* - Should we display an advanced search box? -1 -> 1, from simpler to more advanced
- 'cc_intl' *string* - the i18nized current collection name, used for display
- 'cc' *string* - the internal current collection name
- 'ot', 'sp' *string* - hidden values
- 'action' *string* - the action demanded by the user
- 'fieldslist' *list* - the list of all fields available, for use in select within boxes in advanced search
- 'p, f, f1, f2, f3, m1, m2, m3, p1, p2, p3, op1, op2, op3, rm' *strings* - the search parameters
- 'coll_selects' *array* - a list of lists, each containing the collections selects to display
- 'd1y, d2y, d1m, d2m, d1d, d2d' *int* - the search between dates
- 'dt' *string* - the dates' types (creation dates, modification dates)
- 'sort_fields' *array* - the select information for the sort fields
- 'sf' *string* - the currently selected sort field
- 'so' *string* - the currently selected sort order ("a" or "d")
- 'ranks' *array* - ranking methods
- 'rm' *string* - selected ranking method
- 'sc' *string* - split by collection or not
- 'rg' *string* - selected results/page
- 'formats' *array* - available output formats
- 'of' *string* - the selected output format
- 'pl' *string* - `limit to' search pattern
- show_colls *bool* - propose coll selection box?
- show_title *bool* show cc_intl in page title?
"""
# load the right message language
_ = gettext_set_language(ln)
# These are hidden fields the user does not manipulate
# directly
if aas == -1:
argd = drop_default_urlargd({
'ln': ln, 'aas': aas,
'ot': ot, 'sp': sp, 'ec': ec,
}, self.search_results_default_urlargd)
else:
argd = drop_default_urlargd({
'cc': cc, 'ln': ln, 'aas': aas,
'ot': ot, 'sp': sp, 'ec': ec,
}, self.search_results_default_urlargd)
out = ""
if show_title:
# display cc name if asked for
out += '''
<h1 class="headline">%(ccname)s</h1>''' % {'ccname' : cgi.escape(cc_intl), }
out += '''
<form name="search" action="%(siteurl)s/search" method="get">
''' % {'siteurl' : CFG_SITE_URL}
# Only add non-default hidden values
for field, value in argd.items():
out += self.tmpl_input_hidden(field, value)
leadingtext = _("Search")
if action == 'browse':
leadingtext = _("Browse")
if aas == 1:
# print Advanced Search form:
# define search box elements:
out += '''
<table class="searchbox advancedsearch">
<thead>
<tr>
<th colspan="3" class="searchboxheader">
%(leading)s:
</th>
</tr>
</thead>
<tbody>
<tr valign="top" style="white-space:nowrap;">
<td class="searchboxbody">%(matchbox1)s
<input type="text" name="p1" size="%(sizepattern)d" value="%(p1)s" class="advancedsearchfield"/>
</td>
<td class="searchboxbody">%(searchwithin1)s</td>
<td class="searchboxbody">%(andornot1)s</td>
</tr>
<tr valign="top">
<td class="searchboxbody">%(matchbox2)s
<input type="text" name="p2" size="%(sizepattern)d" value="%(p2)s" class="advancedsearchfield"/>
</td>
<td class="searchboxbody">%(searchwithin2)s</td>
<td class="searchboxbody">%(andornot2)s</td>
</tr>
<tr valign="top">
<td class="searchboxbody">%(matchbox3)s
<input type="text" name="p3" size="%(sizepattern)d" value="%(p3)s" class="advancedsearchfield"/>
</td>
<td class="searchboxbody">%(searchwithin3)s</td>
<td class="searchboxbody" style="white-space:nowrap;">
<input class="formbutton" type="submit" name="action_search" value="%(search)s" />
<input class="formbutton" type="submit" name="action_browse" value="%(browse)s" />&nbsp;
</td>
</tr>
<tr valign="bottom">
<td colspan="3" align="right" class="searchboxbody">
<small>
<a href="%(siteurl)s/help/search-tips%(langlink)s">%(search_tips)s</a> ::
%(simple_search)s
</small>
</td>
</tr>
</tbody>
</table>
''' % {
'simple_search': create_html_link(self.build_search_url(p=p1, f=f1, rm=rm, cc=cc, ln=ln, jrec=jrec, rg=rg),
{}, _("Simple Search")),
'leading' : leadingtext,
'sizepattern' : CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH,
'matchbox1' : self.tmpl_matchtype_box('m1', m1, ln=ln),
'p1' : cgi.escape(p1, 1),
'searchwithin1' : self.tmpl_searchwithin_select(
ln=ln,
fieldname='f1',
selected=f1,
values=self._add_mark_to_field(value=f1, fields=fieldslist, ln=ln)
),
'andornot1' : self.tmpl_andornot_box(
name='op1',
value=op1,
ln=ln
),
'matchbox2' : self.tmpl_matchtype_box('m2', m2, ln=ln),
'p2' : cgi.escape(p2, 1),
'searchwithin2' : self.tmpl_searchwithin_select(
ln=ln,
fieldname='f2',
selected=f2,
values=self._add_mark_to_field(value=f2, fields=fieldslist, ln=ln)
),
'andornot2' : self.tmpl_andornot_box(
name='op2',
value=op2,
ln=ln
),
'matchbox3' : self.tmpl_matchtype_box('m3', m3, ln=ln),
'p3' : cgi.escape(p3, 1),
'searchwithin3' : self.tmpl_searchwithin_select(
ln=ln,
fieldname='f3',
selected=f3,
values=self._add_mark_to_field(value=f3, fields=fieldslist, ln=ln)
),
'search' : _("Search"),
'browse' : _("Browse"),
'siteurl' : CFG_SITE_URL,
'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'search_tips': _("Search Tips")
}
elif aas == 0:
# print Simple Search form:
out += '''
<table class="searchbox simplesearch">
<thead>
<tr>
<th colspan="3" class="searchboxheader">
%(leading)s:
</th>
</tr>
</thead>
<tbody>
<tr valign="top">
<td class="searchboxbody"><input type="text" name="p" size="%(sizepattern)d" value="%(p)s" class="simplesearchfield"/></td>
<td class="searchboxbody">%(searchwithin)s</td>
<td class="searchboxbody">
<input class="formbutton" type="submit" name="action_search" value="%(search)s" />
<input class="formbutton" type="submit" name="action_browse" value="%(browse)s" />&nbsp;
</td>
</tr>
<tr valign="bottom">
<td colspan="3" align="right" class="searchboxbody">
<small>
<a href="%(siteurl)s/help/search-tips%(langlink)s">%(search_tips)s</a> ::
%(advanced_search)s
</small>
</td>
</tr>
</tbody>
</table>
''' % {
'advanced_search': create_html_link(self.build_search_url(p1=p,
f1=f,
rm=rm,
aas=max(CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES),
cc=cc,
jrec=jrec,
ln=ln,
rg=rg),
{}, _("Advanced Search")),
'leading' : leadingtext,
'sizepattern' : CFG_WEBSEARCH_SIMPLESEARCH_PATTERN_BOX_WIDTH,
'p' : cgi.escape(p, 1),
'searchwithin' : self.tmpl_searchwithin_select(
ln=ln,
fieldname='f',
selected=f,
values=self._add_mark_to_field(value=f, fields=fieldslist, ln=ln)
),
'search' : _("Search"),
'browse' : _("Browse"),
'siteurl' : CFG_SITE_URL,
'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'search_tips': _("Search Tips")
}
else:
# EXPERIMENTAL
# print light search form:
search_in = ''
if cc_intl != CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME):
search_in = '''
<input type="radio" name="cc" value="%(collection_id)s" id="searchCollection" checked="checked"/>
<label for="searchCollection">%(search_in_collection_name)s</label>
<input type="radio" name="cc" value="%(root_collection_name)s" id="searchEverywhere" />
<label for="searchEverywhere">%(search_everywhere)s</label>
''' % {'search_in_collection_name': _("Search in %(x_collection_name)s") % \
{'x_collection_name': cgi.escape(cc_intl)},
'collection_id': cc,
'root_collection_name': CFG_SITE_NAME,
'search_everywhere': _("Search everywhere")}
out += '''
<table class="searchbox lightsearch">
<tr valign="top">
<td class="searchboxbody"><input type="text" name="p" size="%(sizepattern)d" value="%(p)s" class="lightsearchfield"/></td>
<td class="searchboxbody">
<input class="formbutton" type="submit" name="action_search" value="%(search)s" />
</td>
<td class="searchboxbody" align="left" rowspan="2" valign="top">
<small><small>
<a href="%(siteurl)s/help/search-tips%(langlink)s">%(search_tips)s</a><br/>
%(advanced_search)s
</td>
</tr>
</table>
<small>%(search_in)s</small>
''' % {
'sizepattern' : CFG_WEBSEARCH_LIGHTSEARCH_PATTERN_BOX_WIDTH,
'advanced_search': create_html_link(self.build_search_url(p1=p,
f1=f,
rm=rm,
aas=max(CFG_WEBSEARCH_ENABLED_SEARCH_INTERFACES),
cc=cc,
jrec=jrec,
ln=ln,
rg=rg),
{}, _("Advanced Search")),
'leading' : leadingtext,
'p' : cgi.escape(p, 1),
'searchwithin' : self.tmpl_searchwithin_select(
ln=ln,
fieldname='f',
selected=f,
values=self._add_mark_to_field(value=f, fields=fieldslist, ln=ln)
),
'search' : _("Search"),
'browse' : _("Browse"),
'siteurl' : CFG_SITE_URL,
'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'search_tips': _("Search Tips"),
'search_in': search_in
}
## secondly, print Collection(s) box:
if show_colls and aas > -1:
# display collections only if there is more than one
selects = ''
for sel in coll_selects:
selects += self.tmpl_select(fieldname='c', values=sel)
out += """
<table class="searchbox">
<thead>
<tr>
<th colspan="3" class="searchboxheader">
%(leading)s %(msg_coll)s:
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td valign="top" class="searchboxbody">
%(colls)s
</td>
</tr>
</tbody>
</table>
""" % {
'leading' : leadingtext,
'msg_coll' : _("collections"),
'colls' : selects,
}
## thirdly, print search limits, if applicable:
if action != _("Browse") and pl:
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(limitto)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">
<input type="text" name="pl" size="%(sizepattern)d" value="%(pl)s" />
</td>
</tr>
</tbody>
</table>""" % {
'limitto' : _("Limit to:"),
'sizepattern' : CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH,
'pl' : cgi.escape(pl, 1),
}
## fourthly, print from/until date boxen, if applicable:
if action == _("Browse") or (d1y == 0 and d1m == 0 and d1d == 0 and d2y == 0 and d2m == 0 and d2d == 0):
pass # do not need it
else:
cell_6_a = self.tmpl_inputdatetype(dt, ln) + self.tmpl_inputdate("d1", ln, d1y, d1m, d1d)
cell_6_b = self.tmpl_inputdate("d2", ln, d2y, d2m, d2d)
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(added)s
</th>
<th class="searchboxheader">
%(until)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">%(added_or_modified)s %(date1)s</td>
<td class="searchboxbody">%(date2)s</td>
</tr>
</tbody>
</table>""" % {
'added' : _("Added/modified since:"),
'until' : _("until:"),
'added_or_modified': self.tmpl_inputdatetype(dt, ln),
'date1' : self.tmpl_inputdate("d1", ln, d1y, d1m, d1d),
'date2' : self.tmpl_inputdate("d2", ln, d2y, d2m, d2d),
}
## fifthly, print Display results box, including sort/rank, formats, etc:
if action != _("Browse") and aas > -1:
rgs = []
for i in [10, 25, 50, 100, 250, 500]:
if i <= CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS:
rgs.append({ 'value' : i, 'text' : "%d %s" % (i, _("results"))})
# enrich sort fields list if we are sorting by some MARC tag:
sort_fields = self._add_mark_to_field(value=sf, fields=sort_fields, ln=ln)
# create sort by HTML box:
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(sort_by)s
</th>
<th class="searchboxheader">
%(display_res)s
</th>
<th class="searchboxheader">
%(out_format)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td valign="top" class="searchboxbody">
%(select_sf)s %(select_so)s %(select_rm)s
</td>
<td valign="top" class="searchboxbody">
%(select_rg)s %(select_sc)s
</td>
<td valign="top" class="searchboxbody">%(select_of)s</td>
</tr>
</tbody>
</table>""" % {
'sort_by' : _("Sort by:"),
'display_res' : _("Display results:"),
'out_format' : _("Output format:"),
'select_sf' : self.tmpl_select(fieldname='sf', values=sort_fields, selected=sf, css_class='address'),
'select_so' : self.tmpl_select(fieldname='so', values=[{
'value' : 'a',
'text' : _("asc.")
}, {
'value' : 'd',
'text' : _("desc.")
}], selected=so, css_class='address'),
'select_rm' : self.tmpl_select(fieldname='rm', values=ranks, selected=rm, css_class='address'),
'select_rg' : self.tmpl_select(fieldname='rg', values=rgs, selected=rg, css_class='address'),
'select_sc' : self.tmpl_select(fieldname='sc', values=[{
'value' : 0,
'text' : _("single list")
}, {
'value' : 1,
'text' : _("split by collection")
}], selected=sc, css_class='address'),
'select_of' : self.tmpl_select(
fieldname='of',
selected=of,
values=self._add_mark_to_field(value=of, fields=formats, chars=3, ln=ln),
css_class='address'),
}
## last but not least, print end of search box:
out += """</form>"""
return out
def tmpl_input_hidden(self, name, value):
"Produces the HTML code for a hidden field "
if isinstance(value, list):
list_input = [self.tmpl_input_hidden(name, val) for val in value]
return "\n".join(list_input)
# # Treat `as', `aas' arguments specially:
if name == 'aas':
name = 'as'
return """<input type="hidden" name="%(name)s" value="%(value)s" />""" % {
'name' : cgi.escape(str(name), 1),
'value' : cgi.escape(str(value), 1),
}
def _add_mark_to_field(self, value, fields, ln, chars=1):
"""Adds the current value as a MARC tag in the fields array
Useful for advanced search"""
# load the right message language
_ = gettext_set_language(ln)
out = fields
if value and str(value[0:chars]).isdigit():
out.append({'value' : value,
'text' : str(value) + " " + _("MARC tag")
})
return out
def tmpl_search_pagestart(self, ln) :
"page start for search page. Will display after the page header"
return """<div class="pagebody"><div class="pagebodystripemiddle">"""
def tmpl_search_pageend(self, ln) :
"page end for search page. Will display just before the page footer"
return """</div></div>"""
def tmpl_print_warning(self, msg, type, prologue, epilogue):
"""Prints warning message and flushes output.
Parameters:
- 'msg' *string* - The message string
- 'type' *string* - the warning type
- 'prologue' *string* - HTML code to display before the warning
- 'epilogue' *string* - HTML code to display after the warning
"""
out = '\n%s<span class="quicknote">' % (prologue)
if type:
out += '%s: ' % type
out += '%s</span>%s' % (msg, epilogue)
return out
def tmpl_print_search_info(self, ln, middle_only,
collection, collection_name, collection_id,
aas, sf, so, rm, rg, nb_found, of, ot, p, f, f1,
f2, f3, m1, m2, m3, op1, op2, p1, p2,
p3, d1y, d1m, d1d, d2y, d2m, d2d, dt,
all_fieldcodes, cpu_time, pl_in_url,
jrec, sc, sp):
"""Prints stripe with the information on 'collection' and 'nb_found' results and CPU time.
Also, prints navigation links (beg/next/prev/end) inside the results set.
If middle_only is set to 1, it will only print the middle box information (beg/netx/prev/end/etc) links.
This is suitable for displaying navigation links at the bottom of the search results page.
Parameters:
- 'ln' *string* - The language to display
- 'middle_only' *bool* - Only display parts of the interface
- 'collection' *string* - the collection name
- 'collection_name' *string* - the i18nized current collection name
- 'aas' *bool* - if we display the advanced search interface
- 'sf' *string* - the currently selected sort format
- 'so' *string* - the currently selected sort order ("a" or "d")
- 'rm' *string* - selected ranking method
- 'rg' *int* - selected results/page
- 'nb_found' *int* - number of results found
- 'of' *string* - the selected output format
- 'ot' *string* - hidden values
- 'p' *string* - Current search words
- 'f' *string* - the fields in which the search was done
- 'f1, f2, f3, m1, m2, m3, p1, p2, p3, op1, op2' *strings* - the search parameters
- 'jrec' *int* - number of first record on this page
- 'd1y, d2y, d1m, d2m, d1d, d2d' *int* - the search between dates
- 'dt' *string* the dates' type (creation date, modification date)
- 'all_fieldcodes' *array* - all the available fields
- 'cpu_time' *float* - the time of the query in seconds
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
# left table cells: print collection name
if not middle_only:
out += '''
<a name="%(collection_id)s"></a>
<form action="%(siteurl)s/search" method="get">
<table class="searchresultsbox"><tr><td class="searchresultsboxheader" align="left">
<strong><big>%(collection_link)s</big></strong></td>
''' % {
'collection_id': collection_id,
'siteurl' : CFG_SITE_URL,
'collection_link': create_html_link(self.build_search_interface_url(c=collection, aas=aas, ln=ln),
{}, cgi.escape(collection_name))
}
else:
out += """
<form action="%(siteurl)s/search" method="get"><div align="center">
""" % { 'siteurl' : CFG_SITE_URL }
# middle table cell: print beg/next/prev/end arrows:
if not middle_only:
out += """<td class="searchresultsboxheader" align="center">
%(recs_found)s &nbsp;""" % {
'recs_found' : _("%s records found") % ('<strong>' + self.tmpl_nice_number(nb_found, ln) + '</strong>')
}
else:
out += "<small>"
if nb_found > rg:
out += "" + cgi.escape(collection_name) + " : " + _("%s records found") % ('<strong>' + self.tmpl_nice_number(nb_found, ln) + '</strong>') + " &nbsp; "
if nb_found > rg: # navig.arrows are needed, since we have many hits
query = {'p': p, 'f': f,
'cc': collection,
'sf': sf, 'so': so,
'sp': sp, 'rm': rm,
'of': of, 'ot': ot,
'aas': aas, 'ln': ln,
'p1': p1, 'p2': p2, 'p3': p3,
'f1': f1, 'f2': f2, 'f3': f3,
'm1': m1, 'm2': m2, 'm3': m3,
'op1': op1, 'op2': op2,
'sc': 0,
'd1y': d1y, 'd1m': d1m, 'd1d': d1d,
'd2y': d2y, 'd2m': d2m, 'd2d': d2d,
'dt': dt,
}
# @todo here
def img(gif, txt):
return '<img src="%(siteurl)s/img/%(gif)s.gif" alt="%(txt)s" border="0" />' % {
'txt': txt, 'gif': gif, 'siteurl': CFG_SITE_URL}
if jrec - rg > 1:
out += create_html_link(self.build_search_url(query, jrec=1, rg=rg),
{}, img('sb', _("begin")),
{'class': 'img'})
if jrec > 1:
out += create_html_link(self.build_search_url(query, jrec=max(jrec - rg, 1), rg=rg),
{}, img('sp', _("previous")),
{'class': 'img'})
if jrec + rg - 1 < nb_found:
out += "%d - %d" % (jrec, jrec + rg - 1)
else:
out += "%d - %d" % (jrec, nb_found)
if nb_found >= jrec + rg:
out += create_html_link(self.build_search_url(query,
jrec=jrec + rg,
rg=rg),
{}, img('sn', _("next")),
{'class':'img'})
if nb_found >= jrec + rg + rg:
out += create_html_link(self.build_search_url(query,
jrec=nb_found - rg + 1,
rg=rg),
{}, img('se', _("end")),
{'class': 'img'})
# still in the navigation part
cc = collection
sc = 0
for var in ['p', 'cc', 'f', 'sf', 'so', 'of', 'rg', 'aas', 'ln', 'p1', 'p2', 'p3', 'f1', 'f2', 'f3', 'm1', 'm2', 'm3', 'op1', 'op2', 'sc', 'd1y', 'd1m', 'd1d', 'd2y', 'd2m', 'd2d', 'dt']:
out += self.tmpl_input_hidden(name=var, value=vars()[var])
for var in ['ot', 'sp', 'rm']:
if vars()[var]:
out += self.tmpl_input_hidden(name=var, value=vars()[var])
if pl_in_url:
fieldargs = cgi.parse_qs(pl_in_url)
for fieldcode in all_fieldcodes:
# get_fieldcodes():
if fieldargs.has_key(fieldcode):
for val in fieldargs[fieldcode]:
out += self.tmpl_input_hidden(name=fieldcode, value=val)
out += """&nbsp; %(jump)s <input type="text" name="jrec" size="4" value="%(jrec)d" />""" % {
'jump' : _("jump to record:"),
'jrec' : jrec,
}
if not middle_only:
out += "</td>"
else:
out += "</small>"
# right table cell: cpu time info
if not middle_only:
if cpu_time > -1:
out += """<td class="searchresultsboxheader" align="right"><small>%(time)s</small>&nbsp;</td>""" % {
'time' : _("Search took %s seconds.") % ('%.2f' % cpu_time),
}
out += "</tr></table>"
else:
out += "</div>"
out += "</form>"
return out
def tmpl_print_hosted_search_info(self, ln, middle_only,
collection, collection_name, collection_id,
aas, sf, so, rm, rg, nb_found, of, ot, p, f, f1,
f2, f3, m1, m2, m3, op1, op2, p1, p2,
p3, d1y, d1m, d1d, d2y, d2m, d2d, dt,
all_fieldcodes, cpu_time, pl_in_url,
jrec, sc, sp):
"""Prints stripe with the information on 'collection' and 'nb_found' results and CPU time.
Also, prints navigation links (beg/next/prev/end) inside the results set.
If middle_only is set to 1, it will only print the middle box information (beg/netx/prev/end/etc) links.
This is suitable for displaying navigation links at the bottom of the search results page.
Parameters:
- 'ln' *string* - The language to display
- 'middle_only' *bool* - Only display parts of the interface
- 'collection' *string* - the collection name
- 'collection_name' *string* - the i18nized current collection name
- 'aas' *bool* - if we display the advanced search interface
- 'sf' *string* - the currently selected sort format
- 'so' *string* - the currently selected sort order ("a" or "d")
- 'rm' *string* - selected ranking method
- 'rg' *int* - selected results/page
- 'nb_found' *int* - number of results found
- 'of' *string* - the selected output format
- 'ot' *string* - hidden values
- 'p' *string* - Current search words
- 'f' *string* - the fields in which the search was done
- 'f1, f2, f3, m1, m2, m3, p1, p2, p3, op1, op2' *strings* - the search parameters
- 'jrec' *int* - number of first record on this page
- 'd1y, d2y, d1m, d2m, d1d, d2d' *int* - the search between dates
- 'dt' *string* the dates' type (creation date, modification date)
- 'all_fieldcodes' *array* - all the available fields
- 'cpu_time' *float* - the time of the query in seconds
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
# left table cells: print collection name
if not middle_only:
out += '''
<a name="%(collection_id)s"></a>
<form action="%(siteurl)s/search" method="get">
<table class="searchresultsbox"><tr><td class="searchresultsboxheader" align="left">
<strong><big>%(collection_link)s</big></strong></td>
''' % {
'collection_id': collection_id,
'siteurl' : CFG_SITE_URL,
'collection_link': create_html_link(self.build_search_interface_url(c=collection, aas=aas, ln=ln),
{}, cgi.escape(collection_name))
}
else:
out += """
<form action="%(siteurl)s/search" method="get"><div align="center">
""" % { 'siteurl' : CFG_SITE_URL }
# middle table cell: print beg/next/prev/end arrows:
if not middle_only:
# in case we have a hosted collection that timed out do not print its number of records, as it is yet unknown
if nb_found != -963:
out += """<td class="searchresultsboxheader" align="center">
%(recs_found)s &nbsp;""" % {
'recs_found' : _("%s records found") % ('<strong>' + self.tmpl_nice_number(nb_found, ln) + '</strong>')
}
#elif nb_found = -963:
# out += """<td class="searchresultsboxheader" align="center">
# %(recs_found)s &nbsp;""" % {
# 'recs_found' : _("%s records found") % ('<strong>' + self.tmpl_nice_number(nb_found, ln) + '</strong>')
# }
else:
out += "<small>"
# we do not care about timed out hosted collections here, because the bumber of records found will never be bigger
# than rg anyway, since it's negative
if nb_found > rg:
out += "" + cgi.escape(collection_name) + " : " + _("%s records found") % ('<strong>' + self.tmpl_nice_number(nb_found, ln) + '</strong>') + " &nbsp; "
if nb_found > rg: # navig.arrows are needed, since we have many hits
query = {'p': p, 'f': f,
'cc': collection,
'sf': sf, 'so': so,
'sp': sp, 'rm': rm,
'of': of, 'ot': ot,
'aas': aas, 'ln': ln,
'p1': p1, 'p2': p2, 'p3': p3,
'f1': f1, 'f2': f2, 'f3': f3,
'm1': m1, 'm2': m2, 'm3': m3,
'op1': op1, 'op2': op2,
'sc': 0,
'd1y': d1y, 'd1m': d1m, 'd1d': d1d,
'd2y': d2y, 'd2m': d2m, 'd2d': d2d,
'dt': dt,
}
# @todo here
def img(gif, txt):
return '<img src="%(siteurl)s/img/%(gif)s.gif" alt="%(txt)s" border="0" />' % {
'txt': txt, 'gif': gif, 'siteurl': CFG_SITE_URL}
if jrec - rg > 1:
out += create_html_link(self.build_search_url(query, jrec=1, rg=rg),
{}, img('sb', _("begin")),
{'class': 'img'})
if jrec > 1:
out += create_html_link(self.build_search_url(query, jrec=max(jrec - rg, 1), rg=rg),
{}, img('sp', _("previous")),
{'class': 'img'})
if jrec + rg - 1 < nb_found:
out += "%d - %d" % (jrec, jrec + rg - 1)
else:
out += "%d - %d" % (jrec, nb_found)
if nb_found >= jrec + rg:
out += create_html_link(self.build_search_url(query,
jrec=jrec + rg,
rg=rg),
{}, img('sn', _("next")),
{'class':'img'})
if nb_found >= jrec + rg + rg:
out += create_html_link(self.build_search_url(query,
jrec=nb_found - rg + 1,
rg=rg),
{}, img('se', _("end")),
{'class': 'img'})
# still in the navigation part
cc = collection
sc = 0
for var in ['p', 'cc', 'f', 'sf', 'so', 'of', 'rg', 'aas', 'ln', 'p1', 'p2', 'p3', 'f1', 'f2', 'f3', 'm1', 'm2', 'm3', 'op1', 'op2', 'sc', 'd1y', 'd1m', 'd1d', 'd2y', 'd2m', 'd2d', 'dt']:
out += self.tmpl_input_hidden(name=var, value=vars()[var])
for var in ['ot', 'sp', 'rm']:
if vars()[var]:
out += self.tmpl_input_hidden(name=var, value=vars()[var])
if pl_in_url:
fieldargs = cgi.parse_qs(pl_in_url)
for fieldcode in all_fieldcodes:
# get_fieldcodes():
if fieldargs.has_key(fieldcode):
for val in fieldargs[fieldcode]:
out += self.tmpl_input_hidden(name=fieldcode, value=val)
out += """&nbsp; %(jump)s <input type="text" name="jrec" size="4" value="%(jrec)d" />""" % {
'jump' : _("jump to record:"),
'jrec' : jrec,
}
if not middle_only:
out += "</td>"
else:
out += "</small>"
# right table cell: cpu time info
if not middle_only:
if cpu_time > -1:
out += """<td class="searchresultsboxheader" align="right"><small>%(time)s</small>&nbsp;</td>""" % {
'time' : _("Search took %s seconds.") % ('%.2f' % cpu_time),
}
out += "</tr></table>"
else:
out += "</div>"
out += "</form>"
return out
def tmpl_nice_number(self, number, ln=CFG_SITE_LANG, thousands_separator=',', max_ndigits_after_dot=None):
"""
Return nicely printed number NUMBER in language LN using
given THOUSANDS_SEPARATOR character.
If max_ndigits_after_dot is specified and the number is float, the
number is rounded by taking in consideration up to max_ndigits_after_dot
digit after the dot.
This version does not pay attention to locale. See
tmpl_nice_number_via_locale().
"""
if type(number) is float:
if max_ndigits_after_dot is not None:
number = round(number, max_ndigits_after_dot)
int_part, frac_part = str(number).split('.')
return '%s.%s' % (self.tmpl_nice_number(int(int_part), ln, thousands_separator), frac_part)
else:
chars_in = list(str(number))
number = len(chars_in)
chars_out = []
for i in range(0, number):
if i % 3 == 0 and i != 0:
chars_out.append(thousands_separator)
chars_out.append(chars_in[number - i - 1])
chars_out.reverse()
return ''.join(chars_out)
def tmpl_nice_number_via_locale(self, number, ln=CFG_SITE_LANG):
"""
Return nicely printed number NUM in language LN using the locale.
See also version tmpl_nice_number().
"""
if number is None:
return None
# Temporarily switch the numeric locale to the requested one, and format the number
# In case the system has no locale definition, use the vanilla form
ol = locale.getlocale(locale.LC_NUMERIC)
try:
locale.setlocale(locale.LC_NUMERIC, self.tmpl_localemap.get(ln, self.tmpl_default_locale))
except locale.Error:
return str(number)
try:
number = locale.format('%d', number, True)
except TypeError:
return str(number)
locale.setlocale(locale.LC_NUMERIC, ol)
return number
def tmpl_record_format_htmlbrief_header(self, ln):
"""Returns the header of the search results list when output
is html brief. Note that this function is called for each collection
results when 'split by collection' is enabled.
See also: tmpl_record_format_htmlbrief_footer,
tmpl_record_format_htmlbrief_body
Parameters:
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<form action="%(siteurl)s/yourbaskets/add" method="post">
<table>
""" % {
'siteurl' : CFG_SITE_URL,
}
return out
def tmpl_record_format_htmlbrief_footer(self, ln, display_add_to_basket=True):
"""Returns the footer of the search results list when output
is html brief. Note that this function is called for each collection
results when 'split by collection' is enabled.
See also: tmpl_record_format_htmlbrief_header(..),
tmpl_record_format_htmlbrief_body(..)
Parameters:
- 'ln' *string* - The language to display
- 'display_add_to_basket' *bool* - whether to display Add-to-basket button
"""
# load the right message language
_ = gettext_set_language(ln)
out = """</table>
<br />
<input type="hidden" name="colid" value="0" />
%(add_to_basket)s
</form>""" % {
'add_to_basket': display_add_to_basket and """<input class="formbutton" type="submit" name="action" value="%s" />""" % _("Add to basket") or "",
}
return out
def tmpl_record_format_htmlbrief_body(self, ln, recid,
row_number, relevance,
record, relevances_prologue,
relevances_epilogue,
display_add_to_basket=True):
"""Returns the html brief format of one record. Used in the
search results list for each record.
See also: tmpl_record_format_htmlbrief_header(..),
tmpl_record_format_htmlbrief_footer(..)
Parameters:
- 'ln' *string* - The language to display
- 'row_number' *int* - The position of this record in the list
- 'recid' *int* - The recID
- 'relevance' *string* - The relevance of the record
- 'record' *string* - The formatted record
- 'relevances_prologue' *string* - HTML code to prepend the relevance indicator
- 'relevances_epilogue' *string* - HTML code to append to the relevance indicator (used mostly for formatting)
"""
# load the right message language
_ = gettext_set_language(ln)
checkbox_for_baskets = """<input name="recid" type="checkbox" value="%(recid)s" />""" % \
{'recid': recid, }
if not display_add_to_basket:
checkbox_for_baskets = ''
out = """
<tr><td valign="top" align="right" style="white-space: nowrap;">
%(checkbox_for_baskets)s
<abbr class="unapi-id" title="%(recid)s"></abbr>
%(number)s.
""" % {'recid': recid,
'number': row_number,
'checkbox_for_baskets': checkbox_for_baskets}
if relevance:
out += """<br /><div class="rankscoreinfo"><a title="rank score">%(prologue)s%(relevance)s%(epilogue)s</a></div>""" % {
'prologue' : relevances_prologue,
'epilogue' : relevances_epilogue,
'relevance' : relevance
}
out += """</td><td valign="top">%s</td></tr>""" % record
return out
def tmpl_print_results_overview(self, ln, results_final_nb_total, cpu_time, results_final_nb, colls, ec, hosted_colls_potential_results_p=False):
"""Prints results overview box with links to particular collections below.
Parameters:
- 'ln' *string* - The language to display
- 'results_final_nb_total' *int* - The total number of hits for the query
- 'colls' *array* - The collections with hits, in the format:
- 'coll[code]' *string* - The code of the collection (canonical name)
- 'coll[name]' *string* - The display name of the collection
- 'results_final_nb' *array* - The number of hits, indexed by the collection codes:
- 'cpu_time' *string* - The time the query took
- 'url_args' *string* - The rest of the search query
- 'ec' *array* - selected external collections
- 'hosted_colls_potential_results_p' *boolean* - check if there are any hosted collections searches
that timed out during the pre-search
"""
if len(colls) == 1 and not ec:
# if one collection only and no external collections, print nothing:
return ""
# load the right message language
_ = gettext_set_language(ln)
# first find total number of hits:
# if there were no hosted collections that timed out during the pre-search print out the exact number of records found
if not hosted_colls_potential_results_p:
out = """<table class="searchresultsbox">
<thead><tr><th class="searchresultsboxheader">%(founds)s</th></tr></thead>
<tbody><tr><td class="searchresultsboxbody"> """ % {
'founds' : _("%(x_fmt_open)sResults overview:%(x_fmt_close)s Found %(x_nb_records)s records in %(x_nb_seconds)s seconds.") % \
{'x_fmt_open': '<strong>',
'x_fmt_close': '</strong>',
'x_nb_records': '<strong>' + self.tmpl_nice_number(results_final_nb_total, ln) + '</strong>',
'x_nb_seconds': '%.2f' % cpu_time}
}
# if there were (only) hosted_collections that timed out during the pre-search print out a fuzzier message
else:
if results_final_nb_total == 0:
out = """<table class="searchresultsbox">
<thead><tr><th class="searchresultsboxheader">%(founds)s</th></tr></thead>
<tbody><tr><td class="searchresultsboxbody"> """ % {
'founds' : _("%(x_fmt_open)sResults overview%(x_fmt_close)s") % \
{'x_fmt_open': '<strong>',
'x_fmt_close': '</strong>'}
}
elif results_final_nb_total > 0:
out = """<table class="searchresultsbox">
<thead><tr><th class="searchresultsboxheader">%(founds)s</th></tr></thead>
<tbody><tr><td class="searchresultsboxbody"> """ % {
'founds' : _("%(x_fmt_open)sResults overview:%(x_fmt_close)s Found at least %(x_nb_records)s records in %(x_nb_seconds)s seconds.") % \
{'x_fmt_open': '<strong>',
'x_fmt_close': '</strong>',
'x_nb_records': '<strong>' + self.tmpl_nice_number(results_final_nb_total, ln) + '</strong>',
'x_nb_seconds': '%.2f' % cpu_time}
}
# then print hits per collection:
for coll in colls:
if results_final_nb.has_key(coll['code']) and results_final_nb[coll['code']] > 0:
out += """
<strong><a href="#%(coll)s">%(coll_name)s</a></strong>, <a href="#%(coll)s">%(number)s</a><br />""" % \
{'coll' : coll['id'],
'coll_name' : cgi.escape(coll['name']),
'number' : _("%s records found") % \
('<strong>' + self.tmpl_nice_number(results_final_nb[coll['code']], ln) + '</strong>')}
# the following is used for hosted collections that have timed out,
# i.e. for which we don't know the exact number of results yet.
elif results_final_nb.has_key(coll['code']) and results_final_nb[coll['code']] == -963:
out += """
<strong><a href="#%(coll)s">%(coll_name)s</a></strong><br />""" % \
{'coll' : coll['id'],
'coll_name' : cgi.escape(coll['name']),
'number' : _("%s records found") % \
('<strong>' + self.tmpl_nice_number(results_final_nb[coll['code']], ln) + '</strong>')}
out += "</td></tr></tbody></table>"
return out
def tmpl_print_hosted_results(self, url_and_engine, ln, of=None, req=None, limit=CFG_EXTERNAL_COLLECTION_MAXRESULTS):
"""Print results of a given search engine.
"""
_ = gettext_set_language(ln)
#url = url_and_engine[0]
engine = url_and_engine[1]
#name = _(engine.name)
db_id = get_collection_id(engine.name)
#base_url = engine.base_url
out = ""
results = engine.parser.parse_and_get_results(None, of=of, req=req, limit=limit, parseonly=True)
if len(results) != 0:
if of == 'hb':
out += """
<form action="%(siteurl)s/yourbaskets/add" method="post">
<input type="hidden" name="colid" value="%(col_db_id)s" />
<table>
""" % {
'siteurl' : CFG_SITE_URL,
'col_db_id' : db_id,
}
else:
if of == 'hb':
out += """
<table>
"""
for result in results:
out += result.html.replace('>Detailed record<', '>External record<').replace('>Similar records<', '>Similar external records<')
if len(results) != 0:
if of == 'hb':
out += """</table>
<br /><input class="formbutton" type="submit" name="action" value="%(basket)s" />
</form>""" % {
'basket' : _("Add to basket")
}
else:
if of == 'hb':
out += """
</table>
"""
# we have already checked if there are results or no, maybe the following if should be removed?
if not results:
if of.startswith("h"):
out = _('No results found...') + '<br />'
return out
def tmpl_print_searchresultbox(self, header, body):
"""print a nicely formatted box for search results """
#_ = gettext_set_language(ln)
# first find total number of hits:
out = '<table class="searchresultsbox"><thead><tr><th class="searchresultsboxheader">' + header + '</th></tr></thead><tbody><tr><td class="searchresultsboxbody">' + body + '</td></tr></tbody></table>'
return out
def tmpl_search_no_boolean_hits(self, ln, nearestterms):
"""No hits found, proposes alternative boolean queries
Parameters:
- 'ln' *string* - The language to display
- 'nearestterms' *array* - Parts of the interface to display, in the format:
- 'nearestterms[nbhits]' *int* - The resulting number of hits
- 'nearestterms[url_args]' *string* - The search parameters
- 'nearestterms[p]' *string* - The search terms
"""
# load the right message language
_ = gettext_set_language(ln)
out = _("Boolean query returned no hits. Please combine your search terms differently.")
out += '''<blockquote><table class="nearesttermsbox" cellpadding="0" cellspacing="0" border="0">'''
for term, hits, argd in nearestterms:
out += '''\
<tr>
<td class="nearesttermsboxbody" align="right">%(hits)s</td>
<td class="nearesttermsboxbody" width="15">&nbsp;</td>
<td class="nearesttermsboxbody" align="left">
%(link)s
</td>
</tr>''' % {'hits' : hits,
'link': create_html_link(self.build_search_url(argd),
{}, cgi.escape(term),
{'class': "nearestterms"})}
out += """</table></blockquote>"""
return out
def tmpl_similar_author_names(self, authors, ln):
"""No hits found, proposes alternative boolean queries
Parameters:
- 'authors': a list of (name, hits) tuples
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''<a name="googlebox"></a>
<table class="googlebox"><tr><th colspan="2" class="googleboxheader">%(similar)s</th></tr>''' % {
'similar' : _("See also: similar author names")
}
for author, hits in authors:
out += '''\
<tr>
<td class="googleboxbody">%(nb)d</td>
<td class="googleboxbody">%(link)s</td>
</tr>''' % {'link': create_html_link(
self.build_search_url(p=author,
f='author',
ln=ln),
{}, cgi.escape(author), {'class':"google"}),
'nb' : hits}
out += """</table>"""
return out
def tmpl_print_record_detailed(self, recID, ln):
"""Displays a detailed on-the-fly record
Parameters:
- 'ln' *string* - The language to display
- 'recID' *int* - The record id
"""
# okay, need to construct a simple "Detailed record" format of our own:
out = "<p>&nbsp;"
# secondly, title:
titles = get_fieldvalues(recID, "245__a")
for title in titles:
out += "<p><center><big><strong>%s</strong></big></center></p>" % cgi.escape(title)
# thirdly, authors:
authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")
if authors:
out += "<p><center>"
for author in authors:
out += '%s; ' % create_html_link(self.build_search_url(
ln=ln,
p=author,
f='author'),
{}, cgi.escape(author))
out += "</center></p>"
# fourthly, date of creation:
dates = get_fieldvalues(recID, "260__c")
for date in dates:
out += "<p><center><small>%s</small></center></p>" % date
# fifthly, abstract:
abstracts = get_fieldvalues(recID, "520__a")
for abstract in abstracts:
out += """<p style="margin-left: 15%%; width: 70%%">
<small><strong>Abstract:</strong> %s</small></p>""" % abstract
# fifthly bis, keywords:
keywords = get_fieldvalues(recID, "6531_a")
if len(keywords):
out += """<p style="margin-left: 15%%; width: 70%%">
<small><strong>Keyword(s):</strong>"""
for keyword in keywords:
out += '%s; ' % create_html_link(
self.build_search_url(ln=ln,
p=keyword,
f='keyword'),
{}, cgi.escape(keyword))
out += '</small></p>'
# fifthly bis bis, published in:
prs_p = get_fieldvalues(recID, "909C4p")
prs_v = get_fieldvalues(recID, "909C4v")
prs_y = get_fieldvalues(recID, "909C4y")
prs_n = get_fieldvalues(recID, "909C4n")
prs_c = get_fieldvalues(recID, "909C4c")
for idx in range(0, len(prs_p)):
out += """<p style="margin-left: 15%%; width: 70%%">
<small><strong>Publ. in:</strong> %s""" % prs_p[idx]
if prs_v and prs_v[idx]:
out += """<strong>%s</strong>""" % prs_v[idx]
if prs_y and prs_y[idx]:
out += """(%s)""" % prs_y[idx]
if prs_n and prs_n[idx]:
out += """, no.%s""" % prs_n[idx]
if prs_c and prs_c[idx]:
out += """, p.%s""" % prs_c[idx]
out += """.</small></p>"""
# sixthly, fulltext link:
urls_z = get_fieldvalues(recID, "8564_z")
urls_u = get_fieldvalues(recID, "8564_u")
# we separate the fulltext links and image links
for url_u in urls_u:
if url_u.endswith('.png'):
continue
else:
link_text = "URL"
try:
if urls_z[idx]:
link_text = urls_z[idx]
except IndexError:
pass
out += """<p style="margin-left: 15%%; width: 70%%">
<small><strong>%s:</strong> <a href="%s">%s</a></small></p>""" % (link_text, urls_u[idx], urls_u[idx])
# print some white space at the end:
out += "<br /><br />"
return out
def tmpl_print_record_list_for_similarity_boxen(self, title, recID_score_list, ln=CFG_SITE_LANG):
"""Print list of records in the "hs" (HTML Similarity) format for similarity boxes.
RECID_SCORE_LIST is a list of (recID1, score1), (recID2, score2), etc.
"""
from invenio.search_engine import print_record, record_public_p
recID_score_list_to_be_printed = []
# firstly find 5 first public records to print:
nb_records_to_be_printed = 0
nb_records_seen = 0
while nb_records_to_be_printed < 5 and nb_records_seen < len(recID_score_list) and nb_records_seen < 50:
# looking through first 50 records only, picking first 5 public ones
(recID, score) = recID_score_list[nb_records_seen]
nb_records_seen += 1
if record_public_p(recID):
nb_records_to_be_printed += 1
recID_score_list_to_be_printed.append([recID, score])
# secondly print them:
out = '''
<table><tr>
<td>
<table><tr><td class="blocknote">%(title)s</td></tr></table>
</td>
</tr>
<tr>
<td><table>
''' % { 'title': cgi.escape(title) }
for recid, score in recID_score_list_to_be_printed:
out += '''
<tr><td><font class="rankscoreinfo"><a>(%(score)s)&nbsp;</a></font><small>&nbsp;%(info)s</small></td></tr>''' % {
'score': score,
'info' : print_record(recid, format="hs", ln=ln),
}
out += """</table></td></tr></table> """
return out
def tmpl_print_record_brief(self, ln, recID):
"""Displays a brief record on-the-fly
Parameters:
- 'ln' *string* - The language to display
- 'recID' *int* - The record id
"""
out = ""
# record 'recID' does not exist in format 'format', so print some default format:
# firstly, title:
titles = get_fieldvalues(recID, "245__a")
# secondly, authors:
authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")
# thirdly, date of creation:
dates = get_fieldvalues(recID, "260__c")
# thirdly bis, report numbers:
rns = get_fieldvalues(recID, "037__a")
rns = get_fieldvalues(recID, "088__a")
# fourthly, beginning of abstract:
abstracts = get_fieldvalues(recID, "520__a")
# fifthly, fulltext link:
urls_z = get_fieldvalues(recID, "8564_z")
urls_u = get_fieldvalues(recID, "8564_u")
# get rid of images
images = []
non_image_urls_u = []
for url_u in urls_u:
if url_u.endswith('.png'):
images.append(url_u)
else:
non_image_urls_u.append(url_u)
## unAPI identifier
out = '<abbr class="unapi-id" title="%s"></abbr>\n' % recID
out += self.tmpl_record_body(
titles=titles,
authors=authors,
dates=dates,
rns=rns,
abstracts=abstracts,
urls_u=non_image_urls_u,
urls_z=urls_z,
ln=ln)
return out
def tmpl_print_record_brief_links(self, ln, recID, sf='', so='d', sp='', rm='', display_claim_link=False):
"""Displays links for brief record on-the-fly
Parameters:
- 'ln' *string* - The language to display
- 'recID' *int* - The record id
"""
from invenio.webcommentadminlib import get_nb_reviews, get_nb_comments
# load the right message language
_ = gettext_set_language(ln)
out = '<div class="moreinfo">'
if CFG_WEBSEARCH_USE_ALEPH_SYSNOS:
alephsysnos = get_fieldvalues(recID, "970__a")
if len(alephsysnos) > 0:
alephsysno = alephsysnos[0]
out += '<span class="moreinfo">%s</span>' % \
create_html_link(self.build_search_url(recid=alephsysno,
ln=ln),
{}, _("Detailed record"),
{'class': "moreinfo"})
else:
out += '<span class="moreinfo">%s</span>' % \
create_html_link(self.build_search_url(recid=recID, ln=ln),
{},
_("Detailed record"),
{'class': "moreinfo"})
else:
out += '<span class="moreinfo">%s</span>' % \
create_html_link(self.build_search_url(recid=recID, ln=ln),
{}, _("Detailed record"),
{'class': "moreinfo"})
out += '<span class="moreinfo"> - %s</span>' % \
create_html_link(self.build_search_url(p="recid:%d" % recID,
rm="wrd",
ln=ln),
{}, _("Similar records"),
{'class': "moreinfo"})
if CFG_BIBRANK_SHOW_CITATION_LINKS:
num_timescited = get_cited_by_count(recID)
if num_timescited:
out += '<span class="moreinfo"> - %s</span>' % \
create_html_link(self.build_search_url(p="refersto:recid:%d" % recID,
sf=sf,
so=so,
sp=sp,
rm=rm,
ln=ln),
{}, num_timescited > 1 and _("Cited by %i records") % num_timescited
or _("Cited by 1 record"),
{'class': "moreinfo"})
else:
out += "<!--not showing citations links-->"
if display_claim_link: #Maybe we want not to show the link to who cannot use id?
out += '<span class="moreinfo"> - %s</span>' % \
create_html_link(CFG_SITE_URL + '/person/action', {'claim':'True','selection':str(recID)},
'Attribute this paper',
{'class': "moreinfo"})
if CFG_WEBCOMMENT_ALLOW_COMMENTS and CFG_WEBSEARCH_SHOW_COMMENT_COUNT:
num_comments = get_nb_comments(recID, count_deleted=False)
if num_comments:
out += '<span class="moreinfo"> - %s</span>' % \
- create_html_link(CFG_SITE_URL + '/record/' + str(recID)
+ create_html_link(CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID)
+ '/comments?ln=%s' % ln, {}, num_comments > 1 and _("%i comments")
% (num_comments) or _("1 comment"),
{'class': "moreinfo"})
else:
out += "<!--not showing reviews links-->"
if CFG_WEBCOMMENT_ALLOW_REVIEWS and CFG_WEBSEARCH_SHOW_REVIEW_COUNT:
num_reviews = get_nb_reviews(recID, count_deleted=False)
if num_reviews:
out += '<span class="moreinfo"> - %s</span>' % \
- create_html_link(CFG_SITE_URL + '/record/' + str(recID)
+ create_html_link(CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(recID)
+ '/reviews?ln=%s' % ln, {}, num_reviews > 1 and _("%i reviews")
% (num_reviews) or _("1 review"), {'class': "moreinfo"})
else:
out += "<!--not showing reviews links-->"
out += '</div>'
return out
def tmpl_xml_rss_prologue(self, current_url=None,
previous_url=None, next_url=None,
first_url=None, last_url=None,
nb_found=None, jrec=None, rg=None):
"""Creates XML RSS 2.0 prologue."""
out = """<rss version="2.0"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
<channel>
<title>%(sitename)s</title>
<link>%(siteurl)s</link>
<description>%(sitename)s latest documents</description>
<language>%(sitelang)s</language>
<pubDate>%(timestamp)s</pubDate>
<category></category>
<generator>Invenio %(version)s</generator>
<webMaster>%(sitesupportemail)s</webMaster>
<ttl>%(timetolive)s</ttl>%(previous_link)s%(next_link)s%(current_link)s%(total_results)s%(start_index)s%(items_per_page)s
<image>
<url>%(siteurl)s/img/site_logo_rss.png</url>
<title>%(sitename)s</title>
<link>%(siteurl)s</link>
</image>
<atom:link rel="search" href="%(siteurl)s/opensearchdescription" type="application/opensearchdescription+xml" title="Content Search" />
<textInput>
<title>Search </title>
<description>Search this site:</description>
<name>p</name>
<link>%(siteurl)s/search</link>
</textInput>
""" % {'sitename': CFG_SITE_NAME,
'siteurl': CFG_SITE_URL,
'sitelang': CFG_SITE_LANG,
'search_syntax': self.tmpl_opensearch_rss_url_syntax,
'timestamp': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
'version': CFG_VERSION,
'sitesupportemail': CFG_SITE_SUPPORT_EMAIL,
'timetolive': CFG_WEBSEARCH_RSS_TTL,
'current_link': (current_url and \
'\n<atom:link rel="self" href="%s" />\n' % current_url) or '',
'previous_link': (previous_url and \
'\n<atom:link rel="previous" href="%s" />' % previous_url) or '',
'next_link': (next_url and \
'\n<atom:link rel="next" href="%s" />' % next_url) or '',
'first_link': (first_url and \
'\n<atom:link rel="first" href="%s" />' % first_url) or '',
'last_link': (last_url and \
'\n<atom:link rel="last" href="%s" />' % last_url) or '',
'total_results': (nb_found and \
'\n<opensearch:totalResults>%i</opensearch:totalResults>' % nb_found) or '',
'start_index': (jrec and \
'\n<opensearch:startIndex>%i</opensearch:startIndex>' % jrec) or '',
'items_per_page': (rg and \
'\n<opensearch:itemsPerPage>%i</opensearch:itemsPerPage>' % rg) or '',
}
return out
def tmpl_xml_rss_epilogue(self):
"""Creates XML RSS 2.0 epilogue."""
out = """\
</channel>
</rss>\n"""
return out
def tmpl_xml_podcast_prologue(self, current_url=None,
previous_url=None, next_url=None,
first_url=None, last_url=None,
nb_found=None, jrec=None, rg=None):
"""Creates XML podcast prologue."""
out = """<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
<channel>
<title>%(sitename)s</title>
<link>%(siteurl)s</link>
<description>%(sitename)s latest documents</description>
<language>%(sitelang)s</language>
<pubDate>%(timestamp)s</pubDate>
<category></category>
<generator>CDS Invenio %(version)s</generator>
<webMaster>%(siteadminemail)s</webMaster>
<ttl>%(timetolive)s</ttl>%(previous_link)s%(next_link)s%(current_link)s
<image>
<url>%(siteurl)s/img/site_logo_rss.png</url>
<title>%(sitename)s</title>
<link>%(siteurl)s</link>
</image>
<itunes:owner>
<itunes:email>%(siteadminemail)s</itunes:email>
</itunes:owner>
""" % {'sitename': CFG_SITE_NAME,
'siteurl': CFG_SITE_URL,
'sitelang': CFG_SITE_LANG,
'siteadminemail': CFG_SITE_ADMIN_EMAIL,
'timestamp': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
'version': CFG_VERSION,
'sitesupportemail': CFG_SITE_SUPPORT_EMAIL,
'timetolive': CFG_WEBSEARCH_RSS_TTL,
'current_link': (current_url and \
'\n<atom:link rel="self" href="%s" />\n' % current_url) or '',
'previous_link': (previous_url and \
'\n<atom:link rel="previous" href="%s" />' % previous_url) or '',
'next_link': (next_url and \
'\n<atom:link rel="next" href="%s" />' % next_url) or '',
'first_link': (first_url and \
'\n<atom:link rel="first" href="%s" />' % first_url) or '',
'last_link': (last_url and \
'\n<atom:link rel="last" href="%s" />' % last_url) or '',
}
return out
def tmpl_xml_podcast_epilogue(self):
"""Creates XML podcast epilogue."""
out = """\n</channel>
</rss>\n"""
return out
def tmpl_xml_nlm_prologue(self):
"""Creates XML NLM prologue."""
out = """<articles>\n"""
return out
def tmpl_xml_nlm_epilogue(self):
"""Creates XML NLM epilogue."""
out = """\n</articles>"""
return out
def tmpl_xml_refworks_prologue(self):
"""Creates XML RefWorks prologue."""
out = """<references>\n"""
return out
def tmpl_xml_refworks_epilogue(self):
"""Creates XML RefWorks epilogue."""
out = """\n</references>"""
return out
def tmpl_xml_endnote_prologue(self):
"""Creates XML EndNote prologue."""
out = """<records>\n"""
return out
def tmpl_xml_endnote_epilogue(self):
"""Creates XML EndNote epilogue."""
out = """\n</records>"""
return out
def tmpl_xml_marc_prologue(self):
"""Creates XML MARC prologue."""
out = """<collection xmlns="http://www.loc.gov/MARC21/slim">\n"""
return out
def tmpl_xml_marc_epilogue(self):
"""Creates XML MARC epilogue."""
out = """\n</collection>"""
return out
def tmpl_xml_mods_prologue(self):
"""Creates XML MODS prologue."""
out = """<modsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n
xsi:schemaLocation="http://www.loc.gov/mods/v3\n
http://www.loc.gov/standards/mods/v3/mods-3-3.xsd">\n"""
return out
def tmpl_xml_mods_epilogue(self):
"""Creates XML MODS epilogue."""
out = """\n</modsCollection>"""
return out
def tmpl_xml_default_prologue(self):
"""Creates XML default format prologue. (Sanity calls only.)"""
out = """<collection>\n"""
return out
def tmpl_xml_default_epilogue(self):
"""Creates XML default format epilogue. (Sanity calls only.)"""
out = """\n</collection>"""
return out
def tmpl_collection_not_found_page_title(self, colname, ln=CFG_SITE_LANG):
"""
Create page title for cases when unexisting collection was asked for.
"""
_ = gettext_set_language(ln)
out = _("Collection %s Not Found") % cgi.escape(colname)
return out
def tmpl_collection_not_found_page_body(self, colname, ln=CFG_SITE_LANG):
"""
Create page body for cases when unexisting collection was asked for.
"""
_ = gettext_set_language(ln)
out = """<h1>%(title)s</h1>
<p>%(sorry)s</p>
<p>%(you_may_want)s</p>
""" % { 'title': self.tmpl_collection_not_found_page_title(colname, ln),
'sorry': _("Sorry, collection %s does not seem to exist.") % \
('<strong>' + cgi.escape(colname) + '</strong>'),
'you_may_want': _("You may want to start browsing from %s.") % \
('<a href="' + CFG_SITE_URL + '?ln=' + ln + '">' + \
cgi.escape(CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)) + '</a>')}
return out
def tmpl_alert_rss_teaser_box_for_query(self, id_query, ln, display_email_alert_part=True):
"""Propose teaser for setting up this query as alert or RSS feed.
Parameters:
- 'id_query' *int* - ID of the query we make teaser for
- 'ln' *string* - The language to display
- 'display_email_alert_part' *bool* - whether to display email alert part
"""
# load the right message language
_ = gettext_set_language(ln)
# get query arguments:
res = run_sql("SELECT urlargs FROM query WHERE id=%s", (id_query,))
argd = {}
if res:
argd = cgi.parse_qs(res[0][0])
rssurl = self.build_rss_url(argd)
alerturl = CFG_SITE_URL + '/youralerts/input?ln=%s&amp;idq=%s' % (ln, id_query)
if display_email_alert_part:
msg_alert = _("""Set up a personal %(x_url1_open)semail alert%(x_url1_close)s
or subscribe to the %(x_url2_open)sRSS feed%(x_url2_close)s.""") % \
{'x_url1_open': '<a href="%s"><img src="%s/img/mail-icon-12x8.gif" border="0" alt="" /></a> ' % (alerturl, CFG_SITE_URL) + ' <a class="google" href="%s">' % (alerturl),
'x_url1_close': '</a>',
'x_url2_open': '<a href="%s"><img src="%s/img/feed-icon-12x12.gif" border="0" alt="" /></a> ' % (rssurl, CFG_SITE_URL) + ' <a class="google" href="%s">' % rssurl,
'x_url2_close': '</a>', }
else:
msg_alert = _("""Subscribe to the %(x_url2_open)sRSS feed%(x_url2_close)s.""") % \
{'x_url2_open': '<a href="%s"><img src="%s/img/feed-icon-12x12.gif" border="0" alt="" /></a> ' % (rssurl, CFG_SITE_URL) + ' <a class="google" href="%s">' % rssurl,
'x_url2_close': '</a>', }
out = '''<a name="googlebox"></a>
<table class="googlebox"><tr><th class="googleboxheader">%(similar)s</th></tr>
<tr><td class="googleboxbody">%(msg_alert)s</td></tr>
</table>
''' % {
'similar' : _("Interested in being notified about new results for this query?"),
'msg_alert': msg_alert, }
return out
def tmpl_detailed_record_metadata(self, recID, ln, format,
content,
creationdate=None,
modificationdate=None):
"""Returns the main detailed page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- 'format' *string* - The format in used to print the record
- 'content' *string* - The main content of the page
- 'creationdate' *string* - The creation date of the printed record
- 'modificationdate' *string* - The last modification date of the printed record
"""
_ = gettext_set_language(ln)
## unAPI identifier
out = '<abbr class="unapi-id" title="%s"></abbr>\n' % recID
out += content
return out
def tmpl_record_plots(self, recID, ln):
"""
Displays little tables containing the images and captions contained in the specified document.
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
"""
from invenio.search_engine import get_record
from invenio.bibrecord import field_get_subfield_values
from invenio.bibrecord import record_get_field_instances
_ = gettext_set_language(ln)
out = ''
rec = get_record(recID)
flds = record_get_field_instances(rec, '856', '4')
images = []
for fld in flds:
image = field_get_subfield_values(fld, 'u')
caption = field_get_subfield_values(fld, 'y')
if type(image) == list and len(image) > 0:
image = image[0]
else:
continue
if type(caption) == list and len(caption) > 0:
caption = caption[0]
else:
continue
if not image.endswith('.png'):
# huh?
continue
if len(caption) >= 5:
images.append((int(caption[:5]), image, caption[5:]))
else:
# we don't have any idea of the order... just put it on
images.append(99999, image, caption)
images = sorted(images, key=lambda x: x[0])
for (index, image, caption) in images:
# let's put everything in nice little subtables with the image
# next to the caption
out = out + '<table width="95%" style="display: inline;">' + \
'<tr><td width="66%"><a name="' + str(index) + '" ' + \
'href="' + image + '">' + \
'<img src="' + image + '" width="95%"/></a></td>' + \
'<td width="33%">' + caption + '</td></tr>' + \
'</table>'
out = out + '<br /><br />'
return out
def tmpl_detailed_record_statistics(self, recID, ln,
downloadsimilarity,
downloadhistory, viewsimilarity):
"""Returns the statistics page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- downloadsimilarity *string* - downloadsimilarity box
- downloadhistory *string* - downloadhistory box
- viewsimilarity *string* - viewsimilarity box
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if CFG_BIBRANK_SHOW_DOWNLOAD_STATS and downloadsimilarity is not None:
similar = self.tmpl_print_record_list_for_similarity_boxen (
_("People who downloaded this document also downloaded:"), downloadsimilarity, ln)
out = '<table>'
out += '''
<tr><td>%(graph)s</td></tr>
<tr><td>%(similar)s</td></tr>
''' % { 'siteurl': CFG_SITE_URL, 'recid': recID, 'ln': ln,
'similar': similar, 'more': _("more"),
'graph': downloadsimilarity
}
out += '</table>'
out += '<br />'
if CFG_BIBRANK_SHOW_READING_STATS and viewsimilarity is not None:
out += self.tmpl_print_record_list_for_similarity_boxen (
_("People who viewed this page also viewed:"), viewsimilarity, ln)
if CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS and downloadhistory is not None:
out += downloadhistory + '<br />'
return out
def tmpl_detailed_record_citations_prologue(self, recID, ln):
"""Returns the prologue of the citations page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
"""
return '<table>'
def tmpl_detailed_record_citations_epilogue(self, recID, ln):
"""Returns the epilogue of the citations page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
"""
return '</table>'
def tmpl_detailed_record_citations_citing_list(self, recID, ln,
citinglist,
sf='', so='d', sp='', rm=''):
"""Returns the list of record citing this one
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- citinglist *list* - a list of tuples [(x1,y1),(x2,y2),..] where x is doc id and y is number of citations
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if CFG_BIBRANK_SHOW_CITATION_STATS and citinglist is not None:
similar = self.tmpl_print_record_list_for_similarity_boxen(
_("Cited by: %s records") % len (citinglist), citinglist, ln)
out += '''
<tr><td>
%(similar)s&nbsp;%(more)s
<br /><br />
</td></tr>''' % {
'more': create_html_link(
self.build_search_url(p='refersto:recid:%d' % recID, #XXXX
sf=sf,
so=so,
sp=sp,
rm=rm,
ln=ln),
{}, _("more")),
'similar': similar}
return out
def tmpl_detailed_record_citations_citation_history(self, recID, ln,
citationhistory):
"""Returns the citations history graph of this record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- citationhistory *string* - citationhistory box
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if CFG_BIBRANK_SHOW_CITATION_GRAPHS and citationhistory is not None:
out = '<!--citation history--><tr><td>%s</td></tr>' % citationhistory
else:
out = "<!--not showing citation history. CFG_BIBRANK_SHOW_CITATION_GRAPHS:"
out += str(CFG_BIBRANK_SHOW_CITATION_GRAPHS) + " citationhistory "
if citationhistory:
out += str(len(citationhistory)) + "-->"
else:
out += "no citationhistory -->"
return out
def tmpl_detailed_record_citations_co_citing(self, recID, ln,
cociting):
"""Returns the list of cocited records
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- cociting *string* - cociting box
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if CFG_BIBRANK_SHOW_CITATION_STATS and cociting is not None:
similar = self.tmpl_print_record_list_for_similarity_boxen (
_("Co-cited with: %s records") % len (cociting), cociting, ln)
out = '''
<tr><td>
%(similar)s&nbsp;%(more)s
<br />
</td></tr>''' % { 'more': create_html_link(self.build_search_url(p='cocitedwith:%d' % recID, ln=ln),
{}, _("more")),
'similar': similar }
return out
def tmpl_detailed_record_citations_self_cited(self, recID, ln,
selfcited, citinglist):
"""Returns the list of self-citations for this record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- selfcited list - a list of self-citations for recID
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if CFG_BIBRANK_SHOW_CITATION_GRAPHS and selfcited is not None:
sc_scorelist = [] #a score list for print..
for s in selfcited:
#copy weight from citations
weight = 0
for c in citinglist:
(crec, score) = c
if crec == s:
weight = score
tmp = [s, weight]
sc_scorelist.append(tmp)
scite = self.tmpl_print_record_list_for_similarity_boxen (
_(".. of which self-citations: %s records") % len (selfcited), sc_scorelist, ln)
out = '<tr><td>' + scite + '</td></tr>'
return out
def tmpl_author_information(self, req, pubs, authorname, num_downloads, aff_pubdict,
citedbylist, kwtuples, authors, vtuples, names_dict, person_link, bibauthorid_data, ln):
"""Prints stuff about the author given as authorname.
1. Author name + his/her institutes. Each institute I has a link
to papers where the auhtor has I as institute.
2. Publications, number: link to search by author.
3. Keywords
4. Author collabs
5. Publication venues like journals
The parameters are data structures needed to produce 1-6, as follows:
req - request
pubs - list of recids, probably the records that have the author as an author
authorname - evident
num_downloads - evident
aff_pubdict - a dictionary where keys are inst names and values lists of recordids
citedbylist - list of recs that cite pubs
kwtuples - keyword tuples like ('HIGGS BOSON',[3,4]) where 3 and 4 are recids
authors - a list of authors that have collaborated with authorname
names_dict - a dict of {name: frequency}
"""
from invenio.search_engine import perform_request_search
from operator import itemgetter
_ = gettext_set_language(ln)
ib_pubs = intbitset(pubs)
# construct an extended search as an interim solution for author id
# searches. Will build "(exactauthor:v1 OR exactauthor:v2)" strings
# extended_author_search_str = ""
# if bibauthorid_data["is_baid"]:
# if len(names_dict.keys()) > 1:
# extended_author_search_str = '('
#
# for name_index, name_query in enumerate(names_dict.keys()):
# if name_index > 0:
# extended_author_search_str += " OR "
#
# extended_author_search_str += 'exactauthor:"' + name_query + '"'
#
# if len(names_dict.keys()) > 1:
# extended_author_search_str += ')'
# rec_query = 'exactauthor:"' + authorname + '"'
#
# if bibauthorid_data["is_baid"] and extended_author_search_str:
# rec_query = extended_author_search_str
baid_query = ""
extended_author_search_str = ""
if 'is_baid' in bibauthorid_data and bibauthorid_data['is_baid']:
if bibauthorid_data["cid"]:
baid_query = 'author:%s' % bibauthorid_data["cid"]
elif bibauthorid_data["pid"] > -1:
baid_query = 'author:%s' % bibauthorid_data["pid"]
## todo: figure out if the author index is filled with pids/cids.
## if not: fall back to exactauthor search.
# if not index:
# baid_query = ""
if not baid_query:
baid_query = 'exactauthor:"' + authorname + '"'
if bibauthorid_data['is_baid']:
if len(names_dict.keys()) > 1:
extended_author_search_str = '('
for name_index, name_query in enumerate(names_dict.keys()):
if name_index > 0:
extended_author_search_str += " OR "
extended_author_search_str += 'exactauthor:"' + name_query + '"'
if len(names_dict.keys()) > 1:
extended_author_search_str += ')'
if bibauthorid_data['is_baid'] and extended_author_search_str:
baid_query = extended_author_search_str
baid_query = baid_query + " "
-
+
sorted_names_list = sorted(names_dict.iteritems(), key=itemgetter(1),
reverse=True)
# Prepare data for display
# construct names box
header = "<strong>" + _("Name variants") + "</strong>"
content = []
for name, frequency in sorted_names_list:
prquery = baid_query + " exactauthor:" + name
name_lnk = create_html_link(self.build_search_url(p=prquery),
{},
str(frequency),)
content.append("%s (%s)" % (name, name_lnk))
if not content:
content = [_("No Name Variants")]
names_box = self.tmpl_print_searchresultbox(header, "<br />\n".join(content))
# construct papers box
rec_query = baid_query
searchstr = create_html_link(self.build_search_url(p=rec_query),
{}, "<strong>" + "All papers (" + str(len(pubs)) + ")" + "</strong>",)
line1 = "<strong>" + _("Papers") + "</strong>"
line2 = searchstr
if CFG_BIBRANK_SHOW_DOWNLOAD_STATS and num_downloads:
line2 += " (" + _("downloaded") + " "
line2 += str(num_downloads) + " " + _("times") + ")"
if CFG_INSPIRE_SITE:
CFG_COLLS = ['Book',
'Conference',
'Introductory',
'Lectures',
'Preprint',
'Published',
'Report',
'Review',
'Thesis']
else:
CFG_COLLS = ['Article',
'Book',
'Preprint', ]
collsd = {}
for coll in CFG_COLLS:
coll_papers = list(ib_pubs & intbitset(perform_request_search(f="collection", p=coll)))
if coll_papers:
collsd[coll] = coll_papers
colls = collsd.keys()
colls.sort(lambda x, y: cmp(len(collsd[y]), len(collsd[x]))) # sort by number of papers
for coll in colls:
rec_query = baid_query + 'collection:' + coll
line2 += "<br />" + create_html_link(self.build_search_url(p=rec_query),
{}, coll + " (" + str(len(collsd[coll])) + ")",)
if not pubs:
line2 = _("No Papers")
papers_box = self.tmpl_print_searchresultbox(line1, line2)
#make a authoraff string that looks like CERN (1), Caltech (2) etc
authoraff = ""
aff_pubdict_keys = aff_pubdict.keys()
aff_pubdict_keys.sort(lambda x, y: cmp(len(aff_pubdict[y]), len(aff_pubdict[x])))
if aff_pubdict_keys:
for a in aff_pubdict_keys:
print_a = a
if (print_a == ' '):
print_a = _("unknown affiliation")
if authoraff:
authoraff += '<br>'
authoraff += create_html_link(self.build_search_url(p=' or '.join(["%s" % x for x in aff_pubdict[a]]),
f='recid'),
{}, print_a + ' (' + str(len(aff_pubdict[a])) + ')',)
else:
authoraff = _("No Affiliations")
line1 = "<strong>" + _("Affiliations") + "</strong>"
line2 = authoraff
affiliations_box = self.tmpl_print_searchresultbox(line1, line2)
# print frequent keywords:
keywstr = ""
if (kwtuples):
for (kw, freq) in kwtuples:
if keywstr:
keywstr += '<br>'
rec_query = baid_query + 'keyword:"' + kw + '"'
searchstr = create_html_link(self.build_search_url(p=rec_query),
{}, kw + " (" + str(freq) + ")",)
keywstr = keywstr + " " + searchstr
else:
keywstr += _('No Keywords')
line1 = "<strong>" + _("Frequent keywords") + "</strong>"
line2 = keywstr
keyword_box = self.tmpl_print_searchresultbox(line1, line2)
header = "<strong>" + _("Frequent co-authors") + "</strong>"
content = []
sorted_coauthors = sorted(sorted(authors.iteritems(), key=itemgetter(0)),
key=itemgetter(1), reverse=True)
for name, frequency in sorted_coauthors:
rec_query = baid_query + 'exactauthor:"' + name + '"'
lnk = create_html_link(self.build_search_url(p=rec_query), {}, "%s (%s)" % (name, frequency),)
content.append("%s" % lnk)
if not content:
content = [_("No Frequent Co-authors")]
coauthor_box = self.tmpl_print_searchresultbox(header, "<br />\n".join(content))
pubs_to_papers_link = create_html_link(self.build_search_url(p=baid_query), {}, str(len(pubs)))
display_name = ""
try:
display_name = sorted_names_list[0][0]
except IndexError:
display_name = "&nbsp;"
req.write('<h1>%s <span style="font-size:50%%;">(%s papers)</span></h1>'
% (display_name, pubs_to_papers_link))
# req.write("<h1>%s</h1>" % (authorname))
if person_link:
req.write('<div><a href="%s/person/%s?open_claim=True">%s</a></div>'
% (CFG_SITE_URL, person_link,
_("This is me. Verify my publication list.")))
req.write("<table width=80%><tr valign=top><td>")
req.write(names_box)
req.write("<br />")
req.write(papers_box)
req.write("<br />")
req.write(keyword_box)
req.write("</td>")
req.write("<td>&nbsp;</td>")
req.write("<td>")
req.write(affiliations_box)
req.write("<br />")
req.write(coauthor_box)
req.write("</td></tr></table>")
# print citations:
rec_query = baid_query
if len(citedbylist):
line1 = "<strong>" + _("Citations:") + "</strong>"
line2 = ""
if not pubs:
line2 = _("No Citation Information available")
req.write(self.tmpl_print_searchresultbox(line1, line2))
# print frequent co-authors:
# collabstr = ""
# if (authors):
# for c in authors:
# c = c.strip()
# if collabstr:
# collabstr += '<br>'
# #do not add this person him/herself in the list
# cUP = c.upper()
# authornameUP = authorname.upper()
# if not cUP == authornameUP:
# commpubs = intbitset(pubs) & intbitset(perform_request_search(p="exactauthor:\"%s\" exactauthor:\"%s\"" % (authorname, c)))
# collabstr = collabstr + create_html_link(self.build_search_url(p='exactauthor:"' + authorname + '" exactauthor:"' + c + '"'),
# {}, c + " (" + str(len(commpubs)) + ")",)
# else: collabstr += 'None'
# banner = self.tmpl_print_searchresultbox("<strong>" + _("Frequent co-authors:") + "</strong>", collabstr)
# print frequently publishes in journals:
#if (vtuples):
# pubinfo = ""
# for t in vtuples:
# (journal, num) = t
# pubinfo += create_html_link(self.build_search_url(p='exactauthor:"' + authorname + '" ' + \
# 'journal:"' + journal + '"'),
# {}, journal + " ("+str(num)+")<br/>")
# banner = self.tmpl_print_searchresultbox("<strong>" + _("Frequently publishes in:") + "<strong>", pubinfo)
# req.write(banner)
def tmpl_detailed_record_references(self, recID, ln, content):
"""Returns the discussion page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- 'content' *string* - The main content of the page
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if content is not None:
out += content
return out
def tmpl_citesummary_prologue(self, d_total_recs, l_colls, searchpattern, searchfield, ln=CFG_SITE_LANG):
"""HTML citesummary format, prologue. A part of HCS format suite."""
_ = gettext_set_language(ln)
out = """<p><table id="citesummary">
<tr><td><strong class="headline">%(msg_title)s</strong></td>""" % \
{'msg_title': _("Citation summary results"), }
for coll, colldef in l_colls:
out += '<td align="right">%s</td>' % coll
out += '</tr>'
out += """<tr><td><strong>%(msg_recs)s</strong></td>""" % \
{'msg_recs': _("Total number of citable papers analyzed:"), }
for coll, colldef in l_colls:
link_url = CFG_SITE_URL + '/search?p='
if searchpattern:
p = searchpattern
if searchfield:
if " " in searchpattern:
p = searchfield + ':"' + searchpattern + '"'
else:
p = searchfield + ':' + searchpattern
link_url += quote(p)
if colldef:
link_url += '%20AND%20' + quote(colldef)
link_url += '&amp;rm=citation';
link_text = self.tmpl_nice_number(d_total_recs[coll], ln)
out += '<td align="right"><a href="%s">%s</a></td>' % (link_url, link_text)
out += '</tr>'
return out
def tmpl_citesummary_overview(self, d_total_cites, d_avg_cites, l_colls, ln=CFG_SITE_LANG):
"""HTML citesummary format, overview. A part of HCS format suite."""
_ = gettext_set_language(ln)
out = """<tr><td><strong>%(msg_cites)s</strong></td>""" % \
{'msg_cites': _("Total number of citations:"), }
for coll, colldef in l_colls:
out += '<td align="right">%s</td>' % self.tmpl_nice_number(d_total_cites[coll], ln)
out += '</tr>'
out += """<tr><td><strong>%(msg_avgcit)s</strong></td>""" % \
{'msg_avgcit': _("Average citations per paper:"), }
for coll, colldef in l_colls:
out += '<td align="right">%.1f</td>' % d_avg_cites[coll]
out += '</tr>'
out += """<tr><td><strong>%(msg_breakdown)s</strong></td></tr>""" % \
{'msg_breakdown': _("Breakdown of papers by citations:"), }
return out
def tmpl_citesummary_breakdown_by_fame(self, d_cites, low, high, fame, l_colls, searchpattern, searchfield, ln=CFG_SITE_LANG):
"""HTML citesummary format, breakdown by fame. A part of HCS format suite."""
_ = gettext_set_language(ln)
out = """<tr><td>%(fame)s</td>""" % \
{'fame': fame, }
for coll, colldef in l_colls:
link_url = CFG_SITE_URL + '/search?p='
if searchpattern:
p = searchpattern
if searchfield:
if " " in searchpattern:
p = searchfield + ':"' + searchpattern + '"'
else:
p = searchfield + ':' + searchpattern
link_url += quote(p) + '%20AND%20'
if colldef:
link_url += quote(colldef) + '%20AND%20'
if low == 0 and high == 0:
link_url += quote('cited:0')
else:
link_url += quote('cited:%i->%i' % (low, high))
link_url += '&amp;rm=citation';
link_text = self.tmpl_nice_number(d_cites[coll], ln)
out += '<td align="right"><a href="%s">%s</a></td>' % (link_url, link_text)
out += '</tr>'
return out
def tmpl_citesummary_h_index(self, d_h_factors, l_colls, ln=CFG_SITE_LANG):
"""HTML citesummary format, h factor output. A part of the HCS suite."""
_ = gettext_set_language(ln)
out = "<tr><td></td></tr><tr><td><strong>%(msg_additional)s</strong> <small><small>[<a href=\"%(help_url)s\">?</a>]</small></small></td></tr>" % \
{'msg_additional': _("Additional Citation Metrics"),
'help_url': CFG_SITE_URL + '/help/citation-metrics', }
out += '<tr><td>h-index <small><small>[<a href="'
# use ? help linking in the style of oai_repository_admin.py
out += '%s">' % (CFG_SITE_URL + '/help/citation-metrics#citesummary_h-index')
out += '?</a>]</small></small></td>'
for coll, colldef in l_colls:
out += '<td align="right">%s</td>' % self.tmpl_nice_number(d_h_factors[coll], ln)
out += '</tr>'
return out
def tmpl_citesummary_epilogue(self, ln=CFG_SITE_LANG):
"""HTML citesummary format, epilogue. A part of HCS format suite."""
_ = gettext_set_language(ln)
out = """</table>"""
return out
def tmpl_unapi(self, formats, identifier=None):
"""
Provide a list of object format available from the unAPI service
for the object identified by IDENTIFIER
"""
out = '<?xml version="1.0" encoding="UTF-8" ?>\n'
if identifier:
out += '<formats id="%i">\n' % (identifier)
else:
out += "<formats>\n"
for format_name, format_type in formats.iteritems():
docs = ''
if format_name == 'xn':
docs = 'http://www.nlm.nih.gov/databases/dtd/'
format_type = 'application/xml'
format_name = 'nlm'
elif format_name == 'xm':
docs = 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd'
format_type = 'application/xml'
format_name = 'marcxml'
elif format_name == 'xr':
format_type = 'application/rss+xml'
docs = 'http://www.rssboard.org/rss-2-0/'
elif format_name == 'xw':
format_type = 'application/xml'
docs = 'http://www.refworks.com/RefWorks/help/RefWorks_Tagged_Format.htm'
elif format_name == 'xoaidc':
format_type = 'application/xml'
docs = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd'
elif format_name == 'xe':
format_type = 'application/xml'
docs = 'http://www.endnote.com/support/'
format_name = 'endnote'
elif format_name == 'xd':
format_type = 'application/xml'
docs = 'http://dublincore.org/schemas/'
format_name = 'dc'
elif format_name == 'xo':
format_type = 'application/xml'
docs = 'http://www.loc.gov/standards/mods/v3/mods-3-3.xsd'
format_name = 'mods'
if docs:
out += '<format name="%s" type="%s" docs="%s" />\n' % (xml_escape(format_name), xml_escape(format_type), xml_escape(docs))
else:
out += '<format name="%s" type="%s" />\n' % (xml_escape(format_name), xml_escape(format_type))
out += "</formats>"
return out
diff --git a/modules/websearch/lib/websearch_webinterface.py b/modules/websearch/lib/websearch_webinterface.py
index 89d41e32a..a4a79f7a3 100644
--- a/modules/websearch/lib/websearch_webinterface.py
+++ b/modules/websearch/lib/websearch_webinterface.py
@@ -1,1605 +1,1606 @@
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSearch URL handler."""
__revision__ = "$Id$"
import cgi
import os
import datetime
import time
import sys
from urllib import quote
from invenio import webinterface_handler_config as apache
#maximum number of collaborating authors etc shown in GUI
MAX_COLLAB_LIST = 10
MAX_KEYWORD_LIST = 10
MAX_VENUE_LIST = 10
#tag constants
AUTHOR_TAG = "100__a"
AUTHOR_INST_TAG = "100__u"
COAUTHOR_TAG = "700__a"
COAUTHOR_INST_TAG = "700__u"
VENUE_TAG = "909C4p"
KEYWORD_TAG = "695__a"
FKEYWORD_TAG = "6531_a"
CFG_INSPIRE_UNWANTED_KEYWORDS_START = ['talk',
'conference',
'conference proceedings',
'numerical calculations',
'experimental results',
'review',
'bibliography',
'upper limit',
'lower limit',
'tables',
'search for',
'on-shell',
'off-shell',
'formula',
'lectures',
'book',
'thesis']
CFG_INSPIRE_UNWANTED_KEYWORDS_MIDDLE = ['GeV',
'((']
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
from invenio.config import \
CFG_SITE_URL, \
CFG_SITE_NAME, \
CFG_CACHEDIR, \
CFG_SITE_LANG, \
CFG_SITE_SECURE_URL, \
CFG_BIBRANK_SHOW_DOWNLOAD_STATS, \
CFG_WEBSEARCH_INSTANT_BROWSE_RSS, \
CFG_WEBSEARCH_RSS_TTL, \
CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS, \
CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE, \
CFG_WEBDIR, \
CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS, \
CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS, \
CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL, \
CFG_WEBSEARCH_USE_ALEPH_SYSNOS, \
CFG_WEBSEARCH_RSS_I18N_COLLECTIONS, \
CFG_INSPIRE_SITE, \
- CFG_WEBSEARCH_WILDCARD_LIMIT
+ CFG_WEBSEARCH_WILDCARD_LIMIT, \
+ CFG_SITE_RECORD
from invenio.dbquery import Error
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import redirect_to_url, make_canonical_urlargd, drop_default_urlargd
from invenio.htmlutils import get_mathjax_header
from invenio.webuser import getUid, page_not_authorized, get_user_preferences, \
collect_user_info, logoutUser, isUserSuperAdmin
from invenio.websubmit_webinterface import WebInterfaceFilesPages
from invenio.webcomment_webinterface import WebInterfaceCommentsPages
from invenio.bibcirculation_webinterface import WebInterfaceHoldingsPages
from invenio.webpage import page, pageheaderonly, create_error_box
from invenio.messages import gettext_set_language
from invenio.search_engine import check_user_can_view_record, \
collection_reclist_cache, \
collection_restricted_p, \
create_similarly_named_authors_link_box, \
get_colID, \
get_coll_i18nname, \
get_fieldvalues, \
get_fieldvalues_alephseq_like, \
get_most_popular_field_values, \
get_mysql_recid_from_aleph_sysno, \
guess_primary_collection_of_a_record, \
page_end, \
page_start, \
perform_request_cache, \
perform_request_log, \
perform_request_search, \
restricted_collection_cache
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
from invenio.bibformat import format_records
from invenio.bibformat_engine import get_output_formats
from invenio.websearch_webcoll import mymkdir, get_collection
from invenio.intbitset import intbitset
from invenio.bibupload import find_record_from_sysno
from invenio.bibrank_citation_searcher import get_cited_by_list
from invenio.bibrank_downloads_indexer import get_download_weight_total
from invenio.search_engine_summarizer import summarize_records
from invenio.errorlib import register_exception
from invenio.bibedit_webinterface import WebInterfaceEditPages
from invenio.bibeditmulti_webinterface import WebInterfaceMultiEditPages
from invenio.bibmerge_webinterface import WebInterfaceMergePages
from invenio.search_engine import get_record
import invenio.template
websearch_templates = invenio.template.load('websearch')
search_results_default_urlargd = websearch_templates.search_results_default_urlargd
search_interface_default_urlargd = websearch_templates.search_interface_default_urlargd
try:
output_formats = [output_format['attrs']['code'].lower() for output_format in \
get_output_formats(with_attributes=True).values()]
except KeyError:
output_formats = ['xd', 'xm', 'hd', 'hb', 'hs', 'hx']
output_formats.extend(['hm', 't', 'h'])
def wash_search_urlargd(form):
"""
Create canonical search arguments from those passed via web form.
"""
argd = wash_urlargd(form, search_results_default_urlargd)
if argd.has_key('as'):
argd['aas'] = argd['as']
del argd['as']
# Sometimes, users pass ot=245,700 instead of
# ot=245&ot=700. Normalize that.
ots = []
for ot in argd['ot']:
ots += ot.split(',')
argd['ot'] = ots
# We can either get the mode of function as
# action=<browse|search>, or by setting action_browse or
# action_search.
if argd['action_browse']:
argd['action'] = 'browse'
elif argd['action_search']:
argd['action'] = 'search'
else:
if argd['action'] not in ('browse', 'search'):
argd['action'] = 'search'
del argd['action_browse']
del argd['action_search']
return argd
class WebInterfaceUnAPIPages(WebInterfaceDirectory):
""" Handle /unapi set of pages."""
_exports = ['']
def __call__(self, req, form):
argd = wash_urlargd(form, {
'id' : (int, 0),
'format' : (str, '')})
formats_dict = get_output_formats(True)
formats = {}
for format in formats_dict.values():
if format['attrs']['visibility']:
formats[format['attrs']['code'].lower()] = format['attrs']['content_type']
del formats_dict
if argd['id'] and argd['format']:
## Translate back common format names
format = {
'nlm' : 'xn',
'marcxml' : 'xm',
'dc' : 'xd',
'endnote' : 'xe',
'mods' : 'xo'
}.get(argd['format'], argd['format'])
if format in formats:
- redirect_to_url(req, '%s/record/%s/export/%s' % (CFG_SITE_URL, argd['id'], format))
+ redirect_to_url(req, '%s/%s/%s/export/%s' % (CFG_SITE_URL, CFG_SITE_RECORD, argd['id'], format))
else:
raise apache.SERVER_RETURN, apache.HTTP_NOT_ACCEPTABLE
elif argd['id']:
return websearch_templates.tmpl_unapi(formats, identifier=argd['id'])
else:
return websearch_templates.tmpl_unapi(formats)
index = __call__
class WebInterfaceAuthorPages(WebInterfaceDirectory):
"""
Handle /author/Doe%2C+John page requests as well as
/author/<bibrec_id>:<authorname_string> (e.g. /author/15:Doe%2C+John)
requests. The latter will try to find a person from the personid
universe and will display the joint information from that particular
author cluster.
This interface will handle the following URLs:
- /author/Doe%2C+John which will show information on the exactauthor search
- /author/<bibrec_id>:<authorname_string> (e.g. /author/15:Doe%2C+John)
will try to find a person from the personid
universe and will display the joint information from that particular
author cluster.
- /author/<personid> (e.g. /author/152) will display the joint information
from that particular author cluster (an entity called person).
"""
_exports = ['author']
def __init__(self, pageparam=''):
"""Constructor."""
self.pageparam = cgi.escape(pageparam.replace("+", " "))
self.personid = -1
self.authorname = " "
self.person_data_available = False
def _lookup(self, component, path):
"""This handler parses dynamic URLs (/author/John+Doe)."""
return WebInterfaceAuthorPages(component), path
def __call__(self, req, form):
"""Serve the page in the given language."""
is_bibauthorid = False
bibauthorid_template = None
personid_status_cacher = None
userinfo = collect_user_info(req)
metaheaderadd = ""
try:
from invenio.bibauthorid_webapi import search_person_ids_by_name
from invenio.bibauthorid_webapi import get_papers_by_person_id
from invenio.bibauthorid_webapi import get_person_names_from_id
from invenio.bibauthorid_webapi import get_person_db_names_from_id
from invenio.bibauthorid_webapi import get_person_id_from_canonical_id
from invenio.bibauthorid_webapi import get_person_redirect_link
from invenio.bibauthorid_webapi import is_valid_canonical_id
from invenio.bibauthorid_webapi import get_personid_status_cacher
from invenio.bibauthorid_utils import create_normalized_name
from invenio.bibauthorid_utils import split_name_parts
# from invenio.bibauthorid_config import CLAIMPAPER_CLAIM_OTHERS_PAPERS
from invenio.bibauthorid_config import AID_ENABLED
from invenio.bibauthorid_config import AID_ON_AUTHORPAGES
bibauthorid_template = invenio.template.load('bibauthorid')
# from invenio.access_control_admin import acc_find_user_role_actions
if not AID_ENABLED or not AID_ON_AUTHORPAGES:
is_bibauthorid = False
else:
is_bibauthorid = True
except (ImportError):
is_bibauthorid = False
from operator import itemgetter
argd = wash_urlargd(form,
{'ln': (str, CFG_SITE_LANG),
'verbose': (int, 0),
'recid': (int, -1)
})
ln = argd['ln']
verbose = argd['verbose']
req.argd = argd #needed since perform_req_search
param_recid = argd['recid']
bibauthorid_data = {"is_baid": is_bibauthorid, "pid": -1, "cid": ""}
pubs = []
authors = []
recid = None
nquery = ""
names_dict = {}
db_names_dict = {}
_ = gettext_set_language(ln)
title_message = "Author Details"
#let's see what takes time..
time1 = time.time()
genstart = time1
time2 = time.time()
if is_bibauthorid:
metaheaderadd = bibauthorid_template.tmpl_meta_includes()
# Start the page in clean manner:
req.content_type = "text/html"
req.send_http_header()
req.write(pageheaderonly(req=req, title=title_message,
metaheaderadd=metaheaderadd,
language=ln))
req.write(websearch_templates.tmpl_search_pagestart(ln=ln))
if is_bibauthorid:
personid_status_cacher = get_personid_status_cacher()
personid_status_cacher.recreate_cache_if_needed()
self.person_data_available = personid_status_cacher.cache
if not self.person_data_available:
is_bibauthorid = False
#check if it is a person id (e.g. 144):
try:
self.personid = int(self.pageparam)
except (ValueError, TypeError):
self.personid = -1
#check if it is a canonical ID (e.g. Ellis_J_1):
if is_bibauthorid and is_valid_canonical_id(self.pageparam):
try:
self.personid = int(get_person_id_from_canonical_id(self.pageparam))
except (ValueError, TypeError):
self.personid = -1
if self.personid < 0 and is_bibauthorid:
if param_recid > -1:
# Well, it's not a person id, did we get a record ID?
recid = param_recid
nquery = self.pageparam
elif self.pageparam.count(":"):
# No recid passed, maybe name is recid:name or name:recid pair?
left, right = self.pageparam.split(":")
try:
recid = int(left)
nquery = str(right)
except (ValueError, TypeError):
try:
recid = int(right)
nquery = str(left)
except (ValueError, TypeError):
recid = None
nquery = self.pageparam
else:
# No recid could be determined. Work with name only
nquery = self.pageparam
sorted_results = search_person_ids_by_name(nquery)
test_results = None
if recid:
for results in sorted_results:
pid = results[0]
authorpapers = get_papers_by_person_id(pid, -1)
authorpapers = sorted(authorpapers, key=itemgetter(0),
reverse=True)
if (recid and
not (str(recid) in [row[0] for row in authorpapers])):
continue
authors.append([results[0], results[1],
authorpapers[0:4]])
test_results = authors
else:
test_results = [i for i in sorted_results if i[1][0][2] > .8]
if len(test_results) == 1:
self.personid = test_results[0][0]
elif len(test_results) > 1:
if bibauthorid_template and nquery:
authors = []
for results in sorted_results:
pid = results[0]
authorpapers = get_papers_by_person_id(pid, -1)
authorpapers = sorted(authorpapers, key=itemgetter(0),
reverse=True)
authors.append([results[0], results[1],
authorpapers[0:4]])
srch = bibauthorid_template.tmpl_author_search
body = srch(nquery, authors, author_pages_mode=True)
req.write(body)
return
# start page
# req.content_type = "text/html"
# req.send_http_header()
# uid = getUid(req)
# page_start(req, "hb", "", "", ln, uid)
if self.personid < 0 and is_bibauthorid:
# Well, no person. Fall back to the exact author name search then.
ptitle = ''
if recid:
try:
ptitle = get_record(recid)['245'][0][0][0][1]
except (IndexError,TypeError):
ptitle = '"Title not available"'
self.authorname = self.pageparam
title = ''
pmsg = ''
if ptitle:
pmsg = " on paper '%s'" % ptitle
# We're sorry we're introducing html tags where they weren't before. XXX
message = ""
if CFG_INSPIRE_SITE:
message += ("<p>We are in the process of attributing papers to people so that we can "
"improve publication lists.</p>\n")
message += ("<p>We have not generated the publication list for author '%s'%s. Please be patient as we "
"continue to match people to author names and publications. '%s' may be attributed in the next "
"few weeks.</p>" % (self.pageparam, pmsg, self.pageparam))
req.write('<div id="header">%s</div><br>' % title)
req.write('%s <br>' % message)
if not nquery:
nquery = self.pageparam
if not authors:
authors = []
sorted_results = search_person_ids_by_name(nquery)
for results in sorted_results:
pid = results[0]
authorpapers = get_papers_by_person_id(pid, -1)
authorpapers = sorted(authorpapers, key=itemgetter(0),
reverse=True)
authors.append([results[0], results[1],
authorpapers[0:4]])
srch = bibauthorid_template.tmpl_author_search
body = srch(nquery, authors, author_pages_mode=True)
req.write(body)
return
# return self._psearch(req, form, is_fallback=True, fallback_query=self.pageparam, fallback_title=title, fallback_message=message)
elif self.personid < 0 and not is_bibauthorid:
if not self.pageparam:
return websearch_templates.tmpl_author_information(req, {},
self.authorname,
0, {}, {}, {},
{}, {}, {},
None,
bibauthorid_data,
ln)
self.authorname = self.pageparam
#search the publications by this author
pubs = perform_request_search(req=None, p=self.authorname, f="exactauthor")
names_dict[self.authorname] = len(pubs)
db_names_dict[self.authorname] = len(pubs)
elif is_bibauthorid and self.personid > -1:
#yay! Person found! find only papers not disapproved by humans
req.write("<!-- Authorpages are Bibauthorid-powered !-->")
full_pubs = get_papers_by_person_id(self.personid, -1)
pubs = [int(row[0]) for row in full_pubs]
longest_name = ""
try:
self.personid = int(self.personid)
except (TypeError, ValueError):
raise ValueError("Personid must be a number!")
for aname, acount in get_person_names_from_id(self.personid):
names_dict[aname] = acount
norm_name = create_normalized_name(split_name_parts(aname))
if len(norm_name) > len(longest_name):
longest_name = norm_name
for aname, acount in get_person_db_names_from_id(self.personid):
aname = aname.replace('"','').strip()
db_names_dict[aname] = acount
self.authorname = longest_name
if not pubs and param_recid > -1:
req.write("<p>")
req.write(_("We're sorry. The requested author \"%s\" seems not to be listed on the specified paper."
% (self.pageparam,)))
req.write("<br />")
req.write(_("Please try the following link to start a broader search on the author: "))
req.write('<a href="%s/author/%s">%s</a>'
% (CFG_SITE_URL, self.pageparam, self.pageparam))
req.write("</p>")
return page_end(req, 'hb', ln)
#get most frequent authors of these pubs
popular_author_tuples = get_most_popular_field_values(pubs, (AUTHOR_TAG, COAUTHOR_TAG))
coauthors = {}
for (coauthor, frequency) in popular_author_tuples:
if coauthor not in db_names_dict:
coauthors[coauthor] = frequency
if len(coauthors) > MAX_COLLAB_LIST:
break
time1 = time.time()
if verbose == 9:
req.write("<br/>popularized authors: " + str(time1 - time2) + "<br/>")
#and publication venues
venuetuples = get_most_popular_field_values(pubs, (VENUE_TAG))
time2 = time.time()
if verbose == 9:
req.write("<br/>venues: " + str(time2 - time1) + "<br/>")
#and keywords
kwtuples = get_most_popular_field_values(pubs, (KEYWORD_TAG, FKEYWORD_TAG), count_repetitive_values=False)
if CFG_INSPIRE_SITE:
# filter kw tuples against unwanted keywords:
kwtuples_filtered = ()
for (kw, num) in kwtuples:
kwlower = kw.lower()
kwlower_unwanted = False
for unwanted_keyword in CFG_INSPIRE_UNWANTED_KEYWORDS_START:
if kwlower.startswith(unwanted_keyword):
kwlower_unwanted = True # unwanted keyword found
break
for unwanted_keyword in CFG_INSPIRE_UNWANTED_KEYWORDS_MIDDLE:
if unwanted_keyword in kwlower:
kwlower_unwanted = True # unwanted keyword found
break
if not kwlower_unwanted:
kwtuples_filtered += ((kw, num),)
kwtuples = kwtuples_filtered
time1 = time.time()
if verbose == 9:
req.write("<br/>keywords: " + str(time1 - time2) + "<br/>")
#construct a simple list of tuples that contains keywords that appear
#more than once moreover, limit the length of the list
#to MAX_KEYWORD_LIST
kwtuples = kwtuples[0:MAX_KEYWORD_LIST]
vtuples = venuetuples[0:MAX_VENUE_LIST]
time2 = time.time()
if verbose == 9:
req.write("<br/>misc: " + str(time2 - time1) + "<br/>")
#a dict. keys: affiliations, values: lists of publications
author_aff_pubs = self.get_institute_pub_dict(pubs, db_names_dict.keys())
time1 = time.time()
if verbose == 9:
req.write("<br/>affiliations: " + str(time1 - time2) + "<br/>")
totaldownloads = 0
if CFG_BIBRANK_SHOW_DOWNLOAD_STATS:
#find out how many times these records have been downloaded
recsloads = {}
recsloads = get_download_weight_total(recsloads, pubs)
#sum up
for k in recsloads.keys():
totaldownloads = totaldownloads + recsloads[k]
#get cited by..
citedbylist = get_cited_by_list(pubs)
person_link = None
if (is_bibauthorid
and self.personid >= 0
and "precached_viewclaimlink" in userinfo
and "precached_usepaperattribution" in userinfo
and "precached_usepaperclaim" in userinfo
and (userinfo["precached_usepaperclaim"]
or userinfo["precached_usepaperattribution"])
):
person_link = self.personid
bibauthorid_data["pid"] = self.personid
cid = get_person_redirect_link(self.personid)
if is_valid_canonical_id(cid):
person_link = cid
bibauthorid_data["cid"] = cid
time1 = time.time()
if verbose == 9:
req.write("<br/>citedby: " + str(time1 - time2) + "<br/>")
#finally all stuff there, call the template
websearch_templates.tmpl_author_information(req, pubs, self.authorname,
totaldownloads,
author_aff_pubs,
citedbylist, kwtuples,
coauthors, vtuples,
db_names_dict, person_link,
bibauthorid_data, ln)
time1 = time.time()
#cited-by summary
rec_query = ""
extended_author_search_str = ""
if bibauthorid_data['is_baid']:
if bibauthorid_data["cid"]:
rec_query = 'author:"%s"' % bibauthorid_data["cid"]
elif bibauthorid_data["pid"] > -1:
rec_query = 'author:"%s"' % bibauthorid_data["pid"]
if not rec_query:
rec_query = 'exactauthor:"' + self.authorname + '"'
if is_bibauthorid:
if len(db_names_dict.keys()) > 1:
extended_author_search_str = '('
for name_index, name_query in enumerate(db_names_dict.keys()):
if name_index > 0:
extended_author_search_str += " OR "
extended_author_search_str += 'exactauthor:"' + name_query + '"'
if len(db_names_dict.keys()) > 1:
extended_author_search_str += ')'
if is_bibauthorid and extended_author_search_str:
rec_query = extended_author_search_str
if pubs:
req.write(summarize_records(intbitset(pubs), 'hcs', ln, rec_query, req=req))
time2 = time.time()
if verbose == 9:
req.write("<br/>summarizer: " + str(time2 - time1) + "<br/>")
# simauthbox = create_similarly_named_authors_link_box(self.authorname)
# req.write(simauthbox)
if verbose == 9:
req.write("<br/>all: " + str(time.time() - genstart) + "<br/>")
return page_end(req, 'hb', ln)
def _psearch(self, req, form, is_fallback=True, fallback_query='', fallback_title='', fallback_message=''):
html = []
h = html.append
if fallback_title:
h('<div id="header">%s</div><br>' % fallback_title)
if fallback_message:
h('%s <br>' % fallback_message)
h(' We may have \'%s\' partially matched; <a href=/person/search?q=%s>click here</a> ' % (fallback_query, fallback_query))
h('to see what we have so far. (Note: this is likely to update frequently.')
return "\n".join(html)
def get_institute_pub_dict(self, recids, names_list):
"""return a dictionary consisting of institute -> list of publications"""
author_aff_pubs = {} #the dictionary to be built
for recid in recids:
#iterate all so that we get first author's intitute
#if this the first author OR
#"his" institute if he is an affliate author
affus = [] #list of insts from the given record
mainauthors = get_fieldvalues(recid, AUTHOR_TAG)
mainauthor = " "
if mainauthors:
mainauthor = mainauthors[0]
if (mainauthor in names_list):
affus = get_fieldvalues(recid, AUTHOR_INST_TAG)
else:
#search for coauthors..
coauthor_field_lines = []
coauthorfield_content = get_fieldvalues_alephseq_like(recid, \
COAUTHOR_TAG[:3])
if coauthorfield_content:
coauthor_field_lines = coauthorfield_content.split("\n")
for line in coauthor_field_lines:
for name_item in names_list:
breakit = False
if line.count(name_item) > 0:
#get affilitions .. the correct ones are $$+code
code = COAUTHOR_INST_TAG[-1]
myparts = line.split("$$")
for part in myparts:
if part and part[0] == code:
myaff = part[1:]
affus.append(myaff)
breakit = True
if breakit:
break
#if this is empty, add a dummy " " value
if (affus == []):
affus = [" "]
for a in affus:
#add in author_aff_pubs
if (author_aff_pubs.has_key(a)):
tmp = author_aff_pubs[a]
tmp.append(recid)
author_aff_pubs[a] = tmp
else:
author_aff_pubs[a] = [recid]
return author_aff_pubs
index = __call__
class WebInterfaceRecordPages(WebInterfaceDirectory):
- """ Handling of a /record/<recid> URL fragment """
+ """ Handling of a /CFG_SITE_RECORD/<recid> URL fragment """
_exports = ['', 'files', 'reviews', 'comments', 'usage',
'references', 'export', 'citations', 'holdings', 'edit',
'keywords', 'multiedit', 'merge', 'plots']
#_exports.extend(output_formats)
def __init__(self, recid, tab, format=None):
self.recid = recid
self.tab = tab
self.format = format
self.files = WebInterfaceFilesPages(self.recid)
self.reviews = WebInterfaceCommentsPages(self.recid, reviews=1)
self.comments = WebInterfaceCommentsPages(self.recid)
self.usage = self
self.references = self
self.keywords = self
self.holdings = WebInterfaceHoldingsPages(self.recid)
self.citations = self
self.plots = self
self.export = WebInterfaceRecordExport(self.recid, self.format)
self.edit = WebInterfaceEditPages(self.recid)
self.merge = WebInterfaceMergePages(self.recid)
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
argd['tab'] = self.tab
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if argd['rg'] > CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS and not isUserSuperAdmin(user_info):
argd['rg'] = CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text=auth_msg, \
navmenuid='search')
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
- # Return the same page wether we ask for /record/123 or /record/123/
+ # Return the same page wether we ask for /CFG_SITE_RECORD/123 or /CFG_SITE_RECORD/123/
index = __call__
class WebInterfaceRecordRestrictedPages(WebInterfaceDirectory):
""" Handling of a /record-restricted/<recid> URL fragment """
_exports = ['', 'files', 'reviews', 'comments', 'usage',
'references', 'export', 'citations', 'holdings', 'edit',
'keywords', 'multiedit', 'merge', 'plots']
#_exports.extend(output_formats)
def __init__(self, recid, tab, format=None):
self.recid = recid
self.tab = tab
self.format = format
self.files = WebInterfaceFilesPages(self.recid)
self.reviews = WebInterfaceCommentsPages(self.recid, reviews=1)
self.comments = WebInterfaceCommentsPages(self.recid)
self.usage = self
self.references = self
self.keywords = self
self.holdings = WebInterfaceHoldingsPages(self.recid)
self.citations = self
self.plots = self
self.export = WebInterfaceRecordExport(self.recid, self.format)
self.edit = WebInterfaceEditPages(self.recid)
self.merge = WebInterfaceMergePages(self.recid)
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
user_info = collect_user_info(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
if argd['rg'] > CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS and not isUserSuperAdmin(user_info):
argd['rg'] = CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS
record_primary_collection = guess_primary_collection_of_a_record(self.recid)
if collection_restricted_p(record_primary_collection):
(auth_code, dummy) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=record_primary_collection)
if auth_code:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
# Keep all the arguments, they might be reused in the
# record page itself to derivate other queries
req.argd = argd
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
- # Return the same page wether we ask for /record/123 or /record/123/
+ # Return the same page wether we ask for /CFG_SITE_RECORD/123 or /CFG_SITE_RECORD/123/
index = __call__
class WebInterfaceSearchResultsPages(WebInterfaceDirectory):
""" Handling of the /search URL and its sub-pages. """
_exports = ['', 'authenticate', 'cache', 'log']
def __call__(self, req, form):
""" Perform a search. """
argd = wash_search_urlargd(form)
_ = gettext_set_language(argd['ln'])
if req.method == 'POST':
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
uid = getUid(req)
user_info = collect_user_info(req)
if uid == -1:
return page_not_authorized(req, "../",
text=_("You are not authorized to view this area."),
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
if CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL == 2:
## Let's update the current collections list with all
## the restricted collections the user has rights to view.
try:
restricted_collections = user_info['precached_permitted_restricted_collections']
argd_collections = set(argd['c'])
argd_collections.update(restricted_collections)
argd['c'] = list(argd_collections)
except KeyError:
pass
if argd['rg'] > CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS and not isUserSuperAdmin(user_info):
argd['rg'] = CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS
involved_collections = set()
involved_collections.update(argd['c'])
involved_collections.add(argd['cc'])
if argd['id'] > 0:
argd['recid'] = argd['id']
if argd['idb'] > 0:
argd['recidb'] = argd['idb']
if argd['sysno']:
tmp_recid = find_record_from_sysno(argd['sysno'])
if tmp_recid:
argd['recid'] = tmp_recid
if argd['sysnb']:
tmp_recid = find_record_from_sysno(argd['sysnb'])
if tmp_recid:
argd['recidb'] = tmp_recid
if argd['recid'] > 0:
if argd['recidb'] > argd['recid']:
# Hack to check if among the restricted collections
# at least a record of the range is there and
# then if the user is not authorized for that
# collection.
recids = intbitset(xrange(argd['recid'], argd['recidb']))
restricted_collection_cache.recreate_cache_if_needed()
for collname in restricted_collection_cache.cache:
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=collname)
if auth_code and user_info['email'] == 'guest':
coll_recids = get_collection(collname).reclist
if coll_recids & recids:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : collname})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text=auth_msg, \
navmenuid='search')
else:
involved_collections.add(guess_primary_collection_of_a_record(argd['recid']))
# If any of the collection requires authentication, redirect
# to the authentication form.
for coll in involved_collections:
if collection_restricted_p(coll):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : coll})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text=auth_msg, \
navmenuid='search')
#check if the user has rights to set a high wildcard limit
#if not, reduce the limit set by user, with the default one
if CFG_WEBSEARCH_WILDCARD_LIMIT > 0 and (argd['wl'] > CFG_WEBSEARCH_WILDCARD_LIMIT or argd['wl'] == 0):
auth_code, auth_message = acc_authorize_action(req,'runbibedit')
if auth_code != 0:
argd['wl'] = CFG_WEBSEARCH_WILDCARD_LIMIT
# Keep all the arguments, they might be reused in the
# search_engine itself to derivate other queries
req.argd = argd
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
def cache(self, req, form):
"""Search cache page."""
argd = wash_urlargd(form, {'action': (str, 'show')})
return perform_request_cache(req, action=argd['action'])
def log(self, req, form):
"""Search log page."""
argd = wash_urlargd(form, {'date': (str, '')})
return perform_request_log(req, date=argd['date'])
def authenticate(self, req, form):
"""Restricted search results pages."""
argd = wash_search_urlargd(form)
user_info = collect_user_info(req)
for coll in argd['c'] + [argd['cc']]:
if collection_restricted_p(coll):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : coll})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text=auth_msg, \
navmenuid='search')
#check if the user has rights to set a high wildcard limit
#if not, reduce the limit set by user, with the default one
if CFG_WEBSEARCH_WILDCARD_LIMIT > 0 and (argd['wl'] > CFG_WEBSEARCH_WILDCARD_LIMIT or argd['wl'] == 0):
auth_code, auth_message = acc_authorize_action(req,'runbibedit')
if auth_code != 0:
argd['wl'] = CFG_WEBSEARCH_WILDCARD_LIMIT
# Keep all the arguments, they might be reused in the
# search_engine itself to derivate other queries
req.argd = argd
uid = getUid(req)
if uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
index = __call__
class WebInterfaceLegacySearchPages(WebInterfaceDirectory):
""" Handling of the /search.py URL and its sub-pages. """
_exports = ['', ('authenticate', 'index')]
def __call__(self, req, form):
""" Perform a search. """
argd = wash_search_urlargd(form)
# We either jump into the generic search form, or the specific
- # /record/... display if a recid is requested
+ # /CFG_SITE_RECORD/... display if a recid is requested
if argd['recid'] != -1:
- target = '/record/%d' % argd['recid']
+ target = '/%s/%d' % (CFG_SITE_RECORD,argd['recid'])
del argd['recid']
else:
target = '/search'
target += make_canonical_urlargd(argd, search_results_default_urlargd)
return redirect_to_url(req, target, apache.HTTP_MOVED_PERMANENTLY)
index = __call__
# Parameters for the legacy URLs, of the form /?c=ALEPH
legacy_collection_default_urlargd = {
'as': (int, CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE),
'aas': (int, CFG_WEBSEARCH_DEFAULT_SEARCH_INTERFACE),
'verbose': (int, 0),
'c': (str, CFG_SITE_NAME)}
class WebInterfaceSearchInterfacePages(WebInterfaceDirectory):
""" Handling of collection navigation."""
_exports = [('index.py', 'legacy_collection'),
('', 'legacy_collection'),
('search.py', 'legacy_search'),
'search', 'openurl',
'opensearchdescription', 'logout_SSO_hook']
search = WebInterfaceSearchResultsPages()
legacy_search = WebInterfaceLegacySearchPages()
def logout_SSO_hook(self, req, form):
"""Script triggered by the display of the centralized SSO logout
dialog. It logouts the user from Invenio and stream back the
expected picture."""
logoutUser(req)
req.content_type = 'image/gif'
req.encoding = None
req.filename = 'wsignout.gif'
req.headers_out["Content-Disposition"] = "inline; filename=wsignout.gif"
req.set_content_length(os.path.getsize('%s/img/wsignout.gif' % CFG_WEBDIR))
req.send_http_header()
req.sendfile('%s/img/wsignout.gif' % CFG_WEBDIR)
def _lookup(self, component, path):
""" This handler is invoked for the dynamic URLs (for
collections and records)"""
if component == 'collection':
c = '/'.join(path)
def answer(req, form):
"""Accessing collections cached pages."""
# Accessing collections: this is for accessing the
# cached page on top of each collection.
argd = wash_urlargd(form, search_interface_default_urlargd)
# We simply return the cached page of the collection
argd['c'] = c
if not argd['c']:
# collection argument not present; display
# home collection by default
argd['c'] = CFG_SITE_NAME
# Treat `as' argument specially:
if argd.has_key('as'):
argd['aas'] = argd['as']
del argd['as']
return display_collection(req, **argd)
return answer, []
- elif component == 'record' and path and path[0] == 'merge':
+ elif component == CFG_SITE_RECORD and path and path[0] == 'merge':
return WebInterfaceMergePages(), path[1:]
- elif component == 'record' and path and path[0] == 'edit':
+ elif component == CFG_SITE_RECORD and path and path[0] == 'edit':
return WebInterfaceEditPages(), path[1:]
- elif component == 'record' and path and path[0] == 'multiedit':
+ elif component == CFG_SITE_RECORD and path and path[0] == 'multiedit':
return WebInterfaceMultiEditPages(), path[1:]
- elif component == 'record' or component == 'record-restricted':
+ elif component == CFG_SITE_RECORD or component == 'record-restricted':
try:
if CFG_WEBSEARCH_USE_ALEPH_SYSNOS:
- # let us try to recognize /record/<SYSNO> style of URLs:
+ # let us try to recognize /<CFG_SITE_RECORD>/<SYSNO> style of URLs:
x = get_mysql_recid_from_aleph_sysno(path[0])
if x:
recid = x
else:
recid = int(path[0])
else:
recid = int(path[0])
except IndexError:
- # display record #1 for URL /record without a number
+ # display record #1 for URL /CFG_SITE_RECORD without a number
recid = 1
except ValueError:
if path[0] == '':
- # display record #1 for URL /record/ without a number
+ # display record #1 for URL /CFG_SITE_RECORD/ without a number
recid = 1
else:
- # display page not found for URLs like /record/foo
+ # display page not found for URLs like /CFG_SITE_RECORD/foo
return None, []
if recid <= 0:
- # display page not found for URLs like /record/-5 or /record/0
+ # display page not found for URLs like /CFG_SITE_RECORD/-5 or /CFG_SITE_RECORD/0
return None, []
format = None
tab = ''
try:
if path[1] in ['', 'files', 'reviews', 'comments', 'usage',
'references', 'citations', 'holdings', 'edit',
'keywords', 'multiedit', 'merge', 'plots']:
tab = path[1]
elif path[1] == 'export':
tab = ''
format = path[2]
# format = None
# elif path[1] in output_formats:
# tab = ''
# format = path[1]
else:
- # display page not found for URLs like /record/references
+ # display page not found for URLs like /CFG_SITE_RECORD/references
# for a collection where 'references' tabs is not visible
return None, []
except IndexError:
# Keep normal url if tabs is not specified
pass
#if component == 'record-restricted':
#return WebInterfaceRecordRestrictedPages(recid, tab, format), path[1:]
#else:
return WebInterfaceRecordPages(recid, tab, format), path[1:]
return None, []
def openurl(self, req, form):
""" OpenURL Handler."""
argd = wash_urlargd(form, websearch_templates.tmpl_openurl_accepted_args)
ret_url = websearch_templates.tmpl_openurl2invenio(argd)
if ret_url:
return redirect_to_url(req, ret_url)
else:
return redirect_to_url(req, CFG_SITE_URL)
def opensearchdescription(self, req, form):
"""OpenSearch description file"""
req.content_type = "application/opensearchdescription+xml"
req.send_http_header()
argd = wash_urlargd(form, {'ln': (str, CFG_SITE_LANG),
'verbose': (int, 0) })
return websearch_templates.tmpl_opensearch_description(ln=argd['ln'])
def legacy_collection(self, req, form):
"""Collection URL backward compatibility handling."""
accepted_args = dict(legacy_collection_default_urlargd)
argd = wash_urlargd(form, accepted_args)
# Treat `as' argument specially:
if argd.has_key('as'):
argd['aas'] = argd['as']
del argd['as']
# If we specify no collection, then we don't need to redirect
# the user, so that accessing <http://yoursite/> returns the
# default collection.
if not form.has_key('c'):
return display_collection(req, **argd)
# make the collection an element of the path, and keep the
# other query elements as is. If the collection is CFG_SITE_NAME,
# however, redirect to the main URL.
c = argd['c']
del argd['c']
if c == CFG_SITE_NAME:
target = '/'
else:
target = '/collection/' + quote(c)
# Treat `as' argument specially:
# We are going to redirect, so replace `aas' by `as' visible argument:
if argd.has_key('aas'):
argd['as'] = argd['aas']
del argd['aas']
target += make_canonical_urlargd(argd, legacy_collection_default_urlargd)
return redirect_to_url(req, target)
def display_collection(req, c, aas, verbose, ln):
"""Display search interface page for collection c by looking
in the collection cache."""
_ = gettext_set_language(ln)
req.argd = drop_default_urlargd({'aas': aas, 'verbose': verbose, 'ln': ln},
search_interface_default_urlargd)
# get user ID:
try:
uid = getUid(req)
user_preferences = {}
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this collection",
navmenuid='search')
elif uid > 0:
user_preferences = get_user_preferences(uid)
except Error:
register_exception(req=req, alert_admin=True)
return page(title=_("Internal Error"),
body=create_error_box(req, verbose=verbose, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req,
navmenuid='search')
# start display:
req.content_type = "text/html"
req.send_http_header()
# deduce collection id:
colID = get_colID(c)
if type(colID) is not int:
page_body = '<p>' + (_("Sorry, collection %s does not seem to exist.") % ('<strong>' + str(c) + '</strong>')) + '</p>'
page_body = '<p>' + (_("You may want to start browsing from %s.") % ('<a href="' + CFG_SITE_URL + '?ln=' + ln + '">' + get_coll_i18nname(CFG_SITE_NAME, ln) + '</a>')) + '</p>'
if req.header_only:
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
return page(title=_("Collection %s Not Found") % cgi.escape(c),
body=page_body,
description=(CFG_SITE_NAME + ' - ' + _("Not found") + ': ' + cgi.escape(str(c))),
keywords="%s" % CFG_SITE_NAME,
uid=uid,
language=ln,
req=req,
navmenuid='search')
# wash `aas' argument:
if not os.path.exists("%s/collections/%d/body-as=%d-ln=%s.html" % \
(CFG_CACHEDIR, colID, aas, ln)):
# nonexistent `aas' asked for, fall back to Simple Search:
aas = 0
# display collection interface page:
try:
filedesc = open("%s/collections/%d/navtrail-as=%d-ln=%s.html" % \
(CFG_CACHEDIR, colID, aas, ln), "r")
c_navtrail = filedesc.read()
filedesc.close()
except:
c_navtrail = ""
try:
filedesc = open("%s/collections/%d/body-as=%d-ln=%s.html" % \
(CFG_CACHEDIR, colID, aas, ln), "r")
c_body = filedesc.read()
filedesc.close()
except:
c_body = ""
try:
filedesc = open("%s/collections/%d/portalbox-tp-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_portalbox_tp = filedesc.read()
filedesc.close()
except:
c_portalbox_tp = ""
try:
filedesc = open("%s/collections/%d/portalbox-te-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_portalbox_te = filedesc.read()
filedesc.close()
except:
c_portalbox_te = ""
try:
filedesc = open("%s/collections/%d/portalbox-lt-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_portalbox_lt = filedesc.read()
filedesc.close()
except:
c_portalbox_lt = ""
try:
# show help boxes (usually located in "tr", "top right")
# if users have not banned them in their preferences:
c_portalbox_rt = ""
if user_preferences.get('websearch_helpbox', 1) > 0:
filedesc = open("%s/collections/%d/portalbox-rt-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_portalbox_rt = filedesc.read()
filedesc.close()
except:
c_portalbox_rt = ""
try:
filedesc = open("%s/collections/%d/last-updated-ln=%s.html" % \
(CFG_CACHEDIR, colID, ln), "r")
c_last_updated = filedesc.read()
filedesc.close()
except:
c_last_updated = ""
try:
title = get_coll_i18nname(c, ln)
except:
title = ""
show_title_p = True
body_css_classes = []
if c == CFG_SITE_NAME:
# Do not display title on home collection
show_title_p = False
body_css_classes.append('home')
if len(collection_reclist_cache.cache.keys()) == 1:
# if there is only one collection defined, do not print its
# title on the page as it would be displayed repetitively.
show_title_p = False
if aas == -1:
show_title_p = False
# RSS:
rssurl = CFG_SITE_URL + '/rss'
rssurl_params = []
if c != CFG_SITE_NAME:
rssurl_params.append('cc=' + quote(c))
if ln != CFG_SITE_LANG and \
c in CFG_WEBSEARCH_RSS_I18N_COLLECTIONS:
rssurl_params.append('ln=' + ln)
if rssurl_params:
rssurl += '?' + '&amp;'.join(rssurl_params)
if 'hb' in CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS:
metaheaderadd = get_mathjax_header()
else:
metaheaderadd = ''
return page(title=title,
body=c_body,
navtrail=c_navtrail,
description="%s - %s" % (CFG_SITE_NAME, c),
keywords="%s, %s" % (CFG_SITE_NAME, c),
metaheaderadd=metaheaderadd,
uid=uid,
language=ln,
req=req,
cdspageboxlefttopadd=c_portalbox_lt,
cdspageboxrighttopadd=c_portalbox_rt,
titleprologue=c_portalbox_tp,
titleepilogue=c_portalbox_te,
lastupdated=c_last_updated,
navmenuid='search',
rssurl=rssurl,
body_css_classes=body_css_classes,
show_title_p=show_title_p)
class WebInterfaceRSSFeedServicePages(WebInterfaceDirectory):
"""RSS 2.0 feed service pages."""
def __call__(self, req, form):
"""RSS 2.0 feed service."""
# Keep only interesting parameters for the search
default_params = websearch_templates.rss_default_urlargd
# We need to keep 'jrec' and 'rg' here in order to have
# 'multi-page' RSS. These parameters are not kept be default
# as we don't want to consider them when building RSS links
# from search and browse pages.
default_params.update({'jrec':(int, 1),
'rg': (int, CFG_WEBSEARCH_INSTANT_BROWSE_RSS)})
argd = wash_urlargd(form, default_params)
user_info = collect_user_info(req)
for coll in argd['c'] + [argd['cc']]:
if collection_restricted_p(coll):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : coll})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text=auth_msg, \
navmenuid='search')
# Create a standard filename with these parameters
current_url = websearch_templates.build_rss_url(argd)
cache_filename = current_url.split('/')[-1]
# In the same way as previously, add 'jrec' & 'rg'
req.content_type = "application/rss+xml"
req.send_http_header()
try:
# Try to read from cache
path = "%s/rss/%s.xml" % (CFG_CACHEDIR, cache_filename)
# Check if cache needs refresh
filedesc = open(path, "r")
last_update_time = datetime.datetime.fromtimestamp(os.stat(os.path.abspath(path)).st_mtime)
assert(datetime.datetime.now() < last_update_time + datetime.timedelta(minutes=CFG_WEBSEARCH_RSS_TTL))
c_rss = filedesc.read()
filedesc.close()
req.write(c_rss)
return
except Exception, e:
# do it live and cache
previous_url = None
if argd['jrec'] > 1:
prev_jrec = argd['jrec'] - argd['rg']
if prev_jrec < 1:
prev_jrec = 1
previous_url = websearch_templates.build_rss_url(argd,
jrec=prev_jrec)
recIDs = perform_request_search(req, of="id",
c=argd['c'], cc=argd['cc'],
p=argd['p'], f=argd['f'],
p1=argd['p1'], f1=argd['f1'],
m1=argd['m1'], op1=argd['op1'],
p2=argd['p2'], f2=argd['f2'],
m2=argd['m2'], op2=argd['op2'],
p3=argd['p3'], f3=argd['f3'],
m3=argd['m3'])
nb_found = len(recIDs)
next_url = None
if len(recIDs) >= argd['jrec'] + argd['rg']:
next_url = websearch_templates.build_rss_url(argd,
jrec=(argd['jrec'] + argd['rg']))
first_url = websearch_templates.build_rss_url(argd, jrec=1)
last_url = websearch_templates.build_rss_url(argd, jrec=nb_found - argd['rg'] + 1)
recIDs = recIDs[-argd['jrec']:(-argd['rg'] - argd['jrec']):-1]
rss_prologue = '<?xml version="1.0" encoding="UTF-8"?>\n' + \
websearch_templates.tmpl_xml_rss_prologue(current_url=current_url,
previous_url=previous_url,
next_url=next_url,
first_url=first_url, last_url=last_url,
nb_found=nb_found,
jrec=argd['jrec'], rg=argd['rg']) + '\n'
req.write(rss_prologue)
rss_body = format_records(recIDs,
of='xr',
ln=argd['ln'],
user_info=user_info,
record_separator="\n",
req=req, epilogue="\n")
rss_epilogue = websearch_templates.tmpl_xml_rss_epilogue() + '\n'
req.write(rss_epilogue)
# update cache
dirname = "%s/rss" % (CFG_CACHEDIR)
mymkdir(dirname)
fullfilename = "%s/rss/%s.xml" % (CFG_CACHEDIR, cache_filename)
try:
# Remove the file just in case it already existed
# so that a bit of space is created
os.remove(fullfilename)
except OSError:
pass
# Check if there's enough space to cache the request.
if len(os.listdir(dirname)) < CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS:
try:
os.umask(022)
f = open(fullfilename, "w")
f.write(rss_prologue + rss_body + rss_epilogue)
f.close()
except IOError, v:
if v[0] == 36:
# URL was too long. Never mind, don't cache
pass
else:
raise repr(v)
index = __call__
class WebInterfaceRecordExport(WebInterfaceDirectory):
- """ Handling of a /record/<recid>/export/<format> URL fragment """
+ """ Handling of a /<CFG_SITE_RECORD>/<recid>/export/<format> URL fragment """
_exports = output_formats
def __init__(self, recid, format=None):
self.recid = recid
self.format = format
for output_format in output_formats:
self.__dict__[output_format] = self
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
if not form.has_key('rg'):
# fetch user rg preference only if not overridden via URL
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
# Check if the record belongs to a restricted primary
# collection. If yes, redirect to the authenticated URL.
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if argd['rg'] > CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS and not isUserSuperAdmin(user_info):
argd['rg'] = CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = CFG_SITE_SECURE_URL + '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : CFG_SITE_URL + req.unparsed_uri}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text=auth_msg, \
navmenuid='search')
# mod_python does not like to return [] in case when of=id:
out = perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
- # Return the same page wether we ask for /record/123/export/xm or /record/123/export/xm/
+ # Return the same page wether we ask for /CFG_SITE_RECORD/123/export/xm or /CFG_SITE_RECORD/123/export/xm/
index = __call__
diff --git a/modules/websearch/lib/websearchadminlib.py b/modules/websearch/lib/websearchadminlib.py
index 896dae124..c60729f3f 100644
--- a/modules/websearch/lib/websearchadminlib.py
+++ b/modules/websearch/lib/websearchadminlib.py
@@ -1,3479 +1,3479 @@
## This file is part of Invenio.
## Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable=C0301
"""Invenio WebSearch Administrator Interface."""
__revision__ = "$Id$"
import cgi
import random
import time
import sys
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
from invenio.config import \
CFG_CACHEDIR, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_URL,\
CFG_WEBCOMMENT_ALLOW_COMMENTS,\
CFG_WEBCOMMENT_ALLOW_REVIEWS,\
CFG_INSPIRE_SITE, \
CFG_CERN_SITE
from invenio.bibrankadminlib import \
write_outcome, \
modify_translations, \
get_def_name, \
get_name, \
get_languages, \
addadminbox, \
tupletotable, \
createhiddenform
from invenio.dbquery import \
run_sql, \
get_table_update_time
from invenio.websearch_external_collections import \
external_collections_dictionary, \
external_collection_sort_engine_by_name, \
external_collection_get_state, \
external_collection_get_update_state_list, \
external_collection_apply_changes
from invenio.websearch_external_collections_utils import \
get_collection_descendants
from invenio.websearch_external_collections_config import CFG_EXTERNAL_COLLECTION_STATES_NAME
#from invenio.bibformat_elements import bfe_references
#from invenio.bibformat_engine import BibFormatObject
from invenio.bibdocfile import BibRecDocs
from invenio.messages import gettext_set_language
#from invenio.bibrank_citation_searcher import get_cited_by
from invenio.access_control_admin import acc_get_action_id
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.errorlib import register_exception
from invenio.intbitset import intbitset
def getnavtrail(previous = ''):
"""Get the navtrail"""
navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (CFG_SITE_URL,)
navtrail = navtrail + previous
return navtrail
def fix_collection_scores():
"""
Re-calculate and re-normalize de scores of the collection relationship.
"""
for id_dad in intbitset(run_sql("SELECT id_dad FROM collection_collection")):
for index, id_son in enumerate(run_sql("SELECT id_son FROM collection_collection WHERE id_dad=%s ORDER BY score DESC", (id_dad, ))):
run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s AND id_son=%s", (index * 10 + 10, id_dad, id_son[0]))
def perform_modifytranslations(colID, ln, sel_type='', trans=[], confirm=-1, callback='yes'):
"""Modify the translations of a collection
sel_type - the nametype to modify
trans - the translations in the same order as the languages from get_languages()"""
output = ''
subtitle = ''
sitelangs = get_languages()
if confirm in ["2", 2] and colID:
finresult = modify_translations(colID, sitelangs, sel_type, trans, "collection")
col_dict = dict(get_def_name('', "collection"))
if colID and col_dict.has_key(int(colID)):
colID = int(colID)
subtitle = """<a name="3">3. Modify translations for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a href="%s/help/admin/websearch-admin-guide#3.3">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
if type(trans) is str:
trans = [trans]
if sel_type == '':
sel_type = get_col_nametypes()[0][0]
header = ['Language', 'Translation']
actions = []
types = get_col_nametypes()
if len(types) > 1:
text = """
<span class="adminlabel">Name type</span>
<select name="sel_type" class="admin_w200">
"""
for (key, value) in types:
text += """<option value="%s" %s>%s""" % (key, key == sel_type and 'selected="selected"' or '', value)
trans_names = get_name(colID, ln, key, "collection")
if trans_names and trans_names[0][0]:
text += ": %s" % trans_names[0][0]
text += "</option>"
text += """</select>"""
output += createhiddenform(action="modifytranslations#3",
text=text,
button="Select",
colID=colID,
ln=ln,
confirm=0)
if confirm in [-1, "-1", 0, "0"]:
trans = []
for (key, value) in sitelangs:
try:
trans_names = get_name(colID, key, sel_type, "collection")
trans.append(trans_names[0][0])
except StandardError, e:
trans.append('')
for nr in range(0, len(sitelangs)):
actions.append(["%s %s" % (sitelangs[nr][1], (sitelangs[nr][0]==CFG_SITE_LANG and '<small>(def)</small>' or ''))])
actions[-1].append('<input type="text" name="trans" size="30" value="%s"/>' % trans[nr])
text = tupletotable(header=header, tuple=actions)
output += createhiddenform(action="modifytranslations#3",
text=text,
button="Modify",
colID=colID,
sel_type=sel_type,
ln=ln,
confirm=2)
if sel_type and len(trans) and confirm in ["2", 2]:
output += write_outcome(finresult)
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_modifytranslations", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyrankmethods(colID, ln, func='', rnkID='', confirm=0, callback='yes'):
"""Modify which rank methods is visible to the collection
func - remove or add rank method
rnkID - the id of the rank method."""
output = ""
subtitle = ""
col_dict = dict(get_def_name('', "collection"))
rnk_dict = dict(get_def_name('', "rnkMETHOD"))
if colID and col_dict.has_key(int(colID)):
colID = int(colID)
if func in ["0", 0] and confirm in ["1", 1]:
finresult = attach_rnk_col(colID, rnkID)
elif func in ["1", 1] and confirm in ["1", 1]:
finresult = detach_rnk_col(colID, rnkID)
subtitle = """<a name="9">9. Modify rank options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.9">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """
<dl>
<dt>The rank methods enabled for the collection '%s' is:</dt>
""" % col_dict[colID]
rnkmethods = get_col_rnk(colID, ln)
output += """<dd>"""
if not rnkmethods:
output += """No rank methods"""
else:
for id, name in rnkmethods:
output += """%s, """ % name
output += """</dd>
</dl>
"""
rnk_list = get_def_name('', "rnkMETHOD")
rnk_dict_in_col = dict(get_col_rnk(colID, ln))
rnk_list = filter(lambda x: not rnk_dict_in_col.has_key(x[0]), rnk_list)
if rnk_list:
text = """
<span class="adminlabel">Enable:</span>
<select name="rnkID" class="admin_w200">
<option value="-1">- select rank method -</option>
"""
for (id, name) in rnk_list:
text += """<option value="%s" %s>%s</option>""" % (id, (func in ["0", 0] and confirm in ["0", 0] and int(rnkID) == int(id)) and 'selected="selected"' or '' , name)
text += """</select>"""
output += createhiddenform(action="modifyrankmethods#9",
text=text,
button="Enable",
colID=colID,
ln=ln,
func=0,
confirm=1)
if confirm in ["1", 1] and func in ["0", 0] and int(rnkID) != -1:
output += write_outcome(finresult)
elif confirm not in ["0", 0] and func in ["0", 0]:
output += """<b><span class="info">Please select a rank method.</span></b>"""
coll_list = get_col_rnk(colID, ln)
if coll_list:
text = """
<span class="adminlabel">Disable:</span>
<select name="rnkID" class="admin_w200">
<option value="-1">- select rank method-</option>
"""
for (id, name) in coll_list:
text += """<option value="%s" %s>%s</option>""" % (id, (func in ["1", 1] and confirm in ["0", 0] and int(rnkID) == int(id)) and 'selected="selected"' or '' , name)
text += """</select>"""
output += createhiddenform(action="modifyrankmethods#9",
text=text,
button="Disable",
colID=colID,
ln=ln,
func=1,
confirm=1)
if confirm in ["1", 1] and func in ["1", 1] and int(rnkID) != -1:
output += write_outcome(finresult)
elif confirm not in ["0", 0] and func in ["1", 1]:
output += """<b><span class="info">Please select a rank method.</span></b>"""
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_modifyrankmethods", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_addcollectiontotree(colID, ln, add_dad='', add_son='', rtype='', mtype='', callback='yes', confirm=-1):
"""Form to add a collection to the tree.
add_dad - the dad to add the collection to
add_son - the collection to add
rtype - add it as a regular or virtual
mtype - add it to the regular or virtual tree."""
output = ""
output2 = ""
subtitle = """Attach collection to tree&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.2">?</a>]</small>""" % (CFG_SITE_URL)
col_dict = dict(get_def_name('', "collection"))
if confirm not in [-1, "-1"] and not (add_son and add_dad and rtype):
output2 += """<b><span class="info">All fields must be filled.</span></b><br /><br />
"""
elif add_son and add_dad and rtype:
add_son = int(add_son)
add_dad = int(add_dad)
if confirm not in [-1, "-1"]:
if add_son == add_dad:
output2 += """<b><span class="info">Cannot add a collection as a pointer to itself.</span></b><br /><br />
"""
elif check_col(add_dad, add_son):
res = add_col_dad_son(add_dad, add_son, rtype)
output2 += write_outcome(res)
if res[0] == 1:
output2 += """<b><span class="info"><br /> The collection will appear on your website after the next webcoll run. You can either run it manually or wait until bibsched does it for you.</span></b><br /><br />
"""
else:
output2 += """<b><span class="info">Cannot add the collection '%s' as a %s subcollection of '%s' since it will either create a loop, or the association already exists.</span></b><br /><br />
""" % (col_dict[add_son], (rtype=="r" and 'regular' or 'virtual'), col_dict[add_dad])
add_son = ''
add_dad = ''
rtype = ''
tree = get_col_tree(colID)
col_list = col_dict.items()
col_list.sort(compare_on_val)
output = show_coll_not_in_tree(colID, ln, col_dict)
text = """
<span class="adminlabel">Attach collection:</span>
<select name="add_son" class="admin_w200">
<option value="">- select collection -</option>
"""
for (id, name) in col_list:
if id != colID:
text += """<option value="%s" %s>%s</option>""" % (id, str(id)==str(add_son) and 'selected="selected"' or '', name)
text += """
</select><br />
<span class="adminlabel">to parent collection:</span>
<select name="add_dad" class="admin_w200">
<option value="">- select parent collection -</option>
"""
for (id, name) in col_list:
text += """<option value="%s" %s>%s</option>
""" % (id, str(id)==add_dad and 'selected="selected"' or '', name)
text += """</select><br />
"""
text += """
<span class="adminlabel">with relationship:</span>
<select name="rtype" class="admin_w200">
<option value="">- select relationship -</option>
<option value="r" %s>Regular (Narrow by...)</option>
<option value="v" %s>Virtual (Focus on...)</option>
</select>
""" % ((rtype=="r" and 'selected="selected"' or ''), (rtype=="v" and 'selected="selected"' or ''))
output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/addcollectiontotree" % CFG_SITE_URL,
text=text,
button="Add",
colID=colID,
ln=ln,
confirm=1)
output += output2
#output += perform_showtree(colID, ln)
body = [output]
if callback:
return perform_index(colID, ln, mtype="perform_addcollectiontotree", content=addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_addcollection(colID, ln, colNAME='', dbquery='', callback="yes", confirm=-1):
"""form to add a new collection.
colNAME - the name of the new collection
dbquery - the dbquery of the new collection"""
output = ""
subtitle = """Create new collection&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.1">?</a>]</small>""" % (CFG_SITE_URL)
text = """
<span class="adminlabel">Default name</span>
<input class="admin_w200" type="text" name="colNAME" value="%s" /><br />
""" % colNAME
output = createhiddenform(action="%s/admin/websearch/websearchadmin.py/addcollection" % CFG_SITE_URL,
text=text,
colID=colID,
ln=ln,
button="Add collection",
confirm=1)
if colNAME and confirm in ["1", 1]:
res = add_col(colNAME, '')
output += write_outcome(res)
if res[0] == 1:
output += perform_addcollectiontotree(colID=colID, ln=ln, add_son=res[1], callback='')
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please give the collection a name.</span></b>"""
body = [output]
if callback:
return perform_index(colID, ln=ln, mtype="perform_addcollection", content=addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifydbquery(colID, ln, dbquery='', callback='yes', confirm=-1):
"""form to modify the dbquery of the collection.
dbquery - the dbquery of the collection."""
subtitle = ''
output = ""
col_dict = dict(get_def_name('', "collection"))
if colID and col_dict.has_key(int(colID)):
colID = int(colID)
subtitle = """<a name="1">1. Modify collection query for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.1">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
if confirm == -1:
res = run_sql("SELECT dbquery FROM collection WHERE id=%s" % colID)
dbquery = res[0][0]
if not dbquery:
dbquery = ''
reg_sons = len(get_col_tree(colID, 'r'))
vir_sons = len(get_col_tree(colID, 'v'))
if reg_sons > 1:
if dbquery:
output += "Warning: This collection got subcollections, and should because of this not have a collection query, for further explanation, check the WebSearch Guide<br />"
elif reg_sons <= 1:
if not dbquery:
output += "Warning: This collection does not have any subcollections, and should because of this have a collection query, for further explanation, check the WebSearch Guide<br />"
text = """
<span class="adminlabel">Query</span>
<input class="admin_w200" type="text" name="dbquery" value="%s" /><br />
""" % cgi.escape(dbquery, 1)
output += createhiddenform(action="modifydbquery",
text=text,
button="Modify",
colID=colID,
ln=ln,
confirm=1)
if confirm in ["1", 1]:
res = modify_dbquery(colID, dbquery)
if res:
if dbquery == "":
text = """<b><span class="info">Query removed for this collection.</span></b>"""
else:
text = """<b><span class="info">Query set for this collection.</span></b>"""
else:
text = """<b><span class="info">Sorry, could not change query.</span></b>"""
output += text
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_modifydbquery", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifycollectiontree(colID, ln, move_up='', move_down='', move_from='', move_to='', delete='', rtype='', callback='yes', confirm=0):
"""to modify the collection tree: move a collection up and down, delete a collection, or change the father of the collection.
colID - the main collection of the tree, the root
move_up - move this collection up (is not the collection id, but the place in the tree)
move_up - move this collection down (is not the collection id, but the place in the tree)
move_from - move this collection from the current positon (is not the collection id, but the place in the tree)
move_to - move the move_from collection and set this as it's father. (is not the collection id, but the place in the tree)
delete - delete this collection from the tree (is not the collection id, but the place in the tree)
rtype - the type of the collection in the tree, regular or virtual"""
colID = int(colID)
tree = get_col_tree(colID, rtype)
col_dict = dict(get_def_name('', "collection"))
subtitle = """Modify collection tree: %s&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.3">?</a>]&nbsp;&nbsp;&nbsp;<a href="%s/admin/websearch/websearchadmin.py/showtree?colID=%s&amp;ln=%s">Printer friendly version</a></small>""" % (col_dict[colID], CFG_SITE_URL, CFG_SITE_URL, colID, ln)
fin_output = ""
output = ""
try:
if move_up:
move_up = int(move_up)
switch = find_last(tree, move_up)
if switch and switch_col_treescore(tree[move_up], tree[switch]):
output += """<b><span class="info">Moved the %s collection '%s' up and '%s' down.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]], col_dict[tree[switch][0]])
else:
output += """<b><span class="info">Could not move the %s collection '%s' up and '%s' down.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]], col_dict[tree[switch][0]])
elif move_down:
move_down = int(move_down)
switch = find_next(tree, move_down)
if switch and switch_col_treescore(tree[move_down], tree[switch]):
output += """<b><span class="info">Moved the %s collection '%s' down and '%s' up.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_down][0]], col_dict[tree[switch][0]])
else:
output += """<b><span class="info">Could not move the %s collection '%s' up and '%s' down.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]],col_dict[tree[switch][0]])
elif delete:
delete = int(delete)
if confirm in [0, "0"]:
if col_dict[tree[delete][0]] != col_dict[tree[delete][3]]:
text = """<b>Do you want to remove the %s collection '%s' and its subcollections in the %s collection '%s'.</b>
""" % ((tree[delete][4]=="r" and 'regular' or 'virtual'), col_dict[tree[delete][0]], (rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]])
else:
text = """<b>Do you want to remove all subcollections of the %s collection '%s'.</b>
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]])
output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifycollectiontree#tree" % CFG_SITE_URL,
text=text,
button="Confirm",
colID=colID,
delete=delete,
rtype=rtype,
ln=ln,
confirm=1)
output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL,
text="<b>To cancel</b>",
button="Cancel",
colID=colID,
ln=ln)
else:
if remove_col_subcol(tree[delete][0], tree[delete][3], rtype):
if col_dict[tree[delete][0]] != col_dict[tree[delete][3]]:
output += """<b><span class="info">Removed the %s collection '%s' and its subcollections in subdirectory '%s'.</span></b><br /><br />
""" % ((tree[delete][4]=="r" and 'regular' or 'virtual'), col_dict[tree[delete][0]], col_dict[tree[delete][3]])
else:
output += """<b><span class="info">Removed the subcollections of the %s collection '%s'.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]])
else:
output += """<b><span class="info">Could not remove the collection from the tree.</span></b><br /><br />
"""
delete = ''
elif move_from and not move_to:
move_from_rtype = move_from[0]
move_from_id = int(move_from[1:len(move_from)])
text = """<b>Select collection to place the %s collection '%s' under.</b><br /><br />
""" % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_from_id][0]])
output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL,
text=text,
button="Cancel",
colID=colID,
ln=ln)
elif move_from and move_to:
move_from_rtype = move_from[0]
move_from_id = int(move_from[1:len(move_from)])
move_to_rtype = move_to[0]
move_to_id = int(move_to[1:len(move_to)])
tree_from = get_col_tree(colID, move_from_rtype)
tree_to = get_col_tree(colID, move_to_rtype)
if confirm in [0, '0']:
if move_from_id == move_to_id and move_from_rtype == move_to_rtype:
output += """<b><span class="info">Cannot move to itself.</span></b><br /><br />
"""
elif tree_from[move_from_id][3] == tree_to[move_to_id][0] and move_from_rtype==move_to_rtype:
output += """<b><span class="info">The collection is already there.</span></b><br /><br />
"""
elif check_col(tree_to[move_to_id][0], tree_from[move_from_id][0]) or (tree_to[move_to_id][0] == 1 and tree_from[move_from_id][3] == tree_to[move_to_id][0] and move_from_rtype != move_to_rtype):
text = """<b>Move %s collection '%s' to the %s collection '%s'.</b>
""" % ((tree_from[move_from_id][4]=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (tree_to[move_to_id][4]=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]])
output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifycollectiontree#tree" % CFG_SITE_URL,
text=text,
button="Confirm",
colID=colID,
move_from=move_from,
move_to=move_to,
ln=ln,
rtype=rtype,
confirm=1)
output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL,
text="""<b>To cancel</b>""",
button="Cancel",
colID=colID,
ln=ln)
else:
output += """<b><span class="info">Cannot move the collection '%s' and set it as a subcollection of '%s' since it will create a loop.</span></b><br /><br />
""" % (col_dict[tree_from[move_from_id][0]], col_dict[tree_to[move_to_id][0]])
else:
if (move_to_id != 0 and move_col_tree(tree_from[move_from_id], tree_to[move_to_id])) or (move_to_id == 0 and move_col_tree(tree_from[move_from_id], tree_to[move_to_id], move_to_rtype)):
output += """<b><span class="info">Moved %s collection '%s' to the %s collection '%s'.</span></b><br /><br />
""" % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (move_to_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]])
else:
output += """<b><span class="info">Could not move %s collection '%s' to the %s collection '%s'.</span></b><br /><br />
""" % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (move_to_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]])
move_from = ''
move_to = ''
else:
output += """
"""
except StandardError, e:
register_exception()
return """<b><span class="info">An error occured.</span></b>
"""
output += """<table border ="0" width="100%">
<tr><td width="50%">
<b>Narrow by collection:</b>
</td><td width="50%">
<b>Focus on...:</b>
</td></tr><tr><td valign="top">
"""
tree = get_col_tree(colID, 'r')
output += create_colltree(tree, col_dict, colID, ln, move_from, move_to, 'r', "yes")
output += """</td><td valign="top">
"""
tree = get_col_tree(colID, 'v')
output += create_colltree(tree, col_dict, colID, ln, move_from, move_to, 'v', "yes")
output += """</td>
</tr>
</table>
"""
body = [output]
if callback:
return perform_index(colID, ln, mtype="perform_modifycollectiontree", content=addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showtree(colID, ln):
"""create collection tree/hiarchy"""
col_dict = dict(get_def_name('', "collection"))
subtitle = "Collection tree: %s" % col_dict[int(colID)]
output = """<table border ="0" width="100%">
<tr><td width="50%">
<b>Narrow by collection:</b>
</td><td width="50%">
<b>Focus on...:</b>
</td></tr><tr><td valign="top">
"""
tree = get_col_tree(colID, 'r')
output += create_colltree(tree, col_dict, colID, ln, '', '', 'r', '')
output += """</td><td valign="top">
"""
tree = get_col_tree(colID, 'v')
output += create_colltree(tree, col_dict, colID, ln, '', '', 'v', '')
output += """</td>
</tr>
</table>
"""
body = [output]
return addadminbox(subtitle, body)
def perform_addportalbox(colID, ln, title='', body='', callback='yes', confirm=-1):
"""form to add a new portalbox
title - the title of the portalbox
body - the body of the portalbox"""
col_dict = dict(get_def_name('', "collection"))
colID = int(colID)
subtitle = """<a name="5.1"></a>Create new portalbox"""
text = """
<span class="adminlabel">Title</span>
<textarea cols="50" rows="1" class="admin_wvar" type="text" name="title">%s</textarea><br />
<span class="adminlabel">Body</span>
<textarea cols="50" rows="10" class="admin_wvar" type="text" name="body">%s</textarea><br />
""" % (cgi.escape(title), cgi.escape(body))
output = createhiddenform(action="addportalbox#5.1",
text=text,
button="Add",
colID=colID,
ln=ln,
confirm=1)
if body and confirm in [1, "1"]:
res = add_pbx(title, body)
output += write_outcome(res)
if res[1] == 1:
output += """<b><span class="info"><a href="addexistingportalbox?colID=%s&amp;ln=%s&amp;pbxID=%s#5">Add portalbox to collection</a></span></b>""" % (colID, ln, res[1])
elif confirm not in [-1, "-1"]:
output += """<b><span class="info">Body field must be filled.</span></b>
"""
body = [output]
return perform_showportalboxes(colID, ln, content=addadminbox(subtitle, body))
def perform_addexistingportalbox(colID, ln, pbxID=-1, score=0, position='', sel_ln='', callback='yes', confirm=-1):
"""form to add an existing portalbox to a collection.
colID - the collection to add the portalbox to
pbxID - the portalbox to add
score - the importance of the portalbox.
position - the position of the portalbox on the page
sel_ln - the language of the portalbox"""
subtitle = """<a name="5.2"></a>Add existing portalbox to collection"""
output = ""
colID = int(colID)
res = get_pbx()
pos = get_pbx_pos()
lang = dict(get_languages())
col_dict = dict(get_def_name('', "collection"))
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
col_pbx = get_col_pbx(colID)
col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx))
if len(res) > 0:
text = """
<span class="adminlabel">Portalbox</span>
<select name="pbxID" class="admin_w200">
<option value="-1">- Select portalbox -</option>
"""
for (id, t_title, t_body) in res:
text += """<option value="%s" %s>%s - %s...</option>\n""" % \
(id, id == int(pbxID) and 'selected="selected"' or '',
t_title[:40], cgi.escape(t_body[0:40 - min(40, len(t_title))]))
text += """</select><br />
<span class="adminlabel">Language</span>
<select name="sel_ln" class="admin_w200">
<option value="">- Select language -</option>
"""
listlang = lang.items()
listlang.sort()
for (key, name) in listlang:
text += """<option value="%s" %s>%s</option>
""" % (key, key == sel_ln and 'selected="selected"' or '', name)
text += """</select><br />
<span class="adminlabel">Position</span>
<select name="position" class="admin_w200">
<option value="">- Select position -</option>
"""
listpos = pos.items()
listpos.sort()
for (key, name) in listpos:
text += """<option value="%s" %s>%s</option>""" % (key, key==position and 'selected="selected"' or '', name)
text += "</select>"
output += createhiddenform(action="addexistingportalbox#5.2",
text=text,
button="Add",
colID=colID,
ln=ln,
confirm=1)
else:
output = """No existing portalboxes to add, please create a new one.
"""
if pbxID > -1 and position and sel_ln and confirm in [1, "1"]:
pbxID = int(pbxID)
res = add_col_pbx(colID, pbxID, sel_ln, position, '')
output += write_outcome(res)
elif pbxID > -1 and confirm not in [-1, "-1"]:
output += """<b><span class="info">All fields must be filled.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showportalboxes(colID, ln, content=output)
def perform_deleteportalbox(colID, ln, pbxID=-1, callback='yes', confirm=-1):
"""form to delete a portalbox which is not in use.
colID - the current collection.
pbxID - the id of the portalbox"""
subtitle = """<a name="5.3"></a>Delete an unused portalbox"""
output = ""
colID = int(colID)
if pbxID not in [-1, "-1"] and confirm in [1, "1"]:
ares = get_pbx()
pbx_dict = dict(map(lambda x: (x[0], x[1]), ares))
if pbx_dict.has_key(int(pbxID)):
pname = pbx_dict[int(pbxID)]
ares = delete_pbx(int(pbxID))
else:
return """<b><span class="info">This portalbox does not exist</span></b>"""
res = get_pbx()
col_dict = dict(get_def_name('', "collection"))
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
col_pbx = get_col_pbx()
col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx))
if len(res) > 0:
text = """
<span class="adminlabel">Portalbox</span>
<select name="pbxID" class="admin_w200">
"""
text += """<option value="-1">- Select portalbox -"""
for (id, t_title, t_body) in res:
if not col_pbx.has_key(id):
text += """<option value="%s" %s>%s - %s...""" % (id, id == int(pbxID) and 'selected="selected"' or '', t_title, cgi.escape(t_body[0:10]))
text += "</option>"
text += """</select><br />"""
output += createhiddenform(action="deleteportalbox#5.3",
text=text,
button="Delete",
colID=colID,
ln=ln,
confirm=1)
if pbxID not in [-1, "-1"]:
pbxID = int(pbxID)
if confirm in [1, "1"]:
output += write_outcome(ares)
elif confirm not in [-1, "-1"]:
output += """<b><span class="info">Choose a portalbox to delete.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showportalboxes(colID, ln, content=output)
def perform_modifyportalbox(colID, ln, pbxID=-1, score='', position='', sel_ln='', title='', body='', callback='yes', confirm=-1):
"""form to modify a portalbox in a collection, or change the portalbox itself.
colID - the id of the collection.
pbxID - the portalbox to change
score - the score of the portalbox connected to colID which should be changed.
position - the position of the portalbox in collection colID to change."""
subtitle = ""
output = ""
colID = int(colID)
res = get_pbx()
pos = get_pbx_pos()
lang = dict(get_languages())
col_dict = dict(get_def_name('', "collection"))
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
col_pbx = get_col_pbx(colID)
col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx))
if pbxID not in [-1, "-1"]:
pbxID = int(pbxID)
subtitle = """<a name="5.4"></a>Modify portalbox '%s' for this collection""" % pbx_dict[pbxID]
col_pbx = get_col_pbx(colID)
if not (score and position) and not (body and title):
for (id_pbx, id_collection, tln, score, position, title, body) in col_pbx:
if id_pbx == pbxID:
break
output += """Collection (presentation) specific values (Changes implies only to this collection.)<br />"""
text = """
<span class="adminlabel">Position</span>
<select name="position" class="admin_w200">
"""
listpos = pos.items()
listpos.sort()
for (key, name) in listpos:
text += """<option value="%s" %s>%s""" % (key, key==position and 'selected="selected"' or '', name)
text += "</option>"
text += """</select><br />"""
output += createhiddenform(action="modifyportalbox#5.4",
text=text,
button="Modify",
colID=colID,
pbxID=pbxID,
score=score,
title=title,
body=cgi.escape(body, 1),
sel_ln=sel_ln,
ln=ln,
confirm=3)
if pbxID > -1 and score and position and confirm in [3, "3"]:
pbxID = int(pbxID)
res = modify_pbx(colID, pbxID, sel_ln, score, position, '', '')
res2 = get_pbx()
pbx_dict = dict(map(lambda x: (x[0], x[1]), res2))
output += write_outcome(res)
output += """<br />Portalbox (content) specific values (any changes appears everywhere the portalbox is used.)"""
text = """
<span class="adminlabel">Title</span>
<textarea cols="50" rows="1" class="admin_wvar" type="text" name="title">%s</textarea><br />
""" % cgi.escape(title)
text += """
<span class="adminlabel">Body</span>
<textarea cols="50" rows="10" class="admin_wvar" type="text" name="body">%s</textarea><br />
""" % cgi.escape(body)
output += createhiddenform(action="modifyportalbox#5.4",
text=text,
button="Modify",
colID=colID,
pbxID=pbxID,
sel_ln=sel_ln,
score=score,
position=position,
ln=ln,
confirm=4)
if pbxID > -1 and confirm in [4, "4"]:
pbxID = int(pbxID)
res = modify_pbx(colID, pbxID, sel_ln, '', '', title, body)
output += write_outcome(res)
else:
output = """No portalbox to modify."""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showportalboxes(colID, ln, content=output)
def perform_switchpbxscore(colID, id_1, id_2, sel_ln, ln):
"""Switch the score of id_1 and id_2 in collection_portalbox.
colID - the current collection
id_1/id_2 - the id's to change the score for.
sel_ln - the language of the portalbox"""
output = ""
res = get_pbx()
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
res = switch_pbx_score(colID, id_1, id_2, sel_ln)
output += write_outcome(res)
return perform_showportalboxes(colID, ln, content=output)
def perform_showportalboxes(colID, ln, callback='yes', content='', confirm=-1):
"""show the portalboxes of this collection.
colID - the portalboxes to show the collection for."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
subtitle = """<a name="5">5. Modify portalboxes for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.5">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = ""
pos = get_pbx_pos()
output = """<dl>
<dt>Portalbox actions (not related to this collection)</dt>
<dd><a href="addportalbox?colID=%s&amp;ln=%s#5.1">Create new portalbox</a></dd>
<dd><a href="deleteportalbox?colID=%s&amp;ln=%s#5.3">Delete an unused portalbox</a></dd>
<dt>Collection specific actions</dt>
<dd><a href="addexistingportalbox?colID=%s&amp;ln=%s#5.2">Add existing portalbox to collection</a></dd>
</dl>
""" % (colID, ln, colID, ln, colID, ln)
header = ['Position', 'Language', '', 'Title', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
pos_list = pos.items()
pos_list.sort()
if len(get_col_pbx(colID)) > 0:
for (key, value) in sitelangs:
for (pos_key, pos_value) in pos_list:
res = get_col_pbx(colID, key, pos_key)
i = 0
for (pbxID, colID_pbx, tln, score, position, title, body) in res:
move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>"""
if i != 0:
move += """<a href="%s/admin/websearch/websearchadmin.py/switchpbxscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;sel_ln=%s&amp;rand=%s#5"><img border="0" src="%s/img/smallup.gif" title="Move portalbox up" alt="up" /></a>""" % (CFG_SITE_URL, colID, ln, pbxID, res[i - 1][0], tln, random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
move += "</td><td>"
i += 1
if i != len(res):
move += """<a href="%s/admin/websearch/websearchadmin.py/switchpbxscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;sel_ln=%s&amp;rand=%s#5"><img border="0" src="%s/img/smalldown.gif" title="Move portalbox down" alt="down" /></a>""" % (CFG_SITE_URL, colID, ln, pbxID, res[i][0], tln, random.randint(0, 1000), CFG_SITE_URL)
move += """</td></tr></table>"""
actions.append(["%s" % (i==1 and pos[position] or ''), "%s" % (i==1 and lang[tln] or ''), move, "%s" % title])
for col in [(('Modify', 'modifyportalbox'), ('Remove', 'removeportalbox'),)]:
actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;pbxID=%s&amp;sel_ln=%s#5.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, pbxID, tln, col[0][0]))
for (str, function) in col[1:]:
actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;pbxID=%s&amp;sel_ln=%s#5.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, pbxID, tln, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No portalboxes exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showportalboxes", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_removeportalbox(colID, ln, pbxID='', sel_ln='', callback='yes', confirm=0):
"""form to remove a portalbox from a collection.
colID - the current collection, remove the portalbox from this collection.
sel_ln - remove the portalbox with this language
pbxID - remove the portalbox with this id"""
subtitle = """<a name="5.5"></a>Remove portalbox"""
output = ""
col_dict = dict(get_def_name('', "collection"))
res = get_pbx()
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
if colID and pbxID and sel_ln:
colID = int(colID)
pbxID = int(pbxID)
if confirm in ["0", 0]:
text = """Do you want to remove the portalbox '%s' from the collection '%s'.""" % (pbx_dict[pbxID], col_dict[colID])
output += createhiddenform(action="removeportalbox#5.5",
text=text,
button="Confirm",
colID=colID,
pbxID=pbxID,
sel_ln=sel_ln,
confirm=1)
elif confirm in ["1", 1]:
res = remove_pbx(colID, pbxID, sel_ln)
output += write_outcome(res)
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showportalboxes(colID, ln, content=output)
def perform_switchfmtscore(colID, type, id_1, id_2, ln):
"""Switch the score of id_1 and id_2 in the table type.
colID - the current collection
id_1/id_2 - the id's to change the score for.
type - like "format" """
fmt_dict = dict(get_def_name('', "format"))
res = switch_score(colID, id_1, id_2, type)
output = write_outcome(res)
return perform_showoutputformats(colID, ln, content=output)
def perform_switchfldscore(colID, id_1, id_2, fmeth, ln):
"""Switch the score of id_1 and id_2 in collection_field_fieldvalue.
colID - the current collection
id_1/id_2 - the id's to change the score for."""
fld_dict = dict(get_def_name('', "field"))
res = switch_fld_score(colID, id_1, id_2)
output = write_outcome(res)
if fmeth == "soo":
return perform_showsortoptions(colID, ln, content=output)
elif fmeth == "sew":
return perform_showsearchfields(colID, ln, content=output)
elif fmeth == "seo":
return perform_showsearchoptions(colID, ln, content=output)
def perform_switchfldvaluescore(colID, id_1, id_fldvalue_1, id_fldvalue_2, ln):
"""Switch the score of id_1 and id_2 in collection_field_fieldvalue.
colID - the current collection
id_1/id_2 - the id's to change the score for."""
name_1 = run_sql("SELECT name from fieldvalue where id=%s", (id_fldvalue_1, ))[0][0]
name_2 = run_sql("SELECT name from fieldvalue where id=%s", (id_fldvalue_2, ))[0][0]
res = switch_fld_value_score(colID, id_1, id_fldvalue_1, id_fldvalue_2)
output = write_outcome(res)
return perform_modifyfield(colID, fldID=id_1, ln=ln, content=output)
def perform_addnewfieldvalue(colID, fldID, ln, name='', value='', callback="yes", confirm=-1):
"""form to add a new fieldvalue.
name - the name of the new fieldvalue
value - the value of the new fieldvalue
"""
output = ""
subtitle = """<a name="7.4"></a>Add new value"""
text = """
<span class="adminlabel">Display name</span>
<input class="admin_w200" type="text" name="name" value="%s" /><br />
<span class="adminlabel">Search value</span>
<input class="admin_w200" type="text" name="value" value="%s" /><br />
""" % (name, value)
output = createhiddenform(action="%s/admin/websearch/websearchadmin.py/addnewfieldvalue" % CFG_SITE_URL,
text=text,
colID=colID,
fldID=fldID,
ln=ln,
button="Add",
confirm=1)
if name and value and confirm in ["1", 1]:
res = add_fldv(name, value)
output += write_outcome(res)
if res[0] == 1:
res = add_col_fld(colID, fldID, 'seo', res[1])
if res[0] == 0:
output += "<br />" + write_outcome(res)
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please fill in name and value.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output)
def perform_modifyfieldvalue(colID, fldID, fldvID, ln, name='', value='', callback="yes", confirm=-1):
"""form to modify a fieldvalue.
name - the name of the fieldvalue
value - the value of the fieldvalue
"""
if confirm in [-1, "-1"]:
res = get_fld_value(fldvID)
(id, name, value) = res[0]
output = ""
subtitle = """<a name="7.4"></a>Modify existing value"""
output = """<dl>
<dt><b><span class="info">Warning: Modifications done below will also inflict on all places the modified data is used.</span></b></dt>
</dl>"""
text = """
<span class="adminlabel">Display name</span>
<input class="admin_w200" type="text" name="name" value="%s" /><br />
<span class="adminlabel">Search value</span>
<input class="admin_w200" type="text" name="value" value="%s" /><br />
""" % (name, value)
output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifyfieldvalue" % CFG_SITE_URL,
text=text,
colID=colID,
fldID=fldID,
fldvID=fldvID,
ln=ln,
button="Update",
confirm=1)
output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifyfieldvalue" % CFG_SITE_URL,
text="Delete value and all associations",
colID=colID,
fldID=fldID,
fldvID=fldvID,
ln=ln,
button="Delete",
confirm=2)
if name and value and confirm in ["1", 1]:
res = update_fldv(fldvID, name, value)
output += write_outcome(res)
#if res:
# output += """<b><span class="info">Operation successfully completed.</span></b>"""
#else:
# output += """<b><span class="info">Operation failed.</span></b>"""
elif confirm in ["2", 2]:
res = delete_fldv(fldvID)
output += write_outcome(res)
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please fill in name and value.</span></b>"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output)
def perform_removefield(colID, ln, fldID='', fldvID='', fmeth='', callback='yes', confirm=0):
"""form to remove a field from a collection.
colID - the current collection, remove the field from this collection.
sel_ln - remove the field with this language
fldID - remove the field with this id"""
if fmeth == "soo":
field = "sort option"
elif fmeth == "sew":
field = "search field"
elif fmeth == "seo":
field = "search option"
else:
field = "field"
subtitle = """<a name="6.4"><a name="7.4"><a name="8.4"></a>Remove %s""" % field
output = ""
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
res = get_fld_value()
fldv_dict = dict(map(lambda x: (x[0], x[1]), res))
if colID and fldID:
colID = int(colID)
fldID = int(fldID)
if fldvID and fldvID != "None":
fldvID = int(fldvID)
if confirm in ["0", 0]:
text = """Do you want to remove the %s '%s' %s from the collection '%s'.""" % (field, fld_dict[fldID], (fldvID not in["", "None"] and "with value '%s'" % fldv_dict[fldvID] or ''), col_dict[colID])
output += createhiddenform(action="removefield#6.5",
text=text,
button="Confirm",
colID=colID,
fldID=fldID,
fldvID=fldvID,
fmeth=fmeth,
confirm=1)
elif confirm in ["1", 1]:
res = remove_fld(colID, fldID, fldvID)
output += write_outcome(res)
body = [output]
output = "<br />" + addadminbox(subtitle, body)
if fmeth == "soo":
return perform_showsortoptions(colID, ln, content=output)
elif fmeth == "sew":
return perform_showsearchfields(colID, ln, content=output)
elif fmeth == "seo":
return perform_showsearchoptions(colID, ln, content=output)
def perform_removefieldvalue(colID, ln, fldID='', fldvID='', fmeth='', callback='yes', confirm=0):
"""form to remove a field from a collection.
colID - the current collection, remove the field from this collection.
sel_ln - remove the field with this language
fldID - remove the field with this id"""
subtitle = """<a name="7.4"></a>Remove value"""
output = ""
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
res = get_fld_value()
fldv_dict = dict(map(lambda x: (x[0], x[1]), res))
if colID and fldID:
colID = int(colID)
fldID = int(fldID)
if fldvID and fldvID != "None":
fldvID = int(fldvID)
if confirm in ["0", 0]:
text = """Do you want to remove the value '%s' from the search option '%s'.""" % (fldv_dict[fldvID], fld_dict[fldID])
output += createhiddenform(action="removefieldvalue#7.4",
text=text,
button="Confirm",
colID=colID,
fldID=fldID,
fldvID=fldvID,
fmeth=fmeth,
confirm=1)
elif confirm in ["1", 1]:
res = remove_fld(colID, fldID, fldvID)
output += write_outcome(res)
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output)
def perform_rearrangefieldvalue(colID, fldID, ln, callback='yes', confirm=-1):
"""rearrang the fieldvalues alphabetically
colID - the collection
fldID - the field to rearrange the fieldvalue for
"""
subtitle = "Order values alphabetically"
output = ""
col_fldv = get_col_fld(colID, 'seo', fldID)
col_fldv = dict(map(lambda x: (x[1], x[0]), col_fldv))
fldv_names = get_fld_value()
fldv_names = map(lambda x: (x[0], x[1]), fldv_names)
if not col_fldv.has_key(None):
vscore = len(col_fldv)
for (fldvID, name) in fldv_names:
if col_fldv.has_key(fldvID):
run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (vscore, colID, fldID, fldvID))
vscore -= 1
output += write_outcome((1, ""))
else:
output += write_outcome((0, (0, "No values to order")))
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID, ln, content=output)
def perform_rearrangefield(colID, ln, fmeth, callback='yes', confirm=-1):
"""rearrang the fields alphabetically
colID - the collection
"""
subtitle = "Order fields alphabetically"
output = ""
col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, fmeth)))
fld_names = get_def_name('', "field")
if len(col_fld) > 0:
score = len(col_fld)
for (fldID, name) in fld_names:
if col_fld.has_key(fldID):
run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s", (score, colID, fldID))
score -= 1
output += write_outcome((1, ""))
else:
output += write_outcome((0, (0, "No fields to order")))
body = [output]
output = "<br />" + addadminbox(subtitle, body)
if fmeth == "soo":
return perform_showsortoptions(colID, ln, content=output)
elif fmeth == "sew":
return perform_showsearchfields(colID, ln, content=output)
elif fmeth == "seo":
return perform_showsearchoptions(colID, ln, content=output)
def perform_addexistingfieldvalue(colID, fldID, fldvID=-1, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
"""form to add an existing fieldvalue to a field.
colID - the collection
fldID - the field to add the fieldvalue to
fldvID - the fieldvalue to add"""
subtitle = """</a><a name="7.4"></a>Add existing value to search option"""
output = ""
if fldvID not in [-1, "-1"] and confirm in [1, "1"]:
fldvID = int(fldvID)
ares = add_col_fld(colID, fldID, 'seo', fldvID)
colID = int(colID)
fldID = int(fldID)
lang = dict(get_languages())
res = get_def_name('', "field")
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(res)
col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, 'seo')))
fld_value = get_fld_value()
fldv_dict = dict(map(lambda x: (x[0], x[1]), fld_value))
text = """
<span class="adminlabel">Value</span>
<select name="fldvID" class="admin_w200">
<option value="-1">- Select value -</option>
"""
res = run_sql("SELECT id,name,value FROM fieldvalue ORDER BY name")
for (id, name, value) in res:
text += """<option value="%s" %s>%s - %s</option>
""" % (id, id == int(fldvID) and 'selected="selected"' or '', name, value)
text += """</select><br />"""
output += createhiddenform(action="addexistingfieldvalue#7.4",
text=text,
button="Add",
colID=colID,
fldID=fldID,
ln=ln,
confirm=1)
if fldvID not in [-1, "-1"] and confirm in [1, "1"]:
output += write_outcome(ares)
elif confirm in [1, "1"]:
output += """<b><span class="info">Select a value to add and try again.</span></b>"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID, ln, content=output)
def perform_addexistingfield(colID, ln, fldID=-1, fldvID=-1, fmeth='', callback='yes', confirm=-1):
"""form to add an existing field to a collection.
colID - the collection to add the field to
fldID - the field to add
sel_ln - the language of the field"""
subtitle = """<a name="6.2"></a><a name="7.2"></a><a name="8.2"></a>Add existing field to collection"""
output = ""
if fldID not in [-1, "-1"] and confirm in [1, "1"]:
fldID = int(fldID)
ares = add_col_fld(colID, fldID, fmeth, fldvID)
colID = int(colID)
lang = dict(get_languages())
res = get_def_name('', "field")
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(res)
col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, fmeth)))
fld_value = get_fld_value()
fldv_dict = dict(map(lambda x: (x[0], x[1]), fld_value))
if fldvID:
fldvID = int(fldvID)
text = """
<span class="adminlabel">Field</span>
<select name="fldID" class="admin_w200">
<option value="-1">- Select field -</option>
"""
for (id, var) in res:
if fmeth == 'seo' or (fmeth != 'seo' and not col_fld.has_key(id)):
text += """<option value="%s" %s>%s</option>
""" % (id, '', fld_dict[id])
text += """</select><br />"""
output += createhiddenform(action="addexistingfield#6.2",
text=text,
button="Add",
colID=colID,
fmeth=fmeth,
ln=ln,
confirm=1)
if fldID not in [-1, "-1"] and confirm in [1, "1"]:
output += write_outcome(ares)
elif fldID in [-1, "-1"] and confirm not in [-1, "-1"]:
output += """<b><span class="info">Select a field.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
if fmeth == "soo":
return perform_showsortoptions(colID, ln, content=output)
elif fmeth == "sew":
return perform_showsearchfields(colID, ln, content=output)
elif fmeth == "seo":
return perform_showsearchoptions(colID, ln, content=output)
def perform_showsortoptions(colID, ln, callback='yes', content='', confirm=-1):
"""show the sort fields of this collection.."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
fld_type = get_sort_nametypes()
subtitle = """<a name="8">8. Modify sort options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.8">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """<dl>
<dt>Field actions (not related to this collection)</dt>
<dd>Go to the BibIndex interface to modify the available sort options</dd>
<dt>Collection specific actions
<dd><a href="addexistingfield?colID=%s&amp;ln=%s&amp;fmeth=soo#8.2">Add sort option to collection</a></dd>
<dd><a href="rearrangefield?colID=%s&amp;ln=%s&amp;fmeth=soo#8.2">Order sort options alphabetically</a></dd>
</dl>
""" % (colID, ln, colID, ln)
header = ['', 'Sort option', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
fld_type_list = fld_type.items()
if len(get_col_fld(colID, 'soo')) > 0:
res = get_col_fld(colID, 'soo')
i = 0
for (fldID, fldvID, stype, score, score_fieldvalue) in res:
move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>"""
if i != 0:
move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=soo&amp;rand=%s#8"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i - 1][0], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;&nbsp;"
move += "</td><td>"
i += 1
if i != len(res):
move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=soo&amp;rand=%s#8"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i][0], random.randint(0, 1000), CFG_SITE_URL)
move += """</td></tr></table>"""
actions.append([move, fld_dict[int(fldID)]])
for col in [(('Remove sort option', 'removefield'),)]:
actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=soo#8.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0]))
for (str, function) in col[1:]:
actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=soo#8.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No sort options exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showsortoptions", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showsearchfields(colID, ln, callback='yes', content='', confirm=-1):
"""show the search fields of this collection.."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
fld_type = get_sort_nametypes()
subtitle = """<a name="6">6. Modify search fields for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.6">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """<dl>
<dt>Field actions (not related to this collection)</dt>
<dd>Go to the BibIndex interface to modify the available search fields</dd>
<dt>Collection specific actions
<dd><a href="addexistingfield?colID=%s&amp;ln=%s&amp;fmeth=sew#6.2">Add search field to collection</a></dd>
<dd><a href="rearrangefield?colID=%s&amp;ln=%s&amp;fmeth=sew#6.2">Order search fields alphabetically</a></dd>
</dl>
""" % (colID, ln, colID, ln)
header = ['', 'Search field', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
fld_type_list = fld_type.items()
if len(get_col_fld(colID, 'sew')) > 0:
res = get_col_fld(colID, 'sew')
i = 0
for (fldID, fldvID, stype, score, score_fieldvalue) in res:
move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>"""
if i != 0:
move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=sew&amp;rand=%s#6"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i - 1][0], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
move += "</td><td>"
i += 1
if i != len(res):
move += '<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=sew&amp;rand=%s#6"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>' % (CFG_SITE_URL, colID, ln, fldID, res[i][0], random.randint(0, 1000), CFG_SITE_URL)
move += """</td></tr></table>"""
actions.append([move, fld_dict[int(fldID)]])
for col in [(('Remove search field', 'removefield'),)]:
actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=sew#6.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0]))
for (str, function) in col[1:]:
actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s#6.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No search fields exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showsearchfields", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showsearchoptions(colID, ln, callback='yes', content='', confirm=-1):
"""show the sort and search options of this collection.."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
fld_type = get_sort_nametypes()
subtitle = """<a name="7">7. Modify search options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.7">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """<dl>
<dt>Field actions (not related to this collection)</dt>
<dd>Go to the BibIndex interface to modify the available search options</dd>
<dt>Collection specific actions
<dd><a href="addexistingfield?colID=%s&amp;ln=%s&amp;fmeth=seo#7.2">Add search option to collection</a></dd>
<dd><a href="rearrangefield?colID=%s&amp;ln=%s&amp;fmeth=seo#7.2">Order search options alphabetically</a></dd>
</dl>
""" % (colID, ln, colID, ln)
header = ['', 'Search option', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
fld_type_list = fld_type.items()
fld_distinct = run_sql("SELECT distinct(id_field) FROM collection_field_fieldvalue WHERE type='seo' AND id_collection=%s ORDER by score desc", (colID, ))
if len(fld_distinct) > 0:
i = 0
for (id) in fld_distinct:
fldID = id[0]
col_fld = get_col_fld(colID, 'seo', fldID)
move = ""
if i != 0:
move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=seo&amp;rand=%s#7"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fld_distinct[i - 1][0], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
i += 1
if i != len(fld_distinct):
move += '<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=seo&amp;rand=%s#7"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>' % (CFG_SITE_URL, colID, ln, fldID, fld_distinct[i][0], random.randint(0, 1000), CFG_SITE_URL)
actions.append([move, "%s" % fld_dict[fldID]])
for col in [(('Modify values', 'modifyfield'), ('Remove search option', 'removefield'),)]:
actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s#7.3">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0]))
for (str, function) in col[1:]:
actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=seo#7.3">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No search options exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showsearchoptions", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyfield(colID, fldID, fldvID='', ln=CFG_SITE_LANG, content='', callback='yes', confirm=0):
"""Modify the fieldvalues for a field"""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
fld_type = get_sort_nametypes()
fldID = int(fldID)
subtitle = """<a name="7.3">Modify values for field '%s'</a>""" % (fld_dict[fldID])
output = """<dl>
<dt>Value specific actions
<dd><a href="addexistingfieldvalue?colID=%s&amp;ln=%s&amp;fldID=%s#7.4">Add existing value to search option</a></dd>
<dd><a href="addnewfieldvalue?colID=%s&amp;ln=%s&amp;fldID=%s#7.4">Add new value to search option</a></dd>
<dd><a href="rearrangefieldvalue?colID=%s&amp;ln=%s&amp;fldID=%s#7.4">Order values alphabetically</a></dd>
</dl>
""" % (colID, ln, fldID, colID, ln, fldID, colID, ln, fldID)
header = ['', 'Value name', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
fld_type_list = fld_type.items()
col_fld = list(get_col_fld(colID, 'seo', fldID))
if len(col_fld) == 1 and col_fld[0][1] is None:
output += """<b><span class="info">No values added for this search option yet</span></b>"""
else:
j = 0
for (fldID, fldvID, stype, score, score_fieldvalue) in col_fld:
fieldvalue = get_fld_value(fldvID)
move = ""
if j != 0:
move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldvaluescore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_fldvalue_1=%s&amp;id_fldvalue_2=%s&amp;rand=%s#7.3"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fldvID, col_fld[j - 1][1], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
j += 1
if j != len(col_fld):
move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldvaluescore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_fldvalue_1=%s&amp;id_fldvalue_2=%s&amp;rand=%s#7.3"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fldvID, col_fld[j][1], random.randint(0, 1000), CFG_SITE_URL)
if fieldvalue[0][1] != fieldvalue[0][2] and fldvID is not None:
actions.append([move, "%s - %s" % (fieldvalue[0][1], fieldvalue[0][2])])
elif fldvID is not None:
actions.append([move, "%s" % fieldvalue[0][1]])
move = ''
for col in [(('Modify value', 'modifyfieldvalue'), ('Remove value', 'removefieldvalue'),)]:
actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fldvID=%s&amp;fmeth=seo#7.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, fldvID, col[0][0]))
for (str, function) in col[1:]:
actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fldvID=%s#7.4">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, fldvID, str)
output += tupletotable(header=header, tuple=actions)
output += content
body = [output]
output = "<br />" + addadminbox(subtitle, body)
if len(col_fld) == 0:
output = content
return perform_showsearchoptions(colID, ln, content=output)
def perform_showoutputformats(colID, ln, callback='yes', content='', confirm=-1):
"""shows the outputformats of the current collection
colID - the collection id."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
subtitle = """<a name="10">10. Modify output formats for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.10">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """
<dl>
<dt>Output format actions (not specific to the chosen collection)
<dd>Go to the BibFormat interface to modify</dd>
<dt>Collection specific actions
<dd><a href="addexistingoutputformat?colID=%s&amp;ln=%s#10.2">Add existing output format to collection</a></dd>
</dl>
""" % (colID, ln)
header = ['', 'Code', 'Output format', 'Actions']
actions = []
col_fmt = get_col_fmt(colID)
fmt_dict = dict(get_def_name('', "format"))
i = 0
if len(col_fmt) > 0:
for (id_format, colID_fld, code, score) in col_fmt:
move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>"""
if i != 0:
move += """<a href="%s/admin/websearch/websearchadmin.py/switchfmtscore?colID=%s&amp;ln=%s&amp;type=format&amp;id_1=%s&amp;id_2=%s&amp;rand=%s#10"><img border="0" src="%s/img/smallup.gif" title="Move format up"></a>""" % (CFG_SITE_URL, colID, ln, id_format, col_fmt[i - 1][0], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
move += "</td><td>"
i += 1
if i != len(col_fmt):
move += '<a href="%s/admin/websearch/websearchadmin.py/switchfmtscore?colID=%s&amp;ln=%s&amp;type=format&amp;id_1=%s&amp;id_2=%s&amp;rand=%s#10"><img border="0" src="%s/img/smalldown.gif" title="Move format down"></a>' % (CFG_SITE_URL, colID, ln, id_format, col_fmt[i][0], random.randint(0, 1000), CFG_SITE_URL)
move += """</td></tr></table>"""
actions.append([move, code, fmt_dict[int(id_format)]])
for col in [(('Remove', 'removeoutputformat'),)]:
actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fmtID=%s#10">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, id_format, col[0][0]))
for (str, function) in col[1:]:
actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fmtID=%s#10">%s</a>' % (CFG_SITE_URL, function, colID, ln, id_format, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No output formats exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showoutputformats", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def external_collections_build_select(colID, external_collection):
output = '<select name="state" class="admin_w200">'
if external_collection.parser:
max_state = 4
else:
max_state = 2
num_selected = external_collection_get_state(external_collection, colID)
for num in range(max_state):
state_name = CFG_EXTERNAL_COLLECTION_STATES_NAME[num]
if num == num_selected:
selected = ' selected'
else:
selected = ''
output += '<option value="%(num)d"%(selected)s>%(state_name)s</option>' % {'num': num, 'selected': selected, 'state_name': state_name}
output += '</select>\n'
return output
def perform_manage_external_collections(colID, ln, callback='yes', content='', confirm=-1):
"""Show the interface to configure external collections to the user."""
colID = int(colID)
subtitle = """<a name="11">11. Configuration of related external collections</a>
&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.11">?</a>]</small>""" % CFG_SITE_URL
output = '<form action="update_external_collections" method="POST"><input type="hidden" name="colID" value="%(colID)d">' % {'colID': colID}
table_header = ['External collection', 'Mode', 'Apply also to daughter collections?']
table_content = []
external_collections = external_collection_sort_engine_by_name(external_collections_dictionary.values())
for external_collection in external_collections:
collection_name = external_collection.name
select = external_collections_build_select(colID, external_collection)
recurse = '<input type=checkbox name="recurse" value="%(collection_name)s">' % {'collection_name': collection_name}
table_content.append([collection_name, select, recurse])
output += tupletotable(header=table_header, tuple=table_content)
output += '<input class="adminbutton" type="submit" value="Modify"/>'
output += '</form>'
return addadminbox(subtitle, [output])
def perform_update_external_collections(colID, ln, state_list, recurse_list):
colID = int(colID)
changes = []
output = ""
if not state_list:
return 'Warning : No state found.<br />' + perform_manage_external_collections(colID, ln)
external_collections = external_collection_sort_engine_by_name(external_collections_dictionary.values())
if len(external_collections) != len(state_list):
return 'Warning : Size of state_list different from external_collections!<br />' + perform_manage_external_collections(colID, ln)
for (external_collection, state) in zip(external_collections, state_list):
state = int(state)
collection_name = external_collection.name
recurse = recurse_list and collection_name in recurse_list
oldstate = external_collection_get_state(external_collection, colID)
if oldstate != state or recurse:
changes += external_collection_get_update_state_list(external_collection, colID, state, recurse)
external_collection_apply_changes(changes)
return output + '<br /><br />' + perform_manage_external_collections(colID, ln)
def perform_showdetailedrecordoptions(colID, ln, callback='yes', content='', confirm=-1):
"""Show the interface to configure detailed record page to the user."""
colID = int(colID)
subtitle = """<a name="12">12. Configuration of detailed record page</a>
&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.12">?</a>]</small>""" % CFG_SITE_URL
output = '''<form action="update_detailed_record_options" method="post">
<table><tr><td>
<input type="hidden" name="colID" value="%(colID)d">
<dl>
<dt><b>Show tabs:</b></dt>
<dd>
''' % {'colID': colID}
for (tab_id, tab_info) in get_detailed_page_tabs(colID).iteritems():
if tab_id == 'comments' and \
not CFG_WEBCOMMENT_ALLOW_REVIEWS and \
not CFG_WEBCOMMENT_ALLOW_COMMENTS:
continue
check = ''
output += '''<input type="checkbox" id="id%(tabid)s" name="tabs" value="%(tabid)s" %(check)s />
<label for="id%(tabid)s">&nbsp;%(label)s</label><br />
''' % {'tabid':tab_id,
'check':((tab_info['visible'] and 'checked="checked"') or ''),
'label':tab_info['label']}
output += '</dd></dl></td><td>'
output += '</td></tr></table><input class="adminbutton" type="submit" value="Modify"/>'
output += '''<input type="checkbox" id="recurse" name="recurse" value="1" />
<label for="recurse">&nbsp;Also apply to subcollections</label>'''
output += '</form>'
return addadminbox(subtitle, [output])
def perform_update_detailed_record_options(colID, ln, tabs, recurse):
"""Update the preferences for the tab to show/hide in the detailed record page."""
colID = int(colID)
changes = []
output = '<b><span class="info">Operation successfully completed.</span></b>'
if '' in tabs:
tabs.remove('')
tabs.append('metadata')
def update_settings(colID, tabs, recurse):
run_sql("DELETE FROM collectiondetailedrecordpagetabs WHERE id_collection=%s", (colID, ))
run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \
" SET id_collection=%s, tabs=%s", (colID, ';'.join(tabs)))
## for enabled_tab in tabs:
## run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \
## " SET id_collection='%s', tabs='%s'" % (colID, ';'.join(tabs)))
if recurse:
for descendant_id in get_collection_descendants(colID):
update_settings(descendant_id, tabs, recurse)
update_settings(colID, tabs, recurse)
## for colID in colIDs:
## run_sql("DELETE FROM collectiondetailedrecordpagetabs WHERE id_collection='%s'" % colID)
## for enabled_tab in tabs:
## run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \
## " SET id_collection='%s', tabs='%s'" % (colID, ';'.join(tabs)))
#if callback:
return perform_editcollection(colID, ln, "perform_modifytranslations",
'<br /><br />' + output + '<br /><br />' + \
perform_showdetailedrecordoptions(colID, ln))
#else:
# return addadminbox(subtitle, body)
#return output + '<br /><br />' + perform_showdetailedrecordoptions(colID, ln)
def perform_addexistingoutputformat(colID, ln, fmtID=-1, callback='yes', confirm=-1):
"""form to add an existing output format to a collection.
colID - the collection the format should be added to
fmtID - the format to add."""
subtitle = """<a name="10.2"></a>Add existing output format to collection"""
output = ""
if fmtID not in [-1, "-1"] and confirm in [1, "1"]:
ares = add_col_fmt(colID, fmtID)
colID = int(colID)
res = get_def_name('', "format")
fmt_dict = dict(res)
col_dict = dict(get_def_name('', "collection"))
col_fmt = get_col_fmt(colID)
col_fmt = dict(map(lambda x: (x[0], x[2]), col_fmt))
if len(res) > 0:
text = """
<span class="adminlabel">Output format</span>
<select name="fmtID" class="admin_w200">
<option value="-1">- Select output format -</option>
"""
for (id, name) in res:
if not col_fmt.has_key(id):
text += """<option value="%s" %s>%s</option>
""" % (id, id == int(fmtID) and 'selected="selected"' or '', name)
text += """</select><br />
"""
output += createhiddenform(action="addexistingoutputformat#10.2",
text=text,
button="Add",
colID=colID,
ln=ln,
confirm=1)
else:
output = """No existing output formats to add, please create a new one."""
if fmtID not in [-1, "-1"] and confirm in [1, "1"]:
output += write_outcome(ares)
elif fmtID in [-1, "-1"] and confirm not in [-1, "-1"]:
output += """<b><span class="info">Please select output format.</span></b>"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showoutputformats(colID, ln, content=output)
def perform_deleteoutputformat(colID, ln, fmtID=-1, callback='yes', confirm=-1):
"""form to delete an output format not in use.
colID - the collection id of the current collection.
fmtID - the format id to delete."""
subtitle = """<a name="10.3"></a>Delete an unused output format"""
output = """
<dl>
<dd>Deleting an output format will also delete the translations associated.</dd>
</dl>
"""
colID = int(colID)
if fmtID not in [-1, "-1"] and confirm in [1, "1"]:
fmt_dict = dict(get_def_name('', "format"))
old_colNAME = fmt_dict[int(fmtID)]
ares = delete_fmt(int(fmtID))
res = get_def_name('', "format")
fmt_dict = dict(res)
col_dict = dict(get_def_name('', "collection"))
col_fmt = get_col_fmt()
col_fmt = dict(map(lambda x: (x[0], x[2]), col_fmt))
if len(res) > 0:
text = """
<span class="adminlabel">Output format</span>
<select name="fmtID" class="admin_w200">
"""
text += """<option value="-1">- Select output format -"""
for (id, name) in res:
if not col_fmt.has_key(id):
text += """<option value="%s" %s>%s""" % (id, id == int(fmtID) and 'selected="selected"' or '', name)
text += "</option>"
text += """</select><br />"""
output += createhiddenform(action="deleteoutputformat#10.3",
text=text,
button="Delete",
colID=colID,
ln=ln,
confirm=0)
if fmtID not in [-1, "-1"]:
fmtID = int(fmtID)
if confirm in [0, "0"]:
text = """<b>Do you want to delete the output format '%s'.</b>
""" % fmt_dict[fmtID]
output += createhiddenform(action="deleteoutputformat#10.3",
text=text,
button="Confirm",
colID=colID,
fmtID=fmtID,
ln=ln,
confirm=1)
elif confirm in [1, "1"]:
output += write_outcome(ares)
elif confirm not in [-1, "-1"]:
output += """<b><span class="info">Choose a output format to delete.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showoutputformats(colID, ln, content=output)
def perform_removeoutputformat(colID, ln, fmtID='', callback='yes', confirm=0):
"""form to remove an output format from a collection.
colID - the collection id of the current collection.
fmtID - the format id.
"""
subtitle = """<a name="10.5"></a>Remove output format"""
output = ""
col_dict = dict(get_def_name('', "collection"))
fmt_dict = dict(get_def_name('', "format"))
if colID and fmtID:
colID = int(colID)
fmtID = int(fmtID)
if confirm in ["0", 0]:
text = """Do you want to remove the output format '%s' from the collection '%s'.""" % (fmt_dict[fmtID], col_dict[colID])
output += createhiddenform(action="removeoutputformat#10.5",
text=text,
button="Confirm",
colID=colID,
fmtID=fmtID,
confirm=1)
elif confirm in ["1", 1]:
res = remove_fmt(colID, fmtID)
output += write_outcome(res)
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showoutputformats(colID, ln, content=output)
def perform_index(colID=1, ln=CFG_SITE_LANG, mtype='', content='', confirm=0):
"""The index method, calling methods to show the collection tree, create new collections and add collections to tree.
"""
subtitle = "Overview"
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
output = ""
fin_output = ""
if not col_dict.has_key(1):
res = add_col(CFG_SITE_NAME, '')
if res:
fin_output += """<b><span class="info">Created root collection.</span></b><br />"""
else:
return "Cannot create root collection, please check database."
if CFG_SITE_NAME != run_sql("SELECT name from collection WHERE id=1")[0][0]:
res = run_sql("update collection set name=%s where id=1", (CFG_SITE_NAME, ))
if res:
fin_output += """<b><span class="info">The name of the root collection has been modified to be the same as the %(sitename)s installation name given prior to installing %(sitename)s.</span><b><br />""" % {'sitename' : CFG_SITE_NAME}
else:
return "Error renaming root collection."
fin_output += """
<table>
<tr>
<td>0.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_showall">Show all</a></small></td>
<td>1.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_addcollection">Create new collection</a></small></td>
<td>2.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_addcollectiontotree">Attach collection to tree</a></small></td>
<td>3.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_modifycollectiontree">Modify collection tree</a></small></td>
<td>4.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_checkwebcollstatus">Webcoll Status</a></small></td>
</tr><tr>
<td>5.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_checkcollectionstatus">Collection Status</a></small></td>
<td>6.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_checkexternalcollections">Check external collections</a></small></td>
<td>7.&nbsp;<small><a href="%s/help/admin/websearch-admin-guide?ln=%s">Guide</a></small></td>
</tr>
</table>
""" % (CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, ln)
if mtype == "":
fin_output += """<br /><br /><b><span class="info">To manage the collections, select an item from the menu.</span><b><br />"""
if mtype == "perform_addcollection" and content:
fin_output += content
elif mtype == "perform_addcollection" or mtype == "perform_showall":
fin_output += perform_addcollection(colID=colID, ln=ln, callback='')
fin_output += "<br />"
if mtype == "perform_addcollectiontotree" and content:
fin_output += content
elif mtype == "perform_addcollectiontotree" or mtype == "perform_showall":
fin_output += perform_addcollectiontotree(colID=colID, ln=ln, callback='')
fin_output += "<br />"
if mtype == "perform_modifycollectiontree" and content:
fin_output += content
elif mtype == "perform_modifycollectiontree" or mtype == "perform_showall":
fin_output += perform_modifycollectiontree(colID=colID, ln=ln, callback='')
fin_output += "<br />"
if mtype == "perform_checkwebcollstatus" and content:
fin_output += content
elif mtype == "perform_checkwebcollstatus" or mtype == "perform_showall":
fin_output += perform_checkwebcollstatus(colID, ln, callback='')
if mtype == "perform_checkcollectionstatus" and content:
fin_output += content
elif mtype == "perform_checkcollectionstatus" or mtype == "perform_showall":
fin_output += perform_checkcollectionstatus(colID, ln, callback='')
if mtype == "perform_checkexternalcollections" and content:
fin_output += content
elif mtype == "perform_checkexternalcollections" or mtype == "perform_showall":
fin_output += perform_checkexternalcollections(colID, ln, callback='')
body = [fin_output]
body = [fin_output]
return addadminbox('<b>Menu</b>', body)
def show_coll_not_in_tree(colID, ln, col_dict):
"""Returns collections not in tree"""
tree = get_col_tree(colID)
in_tree = {}
output = "These collections are not in the tree, and should be added:<br />"
for (id, up, down, dad, reltype) in tree:
in_tree[id] = 1
in_tree[dad] = 1
res = run_sql("SELECT id from collection")
if len(res) != len(in_tree):
for id in res:
if not in_tree.has_key(id[0]):
output += """<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&amp;ln=%s" title="Edit collection">%s</a> ,
""" % (CFG_SITE_URL, id[0], ln, col_dict[id[0]])
output += "<br /><br />"
else:
output = ""
return output
def create_colltree(tree, col_dict, colID, ln, move_from='', move_to='', rtype='', edit=''):
"""Creates the presentation of the collection tree, with the buttons for modifying it.
tree - the tree to present, from get_tree()
col_dict - the name of the collections in a dictionary
colID - the collection id to start with
move_from - if a collection to be moved has been chosen
move_to - the collection which should be set as father of move_from
rtype - the type of the tree, regular or virtual
edit - if the method should output the edit buttons."""
if move_from:
move_from_rtype = move_from[0]
move_from_id = int(move_from[1:len(move_from)])
tree_from = get_col_tree(colID, move_from_rtype)
tree_to = get_col_tree(colID, rtype)
tables = 0
tstack = []
i = 0
text = """
<table border ="0" cellspacing="0" cellpadding="0">"""
for i in range(0, len(tree)):
id_son = tree[i][0]
up = tree[i][1]
down = tree[i][2]
dad = tree[i][3]
reltype = tree[i][4]
tmove_from = ""
j = i
while j > 0:
j = j - 1
try:
if tstack[j][1] == dad:
table = tstack[j][2]
for k in range(0, tables - table):
tables = tables - 1
text += """</table></td></tr>
"""
break
except StandardError, e:
pass
text += """<tr><td>
"""
if i > 0 and tree[i][1] == 0:
tables = tables + 1
text += """</td><td></td><td></td><td></td><td><table border="0" cellspacing="0" cellpadding="0"><tr><td>
"""
if i == 0:
tstack.append((id_son, dad, 1))
else:
tstack.append((id_son, dad, tables))
if up == 1 and edit:
text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_up=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/smallup.gif" title="Move collection up"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL)
else:
text += """&nbsp;"""
text += "</td><td>"
if down == 1 and edit:
text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_down=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/smalldown.gif" title="Move collection down"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL)
else:
text += """&nbsp;"""
text += "</td><td>"
if edit:
if move_from and move_to:
tmove_from = move_from
move_from = ''
if not (move_from == "" and i == 0) and not (move_from != "" and int(move_from[1:len(move_from)]) == i and rtype == move_from[0]):
check = "true"
if move_from:
#if tree_from[move_from_id][0] == tree_to[i][0] or not check_col(tree_to[i][0], tree_from[move_from_id][0]):
# check = ''
#elif not check_col(tree_to[i][0], tree_from[move_from_id][0]):
# check = ''
#if not check and (tree_to[i][0] == 1 and tree_from[move_from_id][3] == tree_to[i][0] and move_from_rtype != rtype):
# check = "true"
if check:
text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_from=%s&amp;move_to=%s%s&amp;rtype=%s#tree"><img border="0" src="%s/img/move_to.gif" title="Move '%s' to '%s'"></a>
""" % (CFG_SITE_URL, colID, ln, move_from, rtype, i, rtype, CFG_SITE_URL, col_dict[tree_from[int(move_from[1:len(move_from)])][0]], col_dict[tree_to[i][0]])
else:
try:
text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_from=%s%s&amp;rtype=%s#%s"><img border="0" src="%s/img/move_from.gif" title="Move '%s' from this location."></a>""" % (CFG_SITE_URL, colID, ln, rtype, i, rtype, tree[i][0], CFG_SITE_URL, col_dict[tree[i][0]])
except KeyError:
pass
else:
text += """<img border="0" src="%s/img/white_field.gif">
""" % CFG_SITE_URL
else:
text += """<img border="0" src="%s/img/white_field.gif">
""" % CFG_SITE_URL
text += """
</td>
<td>"""
if edit:
try:
text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;delete=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/iconcross.gif" title="Remove colletion from tree"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL)
except KeyError:
pass
elif i != 0:
text += """<img border="0" src="%s/img/white_field.gif">
""" % CFG_SITE_URL
text += """</td><td>
"""
if tmove_from:
move_from = tmove_from
try:
text += """<a name="%s"></a>%s<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&amp;ln=%s" title="Edit collection">%s</a>%s%s%s""" % (tree[i][0], (reltype=="v" and '<i>' or ''), CFG_SITE_URL, tree[i][0], ln, col_dict[id_son], (move_to=="%s%s" %(rtype, i) and '&nbsp;<img border="0" src="%s/img/move_to.gif">' % CFG_SITE_URL or ''), (move_from=="%s%s" % (rtype, i) and '&nbsp;<img border="0" src="%s/img/move_from.gif">' % CFG_SITE_URL or ''), (reltype=="v" and '</i>' or ''))
except KeyError:
pass
text += """</td></tr>
"""
while tables > 0:
text += """</table></td></tr>
"""
tables = tables - 1
text += """</table>
"""
return text
def perform_deletecollection(colID, ln, confirm=-1, callback='yes'):
"""form to delete a collection
colID - id of collection
"""
subtitle =''
output = """
<span class="warning">
<strong>
<dl>
<dt>WARNING:</dt>
<dd>When deleting a collection, you also deletes all data related to the collection like translations, relations to other collections and information about which rank methods to use.
<br />For more information, please go to the <a title="See guide" href="%s/help/admin/websearch-admin-guide">WebSearch guide</a> and read the section regarding deleting a collection.</dd>
</dl>
</strong>
</span>
""" % CFG_SITE_URL
col_dict = dict(get_def_name('', "collection"))
if colID != 1 and colID and col_dict.has_key(int(colID)):
colID = int(colID)
subtitle = """<a name="4">4. Delete collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.4">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
res = run_sql("SELECT id_dad,id_son,type,score from collection_collection WHERE id_dad=%s", (colID, ))
res2 = run_sql("SELECT id_dad,id_son,type,score from collection_collection WHERE id_son=%s", (colID, ))
if not res and not res2:
if confirm in ["-1", -1]:
text = """Do you want to delete this collection."""
output += createhiddenform(action="deletecollection#4",
text=text,
colID=colID,
button="Delete",
confirm=0)
elif confirm in ["0", 0]:
text = """Are you sure you want to delete this collection."""
output += createhiddenform(action="deletecollection#4",
text=text,
colID=colID,
button="Confirm",
confirm=1)
elif confirm in ["1", 1]:
result = delete_col(colID)
if not result:
raise Exception
else:
output = """<b><span class="info">Can not delete a collection that is a part of the collection tree, remove collection from the tree and try again.</span></b>"""
else:
subtitle = """4. Delete collection"""
output = """<b><span class="info">Not possible to delete the root collection</span></b>"""
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_deletecollection", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_editcollection(colID=1, ln=CFG_SITE_LANG, mtype='', content=''):
"""interface to modify a collection. this method is calling other methods which again is calling this and sending back the output of the method.
if callback, the method will call perform_editcollection, if not, it will just return its output.
colID - id of the collection
mtype - the method that called this method.
content - the output from that method."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
if not col_dict.has_key(colID):
return """<b><span class="info">Collection deleted.</span></b>
"""
fin_output = """
<table>
<tr>
<td><b>Menu</b></td>
</tr>
<tr>
<td>0.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s">Show all</a></small></td>
<td>1.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_modifydbquery">Modify collection query</a></small></td>
<td>2.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_modifyrestricted">Modify access restrictions</a></small></td>
<td>3.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_modifytranslations">Modify translations</a></small></td>
<td>4.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_deletecollection">Delete collection</a></small></td>
</tr><tr>
<td>5.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showportalboxes">Modify portalboxes</a></small></td>
<td>6.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showsearchfields#6">Modify search fields</a></small></td>
<td>7.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showsearchoptions#7">Modify search options</a></small></td>
<td>8.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showsortoptions#8">Modify sort options</a></small></td>
<td>9.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_modifyrankmethods#9">Modify rank options</a></small></td>
</tr><tr>
<td>10.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showoutputformats#10">Modify output formats</a></small></td>
<td>11.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_manage_external_collections#11">Configuration of related external collections</a></small></td>
<td>12.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showdetailedrecordoptions#12">Detailed record page options</a></small></td>
</tr>
</table>
""" % (colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln)
if mtype == "perform_modifydbquery" and content:
fin_output += content
elif mtype == "perform_modifydbquery" or not mtype:
fin_output += perform_modifydbquery(colID, ln, callback='')
if mtype == "perform_modifyrestricted" and content:
fin_output += content
elif mtype == "perform_modifyrestricted" or not mtype:
fin_output += perform_modifyrestricted(colID, ln, callback='')
if mtype == "perform_modifytranslations" and content:
fin_output += content
elif mtype == "perform_modifytranslations" or not mtype:
fin_output += perform_modifytranslations(colID, ln, callback='')
if mtype == "perform_deletecollection" and content:
fin_output += content
elif mtype == "perform_deletecollection" or not mtype:
fin_output += perform_deletecollection(colID, ln, callback='')
if mtype == "perform_showportalboxes" and content:
fin_output += content
elif mtype == "perform_showportalboxes" or not mtype:
fin_output += perform_showportalboxes(colID, ln, callback='')
if mtype == "perform_showsearchfields" and content:
fin_output += content
elif mtype == "perform_showsearchfields" or not mtype:
fin_output += perform_showsearchfields(colID, ln, callback='')
if mtype == "perform_showsearchoptions" and content:
fin_output += content
elif mtype == "perform_showsearchoptions" or not mtype:
fin_output += perform_showsearchoptions(colID, ln, callback='')
if mtype == "perform_showsortoptions" and content:
fin_output += content
elif mtype == "perform_showsortoptions" or not mtype:
fin_output += perform_showsortoptions(colID, ln, callback='')
if mtype == "perform_modifyrankmethods" and content:
fin_output += content
elif mtype == "perform_modifyrankmethods" or not mtype:
fin_output += perform_modifyrankmethods(colID, ln, callback='')
if mtype == "perform_showoutputformats" and content:
fin_output += content
elif mtype == "perform_showoutputformats" or not mtype:
fin_output += perform_showoutputformats(colID, ln, callback='')
if mtype == "perform_manage_external_collections" and content:
fin_output += content
elif mtype == "perform_manage_external_collections" or not mtype:
fin_output += perform_manage_external_collections(colID, ln, callback='')
if mtype == "perform_showdetailedrecordoptions" and content:
fin_output += content
elif mtype == "perform_showdetailedrecordoptions" or not mtype:
fin_output += perform_showdetailedrecordoptions(colID, ln, callback='')
return addadminbox("Overview of edit options for collection '%s'" % col_dict[colID], [fin_output])
def perform_checkwebcollstatus(colID, ln, confirm=0, callback='yes'):
"""Check status of the collection tables with respect to the webcoll cache."""
subtitle = """<a name="11"></a>Webcoll Status&nbsp;&nbsp;&nbsp;[<a href="%s/help/admin/websearch-admin-guide#5">?</a>]""" % CFG_SITE_URL
output = ""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
output += """<br /><b>Last updates:</b><br />"""
collection_table_update_time = ""
collection_web_update_time = ""
collection_table_update_time = get_table_update_time('collection')
output += "Collection table last updated: %s<br />" % collection_table_update_time
try:
file = open("%s/collections/last_updated" % CFG_CACHEDIR)
collection_web_update_time = file.readline().strip()
output += "Collection cache last updated: %s<br />" % collection_web_update_time
file.close()
except:
pass
# reformat collection_web_update_time to the format suitable for comparisons
try:
collection_web_update_time = time.strftime("%Y-%m-%d %H:%M:%S",
time.strptime(collection_web_update_time, "%d %b %Y %H:%M:%S"))
except ValueError, e:
pass
if collection_table_update_time > collection_web_update_time:
output += """<br /><b><span class="info">Warning: The collections have been modified since last time Webcoll was executed, to process the changes, Webcoll must be executed.</span></b><br />"""
header = ['ID', 'Name', 'Time', 'Status', 'Progress']
actions = []
output += """<br /><b>Last BibSched tasks:</b><br />"""
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='webcoll' and runtime< now() ORDER by runtime")
if len(res) > 0:
(id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[len(res) - 1]
webcoll__update_time = runtime
actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')])
else:
actions.append(['', 'webcoll', '', '', 'Not executed yet'])
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='bibindex' and runtime< now() ORDER by runtime")
if len(res) > 0:
(id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[len(res) - 1]
actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')])
else:
actions.append(['', 'bibindex', '', '', 'Not executed yet'])
output += tupletotable(header=header, tuple=actions)
output += """<br /><b>Next scheduled BibSched run:</b><br />"""
actions = []
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='webcoll' and runtime > now() ORDER by runtime")
webcoll_future = ""
if len(res) > 0:
(id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[0]
webcoll__update_time = runtime
actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')])
webcoll_future = "yes"
else:
actions.append(['', 'webcoll', '', '', 'Not scheduled'])
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='bibindex' and runtime > now() ORDER by runtime")
bibindex_future = ""
if len(res) > 0:
(id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[0]
actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')])
bibindex_future = "yes"
else:
actions.append(['', 'bibindex', '', '', 'Not scheduled'])
output += tupletotable(header=header, tuple=actions)
if webcoll_future == "":
output += """<br /><b><span class="info">Warning: Webcoll is not scheduled for a future run by bibsched, any updates to the collection will not be processed.</span></b><br />"""
if bibindex_future == "":
output += """<br /><b><span class="info">Warning: Bibindex is not scheduled for a future run by bibsched, any updates to the records will not be processed.</span></b><br />"""
body = [output]
if callback:
return perform_index(colID, ln, "perform_checkwebcollstatus", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyrestricted(colID, ln, rest='', callback='yes', confirm=-1):
"""modify which apache group is allowed to access the collection.
rest - the groupname"""
subtitle = ''
output = ""
col_dict = dict(get_def_name('', "collection"))
action_id = acc_get_action_id(VIEWRESTRCOLL)
if colID and col_dict.has_key(int(colID)):
colID = int(colID)
subtitle = """<a name="2">2. Modify access restrictions for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.2">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """<p>Please note that Invenio versions greater than <em>0.92.1</em> manage collection restriction via the standard
<strong><a href="/admin/webaccess/webaccessadmin.py/showactiondetails?id_action=%i">WebAccess Admin Interface</a></strong> (action '%s').</p>
""" % (action_id, VIEWRESTRCOLL)
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_modifyrestricted", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_checkcollectionstatus(colID, ln, confirm=0, callback='yes'):
"""Check the configuration of the collections."""
from invenio.search_engine import collection_restricted_p, restricted_collection_cache
subtitle = """<a name="11"></a>Collection Status&nbsp;&nbsp;&nbsp;[<a href="%s/help/admin/websearch-admin-guide#6">?</a>]""" % CFG_SITE_URL
output = ""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
collections = run_sql("SELECT id, name, dbquery, nbrecs FROM collection "
"ORDER BY id")
header = ['ID', 'Name','Query', 'Subcollections', 'Restricted', 'Hosted',
'I18N', 'Status', 'Number of records']
rnk_list = get_def_name('', "rnkMETHOD")
actions = []
restricted_collection_cache.recreate_cache_if_needed()
for (id, name, dbquery, nbrecs) in collections:
reg_sons = col_has_son(id, 'r')
vir_sons = col_has_son(id, 'v')
status = ""
hosted = ""
if str(dbquery).startswith("hostedcollection:"): hosted = """<b><span class="info">Yes</span></b>"""
else: hosted = """<b><span class="info">No</span></b>"""
langs = run_sql("SELECT ln from collectionname where id_collection=%s", (id, ))
i8n = ""
if len(langs) > 0:
for lang in langs:
i8n += "%s, " % lang
else:
i8n = """<b><span class="info">None</span></b>"""
if reg_sons and dbquery:
status = """<b><span class="warning">1:Conflict</span></b>"""
elif not dbquery and not reg_sons:
status = """<b><span class="warning">2:Empty</span></b>"""
if (reg_sons or vir_sons):
subs = """<b><span class="info">Yes</span></b>"""
else:
subs = """<b><span class="info">No</span></b>"""
if dbquery is None:
dbquery = """<b><span class="info">No</span></b>"""
restricted = collection_restricted_p(name, recreate_cache_if_needed=False)
if restricted:
restricted = """<b><span class="warning">Yes</span></b>"""
if status:
status += """<b><span class="warning">,3:Restricted</span></b>"""
else:
status += """<b><span class="warning">3:Restricted</span></b>"""
else:
restricted = """<b><span class="info">No</span></b>"""
if status == "":
status = """<b><span class="info">OK</span></b>"""
actions.append([id, """<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&amp;ln=%s">%s</a>""" % (CFG_SITE_URL, id, ln, name), dbquery, subs, restricted, hosted, i8n, status, nbrecs])
output += tupletotable(header=header, tuple=actions)
body = [output]
return addadminbox(subtitle, body)
if callback:
return perform_index(colID, ln, "perform_checkcollectionstatus", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_checkexternalcollections(colID, ln, icl=None, update="", confirm=0, callback='yes'):
"""Check the external collections for inconsistencies."""
subtitle = """<a name="7"></a>Check external collections&nbsp;&nbsp;&nbsp;[<a href="%s/help/admin/websearch-admin-guide#7">?</a>]""" % CFG_SITE_URL
output = ""
colID = int(colID)
if icl:
if update == "add":
# icl : the "inconsistent list" comes as a string, it has to be converted back into a list
icl = eval(icl)
#icl = icl[1:-1].split(',')
for collection in icl:
#collection = str(collection[1:-1])
query_select = "SELECT name FROM externalcollection WHERE name like '%(name)s';" % {'name': collection}
results_select = run_sql(query_select)
if not results_select:
query_insert = "INSERT INTO externalcollection (name) VALUES ('%(name)s');" % {'name': collection}
run_sql(query_insert)
output += """<br /><span class=info>New collection \"%s\" has been added to the database table \"externalcollection\".</span><br />""" % (collection)
else:
output += """<br /><span class=info>Collection \"%s\" has already been added to the database table \"externalcollection\" or was already there.</span><br />""" % (collection)
elif update == "del":
# icl : the "inconsistent list" comes as a string, it has to be converted back into a list
icl = eval(icl)
#icl = icl[1:-1].split(',')
for collection in icl:
#collection = str(collection[1:-1])
query_select = "SELECT id FROM externalcollection WHERE name like '%(name)s';" % {'name': collection}
results_select = run_sql(query_select)
if results_select:
query_delete = "DELETE FROM externalcollection WHERE id like '%(id)s';" % {'id': results_select[0][0]}
query_delete_states = "DELETE FROM collection_externalcollection WHERE id_externalcollection like '%(id)s';" % {'id': results_select[0][0]}
run_sql(query_delete)
run_sql(query_delete_states)
output += """<br /><span class=info>Collection \"%s\" has been deleted from the database table \"externalcollection\".</span><br />""" % (collection)
else:
output += """<br /><span class=info>Collection \"%s\" has already been delete from the database table \"externalcollection\" or was never there.</span><br />""" % (collection)
external_collections_file = []
external_collections_db = []
for coll in external_collections_dictionary.values():
external_collections_file.append(coll.name)
external_collections_file.sort()
query = """SELECT name from externalcollection"""
results = run_sql(query)
for result in results:
external_collections_db.append(result[0])
external_collections_db.sort()
number_file = len(external_collections_file)
number_db = len(external_collections_db)
if external_collections_file == external_collections_db:
output += """<br /><span class="info">External collections are consistent.</span><br /><br />
&nbsp;&nbsp;&nbsp;- database table \"externalcollection\" has %(number_db)s collections<br />
&nbsp;&nbsp;&nbsp;- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections""" % {
"number_db" : number_db,
"number_file" : number_file}
elif len(external_collections_file) > len(external_collections_db):
external_collections_diff = list(set(external_collections_file) - set(external_collections_db))
external_collections_db.extend(external_collections_diff)
external_collections_db.sort()
if external_collections_file == external_collections_db:
output += """<br /><span class="warning">There is an inconsistency:</span><br /><br />
&nbsp;&nbsp;&nbsp;- database table \"externalcollection\" has %(number_db)s collections
&nbsp;(<span class="warning">missing: %(diff)s</span>)<br />
&nbsp;&nbsp;&nbsp;- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections
<br /><br /><a href="%(site_url)s/admin/websearch/websearchadmin.py/checkexternalcollections?colID=%(colID)s&amp;icl=%(diff)s&amp;update=add&amp;ln=%(ln)s">
Click here</a> to update your database adding the missing collections. If the problem persists please check your configuration manually.""" % {
"number_db" : number_db,
"number_file" : number_file,
"diff" : external_collections_diff,
"site_url" : CFG_SITE_URL,
"colID" : colID,
"ln" : ln}
else:
output += """<br /><span class="warning">There is an inconsistency:</span><br /><br />
&nbsp;&nbsp;&nbsp;- database table \"externalcollection\" has %(number_db)s collections<br />
&nbsp;&nbsp;&nbsp;- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections
<br /><br /><span class="warning">The external collections do not match.</span>
<br />To fix the problem please check your configuration manually.""" % {
"number_db" : number_db,
"number_file" : number_file}
elif len(external_collections_file) < len(external_collections_db):
external_collections_diff = list(set(external_collections_db) - set(external_collections_file))
external_collections_file.extend(external_collections_diff)
external_collections_file.sort()
if external_collections_file == external_collections_db:
output += """<br /><span class="warning">There is an inconsistency:</span><br /><br />
&nbsp;&nbsp;&nbsp;- database table \"externalcollection\" has %(number_db)s collections
&nbsp;(<span class="warning">extra: %(diff)s</span>)<br />
&nbsp;&nbsp;&nbsp;- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections
<br /><br /><a href="%(site_url)s/admin/websearch/websearchadmin.py/checkexternalcollections?colID=%(colID)s&amp;icl=%(diff)s&amp;update=del&amp;ln=%(ln)s">
Click here</a> to force remove the extra collections from your database (warning: use with caution!). If the problem persists please check your configuration manually.""" % {
"number_db" : number_db,
"number_file" : number_file,
"diff" : external_collections_diff,
"site_url" : CFG_SITE_URL,
"colID" : colID,
"ln" : ln}
else:
output += """<br /><span class="warning">There is an inconsistency:</span><br /><br />
&nbsp;&nbsp;&nbsp;- database table \"externalcollection\" has %(number_db)s collections<br />
&nbsp;&nbsp;&nbsp;- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections
<br /><br /><span class="warning">The external collections do not match.</span>
<br />To fix the problem please check your configuration manually.""" % {
"number_db" : number_db,
"number_file" : number_file}
else:
output += """<br /><span class="warning">There is an inconsistency:</span><br /><br />
&nbsp;&nbsp;&nbsp;- database table \"externalcollection\" has %(number_db)s collections<br />
&nbsp;&nbsp;&nbsp;- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections
<br /><br /><span class="warning">The number of external collections is the same but the collections do not match.</span>
<br />To fix the problem please check your configuration manually.""" % {
"number_db" : number_db,
"number_file" : number_file}
body = [output]
return addadminbox(subtitle, body)
if callback:
return perform_index(colID, ln, "perform_checkexternalcollections", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def col_has_son(colID, rtype='r'):
"""Return True if the collection has at least one son."""
return run_sql("SELECT id_son FROM collection_collection WHERE id_dad=%s and type=%s LIMIT 1", (colID, rtype)) != ()
def get_col_tree(colID, rtype=''):
"""Returns a presentation of the tree as a list. TODO: Add loop detection
colID - startpoint for the tree
rtype - get regular or virtual part of the tree"""
try:
colID = int(colID)
stack = [colID]
ssize = 0
tree = [(colID, 0, 0, colID, 'r')]
while len(stack) > 0:
ccolID = stack.pop()
if ccolID == colID and rtype:
res = run_sql("SELECT id_son, score, type FROM collection_collection WHERE id_dad=%s AND type=%s ORDER BY score ASC,id_son", (ccolID, rtype))
else:
res = run_sql("SELECT id_son, score, type FROM collection_collection WHERE id_dad=%s ORDER BY score ASC,id_son", (ccolID, ))
ssize += 1
ntree = []
for i in range(0, len(res)):
id_son = res[i][0]
score = res[i][1]
rtype = res[i][2]
stack.append(id_son)
if i == (len(res) - 1):
up = 0
else:
up = 1
if i == 0:
down = 0
else:
down = 1
ntree.insert(0, (id_son, up, down, ccolID, rtype))
tree = tree[0:ssize] + ntree + tree[ssize:len(tree)]
return tree
except StandardError, e:
register_exception()
return ()
def add_col_dad_son(add_dad, add_son, rtype):
"""Add a son to a collection (dad)
add_dad - add to this collection id
add_son - add this collection id
rtype - either regular or virtual"""
try:
res = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s ORDER BY score ASC", (add_dad, ))
highscore = 0
for score in res:
if int(score[0]) > highscore:
highscore = int(score[0])
highscore += 1
res = run_sql("INSERT INTO collection_collection(id_dad,id_son,score,type) values(%s,%s,%s,%s)", (add_dad, add_son, highscore, rtype))
return (1, highscore)
except StandardError, e:
register_exception()
return (0, e)
def compare_on_val(first, second):
"""Compare the two values"""
return cmp(first[1], second[1])
def get_col_fld(colID=-1, type = '', id_field=''):
"""Returns either all portalboxes associated with a collection, or based on either colID or language or both.
colID - collection id
ln - language id"""
sql = "SELECT id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue, field WHERE id_field=field.id"
params = []
if colID > -1:
sql += " AND id_collection=%s"
params.append(colID)
if id_field:
sql += " AND id_field=%s"
params.append(id_field)
if type:
sql += " AND type=%s"
params.append(type)
sql += " ORDER BY type, score desc, score_fieldvalue desc"
res = run_sql(sql, tuple(params))
return res
def get_col_pbx(colID=-1, ln='', position = ''):
"""Returns either all portalboxes associated with a collection, or based on either colID or language or both.
colID - collection id
ln - language id"""
sql = "SELECT id_portalbox, id_collection, ln, score, position, title, body FROM collection_portalbox, portalbox WHERE id_portalbox = portalbox.id"
params = []
if colID > -1:
sql += " AND id_collection=%s"
params.append(colID)
if ln:
sql += " AND ln=%s"
params.append(ln)
if position:
sql += " AND position=%s"
params.append(position)
sql += " ORDER BY position, ln, score desc"
res = run_sql(sql, tuple(params))
return res
def get_col_fmt(colID=-1):
"""Returns all formats currently associated with a collection, or for one specific collection
colID - the id of the collection"""
if colID not in [-1, "-1"]:
res = run_sql("SELECT id_format, id_collection, code, score FROM collection_format, format WHERE id_format = format.id AND id_collection=%s ORDER BY score desc", (colID, ))
else:
res = run_sql("SELECT id_format, id_collection, code, score FROM collection_format, format WHERE id_format = format.id ORDER BY score desc")
return res
def get_col_rnk(colID, ln):
""" Returns a list of the rank methods the given collection is attached to
colID - id from collection"""
try:
res1 = dict(run_sql("SELECT id_rnkMETHOD, '' FROM collection_rnkMETHOD WHERE id_collection=%s", (colID, )))
res2 = get_def_name('', "rnkMETHOD")
result = filter(lambda x: res1.has_key(x[0]), res2)
return result
except StandardError, e:
return ()
def get_pbx():
"""Returns all portalboxes"""
res = run_sql("SELECT id, title, body FROM portalbox ORDER by title,body")
return res
def get_fld_value(fldvID = ''):
"""Returns fieldvalue"""
sql = "SELECT id, name, value FROM fieldvalue"
params = []
if fldvID:
sql += " WHERE id=%s"
params.append(fldvID)
sql += " ORDER BY name"
res = run_sql(sql, tuple(params))
return res
def get_pbx_pos():
"""Returns a list of all the positions for a portalbox"""
position = {}
position["rt"] = "Right Top"
position["lt"] = "Left Top"
position["te"] = "Title Epilog"
position["tp"] = "Title Prolog"
position["ne"] = "Narrow by coll epilog"
position["np"] = "Narrow by coll prolog"
return position
def get_sort_nametypes():
"""Return a list of the various translationnames for the fields"""
type = {}
type['soo'] = 'Sort options'
type['seo'] = 'Search options'
type['sew'] = 'Search within'
return type
def get_fmt_nametypes():
"""Return a list of the various translationnames for the output formats"""
type = []
type.append(('ln', 'Long name'))
return type
def get_fld_nametypes():
"""Return a list of the various translationnames for the fields"""
type = []
type.append(('ln', 'Long name'))
return type
def get_col_nametypes():
"""Return a list of the various translationnames for the collections"""
type = []
type.append(('ln', 'Long name'))
return type
def find_last(tree, start_son):
"""Find the previous collection in the tree with the same father as start_son"""
id_dad = tree[start_son][3]
while start_son > 0:
start_son -= 1
if tree[start_son][3] == id_dad:
return start_son
def find_next(tree, start_son):
"""Find the next collection in the tree with the same father as start_son"""
id_dad = tree[start_son][3]
while start_son < len(tree):
start_son += 1
if tree[start_son][3] == id_dad:
return start_son
def remove_col_subcol(id_son, id_dad, type):
"""Remove a collection as a son of another collection in the tree, if collection isn't used elsewhere in the tree, remove all registered sons of the id_son.
id_son - collection id of son to remove
id_dad - the id of the dad"""
try:
if id_son != id_dad:
tree = get_col_tree(id_son)
run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s", (id_son, id_dad))
else:
tree = get_col_tree(id_son, type)
run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s and type=%s", (id_son, id_dad, type))
if not run_sql("SELECT id_dad,id_son,type,score from collection_collection WHERE id_son=%s and type=%s", (id_son, type)):
for (id, up, down, dad, rtype) in tree:
run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s", (id, dad))
return (1, "")
except StandardError, e:
return (0, e)
def check_col(add_dad, add_son):
"""Check if the collection can be placed as a son of the dad without causing loops.
add_dad - collection id
add_son - collection id"""
try:
stack = [add_dad]
res = run_sql("SELECT id_dad FROM collection_collection WHERE id_dad=%s AND id_son=%s", (add_dad, add_son))
if res:
raise StandardError
while len(stack) > 0:
colID = stack.pop()
res = run_sql("SELECT id_dad FROM collection_collection WHERE id_son=%s", (colID, ))
for id in res:
if int(id[0]) == int(add_son):
# raise StandardError # this was the original but it didnt work
return(0)
else:
stack.append(id[0])
return (1, "")
except StandardError, e:
return (0, e)
def attach_rnk_col(colID, rnkID):
"""attach rank method to collection
rnkID - id from rnkMETHOD table
colID - id of collection, as in collection table """
try:
res = run_sql("INSERT INTO collection_rnkMETHOD(id_collection, id_rnkMETHOD) values (%s,%s)", (colID, rnkID))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def detach_rnk_col(colID, rnkID):
"""detach rank method from collection
rnkID - id from rnkMETHOD table
colID - id of collection, as in collection table """
try:
res = run_sql("DELETE FROM collection_rnkMETHOD WHERE id_collection=%s AND id_rnkMETHOD=%s", (colID, rnkID))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def switch_col_treescore(col_1, col_2):
try:
res1 = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s and id_son=%s", (col_1[3], col_1[0]))
res2 = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s and id_son=%s", (col_2[3], col_2[0]))
res = run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s and id_son=%s", (res2[0][0], col_1[3], col_1[0]))
res = run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s and id_son=%s", (res1[0][0], col_2[3], col_2[0]))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def move_col_tree(col_from, col_to, move_to_rtype=''):
"""Move a collection from one point in the tree to another. becomes a son of the endpoint.
col_from - move this collection from current point
col_to - and set it as a son of this collection.
move_to_rtype - either virtual or regular collection"""
try:
res = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s ORDER BY score asc", (col_to[0], ))
highscore = 0
for score in res:
if int(score[0]) > highscore:
highscore = int(score[0])
highscore += 1
if not move_to_rtype:
move_to_rtype = col_from[4]
res = run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s", (col_from[0], col_from[3]))
res = run_sql("INSERT INTO collection_collection(id_dad,id_son,score,type) values(%s,%s,%s,%s)", (col_to[0], col_from[0], highscore, move_to_rtype))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def remove_pbx(colID, pbxID, ln):
"""Removes a portalbox from the collection given.
colID - the collection the format is connected to
pbxID - the portalbox which should be removed from the collection.
ln - the language of the portalbox to be removed"""
try:
res = run_sql("DELETE FROM collection_portalbox WHERE id_collection=%s AND id_portalbox=%s AND ln=%s", (colID, pbxID, ln))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def remove_fmt(colID, fmtID):
"""Removes a format from the collection given.
colID - the collection the format is connected to
fmtID - the format which should be removed from the collection."""
try:
res = run_sql("DELETE FROM collection_format WHERE id_collection=%s AND id_format=%s", (colID, fmtID))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def remove_fld(colID, fldID, fldvID=''):
"""Removes a field from the collection given.
colID - the collection the format is connected to
fldID - the field which should be removed from the collection."""
try:
sql = "DELETE FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s"
params = [colID, fldID]
if fldvID:
if fldvID != "None":
sql += " AND id_fieldvalue=%s"
params.append(fldvID)
else:
sql += " AND id_fieldvalue is NULL"
res = run_sql(sql, tuple(params))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def delete_fldv(fldvID):
"""Deletes all data for the given fieldvalue
fldvID - delete all data in the tables associated with fieldvalue and this id"""
try:
res = run_sql("DELETE FROM collection_field_fieldvalue WHERE id_fieldvalue=%s", (fldvID, ))
res = run_sql("DELETE FROM fieldvalue WHERE id=%s", (fldvID, ))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def delete_pbx(pbxID):
"""Deletes all data for the given portalbox
pbxID - delete all data in the tables associated with portalbox and this id """
try:
res = run_sql("DELETE FROM collection_portalbox WHERE id_portalbox=%s", (pbxID, ))
res = run_sql("DELETE FROM portalbox WHERE id=%s", (pbxID, ))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def delete_fmt(fmtID):
"""Deletes all data for the given format
fmtID - delete all data in the tables associated with format and this id """
try:
res = run_sql("DELETE FROM format WHERE id=%s", (fmtID, ))
res = run_sql("DELETE FROM collection_format WHERE id_format=%s", (fmtID, ))
res = run_sql("DELETE FROM formatname WHERE id_format=%s", (fmtID, ))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def delete_col(colID):
"""Deletes all data for the given collection
colID - delete all data in the tables associated with collection and this id """
try:
res = run_sql("DELETE FROM collection WHERE id=%s", (colID, ))
res = run_sql("DELETE FROM collectionname WHERE id_collection=%s", (colID, ))
res = run_sql("DELETE FROM collection_rnkMETHOD WHERE id_collection=%s", (colID, ))
res = run_sql("DELETE FROM collection_collection WHERE id_dad=%s", (colID, ))
res = run_sql("DELETE FROM collection_collection WHERE id_son=%s", (colID, ))
res = run_sql("DELETE FROM collection_portalbox WHERE id_collection=%s", (colID, ))
res = run_sql("DELETE FROM collection_format WHERE id_collection=%s", (colID, ))
res = run_sql("DELETE FROM collection_field_fieldvalue WHERE id_collection=%s", (colID, ))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def add_fmt(code, name, rtype):
"""Add a new output format. Returns the id of the format.
code - the code for the format, max 6 chars.
name - the default name for the default language of the format.
rtype - the default nametype"""
try:
res = run_sql("INSERT INTO format (code, name) values (%s,%s)", (code, name))
fmtID = run_sql("SELECT id FROM format WHERE code=%s", (code,))
res = run_sql("INSERT INTO formatname(id_format, type, ln, value) VALUES (%s,%s,%s,%s)",
(fmtID[0][0], rtype, CFG_SITE_LANG, name))
return (1, fmtID)
except StandardError, e:
register_exception()
return (0, e)
def update_fldv(fldvID, name, value):
"""Modify existing fieldvalue
fldvID - id of fieldvalue to modify
value - the value of the fieldvalue
name - the name of the fieldvalue."""
try:
res = run_sql("UPDATE fieldvalue set name=%s where id=%s", (name, fldvID))
res = run_sql("UPDATE fieldvalue set value=%s where id=%s", (value, fldvID))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def add_fldv(name, value):
"""Add a new fieldvalue, returns id of fieldvalue
value - the value of the fieldvalue
name - the name of the fieldvalue."""
try:
res = run_sql("SELECT id FROM fieldvalue WHERE name=%s and value=%s", (name, value))
if not res:
res = run_sql("INSERT INTO fieldvalue (name, value) values (%s,%s)", (name, value))
res = run_sql("SELECT id FROM fieldvalue WHERE name=%s and value=%s", (name, value))
if res:
return (1, res[0][0])
else:
raise StandardError
except StandardError, e:
register_exception()
return (0, e)
def add_pbx(title, body):
try:
res = run_sql("INSERT INTO portalbox (title, body) values (%s,%s)", (title, body))
res = run_sql("SELECT id FROM portalbox WHERE title=%s AND body=%s", (title, body))
if res:
return (1, res[0][0])
else:
raise StandardError
except StandardError, e:
register_exception()
return (0, e)
def add_col(colNAME, dbquery=None):
"""Adds a new collection to collection table
colNAME - the default name for the collection, saved to collection and collectionname
dbquery - query related to the collection"""
# BTW, sometimes '' are passed instead of None, so change them to None
if not dbquery:
dbquery = None
try:
rtype = get_col_nametypes()[0][0]
colID = run_sql("SELECT id FROM collection WHERE id=1")
if colID:
res = run_sql("INSERT INTO collection (name,dbquery) VALUES (%s,%s)",
(colNAME,dbquery))
else:
res = run_sql("INSERT INTO collection (id,name,dbquery) VALUES (1,%s,%s)",
(colNAME,dbquery))
colID = run_sql("SELECT id FROM collection WHERE name=%s", (colNAME,))
res = run_sql("INSERT INTO collectionname(id_collection, type, ln, value) VALUES (%s,%s,%s,%s)",
(colID[0][0], rtype, CFG_SITE_LANG, colNAME))
if colID:
return (1, colID[0][0])
else:
raise StandardError
except StandardError, e:
register_exception()
return (0, e)
def add_col_pbx(colID, pbxID, ln, position, score=''):
"""add a portalbox to the collection.
colID - the id of the collection involved
pbxID - the portalbox to add
ln - which language the portalbox is for
score - decides which portalbox is the most important
position - position on page the portalbox should appear."""
try:
if score:
res = run_sql("INSERT INTO collection_portalbox(id_portalbox, id_collection, ln, score, position) values (%s,%s,'%s',%s,%s)", (pbxID, colID, ln, score, position))
else:
res = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and ln=%s and position=%s ORDER BY score desc, ln, position", (colID, ln, position))
if res:
score = int(res[0][0])
else:
score = 0
res = run_sql("INSERT INTO collection_portalbox(id_portalbox, id_collection, ln, score, position) values (%s,%s,%s,%s,%s)", (pbxID, colID, ln, (score + 1), position))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def add_col_fmt(colID, fmtID, score=''):
"""Add a output format to the collection.
colID - the id of the collection involved
fmtID - the id of the format.
score - the score of the format, decides sorting, if not given, place the format on top"""
try:
if score:
res = run_sql("INSERT INTO collection_format(id_format, id_collection, score) values (%s,%s,%s)", (fmtID, colID, score))
else:
res = run_sql("SELECT score FROM collection_format WHERE id_collection=%s ORDER BY score desc", (colID, ))
if res:
score = int(res[0][0])
else:
score = 0
res = run_sql("INSERT INTO collection_format(id_format, id_collection, score) values (%s,%s,%s)", (fmtID, colID, (score + 1)))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def add_col_fld(colID, fldID, type, fldvID=''):
"""Add a sort/search/field to the collection.
colID - the id of the collection involved
fldID - the id of the field.
fldvID - the id of the fieldvalue.
type - which type, seo, sew...
score - the score of the format, decides sorting, if not given, place the format on top"""
try:
if fldvID and fldvID not in [-1, "-1"]:
run_sql("DELETE FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s and id_fieldvalue is NULL", (colID, fldID, type))
res = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s ORDER BY score desc", (colID, fldID, type))
if res:
score = int(res[0][0])
res = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s ORDER BY score_fieldvalue desc", (colID, fldID, type))
else:
res = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and type=%s ORDER BY score desc", (colID, type))
if res:
score = int(res[0][0]) + 1
else:
score = 1
res = run_sql("SELECT id_collection,id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue where id_field=%s and id_collection=%s and type=%s and id_fieldvalue=%s", (fldID, colID, type, fldvID))
if not res:
run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=score_fieldvalue+1 WHERE id_field=%s AND id_collection=%s and type=%s", (fldID, colID, type))
res = run_sql("INSERT INTO collection_field_fieldvalue(id_field, id_fieldvalue, id_collection, type, score, score_fieldvalue) values (%s,%s,%s,%s,%s,%s)", (fldID, fldvID, colID, type, score, 1))
else:
return (0, (1, "Already exists"))
else:
res = run_sql("SELECT id_collection,id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s AND type=%s and id_field=%s and id_fieldvalue is NULL", (colID, type, fldID))
if res:
return (0, (1, "Already exists"))
else:
run_sql("UPDATE collection_field_fieldvalue SET score=score+1")
res = run_sql("INSERT INTO collection_field_fieldvalue(id_field, id_collection, type, score,score_fieldvalue) values (%s,%s,%s,%s, 0)", (fldID, colID, type, 1))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def modify_dbquery(colID, dbquery=None):
"""Modify the dbquery of an collection.
colID - the id of the collection involved
dbquery - the new dbquery"""
# BTW, sometimes '' is passed instead of None, so change it to None
if not dbquery:
dbquery = None
try:
res = run_sql("UPDATE collection SET dbquery=%s WHERE id=%s", (dbquery, colID))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def modify_pbx(colID, pbxID, sel_ln, score='', position='', title='', body=''):
"""Modify a portalbox
colID - the id of the collection involved
pbxID - the id of the portalbox that should be modified
sel_ln - the language of the portalbox that should be modified
title - the title
body - the content
score - if several portalboxes in one position, who should appear on top.
position - position on page"""
try:
if title:
res = run_sql("UPDATE portalbox SET title=%s WHERE id=%s", (title, pbxID))
if body:
res = run_sql("UPDATE portalbox SET body=%s WHERE id=%s", (body, pbxID))
if score:
res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (score, colID, pbxID, sel_ln))
if position:
res = run_sql("UPDATE collection_portalbox SET position=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (position, colID, pbxID, sel_ln))
return (1, "")
except Exception, e:
register_exception()
return (0, e)
def switch_fld_score(colID, id_1, id_2):
"""Switch the scores of id_1 and id_2 in collection_field_fieldvalue
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s", (colID, id_1))
res2 = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s", (colID, id_2))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
else:
res = run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s", (res2[0][0], colID, id_1))
res = run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s", (res1[0][0], colID, id_2))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def switch_fld_value_score(colID, id_1, fldvID_1, fldvID_2):
"""Switch the scores of two field_value
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (colID, id_1, fldvID_1))
res2 = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (colID, id_1, fldvID_2))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
else:
res = run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (res2[0][0], colID, id_1, fldvID_1))
res = run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (res1[0][0], colID, id_1, fldvID_2))
return (1, "")
except Exception, e:
register_exception()
return (0, e)
def switch_pbx_score(colID, id_1, id_2, sel_ln):
"""Switch the scores of id_1 and id_2 in the table given by the argument.
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and id_portalbox=%s and ln=%s", (colID, id_1, sel_ln))
res2 = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and id_portalbox=%s and ln=%s", (colID, id_2, sel_ln))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (res2[0][0], colID, id_1, sel_ln))
res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (res1[0][0], colID, id_2, sel_ln))
return (1, "")
except Exception, e:
register_exception()
return (0, e)
def switch_score(colID, id_1, id_2, table):
"""Switch the scores of id_1 and id_2 in the table given by the argument.
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM collection_%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (colID, id_1))
res2 = run_sql("SELECT score FROM collection_%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (colID, id_2))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
res = run_sql("UPDATE collection_%s SET score=%%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (res2[0][0], colID, id_1))
res = run_sql("UPDATE collection_%s SET score=%%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (res1[0][0], colID, id_2))
return (1, "")
except Exception, e:
register_exception()
return (0, e)
def get_detailed_page_tabs(colID=None, recID=None, ln=CFG_SITE_LANG):
"""
Returns the complete list of tabs to be displayed in the
detailed record pages.
Returned structured is a dict with
- - key : last component of the url that leads to detailed record tab: http:www.../record/74/key
+ - key : last component of the url that leads to detailed record tab: http:www.../CFG_SITE_RECORD/74/key
- values: a dictionary with the following keys:
- label: *string* label to be printed as tab (Not localized here)
- visible: *boolean* if False, tab should not be shown
- enabled: *boolean* if True, tab should be disabled
- order: *int* position of the tab in the list of tabs
- ln: language of the tab labels
returns dict
"""
_ = gettext_set_language(ln)
tabs = {'metadata' : {'label': _('Information'), 'visible': False, 'enabled': True, 'order': 1},
'references': {'label': _('References'), 'visible': False, 'enabled': True, 'order': 2},
'citations' : {'label': _('Citations'), 'visible': False, 'enabled': True, 'order': 3},
'keywords' : {'label': _('Keywords'), 'visible': False, 'enabled': True, 'order': 4},
'comments' : {'label': _('Discussion'), 'visible': False, 'enabled': True, 'order': 5},
'usage' : {'label': _('Usage statistics'), 'visible': False, 'enabled': True, 'order': 6},
'files' : {'label': _('Files'), 'visible': False, 'enabled': True, 'order': 7},
'plots' : {'label': _('Plots'), 'visible': False, 'enabled': True, 'order': 8},
'holdings' : {'label': _('Holdings'), 'visible': False, 'enabled': True, 'order': 9},
}
res = run_sql("SELECT tabs FROM collectiondetailedrecordpagetabs " + \
"WHERE id_collection=%s", (colID, ))
if len(res) > 0:
tabs_state = res[0][0].split(';')
for tab_state in tabs_state:
if tabs.has_key(tab_state):
tabs[tab_state]['visible'] = True;
else:
# no preference set for this collection.
# assume all tabs are displayed
for key in tabs.keys():
tabs[key]['visible'] = True
if not CFG_WEBCOMMENT_ALLOW_COMMENTS and \
not CFG_WEBCOMMENT_ALLOW_REVIEWS:
tabs['comments']['visible'] = False
tabs['comments']['enabled'] = False
if recID is not None:
# Disable references if no references found
#bfo = BibFormatObject(recID)
#if bfe_references.format_element(bfo, '', '') == '':
# tabs['references']['enabled'] = False
## FIXME: the above was commented out because bfe_references
## may be too slow. And we do not really need this anyway
## because we can disable tabs in WebSearch Admin on a
## collection-by-collection basis. If we need this, then we
## should probably call bfo.fields('999') here that should be
## much faster than calling bfe_references.
# Disable citations if not citations found
#if len(get_cited_by(recID)) == 0:
# tabs['citations']['enabled'] = False
## FIXME: the above was commented out because get_cited_by()
## may be too slow. And we do not really need this anyway
## because we can disable tags in WebSearch Admin on a
## collection-by-collection basis.
# Disable Files tab if no file found except for Plots:
disable_files_tab_p = True
for abibdoc in BibRecDocs(recID).list_bibdocs():
abibdoc_type = abibdoc.get_type()
if abibdoc_type == 'Plot':
continue # ignore attached plots
else:
if CFG_INSPIRE_SITE and not \
abibdoc_type in ('', 'Supplementary Material'):
# ignore non-empty, non-suppl doctypes for INSPIRE
continue
# okay, we found at least one non-Plot file:
disable_files_tab_p = False
break
if disable_files_tab_p:
tabs['files']['enabled'] = False
#Disable holdings tab if collection != Books
collection = run_sql("""select name from collection where id=%s""", (colID, ))
if collection[0][0] != 'Books':
tabs['holdings']['enabled'] = False
# Disable Plots tab if no docfile of doctype Plot found
brd = BibRecDocs(recID)
if len(brd.list_bibdocs('Plot')) == 0:
tabs['plots']['enabled'] = False
if CFG_CERN_SITE:
from invenio.search_engine import get_collection_reclist
if recID in get_collection_reclist("Books & Proceedings"):
tabs['holdings']['visible'] = True
tabs['holdings']['enabled'] = True
tabs[''] = tabs['metadata']
del tabs['metadata']
return tabs
diff --git a/modules/websession/lib/webaccount.py b/modules/websession/lib/webaccount.py
index ea62a77f0..0abb43f3a 100644
--- a/modules/websession/lib/webaccount.py
+++ b/modules/websession/lib/webaccount.py
@@ -1,422 +1,423 @@
## This file is part of Invenio.
## Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import re
import MySQLdb
import urllib
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_CERN_SITE, \
CFG_SITE_LANG, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_VERSION, \
CFG_DATABASE_HOST, \
- CFG_DATABASE_NAME
+ CFG_DATABASE_NAME, \
+ CFG_SITE_RECORD
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION, \
SUPERADMINROLE, CFG_EXTERNAL_AUTH_DEFAULT
from invenio.dbquery import run_sql
from invenio.webuser import getUid,isGuestUser, get_user_preferences, \
collect_user_info
from invenio.access_control_admin import acc_find_user_role_actions
from invenio.messages import gettext_set_language
from invenio.external_authentication import InvenioWebAccessExternalAuthError
import invenio.template
websession_templates = invenio.template.load('websession')
def perform_info(req, ln):
"""Display the main features of CDS personalize"""
out = ""
uid = getUid(req)
user_info = collect_user_info(req)
return websession_templates.tmpl_account_info(
ln = ln,
uid = uid,
guest = isGuestUser(uid),
CFG_CERN_SITE = CFG_CERN_SITE,
)
def perform_display_external_user_settings(settings, ln):
"""show external user settings which is a dictionary."""
_ = gettext_set_language(ln)
html_settings = ""
print_settings = False
settings_keys = settings.keys()
settings_keys.sort()
for key in settings_keys:
value = settings[key]
if key.startswith("EXTERNAL_") and not "HIDDEN_" in key:
print_settings = True
key = key[9:].capitalize()
html_settings += websession_templates.tmpl_external_setting(ln, key, value)
return print_settings and websession_templates.tmpl_external_user_settings(ln, html_settings) or ""
def perform_youradminactivities(user_info, ln):
"""Return text for the `Your Admin Activities' box. Analyze
whether user UID has some admin roles, and if yes, then print
suitable links for the actions he can do. If he's not admin,
print a simple non-authorized message."""
your_role_actions = acc_find_user_role_actions(user_info)
your_roles = []
your_admin_activities = []
guest = isGuestUser(user_info['uid'])
for (role, action) in your_role_actions:
if role not in your_roles:
your_roles.append(role)
if action not in your_admin_activities:
your_admin_activities.append(action)
if SUPERADMINROLE in your_roles:
for action in ("runbibedit", "cfgbibformat", "cfgbibharvest", "cfgoairepository", "cfgbibrank", "cfgbibindex", "cfgwebaccess", "cfgwebcomment", "cfgwebsearch", "cfgwebsubmiit", "cfgbibknowledge", "runbatchuploader"):
if action not in your_admin_activities:
your_admin_activities.append(action)
return websession_templates.tmpl_account_adminactivities(
ln = ln,
uid = user_info['uid'],
guest = guest,
roles = your_roles,
activities = your_admin_activities,
)
def perform_display_account(req, username, bask, aler, sear, msgs, loan, grps, sbms, appr, admn, ln):
"""Display a dynamic page that shows the user's account."""
# load the right message language
_ = gettext_set_language(ln)
uid = getUid(req)
user_info = collect_user_info(req)
#your account
if isGuestUser(uid):
user = "guest"
login = "%s/youraccount/login?ln=%s" % (CFG_SITE_SECURE_URL, ln)
accBody = _("You are logged in as guest. You may want to %(x_url_open)slogin%(x_url_close)s as a regular user.") %\
{'x_url_open': '<a href="' + login + '">',
'x_url_close': '</a>'}
accBody += "<br /><br />"
bask=aler=msgs= _("The %(x_fmt_open)sguest%(x_fmt_close)s users need to %(x_url_open)sregister%(x_url_close)s first") %\
{'x_fmt_open': '<strong class="headline">',
'x_fmt_close': '</strong>',
'x_url_open': '<a href="' + login + '">',
'x_url_close': '</a>'}
sear= _("No queries found")
else:
user = username
accBody = websession_templates.tmpl_account_body(
ln = ln,
user = user,
)
#Display warnings if user is superuser
roles = acc_find_user_role_actions(user_info)
warnings = "0"
for role in roles:
if "superadmin" in role:
warnings = "1"
break
warning_list = superuser_account_warnings()
#check if tickets ok
tickets = (acc_authorize_action(user_info, 'runbibedit')[0] == 0)
return websession_templates.tmpl_account_page(
ln = ln,
warnings = warnings,
warning_list = warning_list,
accBody = accBody,
baskets = bask,
alerts = aler,
searches = sear,
messages = msgs,
loans = loan,
groups = grps,
submissions = sbms,
approvals = appr,
tickets = tickets,
administrative = admn
)
def superuser_account_warnings():
"""Check to see whether admin accounts have default / blank password etc. Returns a list"""
warning_array = []
#Try and connect to the mysql database with the default invenio password
try:
conn = MySQLdb.connect (host = CFG_DATABASE_HOST,
user = "root",
passwd = "my123p$ss",
db = "mysql")
conn.close()
warning_array.append("warning_mysql_password_equal_to_invenio_password")
except:
pass
#Try and connect to the invenio database with the default invenio password
try:
conn = MySQLdb.connect (host = CFG_DATABASE_HOST,
user = "invenio",
passwd = "my123p$ss",
db = CFG_DATABASE_NAME)
conn.close ()
warning_array.append("warning_invenio_password_equal_to_default")
except:
pass
#Check if the admin password is empty
res = run_sql("SELECT password, email from user where nickname = 'admin'")
res1 = run_sql("SELECT email from user where nickname = 'admin' and password = AES_ENCRYPT(%s,'')", (res[0][1], ))
for user in res1:
warning_array.append("warning_empty_admin_password")
#Check if the admin email has been changed from the default
if (CFG_SITE_ADMIN_EMAIL == "info@invenio-software.org" or CFG_SITE_SUPPORT_EMAIL == "info@invenio-software.org") and CFG_CERN_SITE == 0:
warning_array.append("warning_site_support_email_equal_to_default")
#Check for a new release of Invenio
try:
find = re.compile('Invenio v[0-9]+.[0-9]+.[0-9]+(\-rc[0-9])? is released')
webFile = urllib.urlopen("http://invenio-software.org/repo/invenio/tree/RELEASE-NOTES")
temp = ""
version = ""
version1 = ""
while 1:
temp = webFile.readline()
match1 = find.match(temp)
try:
version = match1.group()
break
except:
pass
if not temp:
break
webFile.close()
submatch = re.compile('[0-9]+.[0-9]+.[0-9]+(\-rc[0-9])?')
version1 = submatch.search(version)
web_version = version1.group().split(".")
local_version = CFG_VERSION.split(".")
if web_version[0] > local_version[0]:
warning_array.append("note_new_release_available")
elif web_version[0] == local_version[0] and web_version[1] > local_version[1]:
warning_array.append("note_new_release_available")
elif web_version[0] == local_version[0] and web_version[1] == local_version[1] and web_version[2] > local_version[2]:
warning_array.append("note_new_release_available")
except:
warning_array.append("error_cannot_download_release_notes")
return warning_array
def template_account(title, body, ln):
"""It is a template for print each of the options from the user's account."""
return websession_templates.tmpl_account_template(
ln = ln,
title = title,
body = body
)
def warning_guest_user(type, ln=CFG_SITE_LANG):
"""It returns an alert message,showing that the user is a guest user and should log into the system."""
# load the right message language
_ = gettext_set_language(ln)
return websession_templates.tmpl_warning_guest_user(
ln = ln,
type = type,
)
def perform_delete(ln):
"""Delete the account of the user, not implement yet."""
# TODO
return websession_templates.tmpl_account_delete(ln = ln)
def perform_set(email, ln, can_config_bibcatalog = False, verbose = 0):
"""Perform_set(email,password): edit your account parameters, email and
password.
If can_config_bibcatalog is True, show the bibcatalog dialog (if configured).
"""
try:
res = run_sql("SELECT id, nickname FROM user WHERE email=%s", (email,))
uid = res[0][0]
nickname = res[0][1]
except:
uid = 0
nickname = ""
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS
prefs = get_user_preferences(uid)
if CFG_EXTERNAL_AUTHENTICATION.has_key(prefs['login_method']) and CFG_EXTERNAL_AUTHENTICATION[prefs['login_method']] is not None:
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL = 3
out = websession_templates.tmpl_user_preferences(
ln = ln,
email = email,
email_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL >= 2),
password_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL >= 3),
nickname = nickname,
)
if len(CFG_EXTERNAL_AUTHENTICATION) > 1:
try:
uid = run_sql("SELECT id FROM user where email=%s", (email,))
uid = uid[0][0]
except:
uid = 0
current_login_method = prefs['login_method']
methods = CFG_EXTERNAL_AUTHENTICATION.keys()
# Filtering out methods that don't provide user_exists to check if
# a user exists in the external auth method before letting him/her
# to switch.
for method in methods:
if CFG_EXTERNAL_AUTHENTICATION[method] is not None:
try:
if not CFG_EXTERNAL_AUTHENTICATION[method].user_exists(email):
methods.remove(method)
except (AttributeError, InvenioWebAccessExternalAuthError, NotImplementedError):
methods.remove(method)
methods.sort()
if len(methods) > 1:
out += websession_templates.tmpl_user_external_auth(
ln = ln,
methods = methods,
current = current_login_method,
method_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4)
)
current_group_records = prefs.get('websearch_group_records', 10)
show_latestbox = prefs.get('websearch_latestbox', True)
show_helpbox = prefs.get('websearch_helpbox', True)
out += websession_templates.tmpl_user_websearch_edit(
ln = ln,
current = current_group_records,
show_latestbox = show_latestbox,
show_helpbox = show_helpbox,
)
preferred_lang = prefs.get('language', ln)
out += websession_templates.tmpl_user_lang_edit(
ln = ln,
preferred_lang = preferred_lang
)
#show this dialog only if the system has been configured to use a ticket system
from invenio.config import CFG_BIBCATALOG_SYSTEM
if CFG_BIBCATALOG_SYSTEM and can_config_bibcatalog:
bibcatalog_username = prefs.get('bibcatalog_username', "")
bibcatalog_password = prefs.get('bibcatalog_password', "")
out += websession_templates.tmpl_user_bibcatalog_auth(bibcatalog_username, \
bibcatalog_password, ln=ln)
if verbose >= 9:
for key, value in prefs.items():
out += "<b>%s</b>:%s<br />" % (key, value)
out += perform_display_external_user_settings(prefs, ln)
return out
def create_register_page_box(referer='', ln=CFG_SITE_LANG):
"""Register a new account."""
return websession_templates.tmpl_register_page(
referer = referer,
ln = ln,
level = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS,
)
## create_login_page_box(): ask for the user's email and password, for login into the system
def create_login_page_box(referer='', ln=CFG_SITE_LANG):
# List of referer regexep and message to print
_ = gettext_set_language(ln)
login_referrer2msg = (
(re.compile(r"/search"), "<p>" + _("This collection is restricted. If you think you have right to access it, please authenticate yourself.") + "</p>"),
- (re.compile(r"/record/\d+/files/.+"), "<p>" + _("This file is restricted. If you think you have right to access it, please authenticate yourself.") + "</p>"),
+ (re.compile(r"/%s/\d+/files/.+" % CFG_SITE_RECORD), "<p>" + _("This file is restricted. If you think you have right to access it, please authenticate yourself.") + "</p>"),
)
msg = ""
for regexp, txt in login_referrer2msg:
if regexp.search(referer):
msg = txt
break
internal = None
for system in CFG_EXTERNAL_AUTHENTICATION.keys():
if CFG_EXTERNAL_AUTHENTICATION[system] is None:
internal = system
break
register_available = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS <= 1 and internal
## Let's retrieve all the login method that are not dedicated to robots
methods = [method[0] for method in CFG_EXTERNAL_AUTHENTICATION.iteritems() if not method[1] or not method[1].robot_login_method_p()]
methods.sort()
return websession_templates.tmpl_login_form(
ln = ln,
referer = referer,
internal = internal,
register_available = register_available,
methods = methods,
selected_method = CFG_EXTERNAL_AUTH_DEFAULT,
msg = msg,
)
# perform_logout: display the message of not longer authorized,
def perform_logout(req, ln):
return websession_templates.tmpl_account_logout(ln = ln)
#def perform_lost: ask the user for his email, in order to send him the lost password
def perform_lost(ln):
return websession_templates.tmpl_lost_password_form(ln)
#def perform_reset_password: ask the user for a new password to reset the lost one
def perform_reset_password(ln, email, reset_key, msg=''):
return websession_templates.tmpl_reset_password_form(ln, email, reset_key, msg)
# perform_emailSent(email): confirm that the password has been emailed to 'email' address
def perform_emailSent(email, ln):
return websession_templates.tmpl_account_emailSent(ln = ln, email = email)
# peform_emailMessage : display a error message when the email introduced is not correct, and sugest to try again
def perform_emailMessage(eMsg, ln):
return websession_templates.tmpl_account_emailMessage( ln = ln,
msg = eMsg
)
# perform_back(): template for return to a previous page, used for login,register and setting
def perform_back(mess, url, linkname, ln='en'):
return websession_templates.tmpl_back_form(
ln = ln,
message = mess,
url = url,
link = linkname,
)
diff --git a/modules/websession/lib/websession_templates.py b/modules/websession/lib/websession_templates.py
index dcd45c6f9..403eeee9f 100644
--- a/modules/websession/lib/websession_templates.py
+++ b/modules/websession/lib/websession_templates.py
@@ -1,2477 +1,2478 @@
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import urllib
import cgi
from invenio.config import \
CFG_CERN_SITE, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_SITE_URL, \
CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS, \
CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS, \
CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS, \
CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS, \
- CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS
+ CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
+ CFG_SITE_RECORD
from invenio.access_control_config import CFG_EXTERNAL_AUTH_USING_SSO, \
CFG_EXTERNAL_AUTH_LOGOUT_SSO
from invenio.urlutils import make_canonical_urlargd, create_url, create_html_link
from invenio.htmlutils import escape_html, nmtoken_from_string
from invenio.messages import gettext_set_language, language_list_long
from invenio.websession_config import CFG_WEBSESSION_GROUP_JOIN_POLICY
class Template:
def tmpl_back_form(self, ln, message, url, link):
"""
A standard one-message-go-back-link page.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'message' *string* - The message to display
- 'url' *string* - The url to go back to
- 'link' *string* - The link text
"""
out = """
<table>
<tr>
<td align="left">%(message)s
<a href="%(url)s">%(link)s</a></td>
</tr>
</table>
"""% {
'message' : message,
'url' : url,
'link' : link,
'ln' : ln
}
return out
def tmpl_external_setting(self, ln, key, value):
_ = gettext_set_language(ln)
out = """
<tr>
<td align="right"><strong>%s:</strong></td>
<td><i>%s</i></td>
</tr>""" % (key, value)
return out
def tmpl_external_user_settings(self, ln, html_settings):
_ = gettext_set_language(ln)
out = """
<p><big><strong class="headline">%(external_user_settings)s</strong></big></p>
<table>
%(html_settings)s
</table>
<p><big><strong class="headline">%(external_user_groups)s</strong></big></p>
<p>%(consult_external_groups)s</p>
""" % {
'external_user_settings' : _('External account settings'),
'html_settings' : html_settings,
'consult_external_groups' : _('You can consult the list of your external groups directly in the %(x_url_open)sgroups page%(x_url_close)s.') % {
'x_url_open' : '<a href="../yourgroups/display?ln=%s#external_groups">' % ln,
'x_url_close' : '</a>'
},
'external_user_groups' : _('External user groups'),
}
return out
def tmpl_user_preferences(self, ln, email, email_disabled, password_disabled, nickname):
"""
Displays a form for the user to change his email/password.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'email' *string* - The email of the user
- 'email_disabled' *boolean* - If the user has the right to edit his email
- 'password_disabled' *boolean* - If the user has the right to edit his password
- 'nickname' *string* - The nickname of the user (empty string if user does not have it)
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<p><big><strong class="headline">%(edit_params)s</strong></big></p>
<form method="post" action="%(sitesecureurl)s/youraccount/change" name="edit_logins_settings">
<p>%(change_user)s</p>
<table>
<tr><td align="right" valign="top"><strong>
<label for="nickname">%(nickname_label)s:</label></strong><br />
<small class="important">(%(mandatory)s)</small>
</td><td valign="top">
%(nickname_prefix)s%(nickname)s%(nickname_suffix)s<br />
<small><span class="quicknote">%(note)s:</span>
%(fixed_nickname_note)s
</small>
</td>
</tr>
<tr><td align="right"><strong>
<label for="email">%(new_email)s:</label></strong><br />
<small class="important">(%(mandatory)s)</small>
</td><td>
<input type="text" size="25" name="email" id="email" %(email_disabled)s value="%(email)s" /><br />
<small><span class="quicknote">%(example)s:</span>
<span class="example">john.doe@example.com</span>
</small>
</td>
</tr>
<tr><td></td><td align="left">
<code class="blocknote"><input class="formbutton" type="submit" value="%(set_values)s" /></code>&nbsp;&nbsp;&nbsp;
</td></tr>
</table>
<input type="hidden" name="action" value="edit" />
</form>
""" % {
'change_user' : _("If you want to change your email or set for the first time your nickname, please set new values in the form below."),
'edit_params' : _("Edit login credentials"),
'nickname_label' : _("Nickname"),
'nickname' : nickname,
'nickname_prefix' : nickname=='' and '<input type="text" size="25" name="nickname" id="nickname" value=""' or '',
'nickname_suffix' : nickname=='' and '" /><br /><small><span class="quicknote">'+_("Example")+':</span><span class="example">johnd</span></small>' or '',
'new_email' : _("New email address"),
'mandatory' : _("mandatory"),
'example' : _("Example"),
'note' : _("Note"),
'set_values' : _("Set new values"),
'email' : email,
'email_disabled' : email_disabled and "readonly" or "",
'sitesecureurl': CFG_SITE_SECURE_URL,
'fixed_nickname_note' : _('Since this is considered as a signature for comments and reviews, once set it can not be changed.')
}
if not password_disabled and not CFG_EXTERNAL_AUTH_USING_SSO:
out += """
<form method="post" action="%(sitesecureurl)s/youraccount/change" name="edit_password">
<p>%(change_pass)s</p>
<table>
<tr>
<td align="right"><strong><label for="old_password">%(old_password)s:</label></strong><br />
</td><td align="left">
<input type="password" size="25" name="old_password" id="old_password" %(password_disabled)s /><br />
<small><span class="quicknote">%(note)s:</span>
%(old_password_note)s
</small>
</td>
</tr>
<tr>
<td align="right"><strong><label for="new_password">%(new_password)s:</label></strong><br />
</td><td align="left">
<input type="password" size="25" name="password" id="new_password" %(password_disabled)s /><br />
<small><span class="quicknote">%(note)s:</span>
%(password_note)s
</small>
</td>
</tr>
<tr>
<td align="right"><strong><label for="new_password2">%(retype_password)s:</label></strong></td>
<td align="left">
<input type="password" size="25" name="password2" id="new_password2" %(password_disabled)s value="" />
</td>
</tr>
<tr><td></td><td align="left">
<code class="blocknote"><input class="formbutton" type="submit" value="%(set_values)s" /></code>&nbsp;&nbsp;&nbsp;
</td></tr>
</table>
<input type="hidden" name="action" value="edit" />
</form>
""" % {
'change_pass' : _("If you want to change your password, please enter the old one and set the new value in the form below."),
'mandatory' : _("mandatory"),
'old_password' : _("Old password"),
'new_password' : _("New password"),
'optional' : _("optional"),
'note' : _("Note"),
'password_note' : _("The password phrase may contain punctuation, spaces, etc."),
'old_password_note' : _("You must fill the old password in order to set a new one."),
'retype_password' : _("Retype password"),
'set_values' : _("Set new password"),
'password_disabled' : password_disabled and "disabled" or "",
'sitesecureurl': CFG_SITE_SECURE_URL,
}
elif not CFG_EXTERNAL_AUTH_USING_SSO and CFG_CERN_SITE:
out += "<p>" + _("""If you are using a lightweight CERN account you can
%(x_url_open)sreset the password%(x_url_close)s.""") % \
{'x_url_open' : \
'<a href="http://cern.ch/LightweightRegistration/ResetPassword.aspx%s">' \
% (make_canonical_urlargd({'email': email, 'returnurl' : CFG_SITE_SECURE_URL + '/youraccount/edit' + make_canonical_urlargd({'lang' : ln}, {})}, {})), 'x_url_close' : '</a>'} + "</p>"
elif CFG_EXTERNAL_AUTH_USING_SSO and CFG_CERN_SITE:
out += "<p>" + _("""You can change or reset your CERN account password by means of the %(x_url_open)sCERN account system%(x_url_close)s.""") % \
{'x_url_open' : '<a href="https://cern.ch/login/password.aspx">', 'x_url_close' : '</a>'} + "</p>"
return out
def tmpl_user_bibcatalog_auth(self, bibcatalog_username="", bibcatalog_password="", ln=CFG_SITE_LANG):
"""template for setting username and pw for bibcatalog backend"""
_ = gettext_set_language(ln)
out = """
<form method="post" action="%(sitesecureurl)s/youraccount/change" name="edit_bibcatalog_settings">
<p><big><strong class="headline">%(edit_bibcatalog_settings)s</strong></big></p>
<table>
<tr>
<td> %(username)s: <input type="text" size="25" name="bibcatalog_username" value="%(bibcatalog_username)s" id="bibcatuid"></td>
<td> %(password)s: <input type="password" size="25" name="bibcatalog_password" value="%(bibcatalog_password)s" id="bibcatpw"></td>
</tr>
<tr>
<td><input class="formbutton" type="submit" value="%(update_settings)s" /></td>
</tr>
</table>
""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'bibcatalog_username' : bibcatalog_username,
'bibcatalog_password' : bibcatalog_password,
'edit_bibcatalog_settings' : _("Edit cataloging interface settings"),
'username' : _("Username"),
'password' : _("Password"),
'update_settings' : _('Update settings')
}
return out
def tmpl_user_lang_edit(self, ln, preferred_lang):
_ = gettext_set_language(ln)
out = """
<form method="post" action="%(sitesecureurl)s/youraccount/change" name="edit_lang_settings">
<p><big><strong class="headline">%(edit_lang_settings)s</strong></big></p>
<table>
<tr><td align="right"><select name="lang" id="lang">
""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'edit_lang_settings' : _("Edit language-related settings"),
}
for short_ln, long_ln in language_list_long():
out += """<option %(selected)s value="%(short_ln)s">%(long_ln)s</option>""" % {
'selected' : preferred_lang == short_ln and 'selected="selected"' or '',
'short_ln' : short_ln,
'long_ln' : escape_html(long_ln)
}
out += """</select></td><td valign="top"><strong><label for="lang">%(select_lang)s</label></strong></td></tr>
<tr><td></td><td><input class="formbutton" type="submit" value="%(update_settings)s" /></td></tr>
</table></form>""" % {
'select_lang' : _('Select desired language of the web interface.'),
'update_settings' : _('Update settings')
}
return out
def tmpl_user_websearch_edit(self, ln, current = 10, show_latestbox = True, show_helpbox = True):
_ = gettext_set_language(ln)
out = """
<form method="post" action="%(sitesecureurl)s/youraccount/change" name="edit_websearch_settings">
<p><big><strong class="headline">%(edit_websearch_settings)s</strong></big></p>
<table>
<tr><td align="right"><input type="checkbox" %(checked_latestbox)s value="1" name="latestbox" id="latestbox"/></td>
<td valign="top"><b><label for="latestbox">%(show_latestbox)s</label></b></td></tr>
<tr><td align="right"><input type="checkbox" %(checked_helpbox)s value="1" name="helpbox" id="helpbox"/></td>
<td valign="top"><b><label for="helpbox">%(show_helpbox)s</label></b></td></tr>
<tr><td align="right"><select name="group_records" id="group_records">
""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'edit_websearch_settings' : _("Edit search-related settings"),
'show_latestbox' : _("Show the latest additions box"),
'checked_latestbox' : show_latestbox and 'checked="checked"' or '',
'show_helpbox' : _("Show collection help boxes"),
'checked_helpbox' : show_helpbox and 'checked="checked"' or '',
}
for i in 10, 25, 50, 100, 250, 500:
if i <= CFG_WEBSEARCH_MAX_RECORDS_IN_GROUPS:
out += """<option %(selected)s>%(i)s</option>
""" % {
'selected' : current == i and 'selected="selected"' or '',
'i' : i
}
out += """</select></td><td valign="top"><strong><label for="group_records">%(select_group_records)s</label></strong></td></tr>
<tr><td></td><td><input class="formbutton" type="submit" value="%(update_settings)s" /></td></tr>
</table>
</form>""" % {
'update_settings' : _("Update settings"),
'select_group_records' : _("Number of search results per page"),
}
return out
def tmpl_user_external_auth(self, ln, methods, current, method_disabled):
"""
Displays a form for the user to change his authentication method.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'methods' *array* - The methods of authentication
- 'method_disabled' *boolean* - If the user has the right to change this
- 'current' *string* - The currently selected method
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<form method="post" action="%(sitesecureurl)s/youraccount/change">
<big><strong class="headline">%(edit_method)s</strong></big>
<p>%(explain_method)s:</p>
<table>
<tr><td valign="top"><b>%(select_method)s:</b></td><td>
""" % {
'edit_method' : _("Edit login method"),
'explain_method' : _("Please select which login method you would like to use to authenticate yourself"),
'select_method' : _("Select method"),
'sitesecureurl': CFG_SITE_SECURE_URL,
}
for system in methods:
out += """<input type="radio" name="login_method" value="%(system)s" id="%(id)s" %(disabled)s %(selected)s /><label for="%(id)s">%(system)s</label><br />""" % {
'system' : system,
'disabled' : method_disabled and 'disabled="disabled"' or "",
'selected' : current == system and 'checked="checked"' or "",
'id' : nmtoken_from_string(system),
}
out += """ </td></tr>
<tr><td>&nbsp;</td>
<td><input class="formbutton" type="submit" value="%(select_method)s" /></td></tr></table>
</form>""" % {
'select_method' : _("Select method"),
}
return out
def tmpl_lost_password_form(self, ln):
"""
Displays a form for the user to ask for his password sent by email.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'msg' *string* - Explicative message on top of the form.
"""
# load the right message language
_ = gettext_set_language(ln)
out = "<p>" + _("If you have lost the password for your %(sitename)s %(x_fmt_open)sinternal account%(x_fmt_close)s, then please enter your email address in the following form in order to have a password reset link emailed to you.") % {'x_fmt_open' : '<em>', 'x_fmt_close' : '</em>', 'sitename' : CFG_SITE_NAME_INTL[ln]} + "</p>"
out += """
<blockquote>
<form method="post" action="../youraccount/send_email">
<table>
<tr>
<td align="right"><strong><label for="p_email">%(email)s:</label></strong></td>
<td><input type="text" size="25" name="p_email" id="p_email" value="" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="action" value="lost" />
</td>
</tr>
<tr><td>&nbsp;</td>
<td><code class="blocknote"><input class="formbutton" type="submit" value="%(send)s" /></code></td>
</tr>
</table>
</form>
</blockquote>
""" % {
'ln': ln,
'email' : _("Email address"),
'send' : _("Send password reset link"),
}
if CFG_CERN_SITE:
out += "<p>" + _("If you have been using the %(x_fmt_open)sCERN login system%(x_fmt_close)s, then you can recover your password through the %(x_url_open)sCERN authentication system%(x_url_close)s.") % {'x_fmt_open' : '<em>', 'x_fmt_close' : '</em>', 'x_url_open' : '<a href="https://cern.ch/lightweightregistration/ResetPassword.aspx%s">' \
% make_canonical_urlargd({'lf': 'auth', 'returnURL' : CFG_SITE_SECURE_URL + '/youraccount/login?ln='+ln}, {}), 'x_url_close' : '</a>'} + " "
else:
out += "<p>" + _("Note that if you have been using an external login system, then we cannot do anything and you have to ask there.") + " "
out += _("Alternatively, you can ask %s to change your login system from external to internal.") % ("""<a href="mailto:%(email)s">%(email)s</a>""" % { 'email' : CFG_SITE_SUPPORT_EMAIL }) + "</p>"
return out
def tmpl_account_info(self, ln, uid, guest, CFG_CERN_SITE):
"""
Displays the account information
Parameters:
- 'ln' *string* - The language to display the interface in
- 'uid' *string* - The user id
- 'guest' *boolean* - If the user is guest
- 'CFG_CERN_SITE' *boolean* - If the site is a CERN site
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<p>%(account_offer)s</p>
<blockquote>
<dl>
""" % {
'account_offer' : _("%s offers you the possibility to personalize the interface, to set up your own personal library of documents, or to set up an automatic alert query that would run periodically and would notify you of search results by email.") % CFG_SITE_NAME_INTL[ln],
}
if not guest:
out += """
<dt>
<a href="./edit?ln=%(ln)s">%(your_settings)s</a>
</dt>
<dd>%(change_account)s</dd>""" % {
'ln' : ln,
'your_settings' : _("Your Settings"),
'change_account' : _("Set or change your account email address or password. Specify your preferences about the look and feel of the interface.")
}
out += """
<dt><a href="../youralerts/display?ln=%(ln)s">%(your_searches)s</a></dt>
<dd>%(search_explain)s</dd>""" % {
'ln' : ln,
'your_searches' : _("Your Searches"),
'search_explain' : _("View all the searches you performed during the last 30 days."),
}
out += """
<dt><a href="../yourbaskets/display?ln=%(ln)s">%(your_baskets)s</a></dt>
<dd>%(basket_explain)s""" % {
'ln' : ln,
'your_baskets' : _("Your Baskets"),
'basket_explain' : _("With baskets you can define specific collections of items, store interesting records you want to access later or share with others."),
}
if guest and CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
out += self.tmpl_warning_guest_user(ln = ln, type = "baskets")
out += """</dd>
<dt><a href="../youralerts/list?ln=%(ln)s">%(your_alerts)s</a></dt>
<dd>%(explain_alerts)s""" % {
'ln' : ln,
'your_alerts' : _("Your Alerts"),
'explain_alerts' : _("Subscribe to a search which will be run periodically by our service. The result can be sent to you via Email or stored in one of your baskets."),
}
if guest and CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
out += self.tmpl_warning_guest_user(type="alerts", ln = ln)
out += "</dd>"
if CFG_CERN_SITE:
out += """</dd>
<dt><a href="http://weblib.cern.ch/cgi-bin/checkloan?uid=&amp;version=2">%(your_loans)s</a></dt>
<dd>%(explain_loans)s</dd>""" % {
'your_loans' : _("Your Loans"),
'explain_loans' : _("Check out book you have on loan, submit borrowing requests, etc. Requires CERN ID."),
}
out += """
</dl>
</blockquote>"""
return out
def tmpl_warning_guest_user(self, ln, type):
"""
Displays a warning message about the specified type
Parameters:
- 'ln' *string* - The language to display the interface in
- 'type' *string* - The type of data that will get lost in case of guest account (for the moment: 'alerts' or 'baskets')
"""
# load the right message language
_ = gettext_set_language(ln)
if (type=='baskets'):
msg = _("You are logged in as a guest user, so your baskets will disappear at the end of the current session.") + ' '
elif (type=='alerts'):
msg = _("You are logged in as a guest user, so your alerts will disappear at the end of the current session.") + ' '
msg += _("If you wish you can %(x_url_open)slogin or register here%(x_url_close)s.") % {'x_url_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/login?ln=' + ln + '">',
'x_url_close': '</a>'}
return """<table class="errorbox" summary="">
<tr>
<th class="errorboxheader">%s</th>
</tr>
</table>""" % msg
def tmpl_account_body(self, ln, user):
"""
Displays the body of the actions of the user
Parameters:
- 'ln' *string* - The language to display the interface in
- 'user' *string* - The username (nickname or email)
"""
# load the right message language
_ = gettext_set_language(ln)
out = _("You are logged in as %(x_user)s. You may want to a) %(x_url1_open)slogout%(x_url1_close)s; b) edit your %(x_url2_open)saccount settings%(x_url2_close)s.") %\
{'x_user': user,
'x_url1_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/logout?ln=' + ln + '">',
'x_url1_close': '</a>',
'x_url2_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/edit?ln=' + ln + '">',
'x_url2_close': '</a>',
}
return out + "<br /><br />"
def tmpl_account_template(self, title, body, ln, url):
"""
Displays a block of the your account page
Parameters:
- 'ln' *string* - The language to display the interface in
- 'title' *string* - The title of the block
- 'body' *string* - The body of the block
- 'url' *string* - The URL to go to the proper section
"""
out ="""
<table class="youraccountbox" width="90%%" summary="" >
<tr>
<th class="youraccountheader"><a href="%s">%s</a></th>
</tr>
<tr>
<td class="youraccountbody">%s</td>
</tr>
</table>""" % (url, title, body)
return out
def tmpl_account_page(self, ln, warnings, warning_list, accBody, baskets, alerts, searches, messages, loans, groups, submissions, approvals, tickets, administrative):
"""
Displays the your account page
Parameters:
- 'ln' *string* - The language to display the interface in
- 'accBody' *string* - The body of the heading block
- 'baskets' *string* - The body of the baskets block
- 'alerts' *string* - The body of the alerts block
- 'searches' *string* - The body of the searches block
- 'messages' *string* - The body of the messages block
- 'groups' *string* - The body of the groups block
- 'submissions' *string* - The body of the submission block
- 'approvals' *string* - The body of the approvals block
- 'administrative' *string* - The body of the administrative block
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if warnings == "1":
out += self.tmpl_general_warnings(warning_list)
out += self.tmpl_account_template(_("Your Account"), accBody, ln, '/youraccount/edit?ln=%s' % ln)
if messages:
out += self.tmpl_account_template(_("Your Messages"), messages, ln, '/yourmessages/display?ln=%s' % ln)
if loans:
out += self.tmpl_account_template(_("Your Loans"), loans, ln, '/yourloans/display?ln=%s' % ln)
if baskets:
out += self.tmpl_account_template(_("Your Baskets"), baskets, ln, '/yourbaskets/display?ln=%s' % ln)
if alerts:
out += self.tmpl_account_template(_("Your Alert Searches"), alerts, ln, '/youralerts/list?ln=%s' % ln)
if searches:
out += self.tmpl_account_template(_("Your Searches"), searches, ln, '/youralerts/display?ln=%s' % ln)
if groups:
groups_description = _("You can consult the list of %(x_url_open)syour groups%(x_url_close)s you are administering or are a member of.")
groups_description %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yourgroups/display?ln=' + ln + '">',
'x_url_close': '</a>'}
out += self.tmpl_account_template(_("Your Groups"), groups_description, ln, '/yourgroups/display?ln=%s' % ln)
if submissions:
submission_description = _("You can consult the list of %(x_url_open)syour submissions%(x_url_close)s and inquire about their status.")
submission_description %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yoursubmissions.py?ln=' + ln + '">',
'x_url_close': '</a>'}
out += self.tmpl_account_template(_("Your Submissions"), submission_description, ln, '/yoursubmissions.py?ln=%s' % ln)
if approvals:
approval_description = _("You can consult the list of %(x_url_open)syour approvals%(x_url_close)s with the documents you approved or refereed.")
approval_description %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yourapprovals.py?ln=' + ln + '">',
'x_url_close': '</a>'}
out += self.tmpl_account_template(_("Your Approvals"), approval_description, ln, '/yourapprovals.py?ln=%s' % ln)
#check if this user might have tickets
if tickets:
ticket_description = _("You can consult the list of %(x_url_open)syour tickets%(x_url_close)s.")
ticket_description %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yourtickets?ln=' + ln + '">',
'x_url_close': '</a>'}
out += self.tmpl_account_template(_("Your Tickets"), ticket_description, ln, '/yourtickets?ln=%s' % ln)
if administrative:
out += self.tmpl_account_template(_("Your Administrative Activities"), administrative, ln, '/admin')
return out
def tmpl_account_emailMessage(self, ln, msg):
"""
Displays a link to retrieve the lost password
Parameters:
- 'ln' *string* - The language to display the interface in
- 'msg' *string* - Explicative message on top of the form.
"""
# load the right message language
_ = gettext_set_language(ln)
out =""
out +="""
<body>
%(msg)s <a href="../youraccount/lost?ln=%(ln)s">%(try_again)s</a>
</body>
""" % {
'ln' : ln,
'msg' : msg,
'try_again' : _("Try again")
}
return out
def tmpl_account_reset_password_email_body(self, email, reset_key, ip_address, ln=CFG_SITE_LANG):
"""
The body of the email that sends lost internal account
passwords to users.
"""
_ = gettext_set_language(ln)
out = """
%(intro)s
%(intro2)s
<%(link)s>
%(outro)s
%(outro2)s""" % {
'intro': _("Somebody (possibly you) coming from %(x_ip_address)s "
"has asked\nfor a password reset at %(x_sitename)s\nfor "
"the account \"%(x_email)s\"." % {
'x_sitename' :CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
'x_email' : email,
'x_ip_address' : ip_address,
}
),
'intro2' : _("If you want to reset the password for this account, please go to:"),
'link' : "%s/youraccount/access%s" %
(CFG_SITE_SECURE_URL, make_canonical_urlargd({
'ln' : ln,
'mailcookie' : reset_key
}, {})),
'outro' : _("in order to confirm the validity of this request."),
'outro2' : _("Please note that this URL will remain valid for about %(days)s days only.") % {'days': CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS},
}
return out
def tmpl_account_address_activation_email_body(self, email, address_activation_key, ip_address, ln=CFG_SITE_LANG):
"""
The body of the email that sends email address activation cookie
passwords to users.
"""
_ = gettext_set_language(ln)
out = """
%(intro)s
%(intro2)s
<%(link)s>
%(outro)s
%(outro2)s""" % {
'intro': _("Somebody (possibly you) coming from %(x_ip_address)s "
"has asked\nto register a new account at %(x_sitename)s\nfor the "
"email address \"%(x_email)s\"." % {
'x_sitename' :CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
'x_email' : email,
'x_ip_address' : ip_address,
}
),
'intro2' : _("If you want to complete this account registration, please go to:"),
'link' : "%s/youraccount/access%s" %
(CFG_SITE_SECURE_URL, make_canonical_urlargd({
'ln' : ln,
'mailcookie' : address_activation_key
}, {})),
'outro' : _("in order to confirm the validity of this request."),
'outro2' : _("Please note that this URL will remain valid for about %(days)s days only.") % {'days' : CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS},
}
return out
def tmpl_account_emailSent(self, ln, email):
"""
Displays a confirmation message for an email sent
Parameters:
- 'ln' *string* - The language to display the interface in
- 'email' *string* - The email to which the message has been sent
"""
# load the right message language
_ = gettext_set_language(ln)
out =""
out += _("Okay, a password reset link has been emailed to %s.") % email
return out
def tmpl_account_delete(self, ln):
"""
Displays a confirmation message about deleting the account
Parameters:
- 'ln' *string* - The language to display the interface in
"""
# load the right message language
_ = gettext_set_language(ln)
out = "<p>" + _("""Deleting your account""") + '</p>'
return out
def tmpl_account_logout(self, ln):
"""
Displays a confirmation message about logging out
Parameters:
- 'ln' *string* - The language to display the interface in
"""
# load the right message language
_ = gettext_set_language(ln)
out = _("You are no longer recognized by our system.") + ' '
if CFG_EXTERNAL_AUTH_USING_SSO and CFG_EXTERNAL_AUTH_LOGOUT_SSO:
out += _("""You are still recognized by the centralized
%(x_fmt_open)sSSO%(x_fmt_close)s system. You can
%(x_url_open)slogout from SSO%(x_url_close)s, too.""") % \
{'x_fmt_open' : '<strong>', 'x_fmt_close' : '</strong>',
'x_url_open' : '<a href="%s">' % CFG_EXTERNAL_AUTH_LOGOUT_SSO,
'x_url_close' : '</a>'}
out += '<br />'
out += _("If you wish you can %(x_url_open)slogin here%(x_url_close)s.") % \
{'x_url_open': '<a href="./login?ln=' + ln + '">',
'x_url_close': '</a>'}
return out
def tmpl_login_form(self, ln, referer, internal, register_available, methods, selected_method, msg=None):
"""
Displays a login form
Parameters:
- 'ln' *string* - The language to display the interface in
- 'referer' *string* - The referer URL - will be redirected upon after login
- 'internal' *boolean* - If we are producing an internal authentication
- 'register_available' *boolean* - If users can register freely in the system
- 'methods' *array* - The available authentication methods
- 'selected_method' *string* - The default authentication method
- 'msg' *string* - The message to print before the form, if needed
"""
# load the right message language
_ = gettext_set_language(ln)
if msg is "":
out = "<p>%(please_login)s</p>" % {
'please_login' : _("If you already have an account, please login using the form below.")
}
if CFG_CERN_SITE:
out += "<p>" + _("If you don't own a CERN account yet, you can register a %(x_url_open)snew CERN lightweight account%(x_url_close)s.") % {'x_url_open' : '<a href="https://www.cern.ch/lightweightregistration/RegisterAccount.aspx">', 'x_url_close' : '</a>'} + "</p>"
else:
if register_available:
out += "<p>"+_("If you don't own an account yet, please %(x_url_open)sregister%(x_url_close)s an internal account.") %\
{'x_url_open': '<a href="../youraccount/register?ln=' + ln + '">',
'x_url_close': '</a>'} + "</p>"
else:
# users cannot register accounts, so advise them
# how to get one, or be silent about register
# facility if account level is more than 4:
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 5:
out += "<p>" + _("If you don't own an account yet, please contact %s.") % ('<a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL)) + "</p>"
else:
out = "<p>%s</p>" % msg
out += """<form method="post" action="../youraccount/login">
<table>
"""
if len(methods) > 1:
# more than one method, must make a select
login_select = """<select name="login_method" id="login_method">"""
for method in methods:
login_select += """<option value="%(method)s" %(selected)s>%(method)s</option>""" % {
'method' : method,
'selected' : (method == selected_method and 'selected="selected"' or "")
}
login_select += "</select>"
out += """
<tr>
<td align="right"><strong><label for="login_method">%(login_title)s</label></strong></td>
<td>%(login_select)s</td>
</tr>""" % {
'login_title' : _("Login method:"),
'login_select' : login_select,
}
else:
# only one login method available
out += """<input type="hidden" name="login_method" value="%s" />""" % (methods[0])
out += """<tr>
<td align="right">
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="referer" value="%(referer)s" />
<strong><label for="p_un">%(username)s:</label></strong>
</td>
<td><input type="text" size="25" name="p_un" id="p_un" value="" /></td>
</tr>
<tr>
<td align="right"><strong><label for="p_pw">%(password)s:</label></strong></td>
<td align="left"><input type="password" size="25" name="p_pw" id="p_pw" value="" /></td>
</tr>
<tr>
<td></td>
<td align="left"><input type="checkbox" name="remember_me" id="remember_me"/><em><label for="remember_me">%(remember_me)s</label></em></td>
<tr>
<td></td>
<td align="center" colspan="3"><code class="blocknote"><input class="formbutton" type="submit" name="action" value="%(login)s" /></code>""" % {
'ln': ln,
'referer' : cgi.escape(referer),
'username' : _("Username"),
'password' : _("Password"),
'remember_me' : _("Remember login on this computer."),
'login' : _("login"),
}
if internal:
out += """&nbsp;&nbsp;&nbsp;(<a href="./lost?ln=%(ln)s">%(lost_pass)s</a>)""" % {
'ln' : ln,
'lost_pass' : _("Lost your password?")
}
out += """</td>
</tr>
</table></form>"""
out += """<p><strong>%(note)s:</strong> %(note_text)s</p>""" % {
'note' : _("Note"),
'note_text': _("You can use your nickname or your email address to login.")}
return out
def tmpl_lost_your_password_teaser(self, ln=CFG_SITE_LANG):
"""Displays a short sentence to attract user to the fact that
maybe he lost his password. Used by the registration page.
"""
_ = gettext_set_language(ln)
out = ""
out += """<a href="./lost?ln=%(ln)s">%(maybe_lost_pass)s</a>""" % {
'ln' : ln,
'maybe_lost_pass': ("Maybe you have lost your password?")
}
return out
def tmpl_reset_password_form(self, ln, email, reset_key, msg=''):
"""Display a form to reset the password."""
_ = gettext_set_language(ln)
out = ""
out = "<p>%s</p>" % _("Your request is valid. Please set the new "
"desired password in the following form.")
if msg:
out += """<p class='warning'>%s</p>""" % msg
out += """
<form method="post" action="../youraccount/resetpassword?ln=%(ln)s">
<input type="hidden" name="k" value="%(reset_key)s" />
<input type="hidden" name="e" value="%(email)s" />
<input type="hidden" name="reset" value="1" />
<table>
<tr><td align="right"><strong>%(set_password_for)s</strong>:</td><td><em>%(email)s</em></td></tr>
<tr><td align="right"><strong><label for="password">%(type_new_password)s:</label></strong></td>
<td><input type="password" name="password" id="password" value="123" /></td></tr>
<tr><td align="right"><strong><label for="password2">%(type_it_again)s:</label></strong></td>
<td><input type="password" name="password2" id="password2" value="" /></td></tr>
<tr><td align="center" colspan="2">
<input class="formbutton" type="submit" name="action" value="%(set_new_password)s" />
</td></tr>
</table>
</form>""" % {
'ln' : ln,
'reset_key' : reset_key,
'email' : email,
'set_password_for' : _('Set a new password for'),
'type_new_password' : _('Type the new password'),
'type_it_again' : _('Type again the new password'),
'set_new_password' : _('Set the new password')
}
return out
def tmpl_register_page(self, ln, referer, level):
"""
Displays a login form
Parameters:
- 'ln' *string* - The language to display the interface in
- 'referer' *string* - The referer URL - will be redirected upon after login
- 'level' *int* - Login level (0 - all access, 1 - accounts activated, 2+ - no self-registration)
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if level <= 1:
out += _("Please enter your email address and desired nickname and password:")
if level == 1:
out += _("It will not be possible to use the account before it has been verified and activated.")
out += """
<form method="post" action="../youraccount/register">
<input type="hidden" name="referer" value="%(referer)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<table>
<tr>
<td align="right"><strong><label for="p_email">%(email_address)s:</label></strong><br /><small class="important">(%(mandatory)s)</small></td>
<td><input type="text" size="25" name="p_email" id="p_email" value="" /><br />
<small><span class="quicknote">%(example)s:</span>
<span class="example">john.doe@example.com</span></small>
</td>
<td></td>
</tr>
<tr>
<td align="right"><strong><label for="p_nickname">%(nickname)s:</label></strong><br /><small class="important">(%(mandatory)s)</small></td>
<td><input type="text" size="25" name="p_nickname" id="p_nickname" value="" /><br />
<small><span class="quicknote">%(example)s:</span>
<span class="example">johnd</span></small>
</td>
<td></td>
</tr>
<tr>
<td align="right"><strong><label for="p_pw">%(password)s:</label></strong><br /><small class="quicknote">(%(optional)s)</small></td>
<td align="left"><input type="password" size="25" name="p_pw" id="p_pw" value="" /><br />
<small><span class="quicknote">%(note)s:</span> %(password_contain)s</small>
</td>
<td></td>
</tr>
<tr>
<td align="right"><strong><label for="p_pw2">%(retype)s:</label></strong></td>
<td align="left"><input type="password" size="25" name="p_pw2" id="p_pw2" value="" /></td>
<td></td>
</tr>
<tr>
<td></td>
<td align="left" colspan="3"><code class="blocknote"><input class="formbutton" type="submit" name="action" value="%(register)s" /></code></td>
</tr>
</table>
</form>
<p><strong>%(note)s:</strong> %(explain_acc)s""" % {
'referer' : cgi.escape(referer),
'ln' : cgi.escape(ln),
'email_address' : _("Email address"),
'nickname' : _("Nickname"),
'password' : _("Password"),
'mandatory' : _("mandatory"),
'optional' : _("optional"),
'example' : _("Example"),
'note' : _("Note"),
'password_contain' : _("The password phrase may contain punctuation, spaces, etc."),
'retype' : _("Retype Password"),
'register' : _("register"),
'explain_acc' : _("Please do not use valuable passwords such as your Unix, AFS or NICE passwords with this service. Your email address will stay strictly confidential and will not be disclosed to any third party. It will be used to identify you for personal services of %s. For example, you may set up an automatic alert search that will look for new preprints and will notify you daily of new arrivals by email.") % CFG_SITE_NAME,
}
else:
# level >=2, so users cannot register accounts
out += "<p>" + _("It is not possible to create an account yourself. Contact %s if you want an account.") % ('<a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL)) + "</p>"
return out
def tmpl_account_adminactivities(self, ln, uid, guest, roles, activities):
"""
Displays the admin activities block for this user
Parameters:
- 'ln' *string* - The language to display the interface in
- 'uid' *string* - The used id
- 'guest' *boolean* - If the user is guest
- 'roles' *array* - The current user roles
- 'activities' *array* - The user allowed activities
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
# guest condition
if guest:
return _("You seem to be a guest user. You have to %(x_url_open)slogin%(x_url_close)s first.") % \
{'x_url_open': '<a href="../youraccount/login?ln=' + ln + '">',
'x_url_close': '<a/>'}
# no rights condition
if not roles:
return "<p>" + _("You are not authorized to access administrative functions.") + "</p>"
# displaying form
out += "<p>" + _("You are enabled to the following roles: %(x_role)s.") % {'x_role': ('<em>' + ", ".join(roles) + "</em>")} + '</p>'
if activities:
# print proposed links:
activities.sort(lambda x, y: cmp(x.lower(), y.lower()))
tmp_out = ''
for action in activities:
if action == "runbibedit":
- tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/record/edit/">%s</a>""" % (CFG_SITE_URL, _("Run Record Editor"))
+ tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/%s/edit/">%s</a>""" % (CFG_SITE_URL, CFG_SITE_RECORD, _("Run Record Editor"))
if action == "runbibeditmulti":
- tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/record/multiedit/">%s</a>""" % (CFG_SITE_URL, _("Run Multi-Record Editor"))
+ tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/%s/multiedit/">%s</a>""" % (CFG_SITE_URL, CFG_SITE_RECORD, _("Run Multi-Record Editor"))
if action == "runbibcirculation":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibcirculation/bibcirculationadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Run BibCirculation"))
if action == "runbibmerge":
- tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/record/merge/">%s</a>""" % (CFG_SITE_URL, _("Run Record Merger"))
+ tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/%s/merge/">%s</a>""" % (CFG_SITE_URL, CFG_SITE_RECORD, _("Run Record Merger"))
if action == "runbibswordclient":
- tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/record/bibsword/">%s</a>""" % (CFG_SITE_URL, _("Run BibSword Client"))
+ tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/%s/bibsword/">%s</a>""" % (CFG_SITE_URL, CFG_SITE_RECORD, _("Run BibSword Client"))
if action == "runbatchuploader":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/batchuploader/metadata?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Run Batch Uploader"))
if action == "cfgbibformat":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibformat/bibformatadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure BibFormat"))
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/kb?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure BibKnowledge"))
if action == "cfgoaiharvest":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibharvest/oaiharvestadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure OAI Harvest"))
if action == "cfgoairepository":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibharvest/oairepositoryadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure OAI Repository"))
if action == "cfgbibindex":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibindex/bibindexadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure BibIndex"))
if action == "cfgbibrank":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibrank/bibrankadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure BibRank"))
if action == "cfgwebaccess":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/webaccess/webaccessadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebAccess"))
if action == "cfgwebcomment":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/webcomment/webcommentadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebComment"))
if action == "cfgwebjournal":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/webjournal/webjournaladmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebJournal"))
if action == "cfgwebsearch":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/websearch/websearchadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebSearch"))
if action == "cfgwebsubmit":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/websubmit/websubmitadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebSubmit"))
if action == "runbibdocfile":
tmp_out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/submit/managedocfiles?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Run Document File Manager"))
if tmp_out:
out += _("Here are some interesting web admin links for you:") + tmp_out
out += "<br />" + _("For more admin-level activities, see the complete %(x_url_open)sAdmin Area%(x_url_close)s.") %\
{'x_url_open': '<a href="' + CFG_SITE_URL + '/help/admin?ln=' + ln + '">',
'x_url_close': '</a>'}
return out
def tmpl_create_userinfobox(self, ln, url_referer, guest, username, submitter, referee, admin, usebaskets, usemessages, usealerts, usegroups, useloans, usestats):
"""
Displays the user block
Parameters:
- 'ln' *string* - The language to display the interface in
- 'url_referer' *string* - URL of the page being displayed
- 'guest' *boolean* - If the user is guest
- 'username' *string* - The username (nickname or email)
- 'submitter' *boolean* - If the user is submitter
- 'referee' *boolean* - If the user is referee
- 'admin' *boolean* - If the user is admin
- 'usebaskets' *boolean* - If baskets are enabled for the user
- 'usemessages' *boolean* - If messages are enabled for the user
- 'usealerts' *boolean* - If alerts are enabled for the user
- 'usegroups' *boolean* - If groups are enabled for the user
- 'useloans' *boolean* - If loans are enabled for the user
- 'usestats' *boolean* - If stats are enabled for the user
@note: with the update of CSS classes (cds.cds ->
invenio.css), the variables useloans etc are not used in
this function, since they are in the menus. But we keep
them in the function signature for backwards
compatibility.
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<img src="%s/img/user-icon-1-20x20.gif" border="0" alt=""/> """ % CFG_SITE_URL
if guest:
out += """%(guest_msg)s ::
<a class="userinfo" href="%(sitesecureurl)s/youraccount/login?ln=%(ln)s%(referer)s">%(login)s</a>""" % {
'sitesecureurl': CFG_SITE_SECURE_URL,
'ln' : ln,
'guest_msg' : _("guest"),
'referer' : url_referer and ('&amp;referer=%s' % urllib.quote(url_referer)) or '',
'login' : _('login')
}
else:
out += """
<a class="userinfo" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(username)s</a> :: """ % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln' : ln,
'username' : username
}
out += """<a class="userinfo" href="%(sitesecureurl)s/youraccount/logout?ln=%(ln)s">%(logout)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln' : ln,
'logout' : _("logout"),
}
return out
def tmpl_create_useractivities_menu(self, ln, selected, url_referer, guest, username, submitter, referee, admin, usebaskets, usemessages, usealerts, usegroups, useloans, usestats):
"""
Returns the main navigation menu with actions based on user's
priviledges
@param ln: The language to display the interface in
@type ln: string
@param selected: If the menu is currently selected
@type selected: boolean
@param url_referer: URL of the page being displayed
@type url_referer: string
@param guest: If the user is guest
@type guest: string
@param username: The username (nickname or email)
@type username: string
@param submitter: If the user is submitter
@type submitter: boolean
@param referee: If the user is referee
@type referee: boolean
@param admin: If the user is admin
@type admin: boolean
@param usebaskets: If baskets are enabled for the user
@type usebaskets: boolean
@param usemessages: If messages are enabled for the user
@type usemessages: boolean
@param usealerts: If alerts are enabled for the user
@type usealerts: boolean
@param usegroups: If groups are enabled for the user
@type usegroups: boolean
@param useloans: If loans are enabled for the user
@type useloans: boolean
@param usestats: If stats are enabled for the user
@type usestats: boolean
@return: html menu of the user activities
@rtype: string
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''<div class="hassubmenu%(on)s">
<a hreflang="en" class="header%(selected)s" href="%(CFG_SITE_SECURE_URL)s/youraccount/display?ln=%(ln)s">%(personalize)s</a>
<ul class="subsubmenu" style="width: 13em;">''' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'personalize': _("Personalize"),
'on': selected and " on" or '',
'selected': selected and "selected" or ''
}
if not guest:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/youraccount/display?ln=%(ln)s">%(account)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'account' : _('Your account')
}
if usealerts or guest:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/youralerts/list?ln=%(ln)s">%(alerts)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'alerts' : _('Your alerts')
}
if referee:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/yourapprovals.py?ln=%(ln)s">%(approvals)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'approvals' : _('Your approvals')
}
if usebaskets or guest:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/yourbaskets/display?ln=%(ln)s">%(baskets)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'baskets' : _('Your baskets')
}
if usegroups:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/yourgroups/display?ln=%(ln)s">%(groups)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'groups' : _('Your groups')
}
if useloans:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/yourloans/display?ln=%(ln)s">%(loans)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'loans' : _('Your loans')
}
if usemessages:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/yourmessages/display?ln=%(ln)s">%(messages)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'messages' : _('Your messages')
}
if submitter:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/yoursubmissions.py?ln=%(ln)s">%(submissions)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'submissions' : _('Your submissions')
}
if usealerts or guest:
out += '<li><a href="%(CFG_SITE_SECURE_URL)s/youralerts/display?ln=%(ln)s">%(searches)s</a></li>' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'searches' : _('Your searches')
}
out += '</ul></div>'
return out
def tmpl_create_adminactivities_menu(self, ln, selected, url_referer, guest, username, submitter, referee, admin, usebaskets, usemessages, usealerts, usegroups, useloans, usestats, activities):
"""
Returns the main navigation menu with actions based on user's
priviledges
@param ln: The language to display the interface in
@type ln: string
@param selected: If the menu is currently selected
@type selected: boolean
@param url_referer: URL of the page being displayed
@type url_referer: string
@param guest: If the user is guest
@type guest: string
@param username: The username (nickname or email)
@type username: string
@param submitter: If the user is submitter
@type submitter: boolean
@param referee: If the user is referee
@type referee: boolean
@param admin: If the user is admin
@type admin: boolean
@param usebaskets: If baskets are enabled for the user
@type usebaskets: boolean
@param usemessages: If messages are enabled for the user
@type usemessages: boolean
@param usealerts: If alerts are enabled for the user
@type usealerts: boolean
@param usegroups: If groups are enabled for the user
@type usegroups: boolean
@param useloans: If loans are enabled for the user
@type useloans: boolean
@param usestats: If stats are enabled for the user
@type usestats: boolean
@param activities: dictionary of admin activities
@rtype activities: dict
@return: html menu of the user activities
@rtype: string
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if activities:
out += '''<div class="hassubmenu%(on)s">
<a hreflang="en" class="header%(selected)s" href="%(CFG_SITE_SECURE_URL)s/youraccount/youradminactivities?ln=%(ln)s">%(admin)s</a>
<ul class="subsubmenu" style="width: 19em;">''' % {
'CFG_SITE_SECURE_URL' : CFG_SITE_SECURE_URL,
'ln' : ln,
'admin': _("Administration"),
'on': selected and " on" or '',
'selected': selected and "selected" or ''
}
for name in sorted(activities.iterkeys()):
url = activities[name]
out += '<li><a href="%(url)s">%(name)s</a></li>' % {
'url': url,
'name': name
}
if usestats:
out += """<li><a href="%(CFG_SITE_URL)s/stats/?ln=%(ln)s">%(stats)s</a></li>""" % {
'CFG_SITE_URL' : CFG_SITE_URL,
'ln' : ln,
'stats' : _("Statistics"),
}
out += '</ul></div>'
return out
def tmpl_warning(self, warnings, ln=CFG_SITE_LANG):
"""
Prepare the warnings list
@param warnings: list of warning tuples (warning_msg, arg1, arg2, etc)
@return: html string of warnings
"""
from invenio.errorlib import get_msgs_for_code_list
span_class = 'important'
out = ""
if type(warnings) is not list:
warnings = [warnings]
if len(warnings) > 0:
warnings_parsed = get_msgs_for_code_list(warnings, 'warning', ln)
for (warning_code, warning_text) in warnings_parsed:
if not warning_code.startswith('WRN'):
#display only warnings that begin with WRN to user
continue
span_class = 'important'
out += '''
<span class="%(span_class)s">%(warning)s</span><br />''' % \
{ 'span_class' : span_class,
'warning' : warning_text }
return out
else:
return ""
def tmpl_warnings(self, warnings, ln=CFG_SITE_LANG):
"""
Display len(warnings) warning fields
@param infos: list of strings
@param ln=language
@return: html output
"""
if not((type(warnings) is list) or (type(warnings) is tuple)):
warnings = [warnings]
warningbox = ""
if warnings != []:
warningbox = "<div class=\"warningbox\">\n <b>Warning:</b>\n"
for warning in warnings:
lines = warning.split("\n")
warningbox += " <p>"
for line in lines[0:-1]:
warningbox += line + " <br />\n"
warningbox += lines[-1] + " </p>"
warningbox += "</div><br />\n"
return warningbox
def tmpl_display_all_groups(self,
infos,
admin_group_html,
member_group_html,
external_group_html = None,
warnings=[],
ln=CFG_SITE_LANG):
"""
Displays the 3 tables of groups: admin, member and external
Parameters:
- 'ln' *string* - The language to display the interface in
- 'admin_group_html' *string* - HTML code for displaying all the groups
the user is the administrator of
- 'member_group_html' *string* - HTML code for displaying all the groups
the user is member of
- 'external_group_html' *string* - HTML code for displaying all the
external groups the user is member of
"""
_ = gettext_set_language(ln)
group_text = self.tmpl_infobox(infos)
group_text += self.tmpl_warning(warnings)
if external_group_html:
group_text += """
<table>
<tr>
<td>%s</td>
</tr>
<tr>
<td><br />%s</td>
</tr>
<tr>
<td><br /><a name='external_groups'></a>%s</td>
</tr>
</table>""" %(admin_group_html, member_group_html, external_group_html)
else:
group_text += """
<table>
<tr>
<td>%s</td>
</tr>
<tr>
<td><br />%s</td>
</tr>
</table>""" %(admin_group_html, member_group_html)
return group_text
def tmpl_display_admin_groups(self, groups, ln=CFG_SITE_LANG):
"""
Display the groups the user is admin of.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'groups' *list* - All the group the user is admin of
- 'infos' *list* - Display infos on top of admin group table
"""
_ = gettext_set_language(ln)
img_link = """
<a href="%(siteurl)s/yourgroups/%(action)s?grpID=%(grpID)s&amp;ln=%(ln)s">
<img src="%(siteurl)s/img/%(img)s" alt="%(text)s" style="border:0" width="25"
height="25" /><br /><small>%(text)s</small>
</a>"""
out = self.tmpl_group_table_title(img="/img/group_admin.png",
text=_("You are an administrator of the following groups:") )
out += """
<table class="mailbox">
<thead class="mailboxheader">
<tr class="inboxheader">
<td>%s</td>
<td>%s</td>
<td style="width: 20px;" >&nbsp;</td>
<td style="width: 20px;">&nbsp;</td>
</tr>
</thead>
<tfoot>
<tr style="height:0px;">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tfoot>
<tbody class="mailboxbody">""" %(_("Group"), _("Description"))
if len(groups) == 0:
out += """
<tr class="mailboxrecord" style="height: 100px;">
<td colspan="4" style="text-align: center;">
<small>%s</small>
</td>
</tr>""" %(_("You are not an administrator of any groups."),)
for group_data in groups:
(grpID, name, description) = group_data
edit_link = img_link % {'siteurl' : CFG_SITE_URL,
'grpID' : grpID,
'ln': ln,
'img':"webbasket_create_small.png",
'text':_("Edit group"),
'action':"edit"
}
members_link = img_link % {'siteurl' : CFG_SITE_URL,
'grpID' : grpID,
'ln': ln,
'img':"webbasket_usergroup.png",
'text':_("Edit %s members") % '',
'action':"members"
}
out += """
<tr class="mailboxrecord">
<td>%s</td>
<td>%s</td>
<td style="text-align: center;" >%s</td>
<td style="text-align: center;" >%s</td>
</tr>""" % (cgi.escape(name), cgi.escape(description), edit_link, members_link)
out += """
<tr class="mailboxfooter">
<td colspan="2">
<form name="newGroup" action="create?ln=%(ln)s" method="post">
<input type="submit" name="create_group" value="%(write_label)s" class="formbutton" />
</form>
</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
</tbody>
</table>""" % {'ln': ln,
'write_label': _("Create new group"),
}
return out
def tmpl_display_member_groups(self, groups, ln=CFG_SITE_LANG):
"""
Display the groups the user is member of.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'groups' *list* - All the group the user is member of
"""
_ = gettext_set_language(ln)
group_text = self.tmpl_group_table_title(img="/img/webbasket_us.png", text=_("You are a member of the following groups:"))
group_text += """
<table class="mailbox">
<thead class="mailboxheader">
<tr class="inboxheader">
<td>%s</td>
<td>%s</td>
</tr>
</thead>
<tfoot>
<tr style="height:0px;">
<td></td>
<td></td>
</tr>
</tfoot>
<tbody class="mailboxbody">""" % (_("Group"), _("Description"))
if len(groups) == 0:
group_text += """
<tr class="mailboxrecord" style="height: 100px;">
<td colspan="2" style="text-align: center;">
<small>%s</small>
</td>
</tr>""" %(_("You are not a member of any groups."),)
for group_data in groups:
(id, name, description) = group_data
group_text += """
<tr class="mailboxrecord">
<td>%s</td>
<td>%s</td>
</tr>""" % (cgi.escape(name), cgi.escape(description))
group_text += """
<tr class="mailboxfooter">
<td>
<form name="newGroup" action="join?ln=%(ln)s" method="post">
<input type="submit" name="join_group" value="%(join_label)s" class="formbutton" />
</form>
</td>
<td>
<form name="newGroup" action="leave?ln=%(ln)s" method="post">
<input type="submit" name="leave" value="%(leave_label)s" class="formbutton" />
</form>
</td>
</tr>
</tbody>
</table>
""" % {'ln': ln,
'join_label': _("Join new group"),
'leave_label':_("Leave group")
}
return group_text
def tmpl_display_external_groups(self, groups, ln=CFG_SITE_LANG):
"""
Display the external groups the user is member of.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'groups' *list* - All the group the user is member of
"""
_ = gettext_set_language(ln)
group_text = self.tmpl_group_table_title(img="/img/webbasket_us.png", text=_("You are a member of the following external groups:"))
group_text += """
<table class="mailbox">
<thead class="mailboxheader">
<tr class="inboxheader">
<td>%s</td>
<td>%s</td>
</tr>
</thead>
<tfoot>
<tr style="height:0px;">
<td></td>
<td></td>
</tr>
</tfoot>
<tbody class="mailboxbody">""" % (_("Group"), _("Description"))
if len(groups) == 0:
group_text += """
<tr class="mailboxrecord" style="height: 100px;">
<td colspan="2" style="text-align: center;">
<small>%s</small>
</td>
</tr>""" %(_("You are not a member of any external groups."),)
for group_data in groups:
(id, name, description) = group_data
group_text += """
<tr class="mailboxrecord">
<td>%s</td>
<td>%s</td>
</tr>""" % (cgi.escape(name), cgi.escape(description))
group_text += """
</tbody>
</table>
"""
return group_text
def tmpl_display_input_group_info(self,
group_name,
group_description,
join_policy,
act_type="create",
grpID=None,
warnings=[],
ln=CFG_SITE_LANG):
"""
Display group data when creating or updating a group:
Name, description, join_policy.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'group_name' *string* - name of the group
- 'group_description' *string* - description of the group
- 'join_policy' *string* - join policy
- 'act_type' *string* - info about action : create or edit(update)
- 'grpID' *int* - ID of the group(not None in case of group editing)
- 'warnings' *list* - Display warning if values are not correct
"""
_ = gettext_set_language(ln)
#default
hidden_id =""
form_name = "create_group"
action = CFG_SITE_URL + '/yourgroups/create'
button_label = _("Create new group")
button_name = "create_button"
label = _("Create new group")
delete_text = ""
if act_type == "update":
form_name = "update_group"
action = CFG_SITE_URL + '/yourgroups/edit'
button_label = _("Update group")
button_name = "update"
label = _('Edit group %s') % cgi.escape(group_name)
delete_text = """<input type="submit" value="%s" class="formbutton" name="%s" />"""
delete_text %= (_("Delete group"),"delete")
if grpID is not None:
hidden_id = """<input type="hidden" name="grpID" value="%s" />"""
hidden_id %= grpID
out = self.tmpl_warning(warnings)
out += """
<form name="%(form_name)s" action="%(action)s" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<div style="padding:10px;">
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>%(label)s</b><br />
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
<td><label for="group_name">%(name_label)s</label></td>
<td>
<input type="text" name="group_name" id="group_name" value="%(group_name)s" />
</td>
</tr>
<tr>
<td><label for="group_description">%(description_label)s</label></td>
<td>
<input type="text" name="group_description" id="group_description" value="%(group_description)s" />
</td>
</tr>
<tr>
<td>%(join_policy_label)s</td>
<td>
%(join_policy)s
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
%(hidden_id)s
<table>
<tr>
<td>
<input type="submit" value="%(button_label)s" class="formbutton" name="%(button_name)s" />
</td>
<td>
%(delete_text)s
</td>
<td>
<input type="submit" value="%(cancel_label)s" class="formbutton" name="cancel" />
</td>
</tr>
</table>
</div>
</form>
"""
out %= {'action' : action,
'logo': CFG_SITE_URL + '/img/webbasket_create.png',
'label': label,
'form_name' : form_name,
'name_label': _("Group name:"),
'delete_text': delete_text,
'description_label': _("Group description:"),
'join_policy_label': _("Group join policy:"),
'group_name': cgi.escape(group_name, 1),
'group_description': cgi.escape(group_description, 1),
'button_label': button_label,
'button_name':button_name,
'cancel_label':_("Cancel"),
'hidden_id':hidden_id,
'ln': ln,
'join_policy' :self.__create_join_policy_selection_menu("join_policy",
join_policy,
ln)
}
return out
def tmpl_display_input_join_group(self,
group_list,
group_name,
group_from_search,
search,
warnings=[],
ln=CFG_SITE_LANG):
"""
Display the groups the user can join.
He can use default select list or the search box
Parameters:
- 'ln' *string* - The language to display the interface in
- 'group_list' *list* - All the group the user can join
- 'group_name' *string* - Name of the group the user is looking for
- 'group_from search' *list* - List of the group the user can join matching group_name
- 'search' *int* - User is looking for group using group_name
- 'warnings' *list* - Display warning if two group are selected
"""
_ = gettext_set_language(ln)
out = self.tmpl_warning(warnings)
search_content = ""
if search:
search_content = """<tr><td>&nbsp;</td><td>"""
if group_from_search != []:
search_content += self.__create_select_menu('grpID', group_from_search, _("Please select:"))
else:
search_content += _("No matching group")
search_content += """</td><td>&nbsp;</td></tr>"""
out += """
<form name="join_group" action="%(action)s" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<div style="padding:10px;">
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>%(label)s</b><br />
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
<td>%(list_label)s</td>
<td>
%(group_list)s
</td>
<td>
&nbsp;
</td>
</tr>
<tr>
<td><br /><label for="group_name">%(label2)s</label></td>
<td><br /><input type="text" name="group_name" id="group_name" value="%(group_name)s" /></td>
<td><br />
<input type="submit" name="find_button" value="%(find_label)s" class="nonsubmitbutton" />
</td>
</tr>
%(search_content)s
</table>
</td>
</tr>
</tbody>
</table>
<table>
<tr>
<td>
<input type="submit" name="join_button" value="%(label)s" class="formbutton" />
</td>
<td>
<input type="submit" value="%(cancel_label)s" class="formbutton" name="cancel" />
</td>
</tr>
</table>
</div>
</form>
"""
out %= {'action' : CFG_SITE_URL + '/yourgroups/join',
'logo': CFG_SITE_URL + '/img/webbasket_create.png',
'label': _("Join group"),
'group_name': cgi.escape(group_name, 1),
'label2':_("or find it") + ': ',
'list_label':_("Choose group:"),
'ln': ln,
'find_label': _("Find group"),
'cancel_label':_("Cancel"),
'group_list' :self.__create_select_menu("grpID",group_list, _("Please select:")),
'search_content' : search_content
}
return out
def tmpl_display_manage_member(self,
grpID,
group_name,
members,
pending_members,
infos=[],
warnings=[],
ln=CFG_SITE_LANG):
"""Display current members and waiting members of a group.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'grpID *int* - ID of the group
- 'group_name' *string* - Name of the group
- 'members' *list* - List of the current members
- 'pending_members' *list* - List of the waiting members
- 'infos' *tuple of 2 lists* - Message to inform user about his last action
- 'warnings' *list* - Display warning if two group are selected
"""
_ = gettext_set_language(ln)
out = self.tmpl_warning(warnings)
out += self.tmpl_infobox(infos)
out += """
<form name="member" action="%(action)s" method="post">
<p>%(title)s</p>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s"/>
<table>
<tr>
<td>
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(imgurl)s/webbasket_usergroup.png" alt="%(img_alt_header1)s" />
</td>
<td class="bsktitle">
%(header1)s<br />
&nbsp;
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
%(member_text)s
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(imgurl)s/webbasket_usergroup_gray.png" alt="%(img_alt_header2)s" />
</td>
<td class="bsktitle">
%(header2)s<br />
&nbsp;
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
%(pending_text)s
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table class="bskbasket" style="width: 400px">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(imgurl)s/iconpen.gif" alt="%(img_alt_header3)s" />
</td>
<td class="bsktitle">
<b>%(header3)s</b><br />
&nbsp;
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
<td colspan="2" style="padding: 0 5 10 5;">%(invite_text)s</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<input type="submit" value="%(cancel_label)s" class="formbutton" name="cancel" />
</td>
</tr>
</table>
</form>
"""
if members :
member_list = self.__create_select_menu("member_id", members, _("Please select:"))
member_text = """
<td style="padding: 0 5 10 5;">%s</td>
<td style="padding: 0 5 10 5;">
<input type="submit" name="remove_member" value="%s" class="nonsubmitbutton"/>
</td>""" % (member_list,_("Remove member"))
else :
member_text = """<td style="padding: 0 5 10 5;" colspan="2">%s</td>""" % _("No members.")
if pending_members :
pending_list = self.__create_select_menu("pending_member_id", pending_members, _("Please select:"))
pending_text = """
<td style="padding: 0 5 10 5;">%s</td>
<td style="padding: 0 5 10 5;">
<input type="submit" name="add_member" value="%s" class="nonsubmitbutton"/>
</td>
<td style="padding: 0 5 10 5;">
<input type="submit" name="reject_member" value="%s" class="nonsubmitbutton"/>
</td>""" % (pending_list,_("Accept member"), _("Reject member"))
else :
pending_text = """<td style="padding: 0 5 10 5;" colspan="2">%s</td>""" % _("No members awaiting approval.")
header1 = self.tmpl_group_table_title(text=_("Current members"))
header2 = self.tmpl_group_table_title(text=_("Members awaiting approval"))
header3 = _("Invite new members")
write_a_message_url = create_url(
"%s/yourmessages/write" % CFG_SITE_URL,
{
'ln' : ln,
'msg_subject' : _('Invitation to join "%s" group' % escape_html(group_name)),
'msg_body' : _("""\
Hello:
I think you might be interested in joining the group "%(x_name)s".
You can join by clicking here: %(x_url)s.
Best regards.
""") % {'x_name': group_name,
'x_url': create_html_link("%s/yourgroups/join" % CFG_SITE_URL, { 'grpID' : grpID,
'join_button' : "1",
},
link_label=group_name, escape_urlargd=True, escape_linkattrd=True)}})
link_open = '<a href="%s">' % escape_html(write_a_message_url)
invite_text = _("If you want to invite new members to join your group, please use the %(x_url_open)sweb message%(x_url_close)s system.") % \
{'x_url_open': link_open,
'x_url_close': '</a>'}
action = CFG_SITE_URL + '/yourgroups/members?ln=' + ln
out %= {'title':_('Group: %s') % escape_html(group_name),
'member_text' : member_text,
'pending_text' :pending_text,
'action':action,
'grpID':grpID,
'header1': header1,
'header2': header2,
'header3': header3,
'img_alt_header1': _("Current members"),
'img_alt_header2': _("Members awaiting approval"),
'img_alt_header3': _("Invite new members"),
'invite_text': invite_text,
'imgurl': CFG_SITE_URL + '/img',
'cancel_label':_("Cancel"),
'ln':ln
}
return out
def tmpl_display_input_leave_group(self,
groups,
warnings=[],
ln=CFG_SITE_LANG):
"""Display groups the user can leave.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'groups' *list* - List of groups the user is currently member of
- 'warnings' *list* - Display warning if no group is selected
"""
_ = gettext_set_language(ln)
out = self.tmpl_warning(warnings)
out += """
<form name="leave" action="%(action)s" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<div style="padding:10px;">
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>%(label)s</b><br />
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
<td>%(list_label)s</td>
<td>
%(groups)s
</td>
<td>
&nbsp;
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<table>
<tr>
<td>
%(submit)s
</td>
<td>
<input type="submit" value="%(cancel_label)s" class="formbutton" name="cancel" />
</td>
</tr>
</table>
</div>
</form>
"""
if groups:
groups = self.__create_select_menu("grpID", groups, _("Please select:"))
list_label = _("Group list")
submit = """<input type="submit" name="leave_button" value="%s" class="formbutton"/>""" % _("Leave group")
else :
groups = _("You are not member of any group.")
list_label = ""
submit = ""
action = CFG_SITE_URL + '/yourgroups/leave?ln=%s'
action %= (ln)
out %= {'groups' : groups,
'list_label' : list_label,
'action':action,
'logo': CFG_SITE_URL + '/img/webbasket_create.png',
'label' : _("Leave group"),
'cancel_label':_("Cancel"),
'ln' :ln,
'submit' : submit
}
return out
def tmpl_confirm_delete(self, grpID, ln=CFG_SITE_LANG):
"""
display a confirm message when deleting a group
@param grpID *int* - ID of the group
@param ln: language
@return: html output
"""
_ = gettext_set_language(ln)
action = CFG_SITE_URL + '/yourgroups/edit'
out = """
<form name="delete_group" action="%(action)s" method="post">
<table class="confirmoperation">
<tr>
<td colspan="2" class="confirmmessage">
%(message)s
</td>
</tr>
<tr>
<td>
<input type="hidden" name="confirmed" value="1" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s" />
<input type="submit" name="delete" value="%(yes_label)s" class="formbutton" />
</td>
<td>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s" />
<input type="submit" value="%(no_label)s" class="formbutton" />
</td>
</tr>
</table>
</form>"""% {'message': _("Are you sure you want to delete this group?"),
'ln':ln,
'yes_label': _("Yes"),
'no_label': _("No"),
'grpID':grpID,
'action': action
}
return out
def tmpl_confirm_leave(self, uid, grpID, ln=CFG_SITE_LANG):
"""
display a confirm message
@param grpID *int* - ID of the group
@param ln: language
@return: html output
"""
_ = gettext_set_language(ln)
action = CFG_SITE_URL + '/yourgroups/leave'
out = """
<form name="leave_group" action="%(action)s" method="post">
<table class="confirmoperation">
<tr>
<td colspan="2" class="confirmmessage">
%(message)s
</td>
</tr>
<tr>
<td>
<input type="hidden" name="confirmed" value="1" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s" />
<input type="submit" name="leave_button" value="%(yes_label)s" class="formbutton" />
</td>
<td>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s" />
<input type="submit" value="%(no_label)s" class="formbutton" />
</td>
</tr>
</table>
</form>"""% {'message': _("Are you sure you want to leave this group?"),
'ln':ln,
'yes_label': _("Yes"),
'no_label': _("No"),
'grpID':grpID,
'action': action
}
return out
def __create_join_policy_selection_menu(self, name, current_join_policy, ln=CFG_SITE_LANG):
"""Private function. create a drop down menu for selection of join policy
@param current_join_policy: join policy as defined in CFG_WEBSESSION_GROUP_JOIN_POLICY
@param ln: language
"""
_ = gettext_set_language(ln)
elements = [(CFG_WEBSESSION_GROUP_JOIN_POLICY['VISIBLEOPEN'],
_("Visible and open for new members")),
(CFG_WEBSESSION_GROUP_JOIN_POLICY['VISIBLEMAIL'],
_("Visible but new members need approval"))
]
select_text = _("Please select:")
return self.__create_select_menu(name, elements, select_text, selected_key=current_join_policy)
def __create_select_menu(self, name, elements, select_text, multiple=0, selected_key=None):
""" private function, returns a popup menu
@param name: name of HTML control
@param elements: list of (key, value)
"""
if multiple :
out = """
<select name="%s" multiple="multiple" style="width:100%%">"""% (name)
else :
out = """<select name="%s" style="width:100%%">""" % name
out += '<option value="-1">%s</option>' % (select_text)
for (key, label) in elements:
selected = ''
if key == selected_key:
selected = ' selected="selected"'
out += '<option value="%s"%s>%s</option>'% (key, selected, label)
out += '</select>'
return out
def tmpl_infobox(self, infos, ln=CFG_SITE_LANG):
"""Display len(infos) information fields
@param infos: list of strings
@param ln=language
@return: html output
"""
_ = gettext_set_language(ln)
if not((type(infos) is list) or (type(infos) is tuple)):
infos = [infos]
infobox = ""
for info in infos:
infobox += '<div><span class="info">'
lines = info.split("\n")
for line in lines[0:-1]:
infobox += line + "<br />\n"
infobox += lines[-1] + "</span></div>\n"
return infobox
def tmpl_navtrail(self, ln=CFG_SITE_LANG, title=""):
"""
display the navtrail, e.g.:
Your account > Your group > title
@param title: the last part of the navtrail. Is not a link
@param ln: language
return html formatted navtrail
"""
_ = gettext_set_language(ln)
nav_h1 = '<a class="navtrail" href="%s/youraccount/display">%s</a>'
nav_h2 = ""
if (title != ""):
nav_h2 = ' &gt; <a class="navtrail" href="%s/yourgroups/display">%s</a>'
nav_h2 = nav_h2 % (CFG_SITE_URL, _("Your Groups"))
return nav_h1 % (CFG_SITE_URL, _("Your Account")) + nav_h2
def tmpl_group_table_title(self, img="", text="", ln=CFG_SITE_LANG):
"""
display the title of a table:
- 'img' *string* - img path
- 'text' *string* - title
- 'ln' *string* - The language to display the interface in
"""
out = "<div>"
if img:
out += """
<img src="%s" alt="" />
""" % (CFG_SITE_URL + img)
out += """
<b>%s</b>
</div>""" % text
return out
def tmpl_admin_msg(self, group_name, grpID, ln=CFG_SITE_LANG):
"""
return message content for joining group
- 'group_name' *string* - name of the group
- 'grpID' *int* - ID of the group
- 'ln' *string* - The language to display the interface in
"""
_ = gettext_set_language(ln)
subject = _("Group %s: New membership request") % group_name
url = CFG_SITE_URL + "/yourgroups/members?grpID=%s&ln=%s"
url %= (grpID, ln)
# FIXME: which user? We should show his nickname.
body = (_("A user wants to join the group %s.") % group_name) + '<br />'
body += _("Please %(x_url_open)saccept or reject%(x_url_close)s this user's request.") % {'x_url_open': '<a href="' + url + '">',
'x_url_close': '</a>'}
body += '<br />'
return subject, body
def tmpl_member_msg(self,
group_name,
accepted=0,
ln=CFG_SITE_LANG):
"""
return message content when new member is accepted/rejected
- 'group_name' *string* - name of the group
- 'accepted' *int* - 1 if new membership has been accepted, 0 if it has been rejected
- 'ln' *string* - The language to display the interface in
"""
_ = gettext_set_language(ln)
if accepted:
subject = _("Group %s: Join request has been accepted") % (group_name)
body = _("Your request for joining group %s has been accepted.") % (group_name)
else:
subject = _("Group %s: Join request has been rejected") % (group_name)
body = _("Your request for joining group %s has been rejected.") % (group_name)
url = CFG_SITE_URL + "/yourgroups/display?ln=" + ln
body += '<br />'
body += _("You can consult the list of %(x_url_open)syour groups%(x_url_close)s.") % {'x_url_open': '<a href="' + url + '">',
'x_url_close': '</a>'}
body += '<br />'
return subject, body
def tmpl_delete_msg(self,
group_name,
ln=CFG_SITE_LANG):
"""
return message content when new member is accepted/rejected
- 'group_name' *string* - name of the group
- 'ln' *string* - The language to display the interface in
"""
_ = gettext_set_language(ln)
subject = _("Group %s has been deleted") % group_name
url = CFG_SITE_URL + "/yourgroups/display?ln=" + ln
body = _("Group %s has been deleted by its administrator.") % group_name
body += '<br />'
body += _("You can consult the list of %(x_url_open)syour groups%(x_url_close)s.") % {'x_url_open': '<a href="' + url + '">',
'x_url_close': '</a>'}
body += '<br />'
return subject, body
def tmpl_group_info(self, nb_admin_groups=0, nb_member_groups=0, nb_total_groups=0, ln=CFG_SITE_LANG):
"""
display infos about groups (used by myaccount.py)
@param nb_admin_group: number of groups the user is admin of
@param nb_member_group: number of groups the user is member of
@param total_group: number of groups the user belongs to
@param ln: language
return: html output.
"""
_ = gettext_set_language(ln)
out = _("You can consult the list of %(x_url_open)s%(x_nb_total)i groups%(x_url_close)s you are subscribed to (%(x_nb_member)i) or administering (%(x_nb_admin)i).")
out %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yourgroups/display?ln=' + ln + '">',
'x_nb_total': nb_total_groups,
'x_url_close': '</a>',
'x_nb_admin': nb_admin_groups,
'x_nb_member': nb_member_groups}
return out
def tmpl_general_warnings(self, warning_list, ln=CFG_SITE_LANG):
"""
display information to the admin user about possible
ssecurity problems in the system.
"""
message = ""
_ = gettext_set_language(ln)
#Try and connect to the mysql database with the default invenio password
if "warning_mysql_password_equal_to_invenio_password" in warning_list:
message += "<p><font color=red>"
message += _("Warning: The password set for MySQL root user is the same as the default Invenio password. For security purposes, you may want to change the password.")
message += "</font></p>"
#Try and connect to the invenio database with the default invenio password
if "warning_invenio_password_equal_to_default" in warning_list:
message += "<p><font color=red>"
message += _("Warning: The password set for the Invenio MySQL user is the same as the shipped default. For security purposes, you may want to change the password.")
message += "</font></p>"
#Check if the admin password is empty
if "warning_empty_admin_password" in warning_list:
message += "<p><font color=red>"
message += _("Warning: The password set for the Invenio admin user is currently empty. For security purposes, it is strongly recommended that you add a password.")
message += "</font></p>"
#Check if the admin email has been changed from the default
if "warning_site_support_email_equal_to_default" in warning_list:
message += "<p><font color=red>"
message += _("Warning: The email address set for support email is currently set to info@invenio-software.org. It is recommended that you change this to your own address.")
message += "</font></p>"
#Check for a new release
if "note_new_release_available" in warning_list:
message += "<p><font color=red>"
message += _("A newer version of Invenio is available for download. You may want to visit ")
message += "<a href=\"http://invenio-software.org/wiki/Installation/Download\">http://invenio-software.org/wiki/Installation/Download</a>"
message += "</font></p>"
#Error downloading release notes
if "error_cannot_download_release_notes" in warning_list:
message += "<p><font color=red>"
message += _("Cannot download or parse release notes from http://invenio-software.org/repo/invenio/tree/RELEASE-NOTES")
message += "</font></p>"
return message
diff --git a/modules/websession/lib/webuser.py b/modules/websession/lib/webuser.py
index 6022b7b12..d596b3428 100644
--- a/modules/websession/lib/webuser.py
+++ b/modules/websession/lib/webuser.py
@@ -1,1259 +1,1261 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
This file implements all methods necessary for working with users and
sessions in Invenio. Contains methods for logging/registration
when a user log/register into the system, checking if it is a guest
user or not.
At the same time this presents all the stuff it could need with
sessions managements, working with websession.
It also contains Apache-related user authentication stuff.
"""
__revision__ = "$Id$"
from invenio import webinterface_handler_config as apache
import cgi
import urllib
import urlparse
from socket import gaierror
import os
import crypt
import socket
import smtplib
import re
import random
import datetime
import base64
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_ACCESS_CONTROL_LEVEL_GUESTS, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN, \
CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT, \
CFG_APACHE_GROUP_FILE, \
CFG_APACHE_PASSWORD_FILE, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_TMPDIR, \
CFG_SITE_URL, \
CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS, \
CFG_CERN_SITE, \
CFG_INSPIRE_SITE, \
CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL, \
- CFG_BIBAUTHORID_ENABLED
+ CFG_BIBAUTHORID_ENABLED, \
+ CFG_SITE_RECORD
+
try:
from invenio.session import get_session
except ImportError:
pass
from invenio.dbquery import run_sql, OperationalError, \
serialize_via_marshal, deserialize_via_marshal
from invenio.access_control_admin import acc_get_role_id, acc_get_action_roles, acc_get_action_id, acc_is_user_in_role, acc_find_possible_activities
from invenio.access_control_mailcookie import mail_cookie_create_mail_activation
from invenio.access_control_firerole import acc_firerole_check_user, load_role_definition
from invenio.access_control_config import SUPERADMINROLE, CFG_EXTERNAL_AUTH_USING_SSO
from invenio.messages import gettext_set_language, wash_languages, wash_language
from invenio.mailutils import send_email
from invenio.errorlib import register_exception
from invenio.webgroup_dblayer import get_groups
from invenio.external_authentication import InvenioWebAccessExternalAuthError
from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION, \
CFG_WEBACCESS_MSGS, CFG_WEBACCESS_WARNING_MSGS, CFG_EXTERNAL_AUTH_DEFAULT
import invenio.template
tmpl = invenio.template.load('websession')
re_invalid_nickname = re.compile(""".*[,'@]+.*""")
# pylint: disable=C0301
def createGuestUser():
"""Create a guest user , insert into user null values in all fields
createGuestUser() -> GuestUserID
"""
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0:
try:
return run_sql("insert into user (email, note) values ('', '1')")
except OperationalError:
return None
else:
try:
return run_sql("insert into user (email, note) values ('', '0')")
except OperationalError:
return None
def page_not_authorized(req, referer='', uid='', text='', navtrail='', ln=CFG_SITE_LANG,
navmenuid=""):
"""Show error message when user is not authorized to do something.
@param referer: in case the displayed message propose a login link, this
is the url to return to after logging in. If not specified it is guessed
from req.
@param uid: the uid of the user. If not specified it is guessed from req.
@param text: the message to be displayed. If not specified it will be
guessed from the context.
"""
from invenio.webpage import page
_ = gettext_set_language(ln)
if not referer:
referer = req.unparsed_uri
if not CFG_ACCESS_CONTROL_LEVEL_SITE:
title = CFG_WEBACCESS_MSGS[5]
if not uid:
uid = getUid(req)
try:
res = run_sql("SELECT email FROM user WHERE id=%s AND note=1" % uid)
if res and res[0][0]:
if text:
body = text
else:
body = "%s %s" % (CFG_WEBACCESS_WARNING_MSGS[9] % cgi.escape(res[0][0]),
("%s %s" % (CFG_WEBACCESS_MSGS[0] % urllib.quote(referer), CFG_WEBACCESS_MSGS[1])))
else:
if text:
body = text
else:
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 1:
body = CFG_WEBACCESS_MSGS[3]
else:
body = CFG_WEBACCESS_WARNING_MSGS[4] + CFG_WEBACCESS_MSGS[2]
except OperationalError, e:
body = _("Database problem") + ': ' + str(e)
elif CFG_ACCESS_CONTROL_LEVEL_SITE == 1:
title = CFG_WEBACCESS_MSGS[8]
body = "%s %s" % (CFG_WEBACCESS_MSGS[7], CFG_WEBACCESS_MSGS[2])
elif CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
title = CFG_WEBACCESS_MSGS[6]
body = "%s %s" % (CFG_WEBACCESS_MSGS[4], CFG_WEBACCESS_MSGS[2])
return page(title=title,
language=ln,
uid=getUid(req),
body=body,
navtrail=navtrail,
req=req,
navmenuid=navmenuid)
def getUid(req):
"""Return user ID taking it from the cookie of the request.
Includes control mechanism for the guest users, inserting in
the database table when need be, raising the cookie back to the
client.
User ID is set to 0 when client refuses cookie or we are in the
read-only site operation mode.
User ID is set to -1 when we are in the permission denied site
operation mode.
getUid(req) -> userId
"""
if hasattr(req, '_user_info'):
return req._user_info['uid']
if CFG_ACCESS_CONTROL_LEVEL_SITE == 1: return 0
if CFG_ACCESS_CONTROL_LEVEL_SITE == 2: return -1
guest = 0
session = get_session(req)
uid = session.get('uid', -1)
if uid == -1: # first time, so create a guest user
if CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
uid = session['uid'] = createGuestUser()
guest = 1
else:
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0:
session['uid'] = 0
return 0
else:
return -1
else:
if not hasattr(req, '_user_info') and 'user_info' in session:
req._user_info = session['user_info']
req._user_info = collect_user_info(req, refresh=True)
if guest == 0:
guest = isGuestUser(uid)
if guest:
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0:
return uid
elif CFG_ACCESS_CONTROL_LEVEL_GUESTS >= 1:
return -1
else:
res = run_sql("SELECT note FROM user WHERE id=%s", (uid, ))
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 0:
return uid
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 1 and res and res[0][0] in [1, "1"]:
return uid
else:
return -1
def setUid(req, uid, remember_me=False):
"""It sets the userId into the session, and raise the cookie to the client.
"""
if hasattr(req, '_user_info'):
del req._user_info
session = get_session(req)
session['uid'] = uid
if remember_me:
session.set_timeout(86400)
session.set_remember_me()
if uid > 0:
user_info = collect_user_info(req, login_time=True)
session['user_info'] = user_info
req._user_info = user_info
else:
del session['user_info']
session.save()
return uid
def session_param_del(req, key):
"""
Remove a given key from the session.
"""
session = get_session(req)
del session[key]
session.save()
def session_param_set(req, key, value):
"""
Associate a VALUE to the session param KEY for the current session.
"""
session = get_session(req)
session[key] = value
session.save()
def session_param_get(req, key):
"""
Return session parameter value associated with session parameter KEY for the current session.
If the key doesn't exists raise KeyError.
"""
session = get_session(req)
return session[key]
def session_param_list(req):
"""
List all available session parameters.
"""
session = get_session(req)
return session.keys()
def get_last_login(uid):
"""Return the last_login datetime for uid if any, otherwise return the Epoch."""
res = run_sql('SELECT last_login FROM user WHERE id=%s', (uid, ), 1)
if res and res[0][0]:
return res[0][0]
else:
return datetime.datetime(1970, 1, 1)
def get_user_info(uid, ln=CFG_SITE_LANG):
"""Get infos for a given user.
@param uid: user id (int)
@return: tuple: (uid, nickname, display_name)
"""
_ = gettext_set_language(ln)
query = """SELECT id, nickname
FROM user
WHERE id=%s"""
res = run_sql(query, (uid, ))
if res:
if res[0]:
user = list(res[0])
if user[1]:
user.append(user[1])
else:
user[1] = str(user[0])
user.append(_("user") + ' #' + str(user[0]))
return tuple(user)
return (uid, '', _("N/A"))
def get_uid_from_email(email):
"""Return the uid corresponding to an email.
Return -1 when the email does not exists."""
try:
res = run_sql("SELECT id FROM user WHERE email=%s", (email, ))
if res:
return res[0][0]
else:
return -1
except OperationalError:
register_exception()
return -1
def isGuestUser(uid):
"""It Checks if the userId corresponds to a guestUser or not
isGuestUser(uid) -> boolean
"""
out = 1
try:
res = run_sql("SELECT email FROM user WHERE id=%s LIMIT 1", (uid,), 1)
if res:
if res[0][0]:
out = 0
except OperationalError:
register_exception()
return out
def isUserSubmitter(user_info):
"""Return True if the user is a submitter for something; False otherwise."""
u_email = get_email(user_info['uid'])
res = run_sql("SELECT email FROM sbmSUBMISSIONS WHERE email=%s LIMIT 1", (u_email,), 1)
return len(res) > 0
def isUserReferee(user_info):
"""Return True if the user is a referee for something; False otherwise."""
if CFG_CERN_SITE:
return True
else:
for (role_id, role_name, role_description) in acc_get_action_roles(acc_get_action_id('referee')):
if acc_is_user_in_role(user_info, role_id):
return True
return False
def isUserAdmin(user_info):
"""Return True if the user has some admin rights; False otherwise."""
return acc_find_possible_activities(user_info) != {}
def isUserSuperAdmin(user_info):
"""Return True if the user is superadmin; False otherwise."""
if run_sql("""SELECT r.id
FROM accROLE r LEFT JOIN user_accROLE ur
ON r.id = ur.id_accROLE
WHERE r.name = %s AND
ur.id_user = %s AND ur.expiration>=NOW() LIMIT 1""", (SUPERADMINROLE, user_info['uid']), 1):
return True
return acc_firerole_check_user(user_info, load_role_definition(acc_get_role_id(SUPERADMINROLE)))
def nickname_valid_p(nickname):
"""Check whether wanted NICKNAME supplied by the user is valid.
At the moment we just check whether it is not empty, does not
contain blanks or @, is not equal to `guest', etc.
This check relies on re_invalid_nickname regexp (see above)
Return 1 if nickname is okay, return 0 if it is not.
"""
if nickname and \
not(nickname.startswith(' ') or nickname.endswith(' ')) and \
nickname.lower() != 'guest':
if not re_invalid_nickname.match(nickname):
return 1
return 0
def email_valid_p(email):
"""Check whether wanted EMAIL address supplied by the user is valid.
At the moment we just check whether it contains '@' and whether
it doesn't contain blanks. We also check the email domain if
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN is set.
Return 1 if email is okay, return 0 if it is not.
"""
if (email.find("@") <= 0) or (email.find(" ") > 0):
return 0
elif CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN:
if not email.endswith(CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN):
return 0
return 1
def confirm_email(email):
"""Confirm the email. It returns None when there are problems, otherwise
it return the uid involved."""
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 0:
activated = 1
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
activated = 0
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 2:
return -1
run_sql('UPDATE user SET note=%s where email=%s', (activated, email))
res = run_sql('SELECT id FROM user where email=%s', (email, ))
if res:
if CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS:
send_new_admin_account_warning(email, CFG_SITE_ADMIN_EMAIL)
return res[0][0]
else:
return None
def registerUser(req, email, passw, nickname, register_without_nickname=False,
login_method=None, ln=CFG_SITE_LANG):
"""Register user with the desired values of NICKNAME, EMAIL and
PASSW.
If REGISTER_WITHOUT_NICKNAME is set to True, then ignore
desired NICKNAME and do not set any. This is suitable for
external authentications so that people can login without
having to register an internal account first.
Return 0 if the registration is successful, 1 if email is not
valid, 2 if nickname is not valid, 3 if email is already in the
database, 4 if nickname is already in the database, 5 when
users cannot register themselves because of the site policy, 6 when the
site is having problem contacting the user.
If login_method is None or is equal to the key corresponding to local
authentication, then CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS is taken
in account for deciding the behaviour about registering.
"""
# is email valid?
email = email.lower()
if not email_valid_p(email):
return 1
_ = gettext_set_language(ln)
# is email already taken?
res = run_sql("SELECT email FROM user WHERE email=%s", (email,))
if len(res) > 0:
return 3
if register_without_nickname:
# ignore desired nick and use default empty string one:
nickname = ""
else:
# is nickname valid?
if not nickname_valid_p(nickname):
return 2
# is nickname already taken?
res = run_sql("SELECT nickname FROM user WHERE nickname=%s", (nickname,))
if len(res) > 0:
return 4
activated = 1 # By default activated
if not login_method or not CFG_EXTERNAL_AUTHENTICATION[login_method]: # local login
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 2:
return 5
elif CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT:
activated = 2 # Email confirmation required
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 1:
activated = 0 # Administrator confirmation required
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT:
address_activation_key = mail_cookie_create_mail_activation(email)
ip_address = req.remote_host or req.remote_ip
try:
if not send_email(CFG_SITE_SUPPORT_EMAIL, email, _("Account registration at %s") % CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
tmpl.tmpl_account_address_activation_email_body(email,
address_activation_key, ip_address, ln)):
return 1
except (smtplib.SMTPException, socket.error):
return 6
# okay, go on and register the user:
user_preference = get_default_user_preferences()
uid = run_sql("INSERT INTO user (nickname, email, password, note, settings, last_login) "
"VALUES (%s,%s,AES_ENCRYPT(email,%s),%s,%s, NOW())",
(nickname, email, passw, activated, serialize_via_marshal(user_preference)))
if activated == 1: # Ok we consider the user as logged in :-)
setUid(req, uid)
return 0
def updateDataUser(uid, email, nickname):
"""
Update user data. Used when a user changed his email or password
or nickname.
"""
email = email.lower()
if email == 'guest':
return 0
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 2:
run_sql("update user set email=%s where id=%s", (email, uid))
if nickname and nickname != '':
run_sql("update user set nickname=%s where id=%s", (nickname, uid))
return 1
def updatePasswordUser(uid, password):
"""Update the password of a user."""
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 3:
run_sql("update user set password=AES_ENCRYPT(email,%s) where id=%s", (password, uid))
return 1
def loginUser(req, p_un, p_pw, login_method):
"""It is a first simple version for the authentication of user. It returns the id of the user,
for checking afterwards if the login is correct
"""
# p_un passed may be an email or a nickname:
p_email = get_email_from_username(p_un)
# go on with the old stuff based on p_email:
if not login_method in CFG_EXTERNAL_AUTHENTICATION:
return ([], p_email, p_pw, 12)
if CFG_EXTERNAL_AUTHENTICATION[login_method]: # External Authenthication
try:
p_email = CFG_EXTERNAL_AUTHENTICATION[login_method].auth_user(p_email, p_pw, req) or CFG_EXTERNAL_AUTHENTICATION[login_method].auth_user(p_un, p_pw, req) ## We try to login with either the email of the nickname
if p_email:
p_email = p_email.lower()
else:
return([], p_email, p_pw, 15)
except InvenioWebAccessExternalAuthError:
register_exception(req=req, alert_admin=True)
raise
if p_email: # Authenthicated externally
query_result = run_sql("SELECT id from user where email=%s", (p_email,))
if not query_result: # First time user
p_pw_local = int(random.random() * 1000000)
p_nickname = ''
if CFG_EXTERNAL_AUTHENTICATION[login_method].enforce_external_nicknames:
try: # Let's discover the external nickname!
p_nickname = CFG_EXTERNAL_AUTHENTICATION[login_method].fetch_user_nickname(p_email, p_pw, req)
except (AttributeError, NotImplementedError):
pass
except:
register_exception(req=req, alert_admin=True)
raise
res = registerUser(req, p_email, p_pw_local, p_nickname,
register_without_nickname=p_nickname == '',
login_method=login_method)
if res == 4 or res == 2: # The nickname was already taken
res = registerUser(req, p_email, p_pw_local, '',
register_without_nickname=True,
login_method=login_method)
query_result = run_sql("SELECT id from user where email=%s", (p_email,))
elif res == 0: # Everything was ok, with or without nickname.
query_result = run_sql("SELECT id from user where email=%s", (p_email,))
elif res == 6: # error in contacting the user via email
return([], p_email, p_pw_local, 19)
else:
return([], p_email, p_pw_local, 13)
try:
groups = CFG_EXTERNAL_AUTHENTICATION[login_method].fetch_user_groups_membership(p_email, p_pw, req)
# groups is a dictionary {group_name : group_description,}
new_groups = {}
for key, value in groups.items():
new_groups[key + " [" + str(login_method) + "]"] = value
groups = new_groups
except (AttributeError, NotImplementedError):
pass
except:
register_exception(req=req, alert_admin=True)
return([], p_email, p_pw, 16)
else: # Groups synchronization
if groups:
userid = query_result[0][0]
from invenio.webgroup import synchronize_external_groups
synchronize_external_groups(userid, groups, login_method)
user_prefs = get_user_preferences(query_result[0][0])
if not CFG_EXTERNAL_AUTHENTICATION[login_method]:
## I.e. if the login method is not of robot type:
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4:
# Let's prevent the user to switch login_method
if user_prefs.has_key("login_method") and \
user_prefs["login_method"] != login_method:
return([], p_email, p_pw, 11)
user_prefs["login_method"] = login_method
# Cleaning external settings
for key in user_prefs.keys():
if key.startswith('EXTERNAL_'):
del user_prefs[key]
try:
# Importing external settings
new_prefs = CFG_EXTERNAL_AUTHENTICATION[login_method].fetch_user_preferences(p_email, p_pw, req)
for key, value in new_prefs.items():
user_prefs['EXTERNAL_' + key] = value
except (AttributeError, NotImplementedError):
pass
except InvenioWebAccessExternalAuthError:
register_exception(req=req, alert_admin=True)
return([], p_email, p_pw, 16)
# Storing settings
set_user_preferences(query_result[0][0], user_prefs)
else:
return ([], p_un, p_pw, 10)
else: # Internal Authenthication
if not p_pw:
p_pw = ''
query_result = run_sql("SELECT id,email,note from user where email=%s and password=AES_ENCRYPT(email,%s)", (p_email, p_pw,))
if query_result:
#FIXME drop external groups and settings
note = query_result[0][2]
if note == '1': # Good account
preferred_login_method = get_user_preferences(query_result[0][0])['login_method']
p_email = query_result[0][1].lower()
if login_method != preferred_login_method:
if preferred_login_method in CFG_EXTERNAL_AUTHENTICATION:
return ([], p_email, p_pw, 11)
elif note == '2': # Email address need to be confirmed by user
return ([], p_email, p_pw, 17)
elif note == '0': # Account need to be confirmed by administrator
return ([], p_email, p_pw, 18)
else:
return ([], p_email, p_pw, 14)
# Login successful! Updating the last access time
run_sql("UPDATE user SET last_login=NOW() WHERE email=%s", (p_email, ))
return (query_result, p_email, p_pw, 0)
def drop_external_settings(userId):
"""Drop the external (EXTERNAL_) settings of userid."""
prefs = get_user_preferences(userId)
for key in prefs.keys():
if key.startswith('EXTERNAL_'):
del prefs[key]
set_user_preferences(userId, prefs)
def logoutUser(req):
"""It logout the user of the system, creating a guest user.
"""
session = get_session(req)
if CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
uid = createGuestUser()
session['uid'] = uid
session.save()
else:
uid = 0
session.invalidate()
if hasattr(req, '_user_info'):
delattr(req, '_user_info')
return uid
def username_exists_p(username):
"""Check if USERNAME exists in the system. Username may be either
nickname or email.
Return 1 if it does exist, 0 if it does not.
"""
if username == "":
# return not exists if asked for guest users
return 0
res = run_sql("SELECT email FROM user WHERE email=%s", (username,)) + \
run_sql("SELECT email FROM user WHERE nickname=%s", (username,))
if len(res) > 0:
return 1
return 0
def emailUnique(p_email):
"""Check if the email address only exists once. If yes, return userid, if not, -1
"""
query_result = run_sql("select id, email from user where email=%s", (p_email,))
if len(query_result) == 1:
return query_result[0][0]
elif len(query_result) == 0:
return 0
return -1
def nicknameUnique(p_nickname):
"""Check if the nickname only exists once. If yes, return userid, if not, -1
"""
query_result = run_sql("select id, nickname from user where nickname=%s", (p_nickname,))
if len(query_result) == 1:
return query_result[0][0]
elif len(query_result) == 0:
return 0
return -1
def update_Uid(req, p_email, remember_me=False):
"""It updates the userId of the session. It is used when a guest user is logged in succesfully in the system with a given email and password.
As a side effect it will discover all the restricted collection to which the user has right to
"""
query_ID = int(run_sql("select id from user where email=%s",
(p_email,))[0][0])
setUid(req, query_ID, remember_me)
return query_ID
def send_new_admin_account_warning(new_account_email, send_to, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account new_account_email."""
_ = gettext_set_language(ln)
sub = _("New account on") + " '%s'" % CFG_SITE_NAME
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
sub += " - " + _("PLEASE ACTIVATE")
body = _("A new account has been created on") + " '%s'" % CFG_SITE_NAME
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
body += _(" and is awaiting activation")
body += ":\n\n"
body += _(" Username/Email") + ": %s\n\n" % new_account_email
body += _("You can approve or reject this account request at") + ": %s/admin/webaccess/webaccessadmin.py/manageaccounts\n" % CFG_SITE_URL
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, subject=sub, content=body)
def get_email(uid):
"""Return email address of the user uid. Return string 'guest' in case
the user is not found."""
out = "guest"
res = run_sql("SELECT email FROM user WHERE id=%s", (uid,), 1)
if res and res[0][0]:
out = res[0][0].lower()
return out
def get_email_from_username(username):
"""Return email address of the user corresponding to USERNAME.
The username may be either nickname or email. Return USERNAME
untouched if not found in the database or if found several
matching entries.
"""
if username == '':
return ''
out = username
res = run_sql("SELECT email FROM user WHERE email=%s", (username,), 1) + \
run_sql("SELECT email FROM user WHERE nickname=%s", (username,), 1)
if res and len(res) == 1:
out = res[0][0].lower()
return out
#def get_password(uid):
#"""Return password of the user uid. Return None in case
#the user is not found."""
#out = None
#res = run_sql("SELECT password FROM user WHERE id=%s", (uid,), 1)
#if res and res[0][0] != None:
#out = res[0][0]
#return out
def get_nickname(uid):
"""Return nickname of the user uid. Return None in case
the user is not found."""
out = None
res = run_sql("SELECT nickname FROM user WHERE id=%s", (uid,), 1)
if res and res[0][0]:
out = res[0][0]
return out
def get_nickname_or_email(uid):
"""Return nickname (preferred) or the email address of the user uid.
Return string 'guest' in case the user is not found."""
out = "guest"
res = run_sql("SELECT nickname, email FROM user WHERE id=%s", (uid,), 1)
if res and res[0]:
if res[0][0]:
out = res[0][0]
elif res[0][1]:
out = res[0][1].lower()
return out
def create_userinfobox_body(req, uid, language="en"):
"""Create user info box body for user UID in language LANGUAGE."""
if req:
if req.subprocess_env.has_key('HTTPS') \
and req.subprocess_env['HTTPS'] == 'on':
url_referer = CFG_SITE_SECURE_URL + req.unparsed_uri
else:
url_referer = CFG_SITE_URL + req.unparsed_uri
if '/youraccount/logout' in url_referer:
url_referer = ''
else:
url_referer = CFG_SITE_URL
user_info = collect_user_info(req)
try:
return tmpl.tmpl_create_userinfobox(ln=language,
url_referer=url_referer,
guest = isGuestUser(uid),
username = get_nickname_or_email(uid),
submitter = user_info['precached_viewsubmissions'],
referee = user_info['precached_useapprove'],
admin = user_info['precached_useadmin'],
usebaskets = user_info['precached_usebaskets'],
usemessages = user_info['precached_usemessages'],
usealerts = user_info['precached_usealerts'],
usegroups = user_info['precached_usegroups'],
useloans = user_info['precached_useloans'],
usestats = user_info['precached_usestats']
)
except OperationalError:
return ""
def create_useractivities_menu(req, uid, navmenuid, ln="en"):
"""Create user activities menu.
@param req: request object
@param uid: user id
@type uid: int
@param navmenuid: the section of the website this page belongs (search, submit, baskets, etc.)
@type navmenuid: string
@param ln: language
@type ln: string
@return: HTML menu of the user activities
@rtype: string
"""
if req:
if req.subprocess_env.has_key('HTTPS') \
and req.subprocess_env['HTTPS'] == 'on':
url_referer = CFG_SITE_SECURE_URL + req.unparsed_uri
else:
url_referer = CFG_SITE_URL + req.unparsed_uri
if '/youraccount/logout' in url_referer:
url_referer = ''
else:
url_referer = CFG_SITE_URL
user_info = collect_user_info(req)
is_user_menu_selected = False
if navmenuid == 'personalize' or \
navmenuid.startswith('your') and \
navmenuid != 'youraccount':
is_user_menu_selected = True
try:
return tmpl.tmpl_create_useractivities_menu(
ln=ln,
selected=is_user_menu_selected,
url_referer=url_referer,
guest = isGuestUser(uid),
username = get_nickname_or_email(uid),
submitter = user_info['precached_viewsubmissions'],
referee = user_info['precached_useapprove'],
admin = user_info['precached_useadmin'],
usebaskets = user_info['precached_usebaskets'],
usemessages = user_info['precached_usemessages'],
usealerts = user_info['precached_usealerts'],
usegroups = user_info['precached_usegroups'],
useloans = user_info['precached_useloans'],
usestats = user_info['precached_usestats']
)
except OperationalError:
return ""
def create_adminactivities_menu(req, uid, navmenuid, ln="en"):
"""Create admin activities menu.
@param req: request object
@param uid: user id
@type uid: int
@param navmenuid: the section of the website this page belongs (search, submit, baskets, etc.)
@type navmenuid: string
@param ln: language
@type ln: string
@return: HTML menu of the user activities
@rtype: string
"""
_ = gettext_set_language(ln)
if req:
if req.subprocess_env.has_key('HTTPS') \
and req.subprocess_env['HTTPS'] == 'on':
url_referer = CFG_SITE_SECURE_URL + req.unparsed_uri
else:
url_referer = CFG_SITE_URL + req.unparsed_uri
if '/youraccount/logout' in url_referer:
url_referer = ''
else:
url_referer = CFG_SITE_URL
user_info = collect_user_info(req)
activities = acc_find_possible_activities(user_info, ln)
# For BibEdit and BibDocFile menu items, take into consideration
# current record whenever possible
if activities.has_key(_("Run Record Editor")) or \
activities.has_key(_("Run Document File Manager")) and \
- user_info['uri'].startswith('/record/'):
+ user_info['uri'].startswith('/'+ CFG_SITE_RECORD +'/'):
try:
# Get record ID and try to cast it to an int
current_record_id = int(urlparse.urlparse(user_info['uri'])[2].split('/')[2])
except:
pass
else:
if activities.has_key(_("Run Record Editor")):
activities[_("Run Record Editor")] = activities[_("Run Record Editor")] + '&amp;#state=edit&amp;recid=' + str(current_record_id)
if activities.has_key(_("Run Document File Manager")):
activities[_("Run Document File Manager")] = activities[_("Run Document File Manager")] + '&amp;recid=' + str(current_record_id)
try:
return tmpl.tmpl_create_adminactivities_menu(
ln=ln,
selected=navmenuid == 'admin',
url_referer=url_referer,
guest = isGuestUser(uid),
username = get_nickname_or_email(uid),
submitter = user_info['precached_viewsubmissions'],
referee = user_info['precached_useapprove'],
admin = user_info['precached_useadmin'],
usebaskets = user_info['precached_usebaskets'],
usemessages = user_info['precached_usemessages'],
usealerts = user_info['precached_usealerts'],
usegroups = user_info['precached_usegroups'],
useloans = user_info['precached_useloans'],
usestats = user_info['precached_usestats'],
activities = activities
)
except OperationalError:
return ""
def list_registered_users():
"""List all registered users."""
return run_sql("SELECT id,email FROM user where email!=''")
def list_users_in_role(role):
"""List all users of a given role (see table accROLE)
@param role: role of user (string)
@return: list of uids
"""
res = run_sql("""SELECT uacc.id_user
FROM user_accROLE uacc JOIN accROLE acc
ON uacc.id_accROLE=acc.id
WHERE acc.name=%s""",
(role,))
if res:
return map(lambda x: int(x[0]), res)
return []
def list_users_in_roles(role_list):
"""List all users of given roles (see table accROLE)
@param role_list: list of roles [string]
@return: list of uids
"""
if not(type(role_list) is list or type(role_list) is tuple):
role_list = [role_list]
query = """SELECT DISTINCT(uacc.id_user)
FROM user_accROLE uacc JOIN accROLE acc
ON uacc.id_accROLE=acc.id
"""
query_addons = ""
query_params = ()
if len(role_list) > 0:
query_params = role_list
query_addons = " WHERE "
for role in role_list[:-1]:
query_addons += "acc.name=%s OR "
query_addons += "acc.name=%s"
res = run_sql(query + query_addons, query_params)
if res:
return map(lambda x: int(x[0]), res)
return []
def get_uid_based_on_pref(prefname, prefvalue):
"""get the user's UID based where his/her preference prefname has value prefvalue in preferences"""
prefs = run_sql("SELECT id, settings FROM user WHERE settings is not NULL")
the_uid = None
for pref in prefs:
try:
settings = deserialize_via_marshal(pref[1])
if (settings.has_key(prefname)) and (settings[prefname] == prefvalue):
the_uid = pref[0]
except:
pass
return the_uid
def get_user_preferences(uid):
pref = run_sql("SELECT id, settings FROM user WHERE id=%s", (uid,))
if pref:
try:
return deserialize_via_marshal(pref[0][1])
except:
pass
return get_default_user_preferences() # empty dict mean no preferences
def set_user_preferences(uid, pref):
assert(type(pref) == type({}))
run_sql("UPDATE user SET settings=%s WHERE id=%s",
(serialize_via_marshal(pref), uid))
def get_default_user_preferences():
user_preference = {
'login_method': ''}
if CFG_EXTERNAL_AUTH_DEFAULT in CFG_EXTERNAL_AUTHENTICATION:
user_preference['login_method'] = CFG_EXTERNAL_AUTH_DEFAULT
return user_preference
def get_preferred_user_language(req):
def _get_language_from_req_header(accept_language_header):
"""Extract langs info from req.headers_in['Accept-Language'] which
should be set to something similar to:
'fr,en-us;q=0.7,en;q=0.3'
"""
tmp_langs = {}
for lang in accept_language_header.split(','):
lang = lang.split(';q=')
if len(lang) == 2:
lang[1] = lang[1].replace('"', '') # Hack for Yeti robot
try:
tmp_langs[float(lang[1])] = lang[0]
except ValueError:
pass
else:
tmp_langs[1.0] = lang[0]
ret = []
priorities = tmp_langs.keys()
priorities.sort()
priorities.reverse()
for priority in priorities:
ret.append(tmp_langs[priority])
return ret
uid = getUid(req)
guest = isGuestUser(uid)
new_lang = None
preferred_lang = None
if not guest:
user_preferences = get_user_preferences(uid)
preferred_lang = new_lang = user_preferences.get('language', None)
if not new_lang:
try:
new_lang = wash_languages(cgi.parse_qs(req.args)['ln'])
except (TypeError, AttributeError, KeyError):
pass
if not new_lang:
try:
new_lang = wash_languages(_get_language_from_req_header(req.headers_in['Accept-Language']))
except (TypeError, AttributeError, KeyError):
pass
new_lang = wash_language(new_lang)
if new_lang != preferred_lang and not guest:
user_preferences['language'] = new_lang
set_user_preferences(uid, user_preferences)
return new_lang
def collect_user_info(req, login_time=False, refresh=False):
"""Given the mod_python request object rec or a uid it returns a dictionary
containing at least the keys uid, nickname, email, groups, plus any external keys in
the user preferences (collected at login time and built by the different
external authentication plugins) and if the mod_python request object is
provided, also the remote_ip, remote_host, referer, agent fields.
NOTE: if req is a mod_python request object, the user_info dictionary
is saved into req._user_info (for caching purpouses)
setApacheUser & setUid will properly reset it.
"""
from invenio.search_engine import get_permitted_restricted_collections
user_info = {
'remote_ip' : '',
'remote_host' : '',
'referer' : '',
'uri' : '',
'agent' : '',
'uid' : -1,
'nickname' : '',
'email' : '',
'group' : [],
'guest' : '1',
'session' : None,
'precached_permitted_restricted_collections' : [],
'precached_usebaskets' : False,
'precached_useloans' : False,
'precached_usegroups' : False,
'precached_usealerts' : False,
'precached_usemessages' : False,
'precached_viewsubmissions' : False,
'precached_useapprove' : False,
'precached_useadmin' : False,
'precached_usestats' : False,
'precached_viewclaimlink' : False,
'precached_usepaperclaim' : False,
'precached_usepaperattribution' : False,
}
try:
is_req = False
if not req:
uid = -1
elif type(req) in (type(1), type(1L)):
## req is infact a user identification
uid = req
elif type(req) is dict:
## req is by mistake already a user_info
try:
assert(req.has_key('uid'))
assert(req.has_key('email'))
assert(req.has_key('nickname'))
except AssertionError:
## mmh... misuse of collect_user_info. Better warn the admin!
register_exception(alert_admin=True)
user_info.update(req)
return user_info
else:
is_req = True
uid = getUid(req)
if hasattr(req, '_user_info') and not login_time:
user_info = req._user_info
if not refresh:
return req._user_info
req._user_info = user_info
try:
user_info['remote_ip'] = req.remote_ip
except gaierror:
#FIXME: we should support IPV6 too. (hint for FireRole)
pass
user_info['session'] = get_session(req).sid()
user_info['remote_host'] = req.remote_host or ''
user_info['referer'] = req.headers_in.get('Referer', '')
user_info['uri'] = req.unparsed_uri or ()
user_info['agent'] = req.headers_in.get('User-Agent', 'N/A')
user_info['uid'] = uid
user_info['nickname'] = get_nickname(uid) or ''
user_info['email'] = get_email(uid) or ''
user_info['group'] = []
user_info['guest'] = str(isGuestUser(uid))
if user_info['guest'] == '1' and CFG_INSPIRE_SITE:
usepaperattribution = False
viewclaimlink = False
if (CFG_BIBAUTHORID_ENABLED
and acc_is_user_in_role(user_info, acc_get_role_id("paperattributionviewers"))):
usepaperattribution = True
# if (CFG_BIBAUTHORID_ENABLED
# and usepaperattribution
# and acc_is_user_in_role(user_info, acc_get_role_id("paperattributionlinkviewers"))):
# viewclaimlink = True
if is_req:
session = get_session(req)
viewlink = False
try:
viewlink = session['personinfo']['claim_in_process']
except (KeyError, TypeError):
viewlink = False
else:
viewlink = False
-
+
if (CFG_BIBAUTHORID_ENABLED
and usepaperattribution
- and viewlink):
+ and viewlink):
viewclaimlink = True
user_info['precached_viewclaimlink'] = viewclaimlink
user_info['precached_usepaperattribution'] = usepaperattribution
if user_info['guest'] == '0':
user_info['group'] = [group[1] for group in get_groups(uid)]
prefs = get_user_preferences(uid)
login_method = prefs['login_method']
login_object = CFG_EXTERNAL_AUTHENTICATION[login_method]
if login_object and ((datetime.datetime.now() - get_last_login(uid)).seconds > 3600):
## The user uses an external authentication method and it's a bit since
## she has not performed a login
if not CFG_EXTERNAL_AUTH_USING_SSO or (
is_req and req.is_https()):
## If we're using SSO we must be sure to be in HTTPS
## otherwise we can't really read anything, hence
## it's better skeep the synchronization
try:
groups = login_object.fetch_user_groups_membership(user_info['email'], req=req)
# groups is a dictionary {group_name : group_description,}
new_groups = {}
for key, value in groups.items():
new_groups[key + " [" + str(login_method) + "]"] = value
groups = new_groups
except (AttributeError, NotImplementedError, TypeError, InvenioWebAccessExternalAuthError):
pass
else: # Groups synchronization
from invenio.webgroup import synchronize_external_groups
synchronize_external_groups(uid, groups, login_method)
user_info['group'] = [group[1] for group in get_groups(uid)]
try:
# Importing external settings
new_prefs = login_object.fetch_user_preferences(user_info['email'], req=req)
for key, value in new_prefs.items():
prefs['EXTERNAL_' + key] = value
except (AttributeError, NotImplementedError, TypeError, InvenioWebAccessExternalAuthError):
pass
else:
set_user_preferences(uid, prefs)
prefs = get_user_preferences(uid)
run_sql('UPDATE user SET last_login=NOW() WHERE id=%s', (uid, ))
if prefs:
for key, value in prefs.iteritems():
user_info[key.lower()] = value
if login_time:
## Heavy computational information
from invenio.access_control_engine import acc_authorize_action
if CFG_WEBSEARCH_PERMITTED_RESTRICTED_COLLECTIONS_LEVEL > 0:
user_info['precached_permitted_restricted_collections'] = get_permitted_restricted_collections(user_info)
user_info['precached_usebaskets'] = acc_authorize_action(user_info, 'usebaskets')[0] == 0
user_info['precached_useloans'] = acc_authorize_action(user_info, 'useloans')[0] == 0
user_info['precached_usegroups'] = acc_authorize_action(user_info, 'usegroups')[0] == 0
user_info['precached_usealerts'] = acc_authorize_action(user_info, 'usealerts')[0] == 0
user_info['precached_usemessages'] = acc_authorize_action(user_info, 'usemessages')[0] == 0
user_info['precached_usestats'] = acc_authorize_action(user_info, 'runwebstatadmin')[0] == 0
user_info['precached_viewsubmissions'] = isUserSubmitter(user_info)
user_info['precached_useapprove'] = isUserReferee(user_info)
user_info['precached_useadmin'] = isUserAdmin(user_info)
usepaperclaim = False
usepaperattribution = False
viewclaimlink = False
if (CFG_BIBAUTHORID_ENABLED
and acc_is_user_in_role(user_info, acc_get_role_id("paperclaimviewers"))):
usepaperclaim = True
if (CFG_BIBAUTHORID_ENABLED
and acc_is_user_in_role(user_info, acc_get_role_id("paperattributionviewers"))):
usepaperattribution = True
-
+
if is_req:
session = get_session(req)
viewlink = False
try:
viewlink = session['personinfo']['claim_in_process']
except (KeyError, TypeError):
viewlink = False
else:
viewlink = False
-
+
if (CFG_BIBAUTHORID_ENABLED
and usepaperattribution
- and viewlink):
+ and viewlink):
viewclaimlink = True
# if (CFG_BIBAUTHORID_ENABLED
# and ((usepaperclaim or usepaperattribution)
# and acc_is_user_in_role(user_info, acc_get_role_id("paperattributionlinkviewers")))):
# viewclaimlink = True
user_info['precached_viewclaimlink'] = viewclaimlink
user_info['precached_usepaperclaim'] = usepaperclaim
user_info['precached_usepaperattribution'] = usepaperattribution
except Exception, e:
register_exception()
return user_info
diff --git a/modules/webstat/lib/webstatadmin.py b/modules/webstat/lib/webstatadmin.py
index 196b0f9a9..5dbd0e7c9 100644
--- a/modules/webstat/lib/webstatadmin.py
+++ b/modules/webstat/lib/webstatadmin.py
@@ -1,247 +1,248 @@
## $id: webstatadmin.py,v 1.28 2007/04/01 23:46:46 tibor exp $
##
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
__lastupdated__ = "$Date$"
import sys, webstat
from invenio.dbquery import run_sql
from invenio.bibtask import task_init, task_get_option, task_set_option, \
task_has_option, task_update_progress, write_message
from invenio.webstat_config import CFG_WEBSTAT_CONFIG_PATH
+from invenio.config import CFG_SITE_RECORD
def main():
"""Main dealing with all the BibTask magic."""
task_init(authorization_action="runwebstatadmin",
authorization_msg="Webstat Administrator",
description="Description: %s Creates/deletes custom events. Can be set\n"
" to cache key events and previously defined custom events.\n" % sys.argv[0],
help_specific_usage=" -n, --new-event=ID create a new custom event with the human-readable ID\n"
" -r, --remove-event=ID remote the custom event with id ID and all its data\n"
" -S, --show-events show all currently available custom events\n"
" -c, --cache-events=CLASS|[ID] caches the events defined by the class or IDs, e.g.:\n"
" -c ALL\n"
" -c KEYEVENTS\n"
" -c CUSTOMEVENTS\n"
" -c 'event id1',id2,'testevent'\n"
" -d,--dump-config dump default config file\n"
" -e,--load-config create the custom events described in config_file\n"
"\nWhen creating events (-n) the following parameters are also applicable:\n"
" -l, --event-label=NAME set a descriptive label to the custom event\n"
" -a, --args=[NAME] set column headers for additional custom event arguments\n"
" (e.g. -a country,person,car)\n",
version=__revision__,
specific_params=("n:r:Sl:a:c:de", ["new-event=", "remove-event=", "show-events",
"event-label=", "args=", "cache-events=", "dump-config",
"load-config"]),
task_submit_elaborate_specific_parameter_fnc=task_submit_elaborate_specific_parameter,
task_submit_check_options_fnc=task_submit_check_options,
task_run_fnc=task_run_core)
def task_submit_elaborate_specific_parameter(key, value, opts, args):
"""
Given the string key it checks it's meaning, eventually using the value.
Usually it fills some key in the options dict. It must return True if
it has elaborated the key, False, if it doesn't know that key. eg:
"""
if key in ("-n", "--new-event"):
task_set_option("create_event_with_id", value)
elif key in ("-r", "--remove-event"):
task_set_option("destroy_event_with_id", value)
elif key in ("-S", "--show-events"):
task_set_option("list_events", True)
elif key in ("-l", "--event-label"):
task_set_option("event_name", value)
elif key in ("-a", "--args"):
task_set_option("column_headers", value.split(','))
elif key in ("-c", "--cache-events"):
task_set_option("cache_events", value.split(','))
elif key in ("-d", "--dump-config"):
task_set_option("dump_config", True)
elif key in ("-e", "--load-config"):
task_set_option("load_config", True)
else:
return False
return True
def task_submit_check_options():
"""
NOTE: Depending on the parameters, either "BibSched mode" or plain
straigh-forward execution mode is entered.
"""
if task_has_option("create_event_with_id"):
print webstat.create_customevent(task_get_option("create_event_with_id"),
task_get_option("event_name", None),
task_get_option("column_headers", []))
sys.exit(0)
elif task_has_option("destroy_event_with_id"):
print webstat.destroy_customevent(task_get_option("destroy_event_with_id"))
sys.exit(0)
elif task_has_option("list_events"):
events = webstat._get_customevents()
if len(events) == 0:
print "There are no custom events available."
else:
print "Available custom events are:\n"
print '\n'.join([x[0] + ": " + ((x[1] == None) and "No descriptive name" or str(x[1])) for x in events])
sys.exit(0)
elif task_has_option("cache_events"):
events = task_get_option("cache_events")
write_message(str(events), verbose=9)
if events[0] == 'ALL':
keyevents_to_cache = webstat.KEYEVENT_REPOSITORY.keys()
customevents_to_cache = [x[0] for x in webstat._get_customevents()]
elif events[0] == 'KEYEVENTS':
keyevents_to_cache = webstat.KEYEVENT_REPOSITORY.keys()
customevents_to_cache = []
elif events[0] == 'CUSTOMEVENTS':
keyevents_to_cache = []
customevents_to_cache = [x[0] for x in webstat._get_customevents()]
elif events[0] != '':
keyevents_to_cache = [x for x in webstat.KEYEVENT_REPOSITORY.keys() if x in events]
customevents_to_cache = [x[0] for x in webstat._get_customevents() if x in events]
# Control so that we have valid event names
if len(keyevents_to_cache + customevents_to_cache) == 0:
# Oops, no events. Abort and display help.
return False
else:
task_set_option("keyevents", keyevents_to_cache)
task_set_option("customevents", customevents_to_cache)
return True
elif task_has_option("dump_config"):
print """\
[general]
visitors_box = True
search_box = True
record_box = True
bibsched_box = True
basket_box = True
apache_box = True
uptime_box = True
[webstat_custom_event_1]
name = baskets
param1 = action
param2 = basket
param3 = user
[apache_log_analyzer]
profile = nil
nb-histogram-items-to-print = 20
exclude-ip-list = ("137.138.249.162")
home-collection = "Atlantis Institute of Fictive Science"
search-interface-url = "/?"
-detailed-record-url = "/record/"
+detailed-record-url = "/%s/"
search-engine-url = "/search?"
search-engine-url-old-style = "/search.py?"
basket-url = "/yourbaskets/"
add-to-basket-url = "/yourbaskets/add"
display-basket-url = "/yourbaskets/display"
display-public-basket-url = "/yourbaskets/display_public"
alert-url = "/youralerts/"
display-your-alerts-url = "/youralerts/list"
display-your-searches-url = "/youralerts/display"
-"""
+""" % CFG_SITE_RECORD
sys.exit(0)
elif task_has_option("load_config"):
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read(CFG_WEBSTAT_CONFIG_PATH)
for section in conf.sections():
if section[:21] == "webstat_custom_event_":
cols = []
name = ""
for option, value in conf.items(section):
if option == "name":
name = value
if option[:5] == "param":
# add the column name in it's position
index = int(option[-1]) - 1
while len(cols) <= index:
cols.append("")
cols[index] = value
if name:
res = run_sql("SELECT COUNT(id) FROM staEVENT WHERE id = %s", (name, ))
if res[0][0] == 0:
# name does not exist, create customevent
webstat.create_customevent(name, name, cols)
else:
# name already exists, update customevent
webstat.modify_customevent(name, cols=cols)
sys.exit(0)
else:
# False means that the --help should be displayed
return False
def task_run_core():
"""
When this function is called, the tool has entered BibSched mode, which means
that we're going to cache events according to the parameters.
"""
write_message("Initiating rawdata caching")
task_update_progress("Initating rawdata caching")
# Cache key events
keyevents = task_get_option("keyevents")
if keyevents and len(keyevents) > 0:
for i in range(len(keyevents)):
write_message("Caching key event 1: %s" % keyevents[i])
webstat.cache_keyevent_trend(keyevents)
task_update_progress("Part 1/2: done %d/%d" % (i + 1, len(keyevents)))
# Cache custom events
customevents = task_get_option("customevents")
if len(customevents) > 0:
for i in range(len(customevents)):
write_message("Caching custom event 1: %s" % customevents[i])
webstat.cache_customevent_trend(customevents)
task_update_progress("Part 2/2: done %d/%d" % (i + 1, len(customevents)))
write_message("Finished rawdata caching succesfully")
task_update_progress("Finished rawdata caching succesfully")
return True
diff --git a/modules/webstyle/doc/admin/webstyle-admin-guide.webdoc b/modules/webstyle/doc/admin/webstyle-admin-guide.webdoc
index 5b750cbcc..f8c66d083 100644
--- a/modules/webstyle/doc/admin/webstyle-admin-guide.webdoc
+++ b/modules/webstyle/doc/admin/webstyle-admin-guide.webdoc
@@ -1,516 +1,516 @@
## -*- mode: html; coding: utf-8; -*-
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebStyle Admin Guide -->
<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<ul style="list-style-type:None">
<li><strong>1. <a href="#overview_page_layout">Overview</a></strong>
<ul style="list-style-type:None">
<li>1.1&nbsp;&nbsp;<a href="#overview_css">CSS Style Sheet and Images</a></li>
<li>1.2&nbsp;&nbsp;<a href="#overview_page_layout">HTML Page Layout</a>
<ul style="list-style-type:None">
<li>1.2.1&nbsp;&nbsp;&nbsp;&nbsp;<a href="#overview_page_layout_stat">Layout of HTML Static Pages</a></li>
<li>1.2.2&nbsp;&nbsp;&nbsp;&nbsp;<a href="#overview_page_layout_dyn">Layout of Python Dynamic Pages</a></li>
</ul>
</li>
<li>1.3&nbsp;&nbsp;<a href="#overview_bib">Look of Bibliographic References</a></li>
<li>1.4&nbsp;&nbsp;<a href="#overview_spec_conf">Specific Configurations</a></li>
</ul>
</li>
<li><strong>2. <a href="#det_page">Detailed Record Pages</a></strong>
<ul style="list-style-type:None">
<li>2.1&nbsp;&nbsp;<a href="#det_page">Available tabs</a></li>
<li>2.2&nbsp;&nbsp;<a href="#det_show_hide_tabs">Showing/Hiding tabs</a></li>
<li>2.3&nbsp;&nbsp;<a href="#det_page_cust_cont_tabs">Customizing content of tabs</a></li>
<li>2.4&nbsp;&nbsp;<a href="#det_page_cust_look_tabs">Customizing look of tabs</a></li>
</ul>
</li>
</ul>
<h2><a name="overview">1. Overview</a></h2>
<p>This document describes how to change the look and feel of your CDS
Invenio installation.
<h3><a name="overview_css">1.1 CSS Style Sheet and Images</a></h3>
<p>The most obvious modification you may want to do is the
modification of <a href="<CFG_SITE_URL>/img/invenio.css">CSS style sheet</a>.
You may also customize default <a href="<CFG_SITE_URL>/img/">images</a>.</p>
<h3><a name="overview_page_layout">1.2 HTML Page Layout</a></h3>
<p>The customization of the general page look and feel is currently
different depending on whether you customize HTML-like static pages
or dynamic Python pages.</p>
<p>Dynamic HTML pages are used to build the 'interactive' parts of the
website, such as the search and browse pages, as well as the admin
pages. The content of these pages is defined at run time, depending on
the users parameters. You can modify them to provide a totally
different experience to your users when browsing <CFG_SITE_NAME>. Most
probably, you will only want to customize 'webstyle_templates.py',
which define the headers and footer of a page.</p>
<p>Static HTML pages are used for basic pages that do not embed
dynamic content, such as this guide. They help reducing the load of
the server and speed up pages serving. As you will see, even HTML
static pages can still contain some values defined at run time, but
these are reduced to the minimum compared to dynamic pages. Most
probably you will not want to modify these pages, since the small
amount of dynamic content they have allow these pages to inherit from
the customizations you have made elsewhere, such as in the CSS, and the
page header and footer.</p>
<h4><a name="overview_page_layout_stat">1.2.1 Layout of HTML Static Pages</a></h4>
<p>Static HTML pages are all located in the /opt/invenio/lib/webdoc/
installation directory. These files are organized in 3 directories:</p>
<ul>
<li><b>help:</b> Help pages available to users of your website</li>
<li><b>admin:</b> Mostly guides for admin users</li>
<li><b>hacking:</b> Mostly guides for administrators and developers</li>
</ul>
<p>
These directories do not contain the <code>.html</code> files, but
<code>.webdoc</code> files. These '<em>WebDoc</em>' files are
basically HTML with the following main differences:
</p>
<ul>
<li>They only contain the body of your page (only content of the
<code>&lt;body&gt;</code> tag)
</li>
<li>You can make use of special tags such as
<code>&lt;CFG_SITE_URL&gt;</code> and <code>&lt;CFG_SITE_SUPPORT_EMAIL&gt;</code>,
that will be replaced by <CFG_SITE_URL> and <CFG_SITE_SUPPORT_EMAIL>, the values that
you should have configured in your <code>config.py</code> file.
</li>
<li>You can internationalize the content. For example:
<pre>
&lt;lang&gt;
&lt;en&gt;Book&lt;/en&gt;
&lt;fr&gt;Livre&lt;/fr&gt;
&lt;de&gt;Buch&lt;/de&gt;
&lt;/lang&gt;
</pre>
will be replaced by "Book" if the user has chosen to display the English version of your site,
"Livre" in French or "Buch" in German.
</li>
</ul>
<p>Read the <a href="<CFG_SITE_URL>/help/hacking/webstyle-webdoc-syntax">WebDoc syntax guide</a> to learn more about details of the WebDoc syntax.</p>
<p>The advantage of not using raw HTML for these static pages is that
they can for example reuse the header and footer that you have defined
for dynamic pages, and make use of the variables you have defined in
your invenio.conf file. In that way you should not need to adapt them to
your needs: <b>they</b> will adapt themselves to your needs.</p>
<p>Any modification should be immediatly visible when looking at the
pages from the web: the pages are built "dynamically" and then cached.
If you want to force the cache of the pages, use the WebDoc CLI
(type <code>/opt/invenio/bin/webdoc --help</code> to learn more about it).</p>
<h4><a name="overview_page_layout_dyn">1.2.2 Layout of Python Dynamic Pages</a></h4>
<p>The dynamic Python-powered pages can be customized by making use of
Invenio templating system that uses a notion of a template skin.
How this works?</p>
<p>When you edit <code>invenio-local.conf</code> during installation
or later during runtime (when during runtime, you have to
run <code>inveniocfg --update-config-py</code> after editing the conf
file), you may choose to use your own templates instead of the
provided default ones by editing
<code>CFG_WEBSTYLE_TEMPLATE_SKIN</code> variable. Let us say you
put <code>ithaca</code> there in order to use your
own <code>ithaca</code> style. Now, when you start Apache, then
instead of Invenio's usual template files such
as <code>webbasket_templates.py</code> the system will look for file
named <code>webbasket_templates_ithaca.py</code> and will load the
template functions from there instead, provided that they exist.
(Otherwise it would fall back to the default ones.)</p>
<p>How do you create such an <code>ithaca</code> style templates file?
We do not use one of many existing templating frameworks in Python but
a very simple programmer-friendly templating system that enables you
to use the full power of Python to inherit from the default templates
the output generating functions you want to reuse and to write anew
only the functions you would like to modify.</p>
<p>Let's show an example of how to modify the page footer. Create a
file named <code>webstyle_templates_ithaca.py</code> with the
following content:</p>
<blockquote>
<pre>
from invenio.config import CFG_SITE_LANG
from invenio.webstyle_templates import Template as DefaultTemplate
class Template(DefaultTemplate):
"""Ithaca style templates."""
def tmpl_pagefooter(self, req=None, ln=CFG_SITE_LANG, lastupdated=None,
pagefooteradd=""):
"""
Ithaca style page footer. See the default function for
the meaning of parameters.
"""
out = ""
out += """&lt;hr>This site has no footer.
&lt;/body>
&lt;/html>"""
return out
</pre>
</blockquote>
<p>After the file was created, restart Apache and lo, your new ithaca
style footer will be seen in action.</p>
<p>(A side comment: note that <code>tmpl_page_footer()</code> is an
ideal place to put any local code you may want to execute at the end
of web request processing when the main page content was just served
to the user. As an example, if you are using Google Analytics, you
may want to put just after the above <code>out = ""</code> statement
your GA script code:</p>
</blockquote>
<pre>
[...]
out += """&lt;script src="http://www.google-analytics.com/urchin.js"
type="text/javascript">
&lt;/script>
&lt;script type="text/javascript">
_uacct = "UA-1234567-8";
urchinTracker();
&lt;/script>"""
[...]
</pre>
</blockquote>
End of the side comment.)
<p>Some further remarks on this templating system:</p>
<ul>
<li>We have observed that in practice the HTML page designers were
ofter Python programmers, therefore we have adopted a
programmer-friendly templating system.</li>
<li>You have to know a bit of Python in order to use it. If you don't
know Python, do not worry, because you can basically copy and
paste the original <code>tmpl_foo()</code> function definition "as
is" into the above-cited example and then you would only modify
its HTML snippets. The important thing is to preserve the imports
(<code>from invenio.config import CFG_SITE_LANG</code>) as in the original
<code>webstyle_templates.py</code> file and to preserve the
leading whitespace Pythonic indentation.</li>
<li>You do not have to learn "yet another templating language", you
can use the full power of Python. The <code>tmpl_foo()</code>
functions do not contain any business logic in them, their only
purpose is to make the HTML presentation of data supplied to them.
But, should you need to carry out a little data transformation,
you can do it within the <code>tmpl_foo()</code> function itself,
thanks to the full Python power.</li>
<li>If you feel like doing so, you can modify all
the <code>tmpl_foo()</code> functions across all Invenio
modules in a way that will completely change the presentation of
elements including their content, position and order on the
screen.</li>
<li>In practice, it is sufficient to modify the CSS and the
webstyle_templates_ithaca.py (and possibly
websearch_templates_ithaca.py) files to achieve most important
customizations.</li>
<li>If you would like to discover which method of which template
generate which region on the web page, you can switch on
the <tt>CFG_WEBSTYLE_INSPECT_TEMPLATES</tt> configuration variable
in your <tt>invenio-local.conf</tt> file and rerun <code>sudo -u
apache /opt/invenio/bin/inveniocfg
--update-config-py</code>. Then, after optionally running
<code>bibreformat -a</code> and <code>webcoll -f</code> (if you
want to debug search pages) and after having restarted your Apache
server (in every case), you will find in your browser that a
place-mark has been put next to every region of every page, and
that you can hover your mouse pointer over any region of the page
in order to discover which module/method/parameters have been used
to generate it. This is useful for debugging Python templates
and/or for understanding which part of code generates which HTML
snippet in the output.</li>
<li>We expect to provide possibly more than one skin with the default
distribution, so if you have modified Invenio look and feel in
an interesting way, please consider donating us your templates.</li>
<li>When upgrading from one Invenio release to another, you may
find out that the default templates have changed in a way that
requires changes to your templates (such as an addition of
parameters to cover the new functionality). This is inevitable in
any templating system; unless you introduce new parameters, you
would not see them being printed. Therefore, if you have
modified <code>tmpl_foo()</code> and <code>tmpl_bar()</code>, and
you are ugrading to a new release, you may at least briefly check
whether the function arguments are the same. A quick check of the
body would be helpful too, in case the new release fixed some
display-related problems in these functions.
<br/><br/>
In order to help you in this task, we provide a tool to check
incompatibilities between your customized templates and the default
templates.<br/>
This tool can be run before doing a <code>'make install'</code>,
therefore giving you a chance to fix your templates before
upgrading. Just run <code>'make check-custom-templates'</code> to
get the list of problems found with your templates. <br /><br />
You can also run this tool any time after the new default
templates have been installed, in order to ensure that
modifications you have done to your templates are valid. To do so
move to your Invenio installation directory, and run: <br/>
<code>$ python /opt/invenio/lib/python/invenio/template.py
--check-custom-templates</code>
</li>
</ul>
<h3><a name="overview_bib">1.3 Look of Bibliographic
References</a></h3>
<p>Bibliographic metadata is formatted using
<a href="bibformat-admin">BibFormat</a>. Read the
<a href="bibformat-admin-guide">BibFormat documentation</a>
for more information.</p>
<h3><a name="overview_spec_conf">1.4 Specific Configurations</a></h3>
<p>Note that the search interface pages may be modified to a large
extent in the <a href="websearch-admin">WebSearch Admin
Interface</a> by adding HTML portalboxes on various places on the page
(right top, before/after page title, before/after narrow by collection
boxes, etc).</p>
<h2><a name="det_page">2. Detailed Record Pages</a></h2>
<p>The web pages displaying the details of a record (such as
-<a href="<CFG_SITE_URL>/record/1"><CFG_SITE_URL>/record/1</a>) do not only show metadata, but
+<a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/1"><CFG_SITE_URL>/<CFG_SITE_RECORD>/1</a>) do not only show metadata, but
also users' comments and reviews, statistics, etc. This information is
organized into tabs.</p>
<p>The content of these tabs can be customized on a collection basis. It
is also possible to show/hide tabs depending on the displayed
collection.</p>
<p>The detailed record pages also feature a mini panel at the bottom of
the page that links to popular functions (The mini panel is only
displayed when <em>Information</em> tab is selected).</p>
<pre>
+--------------Detailed record page-------------+
| header |
|nav. breadcrumb |
| |
| .--------------------------------------. |
| .-|Info.|Ref.|Discussion.|Stats.|Files |-. |
| | '--------------------------------------' | |
| | | |
| | content | |
| | | |
| '------------------------------------------' |
| |
| .---------------(Mini Panel)---------------. |
| | Mini | Mini | Mini | |
| | File | Review | Actions | |
| '------------------------------------------' |
+-----------------------------------------------+
</pre>
<h4><a name="det_page_avail_tabs">2.1 Available tabs</a></h4>
The following tabs are available:
<table border="1">
<tr>
<th>Name</th>
<th>Description</th>
<th>URL (eg. for record '10')</th>
</tr>
<tr>
<td>Information</td>
<td>Show the formatted metadata of the record</td>
- <td><a href="<CFG_SITE_URL>/record/10"><CFG_SITE_URL>/record/10</a></td>
+ <td><a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/10"><CFG_SITE_URL>/<CFG_SITE_RECORD>/10</a></td>
</tr>
<tr>
<td>References</td>
<td>Displays the references (bibliography) of the record</td>
- <td><a href="<CFG_SITE_URL>/record/10/references"><CFG_SITE_URL>/record/10/references</a></td>
+ <td><a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/10/references"><CFG_SITE_URL>/<CFG_SITE_RECORD>/10/references</a></td>
</tr>
<tr>
<td>Discussion</td>
<td>Displays the users' comments and reviews</td>
- <td><a href="<CFG_SITE_URL>/record/10/comments"><CFG_SITE_URL>/record/10/comments</a><br/>
- <a href="<CFG_SITE_URL>/record/10/reviews"><CFG_SITE_URL>/record/10/reviews</a></td>
+ <td><a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/10/comments"><CFG_SITE_URL>/<CFG_SITE_RECORD>/10/comments</a><br/>
+ <a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/10/reviews"><CFG_SITE_URL>/<CFG_SITE_RECORD>/10/reviews</a></td>
</tr>
<tr>
<td>Usage Statistics</td>
<td>Statistics data and graph about file downloads/views</td>
- <td><a href="<CFG_SITE_URL>/record/10/usage"><CFG_SITE_URL>/record/10/usage</a></td>
+ <td><a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/10/usage"><CFG_SITE_URL>/<CFG_SITE_RECORD>/10/usage</a></td>
</tr>
<tr><td>Files</td>
<td>Link(s) to full-text and associated file(s)</td>
- <td><a href="<CFG_SITE_URL>/record/10/files"><CFG_SITE_URL>/record/10/files</a></td>
+ <td><a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/10/files"><CFG_SITE_URL>/<CFG_SITE_RECORD>/10/files</a></td>
</tr>
</table>
<p>The mini panel is only displayed when the <em>Information</em> tab is
selected. It is divided into the following sections:</p>
<ul>
<li>Files : quick access to full-text file(s)</li>
<li>Review : quick access to reviewing feature</li>
<li>Actions: quick access to several other features</li>
</ul>
<h4><a name="det_page_show_hide_tabs">2.2 Showing/Hiding tabs</a></h4>
<p>The <a href="websearch-admin">WebSearch admin web
interface</a> lets you decide for each collection which tabs are to be
displayed. Choose a collection to edit in the collection tree and go
to its <em>detailed record page options</em>. From there you can select which
tabs to show for that collection.</p>
<p>If you want to apply these settings to the subcollections, select
<em><input type="checkbox" checked="checked" disabled="disabled">Also apply to subcollections</input></em>
before you click on the
<input type="submit" value="Modify" class="formbutton" disabled="disabled"/>
button.</p>
<p>Note that these settings only affect the tabs, not the content of the
tabs: even if a tab is not displayed, it is still possible to access
its content using its usual url. This is useful if you decide to
completely change the detailed record pages, dropping the tab-metaphor
(eg. for a side bar) but still want to access the comments, reviews,
etc pages.</p>
<p>Here are some behaviours you should expect when changing the tabs
configuration:</p>
<ul>
- <li>Given that search results pages always link to <CFG_SITE_URL>/record/10,
+ <li>Given that search results pages always link to <CFG_SITE_URL>/<CFG_SITE_RECORD>/10,
and given the above comment about accessibility of tabs when they
are not displayed, the content of the <em>Information</em> will always be
show when clicking on <a href="#" class="moreinfo">detailed record</a> link in search results, even
if the <em>Information</em> tab is set not to be displayed.</li>
<li>If you select only 1 tab, none of the tabs will be displayed at the
top of the page. This also means that whatever tabs you have
selected, you users will always see the content of the 'Information'
tabs (see above behaviour).</li>
<li>If you select 0 tab, only the content of <em>Information</em> tab is shown.
None of the tabs, nor the border that usually surrounds the content
of the tabs, nor the minipanel are shown. You should choose this
option if you decide to drop the tabs metaphor for the detailed
record pages. You can then build your own user interface on this
almost blank page (See <a href="#det_page_cust_cont_tabs">Customizing content of tabs</a>).</li>
<li>Note that <em>Discussion</em> tab will not be shown if you have disabled
both the commenting and reviewing features in your installation
(<code>CFG_WEBCOMMENT_ALLOW_COMMENTS</code> and
<code>CFG_WEBCOMMENT_ALLOW_REVIEWS</code> variable in your config file).</li>
</ul>
<h4><a name="det_page_cust_cont_tabs">2.3 Customizing content of tabs</a></h4>
<p>The contents of tabs are defined in the following ways:</p>
<dl>
<dt><strong><em>Information</em> tab</strong></dt>
<dd>The content of this tab is defined by function
<code>tmpl_detailed_record_metadata(..)</code> in <code>websearch_templates.py</code>.
By default <code>tmpl_detailed_record_metadata</code> simply returns the result
of the formatting of the metadata by BibFormat using the "HD" output
format. It can therefore be collection-specific.</dd>
<dt><strong><em>References</em> tab</strong></dt>
<dd>The content of this tab is defined by function
<code>tmpl_detailed_record_references(..)</code> in <code>websearch_templates.py</code>.
By default <code>tmpl_detailed_record_metadata</code> simply returns the result
of the formatting of the metadata by BibFormat using the "HDREF"
output format. If the result returned by BibFormat is empty, the tab
is disabled (visible, but not clickable). It can therefore be
collection-specific.</dd>
<dt><strong><em>Discussion</em> tab</strong></dt>
<dd>The content of this tab is mainly defined by function
<code>tmpl_get_comments(..)</code> in <code>webcomment_templates.py</code>. Other
functions in this file are also involved in the display.</dd>
<dt><strong><em>Usage Statistics</em> tab</strong></dt>
<dd>The content of this tab is defined by function
<code>tmpl_detailed_record_statistics(..)</code> in <code>websearch_templates.py</code>.
If the returned content is empty, then the tabs will be disabled
(visible, but cannot be clicked).</dd>
<dt><strong><em>Files</em> tab</strong></dt>
<dd>The content of this tab is defined by function <code>tmpl_filelist(..)</code>
in <code>websubmit_templates.py</code>.</dd>
</dl>
<br/>
<p>The content of the mini panel is defined in the following ways:</p>
<dl>
<dt><strong><em>Files</em></strong></dt>
<dd>The content of this section is defined by the output format
'HDFILE'. It can therefore be collection-specific.</dd>
<dt><strong><em>Review</em></strong></dt>
<dd>The content of this section is defined by function
<code>tmpl_mini_review(..)</code> inside <code>webcomment_templates.py</code></dd>
<dt><strong><em>Actions</em></strong></dt>
<dd>The content of this section is defined by the output format
<code>HDACT</code>. It can therefore be collection-specific.</dd>
</dl>
<h4><a name="det_page_cust_look_tabs">2.4 Customizing look of tabs</a></h4>
<p>You can customize how tabs look like, as well change the look of the
border that surrounds the content of tabs. The mini panel can
similarly be customized.</p>
<p>Have a look at the following classes in the CDS css stylesheet:</p>
<ul>
<li><code>detailedrecordtabs</code></li>
<li><code>detailedrecordbox</code></li>
<li><code>detailedrecordminipanel</code></li>
<li><code>top-left, top-right, bottom-left, bottom-right</code></li>
<li><code>detailedrecordminipanel{actions,review,file}, detailedrecordshortreminder</code></li>
</ul>
<p>Note that a tab might be greyed out (disabled) when its content is
empty. This is the case for the <em>References</em> tab (see <a href="#det_page_cust_cont_tabs">Customizing
content of tabs</a> -&gt; 'References tab') and the <em>Files</em> tab (if no
file could be found for the record).<p>
<p>For more advanced modifications (like changing the HTML code of the
tabs), you can modify the <code>detailed_record_container(..)</code> and
<code>detailed_record_mini_panel(..)</code> functions inside your
<code>webstyle_templates.py</code> file.</p>
diff --git a/modules/webstyle/lib/fckeditor_invenio_connector.py b/modules/webstyle/lib/fckeditor_invenio_connector.py
index 2aee72f81..17aabe2e9 100644
--- a/modules/webstyle/lib/fckeditor_invenio_connector.py
+++ b/modules/webstyle/lib/fckeditor_invenio_connector.py
@@ -1,130 +1,130 @@
# -*- coding: utf-8 -*-
## Comments and reviews for records.
## This file is part of Invenio.
## Copyright (C) 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Invenio implementation of the connector to FCKEditor for file upload.
This is heavily borrowed from FCKeditor 'upload.py' sample connector.
"""
import os
from invenio.fckeditor.editor.filemanager.connectors.py.fckcommands import \
UploadFileCommandMixin, \
CreateFolderCommandMixin
from invenio.fckeditor.editor.filemanager.connectors.py.fckconnector import FCKeditorConnectorBase
from invenio.fckeditor.editor.filemanager.connectors.py.fckoutput import \
BaseHttpMixin, \
BaseHtmlMixin, \
BaseXmlMixin
class FCKeditorConnectorInvenio(FCKeditorConnectorBase,
UploadFileCommandMixin,
CreateFolderCommandMixin,
BaseHttpMixin, BaseHtmlMixin,
BaseXmlMixin):
"""
Invenio connector for the upload of files from the FCKeditor.
Check webcomment_webinterface for an example of use of this class.
"""
def __init__(self, args, recid, uid, allowed_commands,
allowed_types, user_files_path,
user_files_absolute_path):
"""Constructor
Parameters:
args - *dict* the arguments submitted via
GET/POST
uid - the user id of the submitted
allowed_commands - *list* commands allowed for
uploading. These are supported by
FCKeditor: ['QuickUpload', 'FileUpload',
'GetFolders', 'GetFoldersAndFiles',
'CreateFolder' ] but this connector only
support 'QuickUpload'
allowed_types - *list* types allowed for uploading. These
are supported by FCKeditor: ['File',
'Image', 'Flash', 'Media']
user_files_path - *str* the URL where the file should be
accessible after upload. Eg:
- (CFG_SITE_URL)s/record/%(recid)i/comments/attachments/get/%(uid)s
+ %(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)i/comments/attachments/get/%(uid)s
(the next parts of the URL will be append
automatically)
user_files_absolute_path - *str* the path to the directory on the
server where the files will be saved. Eg:
%(CFG_PREFIX)s/var/data/comments/%(recid)s/%(uid)s
(the next parts of the path will be append
automatically)
"""
self.request = args
self.headers = [] # Clean Headers
self.uid = uid
self.recid = recid
self.allowed_commands = allowed_commands
self.allowed_types = allowed_types
self.user_files_path = user_files_path
self.user_files_absolute_path = user_files_absolute_path
def setHeader(self, key, value):
"""
Set a new header that will be sent when answering the user.
"""
self.headers.append((key, value))
return
def doResponse(self):
"""Main function. Process the request, set
headers and return a string as response.
You should check authorizations prior to calling this
function.
"""
# The file type (from the QueryString, by default 'File').
resource_type = self.request.get('type', 'File')
command = 'QuickUpload' # only supported one for the moment
# Check if it is an allowed command
if not command in self.allowed_commands:
return self.sendUploadResults( 1, '', '', 'The %s command isn\'t allowed' % command )
if not resource_type in self.allowed_types:
return self.sendUploadResults( 1, '', '', 'Invalid type specified' )
# Setup paths
self.userFilesFolder = self.user_files_absolute_path + '/' + resource_type.lower()
self.webUserFilesFolder = self.user_files_path + '/' + resource_type.lower()
# Ensure that the directory exists.
if not os.path.exists(self.userFilesFolder):
try:
self.createServerFolder(self.userFilesFolder)
except Exception, e:
return self.sendError(1, "This connector couldn\'t access to local user\'s files directories. Please check the UserFilesAbsolutePath in \"editor/filemanager/connectors/py/config.py\" and try again. ")
# File upload doesn't have to return XML, so intercept here
return self.uploadFile(resource_type, '/')
diff --git a/modules/webstyle/lib/webdoc.py b/modules/webstyle/lib/webdoc.py
index 507cb330d..90c6d6946 100644
--- a/modules/webstyle/lib/webdoc.py
+++ b/modules/webstyle/lib/webdoc.py
@@ -1,904 +1,906 @@
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
WebDoc -- Transform webdoc sources into static html files
"""
__revision__ = \
"$Id$"
from invenio.config import \
CFG_PREFIX, \
CFG_SITE_LANG, \
CFG_SITE_LANGS, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
+ CFG_SITE_RECORD, \
CFG_VERSION, \
CFG_SITE_NAME_INTL, \
CFG_CACHEDIR
from invenio.dateutils import \
convert_datestruct_to_datetext, \
convert_datestruct_to_dategui, \
convert_datecvs_to_datestruct
from invenio.messages import \
gettext_set_language, \
wash_language, \
language_list_long
import re
import getopt
import os
import sys
import time
# List of (webdoc_source_dir, webdoc_cache_dir)
webdoc_dirs = {'help':('%s/lib/webdoc/invenio/help' % CFG_PREFIX, \
'%s/webdoc/help-pages' % CFG_CACHEDIR),
'admin':('%s/lib/webdoc/invenio/admin' % CFG_PREFIX, \
'%s/webdoc/admin-pages' % CFG_CACHEDIR),
'hacking':('%s/lib/webdoc/invenio/hacking' % CFG_PREFIX, \
'%s/webdoc/hacking-pages' % CFG_CACHEDIR)}
# Regular expression for finding text to be translated
translation_pattern = re.compile(r'_\((?P<word>.*?)\)_', \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# # Regular expression for finding comments
comments_pattern = re.compile(r'^\s*#.*$', \
re.MULTILINE)
# Regular expression for finding <lang:current/> tag
pattern_lang_current = re.compile(r'<lang \s*:\s*current\s*\s*/>', \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding <lang:link/> tag
pattern_lang_link_current = re.compile(r'<lang \s*:\s*link\s*\s*/>', \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding <!-- %s: %s --> tag
# where %s will be replaced at run time
pattern_tag = r'''
<!--\s*(?P<tag>%s) #<!-- %%s tag (no matter case)
\s*:\s*
(?P<value>.*?) #description value. any char that is not end tag
(\s*-->) #end tag
'''
# List of available tags in webdoc, and the pattern to find it
pattern_tags = {'WebDoc-Page-Title': '',
'WebDoc-Page-Navtrail': '',
'WebDoc-Page-Description': '',
'WebDoc-Page-Keywords': '',
'WebDoc-Page-Header-Add': '',
'WebDoc-Page-Box-Left-Top-Add': '',
'WebDoc-Page-Box-Left-Bottom-Add': '',
'WebDoc-Page-Box-Right-Top-Add': '',
'WebDoc-Page-Box-Right-Bottom-Add': '',
'WebDoc-Page-Footer-Add': '',
'WebDoc-Page-Revision': ''
}
for tag in pattern_tags.keys():
pattern_tags[tag] = re.compile(pattern_tag % tag, \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding <lang>...</lang> tag
pattern_lang = re.compile(r'''
<lang #<lang tag (no matter case)
\s*
(?P<keep>keep=all)*
\s* #any number of white spaces
> #closing <lang> start tag
(?P<langs>.*?) #anything but the next group (greedy)
(</lang\s*>) #end tag
''', re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding <en>...</en> tag (particular case of
# pattern_lang)
pattern_CFG_SITE_LANG = re.compile(r"<("+CFG_SITE_LANG+ \
r")\s*>(.*?)(</"+CFG_SITE_LANG+r"\s*>)",
re.IGNORECASE | re.DOTALL)
# Builds regular expression for finding each known language in <lang> tags
ln_pattern_text = r"<(?P<lang>"
ln_pattern_text += r"|".join([lang[0] for lang in \
language_list_long(enabled_langs_only=False)])
ln_pattern_text += r')\s*(revision="[^"]"\s*)?>(?P<translation>.*?)</\1>'
ln_pattern = re.compile(ln_pattern_text, re.IGNORECASE | re.DOTALL)
defined_tags = {'<CFG_SITE_NAME>': CFG_SITE_NAME,
'<CFG_SITE_SUPPORT_EMAIL>': CFG_SITE_SUPPORT_EMAIL,
'<CFG_SITE_ADMIN_EMAIL>': CFG_SITE_ADMIN_EMAIL,
'<CFG_SITE_URL>': CFG_SITE_URL,
'<CFG_SITE_SECURE_URL>': CFG_SITE_SECURE_URL,
+ '<CFG_SITE_RECORD>': CFG_SITE_RECORD,
'<CFG_VERSION>': CFG_VERSION,
'<CFG_SITE_NAME_INTL>': CFG_SITE_NAME_INTL}
def get_webdoc_parts(webdoc,
parts=['title', \
'keywords', \
'navtrail', \
'body',
'lastupdated',
'description'],
categ="",
update_cache_mode=1,
ln=CFG_SITE_LANG,
verbose=0):
"""
Returns the html of the specified 'webdoc' part(s).
Also update the cache if 'update_cache' is True.
Parameters:
webdoc - *string* the name of a webdoc that can be
found in standard webdoc dir, or a webdoc
filepath. Priority is given to filepath if
both match.
parts - *list(string)* the parts that should be
returned by this function. Can be in:
'title', 'keywords', 'navtrail', 'body',
'description', 'lastupdated'.
categ - *string* (optional) The category to which
the webdoc file belongs. 'help', 'admin'
or 'hacking'. If "", look in all categories.
update_cache_mode - *int* update the cached version of the
given 'webdoc':
- 0 : do not update
- 1 : update if needed
- 2 : always update
Returns : *dictionary* with keys being in 'parts' input parameter and values
being the corresponding html part.
"""
html_parts = {}
if update_cache_mode in [1, 2]:
update_webdoc_cache(webdoc, update_cache_mode, verbose)
def get_webdoc_cached_part_path(webdoc_cache_dir, webdoc, ln, part):
"Build path for given webdoc, ln and part"
return webdoc_cache_dir + os.sep + webdoc + \
os.sep + webdoc + '.' + part + '-' + \
ln + '.html'
for part in parts:
if categ != "":
locations = [webdoc_dirs.get(categ, ('',''))]
else:
locations = webdoc_dirs.values()
for (_webdoc_source_dir, _web_doc_cache_dir) in locations:
webdoc_cached_part_path = None
if os.path.exists(get_webdoc_cached_part_path(_web_doc_cache_dir,
webdoc, ln, part)):
# Check given language
webdoc_cached_part_path = get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, ln, part)
elif os.path.exists(get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, CFG_SITE_LANG, part)):
# Check CFG_SITE_LANG
webdoc_cached_part_path = get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, CFG_SITE_LANG, part)
elif os.path.exists(get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, 'en', part)):
# Check English
webdoc_cached_part_path = get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, 'en', part)
if webdoc_cached_part_path is not None:
try:
webdoc_cached_part = file(webdoc_cached_part_path, 'r').read()
html_parts[part] = webdoc_cached_part
except IOError:
# Could not read cache file. Generate on-the-fly,
# get all the parts at the same time, and return
(webdoc_source_path, \
webdoc_cache_dir, \
webdoc_name,\
webdoc_source_modification_date, \
webdoc_cache_modification_date) = get_webdoc_info(webdoc)
webdoc_source = file(webdoc_source_path, 'r').read()
htmls = transform(webdoc_source, languages=[ln])
if len(htmls) > 0:
(lang, body, title, keywords, \
navtrail, lastupdated, description) = htmls[-1]
html_parts = {'body': body or '',
'title': title or '',
'keywords': keywords or '',
'navtrail': navtrail or '',
'lastupdated': lastupdated or '',
'description': description or ''}
# We then have all the parts, or there is no
# translation for this file (if len(htmls)==0)
break
else:
# Look in other categories
continue
if html_parts == {}:
# Could not find/read the folder where cache should
# be. Generate on-the-fly, get all the parts at the
# same time, and return
(webdoc_source_path, \
webdoc_cache_dir, \
webdoc_name,\
webdoc_source_modification_date, \
webdoc_cache_modification_date) = get_webdoc_info(webdoc)
if webdoc_source_path is not None:
try:
webdoc_source = file(webdoc_source_path, 'r').read()
htmls = transform(webdoc_source, languages=[ln])
if len(htmls) > 0:
(lang, body, title, keywords, \
navtrail, lastupdated, description) = htmls[-1]
html_parts = {'body': body or '',
'title': title or '',
'keywords': keywords or '',
'navtrail': navtrail or '',
'lastupdated': lastupdated or '',
'description': description or ''}
# We then have all the parts, or there is no
# translation for this file (if len(htmls)==0)
break
except IOError:
# Nothing we can do..
pass
return html_parts
def update_webdoc_cache(webdoc, mode=1, verbose=0, languages=CFG_SITE_LANGS):
"""
Update the cache (on disk) of the given webdoc.
Parameters:
webdoc - *string* the name of a webdoc that can be
found in standard webdoc dir, or a webdoc
filepath.
mode - *int* update cache mode:
- 0 : do not update
- 1 : only if necessary (webdoc source
is newer than its cache)
- 2 : always update
"""
if mode in [1, 2]:
(webdoc_source_path, \
webdoc_cache_dir, \
webdoc_name,\
webdoc_source_modification_date, \
webdoc_cache_modification_date) = get_webdoc_info(webdoc)
if mode == 1 and \
webdoc_source_modification_date < webdoc_cache_modification_date and \
get_mo_last_modification() < webdoc_cache_modification_date:
# Cache was updated after source. No need to update
return
(webdoc_source, \
webdoc_cache_dir, \
webdoc_name) = read_webdoc_source(webdoc)
if webdoc_source is not None:
htmls = transform(webdoc_source, languages=languages)
for (lang, body, title, keywords, \
navtrail, lastupdated, description) in htmls:
# Body
if body is not None or lang == CFG_SITE_LANG:
try:
write_cache_file('%(name)s.body%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
body,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Title
if title is not None or lang == CFG_SITE_LANG:
try:
write_cache_file('%(name)s.title%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
title,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Keywords
if keywords is not None or lang == CFG_SITE_LANG:
try:
write_cache_file('%(name)s.keywords%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
keywords,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Navtrail
if navtrail is not None or lang == CFG_SITE_LANG:
try:
write_cache_file('%(name)s.navtrail%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
navtrail,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Description
if description is not None or lang == CFG_SITE_LANG:
try:
write_cache_file('%(name)s.description%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
description,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Last updated timestamp (CVS timestamp)
if lastupdated is not None or lang == CFG_SITE_LANG:
try:
write_cache_file('%(name)s.lastupdated%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
lastupdated,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Last updated cache file
try:
write_cache_file('last_updated',
webdoc_cache_dir,
convert_datestruct_to_dategui(time.localtime()),
verbose=0)
except IOError, e:
print e
except OSError, e:
print e
if verbose > 0:
print 'Written cache in %s' % webdoc_cache_dir
def read_webdoc_source(webdoc):
"""
Returns the source of the given webdoc, along with the path to its
cache directory.
Returns (None, None, None) if webdoc cannot be found.
Parameters:
webdoc - *string* the name of a webdoc that can be
found in standard webdoc dir, or a webdoc
filepath. Priority is given to filepath if
both match.
Returns: *tuple* (webdoc_source, webdoc_cache_dir, webdoc_name)
"""
(webdoc_source_path, \
webdoc_cache_dir, \
webdoc_name,\
webdoc_source_modification_date, \
webdoc_cache_modification_date) = get_webdoc_info(webdoc)
if webdoc_source_path is not None:
try:
webdoc_source = file(webdoc_source_path, 'r').read()
except IOError:
webdoc_source = None
else:
webdoc_source = None
return (webdoc_source, webdoc_cache_dir, webdoc_name)
def get_webdoc_info(webdoc):
"""
Locate the file corresponding to given webdoc and return its
path, the path to its cache directory (even if it does not exist
yet), the last modification dates of the source and the cache, and
the webdoc name (i.e. webdoc id)
Parameters:
webdoc - *string* the name of a webdoc that can be found in
standard webdoc dirs. (Without extension '.webdoc',
hence 'search-guide', not'search-guide.webdoc'.)
Returns: *tuple* (webdoc_source_path, webdoc_cache_dir,
webdoc_name webdoc_source_modification_date,
webdoc_cache_modification_date)
"""
webdoc_source_path = None
webdoc_cache_dir = None
webdoc_name = None
last_updated_date = None
webdoc_source_modification_date = 1
webdoc_cache_modification_date = 0
for (_webdoc_source_dir, _web_doc_cache_dir) in webdoc_dirs.values():
webdoc_source_path = _webdoc_source_dir + os.sep + \
webdoc + '.webdoc'
if os.path.exists(webdoc_source_path):
webdoc_cache_dir = _web_doc_cache_dir + os.sep + webdoc
webdoc_name = webdoc
webdoc_source_modification_date = os.stat(webdoc_source_path).st_mtime
break
else:
webdoc_source_path = None
webdoc_name = None
webdoc_source_modification_date = 1
if webdoc_cache_dir is not None and \
os.path.exists(webdoc_cache_dir + os.sep + 'last_updated'):
webdoc_cache_modification_date = os.stat(webdoc_cache_dir + \
os.sep + \
'last_updated').st_mtime
return (webdoc_source_path, webdoc_cache_dir, webdoc_name,
webdoc_source_modification_date, webdoc_cache_modification_date)
def get_webdoc_topics(sort_by='name', sc=0, limit=-1,
categ=['help', 'admin', 'hacking'],
ln=CFG_SITE_LANG):
"""
List the available webdoc files in html format.
sort_by - *string* Sort topics by 'name' or 'date'.
sc - *int* Split the topics by categories if sc=1.
limit - *int* Max number of topics to be printed.
No limit if limit < 0.
categ - *list(string)* the categories to consider
ln - *string* Language of the page
"""
_ = gettext_set_language(ln)
topics = {}
ln_link = (ln != CFG_SITE_LANG and '?ln=' + ln) or ''
for category in categ:
if not webdoc_dirs.has_key(category):
continue
(source_path, cache_path) = webdoc_dirs[category]
if not topics.has_key(category):
topics[category] = []
# Build list of tuples(webdoc_name, webdoc_date, webdoc_url)
for webdocfile in [path for path in \
os.listdir(source_path) \
if path.endswith('.webdoc')]:
webdoc_name = webdocfile[:-7]
webdoc_url = CFG_SITE_URL + "/help/" + \
((category != 'help' and category + '/') or '') + \
webdoc_name
try:
webdoc_date = time.strptime(get_webdoc_parts(webdoc_name,
parts=['lastupdated']).get('lastupdated', "1970-01-01 00:00:00"),
"%Y-%m-%d %H:%M:%S")
except:
webdoc_date = time.strptime("1970-01-01 00:00:00", "%Y-%m-%d %H:%M:%S")
topics[category].append((webdoc_name, webdoc_date, webdoc_url))
# If not split by category, merge everything
if sc == 0:
all_topics = []
for topic in topics.values():
all_topics.extend(topic)
topics.clear()
topics[''] = all_topics
# Sort topics
if sort_by == 'name':
for topic in topics.values():
topic.sort()
elif sort_by == 'date':
for topic in topics.values():
topic.sort(lambda x, y:cmp(x[1], y[1]))
topic.reverse()
out = ''
for category, topic in topics.iteritems():
if category != '' and len(categ) > 1:
out += '<strong>'+ _("%(category)s Pages") % \
{'category': _(category).capitalize()} + '</strong>'
if limit < 0:
limit = len(topic)
out += '<ul><li>' + \
'</li><li>'.join(['%s <a href="%s%s">%s</a>' % \
((sort_by == 'date' and time.strftime('%Y-%m-%d', topic_item[1])) or '', \
topic_item[2], \
ln_link, \
get_webdoc_parts(topic_item[0], \
parts=['title'], \
ln=ln).get('title', '')) \
for topic_item in topic[:limit]]) + \
'</li></ul>'
return out
def transform(webdoc_source, verbose=0, req=None, languages=CFG_SITE_LANGS):
"""
Transform a WebDoc into html
This is made through a serie of transformations, mainly substitutions.
Parameters:
- webdoc_source : *string* the WebDoc input to transform to HTML
"""
parameters = {} # Will store values for specified parameters, such
# as 'Title' for <!-- WebDoc-Page-Title: Title -->
def get_param_and_remove(match):
"""
Analyses 'match', get the parameter and return empty string to
remove it.
Called by substitution in 'transform(...)', used to collection
parameters such as <!-- WebDoc-Page-Title: Title -->
@param match: a match object corresponding to the special tag
that must be interpreted
"""
tag = match.group("tag")
value = match.group("value")
parameters[tag] = value
return ''
def translate(match):
"""
Translate matching values
"""
word = match.group("word")
translated_word = _(word)
return translated_word
# 1 step
## First filter, used to remove comments
## and <protect> tags
uncommented_webdoc = ''
for line in webdoc_source.splitlines(True):
if not line.strip().startswith('#'):
uncommented_webdoc += line
webdoc_source = uncommented_webdoc.replace('<protect>', '')
webdoc_source = webdoc_source.replace('</protect>', '')
html_texts = {}
# Language dependent filters
for ln in languages:
_ = gettext_set_language(ln)
# Check if translation is really needed
## Just a quick check. Might trigger false negative, but it is
## ok.
if ln != CFG_SITE_LANG and \
translation_pattern.search(webdoc_source) is None and \
pattern_lang_link_current.search(webdoc_source) is None and \
pattern_lang_current.search(webdoc_source) is None and \
'<%s>' % ln not in webdoc_source and \
('_(') not in webdoc_source:
continue
# 2 step
## Filter used to translate string in _(..)_
localized_webdoc = translation_pattern.sub(translate, webdoc_source)
# 3 step
## Print current language 'en', 'fr', .. instead of
## <lang:current /> tags and '?ln=en', '?ln=fr', .. instead of
## <lang:link /> if ln is not default language
if ln != CFG_SITE_LANG:
localized_webdoc = pattern_lang_link_current.sub('?ln=' + ln,
localized_webdoc)
else:
localized_webdoc = pattern_lang_link_current.sub('',
localized_webdoc)
localized_webdoc = pattern_lang_current.sub(ln, localized_webdoc)
# 4 step
## Filter out languages
localized_webdoc = filter_languages(localized_webdoc, ln, defined_tags)
# 5 Step
## Replace defined tags with their value from config file
## Eg. replace <CFG_SITE_URL> with 'http://cdsweb.cern.ch/':
for defined_tag, value in defined_tags.iteritems():
if defined_tag.upper() == '<CFG_SITE_NAME_INTL>':
localized_webdoc = localized_webdoc.replace(defined_tag, \
value.get(ln, value['en']))
else:
localized_webdoc = localized_webdoc.replace(defined_tag, value)
# 6 step
## Get the parameters defined in HTML comments, like
## <!-- WebDoc-Page-Title: My Title -->
localized_body = localized_webdoc
for tag, pattern in pattern_tags.iteritems():
localized_body = pattern.sub(get_param_and_remove, localized_body)
out = localized_body
# Pre-process date
last_updated = parameters.get('WebDoc-Page-Revision', '')
last_updated = convert_datecvs_to_datestruct(last_updated)
last_updated = convert_datestruct_to_datetext(last_updated)
html_texts[ln] = (ln,
out,
parameters.get('WebDoc-Page-Title'),
parameters.get('WebDoc-Page-Keywords'),
parameters.get('WebDoc-Page-Navtrail'),
last_updated,
parameters.get('WebDoc-Page-Description'))
# Remove duplicates
filtered_html_texts = []
if html_texts.has_key(CFG_SITE_LANG):
filtered_html_texts = [(html_text[0], \
(html_text[1] != html_texts[CFG_SITE_LANG][1] and html_text[1]) or None, \
(html_text[2] != html_texts[CFG_SITE_LANG][2] and html_text[2]) or None, \
(html_text[3] != html_texts[CFG_SITE_LANG][3] and html_text[3]) or None, \
(html_text[4] != html_texts[CFG_SITE_LANG][4] and html_text[4]) or None, \
(html_text[5] != html_texts[CFG_SITE_LANG][5] and html_text[5]) or None, \
(html_text[6] != html_texts[CFG_SITE_LANG][6] and html_text[6]) or None)
for html_text in html_texts.values() \
if html_text[0] != CFG_SITE_LANG]
filtered_html_texts.append(html_texts[CFG_SITE_LANG])
else:
filtered_html_texts = html_texts.values()
return filtered_html_texts
def mymkdir(newdir, mode=0777):
"""works the way a good mkdir should :)
- already exists, silently complete
- regular file in the way, raise an exception
- parent directory(ies) does not exist, make them as well
"""
if os.path.isdir(newdir):
pass
elif os.path.isfile(newdir):
raise OSError("a file with the same name as the desired " \
"dir, '%s', already exists." % newdir)
else:
head, tail = os.path.split(newdir)
if head and not os.path.isdir(head):
mymkdir(head, mode)
if tail:
os.umask(022)
os.mkdir(newdir, mode)
def write_cache_file(filename, webdoc_cache_dir, filebody, verbose=0):
"""Write a file inside WebDoc cache dir.
Raise an exception if not possible
"""
# open file:
mymkdir(webdoc_cache_dir)
fullfilename = webdoc_cache_dir + os.sep + filename
if filebody is None:
filebody = ''
os.umask(022)
f = open(fullfilename, "w")
f.write(filebody)
f.close()
if verbose > 2:
print 'Written %s' % fullfilename
def get_mo_last_modification():
"""
Returns the timestamp of the most recently modified mo (compiled
po) file
"""
# Take one of the mo files. They are all installed at the same
# time, so last modication date should be the same
mo_file = '%s/share/locale/%s/LC_MESSAGES/invenio.mo' % (CFG_PREFIX, CFG_SITE_LANG)
if os.path.exists(os.path.abspath(mo_file)):
return os.stat(mo_file).st_mtime
else:
return 0
def filter_languages(text, ln='en', defined_tags=None):
"""
Filters the language tags that do not correspond to the specified language.
Eg: <lang><en>A book</en><de>Ein Buch</de></lang> will return
- with ln = 'de': "Ein Buch"
- with ln = 'en': "A book"
- with ln = 'fr': "A book"
Also replace variables such as <CFG_SITE_URL> and <CFG_SITE_NAME_INTL> inside
<lang><..><..></lang> tags in order to print them with the correct
language
@param text: the input text
@param ln: the language that is NOT filtered out from the input
@return: the input text as string with unnecessary languages filtered out
@see: bibformat_engine.py, from where this function was originally extracted
"""
# First define search_lang_tag(match) and clean_language_tag(match), used
# in re.sub() function
def search_lang_tag(match):
"""
Searches for the <lang>...</lang> tag and remove inner localized tags
such as <en>, <fr>, that are not current_lang.
If current_lang cannot be found inside <lang> ... </lang>, try to use 'CFG_SITE_LANG'
@param match: a match object corresponding to the special tag that must be interpreted
"""
current_lang = ln
# If <lang keep=all> is used, keep all empty line (this is
# currently undocumented and behaviour might change)
keep = False
if match.group("keep") is not None:
keep = True
def clean_language_tag(match):
"""
Return tag text content if tag language of match is output language.
Called by substitution in 'filter_languages(...)'
@param match: a match object corresponding to the special tag that must be interpreted
"""
if match.group('lang') == current_lang or \
keep == True:
return match.group('translation')
else:
return ""
# End of clean_language_tag(..)
lang_tag_content = match.group("langs")
# Try to find tag with current lang. If it does not exists,
# then try to look for CFG_SITE_LANG. If still does not exist, use
# 'en' as current_lang
pattern_current_lang = re.compile(r"<(" + current_lang + \
r")\s*>(.*?)(</"+current_lang+r"\s*>)",
re.IGNORECASE | re.DOTALL)
if re.search(pattern_current_lang, lang_tag_content) is None:
current_lang = CFG_SITE_LANG
# Can we find translation in 'CFG_SITE_LANG'?
if re.search(pattern_CFG_SITE_LANG, lang_tag_content) is None:
current_lang = 'en'
cleaned_lang_tag = ln_pattern.sub(clean_language_tag, lang_tag_content)
# Remove empty lines
# Only if 'keep' has not been set
if keep == False:
stripped_text = ''
for line in cleaned_lang_tag.splitlines(True):
if line.strip():
stripped_text += line
cleaned_lang_tag = stripped_text
return cleaned_lang_tag
# End of search_lang_tag(..)
filtered_text = pattern_lang.sub(search_lang_tag, text)
return filtered_text
def usage(exitcode=1, msg=""):
"""Prints usage info."""
if msg:
sys.stderr.write("Error: %s.\n" % msg)
sys.stderr.write("Usage: %s [options] <webdocname>\n" % sys.argv[0])
sys.stderr.write(" -h, --help \t\t Print this help.\n")
sys.stderr.write(" -V, --version \t\t Print version information.\n")
sys.stderr.write(" -v, --verbose=LEVEL \t\t Verbose level (0=min,1=normal,9=max).\n")
sys.stderr.write(" -l, --language=LN1,LN2,.. \t\t Language(s) to process (default all)\n")
sys.stderr.write(" -m, --mode=MODE \t\t Update cache mode(0=Never,1=if necessary,2=always) (default 2)\n")
sys.stderr.write("\n")
sys.stderr.write(" Example: webdoc search-guide\n")
sys.stderr.write(" Example: webdoc -l en,fr search-guide\n")
sys.stderr.write(" Example: webdoc -m 1 search-guide")
sys.stderr.write("\n")
sys.exit(exitcode)
def main():
"""
main entry point for webdoc via command line
"""
options = {'language':CFG_SITE_LANGS, 'verbose':1, 'mode':2}
try:
opts, args = getopt.getopt(sys.argv[1:],
"hVv:l:m:",
["help",
"version",
"verbose=",
"language=",
"mode="])
except getopt.GetoptError, err:
usage(1, err)
try:
for opt in opts:
if opt[0] in ["-h", "--help"]:
usage(0)
elif opt[0] in ["-V", "--version"]:
print __revision__
sys.exit(0)
elif opt[0] in ["-v", "--verbose"]:
options["verbose"] = int(opt[1])
elif opt[0] in ["-l", "--language"]:
options["language"] = [wash_language(lang.strip().lower()) \
for lang in opt[1].split(',') \
if lang in CFG_SITE_LANGS]
elif opt[0] in ["-m", "--mode"]:
options["mode"] = opt[1]
except StandardError, e:
usage(e)
try:
options["mode"] = int(options["mode"])
except ValueError:
usage(1, "Mode must be an integer")
if len(args) > 0:
options["webdoc"] = args[0]
if not options.has_key("webdoc"):
usage(0)
# check if webdoc exists
infos = get_webdoc_info(options["webdoc"])
if infos[0] is None:
usage(1, "Could not find %s" % options["webdoc"])
update_webdoc_cache(webdoc=options["webdoc"],
mode=options["mode"],
verbose=options["verbose"],
languages=options["language"])
if __name__ == "__main__":
main()
diff --git a/modules/webstyle/lib/webinterface_handler.py b/modules/webstyle/lib/webinterface_handler.py
index 86a53898f..49138a639 100644
--- a/modules/webstyle/lib/webinterface_handler.py
+++ b/modules/webstyle/lib/webinterface_handler.py
@@ -1,496 +1,497 @@
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Apache request handler mechanism.
It gives the tools to map url to functions, handles the legacy url
scheme (/search.py queries), HTTP/HTTPS switching, language
specification,...
"""
__revision__ = "$Id$"
import urlparse
import cgi
import sys
import re
import os
import gc
from invenio import webinterface_handler_config as apache
-from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_TMPDIR
+from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_TMPDIR, \
+ CFG_SITE_RECORD
from invenio.access_control_config import CFG_EXTERNAL_AUTH_USING_SSO
from invenio.messages import wash_language
from invenio.urlutils import redirect_to_url
from invenio.errorlib import register_exception
from invenio.webuser import get_preferred_user_language, isGuestUser, \
getUid, loginUser, update_Uid, isUserSuperAdmin, collect_user_info
from invenio.webinterface_handler_wsgi_utils import StringField
## The following variable is True if the installation make any difference
## between HTTP Vs. HTTPS connections.
CFG_HAS_HTTPS_SUPPORT = CFG_SITE_URL != CFG_SITE_SECURE_URL
## Set this to True in order to log some more information.
DEBUG = False
# List of URIs for which the 'ln' argument must not be added
# automatically
CFG_NO_LANG_RECOGNITION_URIS = ['/rss',
'/oai2d',
'/journal']
RE_SLASHES = re.compile('/+')
-RE_SPECIAL_URI = re.compile('^/record/\d+|^/collection/.+')
+RE_SPECIAL_URI = re.compile('^/%s/\d+|^/collection/.+' % CFG_SITE_RECORD)
def _debug(req, msg):
"""
Log the message.
@param req: the request.
@param msg: the message.
@type msg: string
"""
if DEBUG:
req.log_error(msg)
def _check_result(req, result):
"""
Check that a page handler actually wrote something, and
properly finish the apache request.
@param req: the request.
@param result: the produced output.
@type result: string
@return: an apache error code
@rtype: int
@raise apache.SERVER_RETURN: in case of a HEAD request.
@note: that this function actually takes care of writing the result
to the client.
"""
if result or req.bytes_sent > 0:
if result is None:
result = ""
else:
result = str(result)
# unless content_type was manually set, we will attempt
# to guess it
if not req.content_type_set_p:
# make an attempt to guess content-type
if result[:100].strip()[:6].lower() == '<html>' \
or result.find('</') > 0:
req.content_type = 'text/html'
else:
req.content_type = 'text/plain'
if req.header_only:
if req.status in (apache.HTTP_NOT_FOUND, ):
raise apache.SERVER_RETURN, req.status
else:
req.write(result)
return apache.OK
else:
req.log_error("publisher: %s returned nothing." % `object`)
return apache.HTTP_INTERNAL_SERVER_ERROR
class TraversalError(Exception):
"""
Exception raised in case of an error in parsing the URL of the request.
"""
pass
class WebInterfaceDirectory(object):
"""
A directory groups web pages, and can delegate dispatching of
requests to the actual handler. This has been heavily borrowed
from Quixote's dispatching mechanism, with specific adaptations.
"""
# Lists the valid URLs contained in this directory.
_exports = []
# Set this to True in order to redirect queries over HTTPS
_force_https = False
def _translate(self, component):
"""(component : string) -> string | None
Translate a path component into a Python identifier. Returning
None signifies that the component does not exist.
"""
if component in self._exports:
if component == '':
return 'index' # implicit mapping
else:
return component
else:
# check for an explicit external to internal mapping
for value in self._exports:
if isinstance(value, tuple):
if value[0] == component:
return value[1]
else:
return None
def _lookup(self, component, path):
""" Override this method if you need to map dynamic URLs.
It can eat up as much of the remaining path as needed, and
return the remaining parts, so that the traversal can
continue.
"""
return None, path
def _traverse(self, req, path, do_head=False, guest_p=True):
""" Locate the handler of an URI by traversing the elements of
the path."""
_debug(req, 'traversing %r' % path)
component, path = path[0], path[1:]
name = self._translate(component)
if name is None:
obj, path = self._lookup(component, path)
else:
obj = getattr(self, name)
if obj is None:
_debug(req, 'could not resolve %s' % repr((component, path)))
raise TraversalError()
# We have found the next segment. If we know that from this
# point our subpages are over HTTPS, do the switch.
if CFG_HAS_HTTPS_SUPPORT and self._force_https and not req.is_https():
# We need to isolate the part of the URI that is after
# CFG_SITE_URL, and append that to our CFG_SITE_SECURE_URL.
original_parts = urlparse.urlparse(req.unparsed_uri)
plain_prefix_parts = urlparse.urlparse(CFG_SITE_URL)
secure_prefix_parts = urlparse.urlparse(CFG_SITE_SECURE_URL)
# Compute the new path
plain_path = original_parts[2]
plain_path = secure_prefix_parts[2] + \
plain_path[len(plain_prefix_parts[2]):]
# ...and recompose the complete URL
final_parts = list(secure_prefix_parts)
final_parts[2] = plain_path
final_parts[-3:] = original_parts[-3:]
target = urlparse.urlunparse(final_parts)
redirect_to_url(req, target)
if CFG_EXTERNAL_AUTH_USING_SSO and req.is_https() and guest_p:
(iden, p_un, dummy, dummy) = loginUser(req, '', '',
CFG_EXTERNAL_AUTH_USING_SSO)
if len(iden)>0:
update_Uid(req, p_un)
guest_p = False
# Continue the traversal. If there is a path, continue
# resolving, otherwise call the method as it is our final
# renderer. We even pass it the parsed form arguments.
if path:
if hasattr(obj, '_traverse'):
return obj._traverse(req, path, do_head, guest_p)
else:
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
if do_head:
req.content_type = "text/html; charset=UTF-8"
raise apache.SERVER_RETURN, apache.DONE
form = req.form
if 'ln' not in form and \
req.uri not in CFG_NO_LANG_RECOGNITION_URIS:
ln = get_preferred_user_language(req)
form.add_field('ln', ln)
result = _check_result(req, obj(req, form))
return result
def __call__(self, req, form):
""" Maybe resolve the final / of a directory """
# When this method is called, we either are a directory which
# has an 'index' method, and we redirect to it, or we don't
# have such a method, in which case it is a traversal error.
if "" in self._exports:
if not form:
# Fix missing trailing slash as a convenience, unless
# we are processing a form (in which case it is better
# to fix the form posting).
redirect_to_url(req, req.uri + "/", apache.HTTP_MOVED_PERMANENTLY)
_debug(req, 'directory %r is not callable' % self)
raise TraversalError()
def create_handler(root):
""" Return a handler function that will dispatch apache requests
through the URL layout passed in parameter."""
def _profiler(req):
""" This handler wrap the default handler with a profiler.
Profiling data is written into
CFG_TMPDIR/invenio-profile-stats-datetime.raw, and
is displayed at the bottom of the webpage.
To use add profile=1 to your url. To change sorting algorithm you
can provide profile=algorithm_name. You can add more than one
profile requirement like ?profile=time&profile=cumulative.
The list of available algorithm is displayed at the end of the profile.
"""
args = {}
if req.args:
args = cgi.parse_qs(req.args)
if 'profile' in args:
if not isUserSuperAdmin(collect_user_info(req)):
return _handler(req)
if 'memory' in args['profile']:
gc.set_debug(gc.DEBUG_LEAK)
ret = _handler(req)
req.write("\n<pre>%s</pre>" % gc.garbage)
gc.collect()
req.write("\n<pre>%s</pre>" % gc.garbage)
gc.set_debug(0)
return ret
from cStringIO import StringIO
try:
import pstats
except ImportError:
ret = _handler(req)
req.write("<pre>%s</pre>" % "The Python Profiler is not installed!")
return ret
import datetime
date = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
filename = '%s/invenio-profile-stats-%s.raw' % (CFG_TMPDIR, date)
existing_sorts = pstats.Stats.sort_arg_dict_default.keys()
required_sorts = []
profile_dump = []
for sort in args['profile']:
if sort not in existing_sorts:
sort = 'cumulative'
if sort not in required_sorts:
required_sorts.append(sort)
if sys.hexversion < 0x02050000:
import hotshot
import hotshot.stats
pr = hotshot.Profile(filename)
ret = pr.runcall(_handler, req)
for sort_type in required_sorts:
tmp_out = sys.stdout
sys.stdout = StringIO()
hotshot.stats.load(filename).strip_dirs().sort_stats(sort_type).print_stats()
# pylint: disable=E1103
# This is a hack. sys.stdout was replaced by a StringIO.
profile_dump.append(sys.stdout.getvalue())
# pylint: enable=E1103
sys.stdout = tmp_out
else:
import cProfile
pr = cProfile.Profile()
ret = pr.runcall(_handler, req)
pr.dump_stats(filename)
for sort_type in required_sorts:
strstream = StringIO()
pstats.Stats(filename, stream=strstream).strip_dirs().sort_stats(sort_type).print_stats()
profile_dump.append(strstream.getvalue())
profile_dump = '\n'.join(profile_dump)
profile_dump += '\nYou can use profile=%s or profile=memory' % existing_sorts
req.write("\n<pre>%s</pre>" % profile_dump)
return ret
else:
return _handler(req)
def _handler(req):
""" This handler is invoked by mod_python with the apache request."""
try:
allowed_methods = ("GET", "POST", "HEAD", "OPTIONS")
req.allow_methods(allowed_methods, 1)
if req.method not in allowed_methods:
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
if req.method == 'OPTIONS':
## OPTIONS is used to now which method are allowed
req.headers_out['Allow'] = ', '.join(allowed_methods)
raise apache.SERVER_RETURN, apache.OK
# Set user agent for fckeditor.py, which needs it here
os.environ["HTTP_USER_AGENT"] = req.headers_in.get('User-Agent', '')
guest_p = isGuestUser(getUid(req))
uri = req.uri
if uri == '/':
path = ['']
else:
## Let's collapse multiple slashes into a single /
uri = RE_SLASHES.sub('/', uri)
path = uri[1:].split('/')
if uri.startswith('/yours') or not guest_p:
## Private/personalized request should not be cached
req.headers_out['Cache-Control'] = 'private, no-cache, no-store, max-age=0, must-revalidate'
req.headers_out['Pragma'] = 'no-cache'
req.headers_out['Vary'] = '*'
else:
req.headers_out['Cache-Control'] = 'public, max-age=3600'
req.headers_out['Vary'] = 'Cookie, ETag, Cache-Control'
try:
if req.header_only and not RE_SPECIAL_URI.match(req.uri):
return root._traverse(req, path, True, guest_p)
else:
## bibdocfile have a special treatment for HEAD
return root._traverse(req, path, False, guest_p)
except TraversalError:
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
except apache.SERVER_RETURN:
## This is one of mod_python way of communicating
raise
except IOError, exc:
if 'Write failed, client closed connection' not in "%s" % exc:
## Workaround for considering as false positive exceptions
## rised by mod_python when the user close the connection
## or in some other rare and not well identified cases.
register_exception(req=req, alert_admin=True)
raise
except Exception:
register_exception(req=req, alert_admin=True)
raise
# Serve an error by default.
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
finally:
if hasattr(req, '_session'):
## The session handler saves for caching a request_wrapper
## in req.
## This saves req as an attribute, creating a circular
## reference.
## Since we have have reached the end of the request handler
## we can safely drop the request_wrapper so to avoid
## memory leaks.
delattr(req, '_session')
if hasattr(req, '_user_info'):
## For the same reason we can delete the user_info.
delattr(req, '_user_info')
## as suggested in
## <http://www.python.org/doc/2.3.5/lib/module-gc.html>
del gc.garbage[:]
return _profiler
def wash_urlargd(form, content):
"""
Wash the complete form based on the specification in
content. Content is a dictionary containing the field names as a
key, and a tuple (type, default) as value.
'type' can be list, str, invenio.webinterface_handler_wsgi_utils.StringField, int, tuple, or
invenio.webinterface_handler_wsgi_utils.Field (for
file uploads).
The specification automatically includes the 'ln' field, which is
common to all queries.
Arguments that are not defined in 'content' are discarded.
Note that in case {list,tuple} were asked for, we assume that
{list,tuple} of strings is to be returned. Therefore beware when
you want to use wash_urlargd() for multiple file upload forms.
@Return: argd dictionary that can be used for passing function
parameters by keywords.
"""
result = {}
content['ln'] = (str, '')
for k, (dst_type, default) in content.items():
try:
value = form[k]
except KeyError:
result[k] = default
continue
src_type = type(value)
# First, handle the case where we want all the results. In
# this case, we need to ensure all the elements are strings,
# and not Field instances.
if src_type in (list, tuple):
if dst_type is list:
result[k] = [str(x) for x in value]
continue
if dst_type is tuple:
result[k] = tuple([str(x) for x in value])
continue
# in all the other cases, we are only interested in the
# first value.
value = value[0]
# Maybe we already have what is expected? Then don't change
# anything.
if isinstance(value, dst_type):
if isinstance(value, StringField):
result[k] = str(value)
else:
result[k] = value
continue
# Since we got here, 'value' is sure to be a single symbol,
# not a list kind of structure anymore.
if dst_type in (str, int):
try:
result[k] = dst_type(value)
except:
result[k] = default
elif dst_type is tuple:
result[k] = (str(value), )
elif dst_type is list:
result[k] = [str(value)]
else:
raise ValueError('cannot cast form value %s of type %r into type %r' % (value, src_type, dst_type))
result['ln'] = wash_language(result['ln'])
return result
diff --git a/modules/websubmit/doc/admin/websubmit-admin-guide.webdoc b/modules/websubmit/doc/admin/websubmit-admin-guide.webdoc
index 82a684a6a..860e3e774 100644
--- a/modules/websubmit/doc/admin/websubmit-admin-guide.webdoc
+++ b/modules/websubmit/doc/admin/websubmit-admin-guide.webdoc
@@ -1,3777 +1,3777 @@
## -*- mode: html; coding: utf-8; -*-
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(WebSubmit Admin Guide)_ -->
<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<style type="text/css">
<!--
.helpnote {
color: #333;
background-color: #ddd;
border: 1px solid #aaa;
padding:4px;
margin:4px;
}
.center {
margin:auto;
text-align:center;
margin:4px;
}
.center label{
clear:both;
text-align:center;
margin:auto;
font-style: italic;
display:block;
}
.code {
border: 1px solid #aaa;
overflow-x: auto;
padding: 8px;
}
-->
</style>
<p class="helpnote"><em>Disclaimer:</em> Though this guide provides
all the necessary information to start learning WebSubmit from scratch
and reach a good level in administrating it, this guide is not yet
fully complete, and might contain information that has not been
strictly verified (sample codes are for eg. provided "AS IS", only to
offer some guidance to the admin). <br/> Specific topics would gain to
be more developed, such as HOW-TOs, sample workflows (for eg. approval
workflows and referee management). At this point the demo submissions
that come standard with the Atlantis demo site remain essential
companions to this guide.<br/>
<b>Contributions are welcome (for eg. sample workflows, function
descriptions, etc.)</b>
</p>
<h2>Contents</h2>
<ul style="list-style-type:None">
<li><strong>1. <a href="#shortIntro">Overview</a></strong>
<ul style="list-style-type:None">
<li>1.1&nbsp;&nbsp;<a href="#philosophy">How WebSubmit works</a></li>
<li>1.2&nbsp;&nbsp;<a href="#behindthescenes">Behind the scenes</a></li>
</ul>
</li>
<li><strong>2. <a href="#2">Configure Submissions: a Tutorial</a></strong>
<ul style="list-style-type:None">
<li>2.1&nbsp;&nbsp;<a href="#2.1">Creating the submission</a></li>
<li>2.2&nbsp;&nbsp;<a href="#2.2">Building the interface</a></li>
<li>2.3&nbsp;&nbsp;<a href="#2.3">Adding the functions</a></li>
<li>2.4&nbsp;&nbsp;<a href="#2.4">Restricting the submission</a></li>
</ul>
</li>
<li><strong>3. <a href="#3">WebSubmit Elements</a></strong>
<ul style="list-style-type:None">
<li>3.1&nbsp;&nbsp;<a href="#3.1">Existing elements</a></li>
<li>3.2&nbsp;&nbsp;<a href="#3.2">Creating a new element</a></li>
<li>3.3&nbsp;&nbsp;<a href="#3.3">Creating a new check</a></li>
</ul>
</li>
<li><strong>4. <a href="#4">WebSubmit Functions</a></strong>
<ul style="list-style-type:None">
<li>4.1&nbsp;&nbsp;<a href="#4.1">Existing functions</a></li>
<li>4.2&nbsp;&nbsp;<a href="#4.2">Creating a new function</a></li>
</ul>
</li>
<!--<li><strong>2. <a href="#2"></a></strong>
<ul style="list-style-type:None">
<li>2.1&nbsp;&nbsp;<a href="#2.1"></a></li>
</ul>
</li>-->
<li><strong>5. <a href="#5">File Management with WebSubmit</a></strong>
<ul style="list-style-type:None">
<li>5.1&nbsp;&nbsp;<a href="#5.1"><b>Option 1:</b> File Input element + FFT</a></li>
<li>5.2&nbsp;&nbsp;<a href="#5.2"><b>Option 2:</b> File Input element + Move_Files_To_Storage function</a></li>
<li>5.3&nbsp;&nbsp;<a href="#5.3"><b>Option 3:</b> Create_Upload_Files_Interface + Move_Uploaded_Files_To_Storage functions</a></li>
<li>5.4&nbsp;&nbsp;<a href="#5.4"><b>Option 4:</b> Upload_File element instance + Move_Uploaded_Files_To_Storage function</a></li>
<li>5.5&nbsp;&nbsp;<a href="#5.5"><b>Option 5:</b> FCKeditor element instance + Move_FCKeditor_Files_To_Storage function</a></li>
<li>5.6&nbsp;&nbsp;<a href="#5.6"><b>Option 6:</b> Upload_Photo_interface element instance + Move_Photos_To_Storage function</a></li>
<li>5.7&nbsp;&nbsp;<a href="#5.7">Alternatives to WebSubmit: BibDocFile CLI or BibDocFile Web Interface</a></li>
</ul>
</li>
<li><strong>6. <a href="#6">Access restrictions</a></strong>
<ul style="list-style-type:None">
<li>6.1&nbsp;&nbsp;<a href="#6.1">Admin-level</a></li>
<li>6.2&nbsp;&nbsp;<a href="#6.2">User-level</a></li>
</ul>
</li>
</ul>
&nbsp;&nbsp;&nbsp;&nbsp;(Check out the <a href="#oldwebsubmitguide">old WebSubmit admin guide</a>)<br/>
<h2><a name="shortIntro">1. Overview</a></h2>
<h3><a name="philosophy">1.1 How WebSubmit Works</a></h3>
<p>WebSubmit provides the infrastructure to set up customized pages
for your users to submit new metadata and files to your repository. It
is highly flexible in order to accomodate to the various type of
documents that you might need to archive. As a consequence of this
flexibility, it requires a good level of understanding of the
concepts behind WebSubmit.</p>
<p>A simplied schema of a typical WebSubmit workflow is the following
one (figure 1): one or several pages are presented to the user to
enter some information, such as title of the document, authors,
etc. Each of these pages contain a form with one or several <a href="#3">WebSubmit
elements</a>, which can either display some information to the user, or
ask for input from the user. <a href="#3">WebSubmit elements</a> are described more in
detailed further below. After the user has finished filling the forms
on all pages, a series of <a href="#4">WebSubmit functions</a> are called. These functions will
typically (1) post-process the data, (2) create a MARCXML, (3) upload
the created MARCXML file, and (4) send confirmation email(s).</p>
<div class="center">
<img id="figure1" src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-workflow1.png" />
<label for="figure1">Figure 1</label>
</div>
<p>One thing worth to learn from this simple workflow is that (1)
functions are executed one after each other, (2) that each function
can have side effects, such as sending email, and that (3) the output
of these functions is displayed to the user. Typical submissions use
many side-effect functions, and only one function that give some
feedback to the user (in the form of a web page). Also most submissions
usually need only a single page.</p>
<p>Finally note that you can plug check on each field of a page, so that
the user cannot proceed further if some invalid text has been input.</p>
<div class="center">
<img id="figure2" src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-workflow2.png" />
<label for="figure2">Figure 2</label>
</div>
<p>Functions are also organized in steps (Figure 2). By default,
WebSubmit runs the "step 1 block" and then stops: to run the next
steps one must have a function at the end of step 1 that jump to
another block (for eg. CaseEDS function) or have a WebSubmit element
that set the input form "step" to the value of the block number (such
as "<code>Create_Modify_Interface</code>" function).</p>
<div class="center">
<img id="figure3" src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-workflow3.png" />
<label for="figure3">Figure 3</label>
</div>
<div class="helpnote">A set of WebSubmit functions comes installed by
default in Invenio, to provide all the necessary functionalities
to create your own workflow. The behaviour of these functions can be
customized through their parameters. Advanced users can also create
their own functions in Python (see further below in this guide). The
main difficulty for beginners is to pick the adequate function and
carefully choose their ordering. It is recommended to get inspiration
from the sample demo submission at first.<br/><br/>
It is particulary important at this point to understand that the
WebSubmit engine more or less limits to 1) displaying page to collect data,
and 2) run functions. It does not take care of building a record and
inserting it into a collection, but expects to have a set of functions
configured to do so.
</div>
<p>Such a multi-page submission could appear to users as shown in
figure 4. Note that this figure shows a special page <em>0</em>. This
"cover" page is mandatory for all submissions, and is automatically
generated by WebSubmit. It can be customized to 1) display a
description of the submission, 2) show the available "actions"
(described further below) and 3) let the users chose among the
available "categories" (described further below).</p>
<div class="center">
<div style="width:100%;overflow-x:auto" id="figure4">
<table align="center" style="text-align:center"><tr>
<td><a href="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-screenshot1.png"><img src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-screenshot1-small.png"></a></td><td> &rarr;</td>
<td><a href="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-screenshot2.png"><img src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-screenshot2-small.png"></a></td><td> &rarr;</td>
<td><a href="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-screenshot3.png"><img src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-screenshot3-small.png"></a></td><td> &rarr;</td>
<td><a href="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-screenshot4.png"><img src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-screenshot4-small.png"></a></td>
<tr style="font-weight:700;font-size:x-small">
<td>Page "0" </td><td>&nbsp;</td>
<td>Page 1 </td><td>&nbsp;</td>
<td>Page 2 </td><td>&nbsp;</td>
<td>Functions ouput</td>
</tr>
</tr></table>
</div>
<label for="figure4">Figure 4</label>
</div>
<p>Indeed, typical submissions do not contain only one, but several
independant workflows called "actions": one action might be dedicated
to the submission of a document, while another one will let the user
modify a previously submitted record. Different actions can therefore
display different sets of pages and call different post-processing
functions. The first page of a submission (page "0") will let users
chose among the offered actions. <br/>By convention we use 3-letters
names for the actions of a submission. For example:
<ul>
<li><b>SBI</b>: submit a new record</li>
<li><b>MBI</b>: modify the metadata of a record</li>
<li><b>SRV</b>: submit a revised file</li>
</ul>
</p>
<div class="center">
<img id="figure5" src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-workflow4b.png" />
<label for="figure5">Figure 5</label>
</div>
<p>
Actions are displayed as several buttons (blue by default) for users
to choose from to start a new submission (Figure 6):
</p>
<div class="center">
<img id="figure6" src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-page0.jpg" />
<label for="figure6">Figure 6</label>
</div>
<p>Figure 6 also shows the possibility to select among various
categories prior to jumping into one of the available actions. These
categories usually don't have a direct impact on the chosen
workflow. Think of them simply as a simple WebSubmit element place on
the first page, that is common to all the actions of your submission
(indeed you could set up your submissions to have such categories
inside your submission actions pages, but that would require
additional work).</p>
<p>Last, but not least, a submission is usually referred to by a short
name (at most 5 letters), reused in many places in the WebSubmit admin
interface.</p>
To summarize:
<ul>
<li>A <b>submission</b> is made of different actions</li>
<li>An <b>action</b> is a workflow made of pages, checks and a flow of functions.</li>
<li>A <b>page</b> contains several WebSubmit elements, usually input elements with some label.</li>
<li>A <b>WebSubmit element</b> is a control on the interface to input or display values.</li>
<li>Javacript <b>checks</b> can be attached to WebSubmit elements, in order to validate the input data before going to a futher step of the submission.</li>
<li>A <b>function</b> performs some post-processing operations, usually
on data collected thanks to WebSubmit elements. Functions can have side-effects and outputs</li>
<li>Functions are organized in <b>steps</b>, blocks of functions</li>
</ul>
<p style="font-style: italic;">Another concept remains to be explained, but this functionality tends to disappear from submissions, and might be deprecated at some point. We provide the explanation about it below only for completeness, but it is strongly discouraged to go that way:<br/>
<blockquote>
It is possible to group actions in <b>sets</b>: an action set is a succession of actions which should be done in a given order when a user starts.<br/>
For example the submission of a document can be composed of two actions: Submission of Bibliographic Information (SBI) and Fulltext Transfer (FTT) which should be done one after the other.<br/>
When the user starts the submission, we want the submission to get him first in SBI and when he finishes SBI to carry him to FTT. SBI and FTT are in this case in the same action set. They will both have a level of 1 ("level" is a bad name, it should be "action set number"), SBI will have a score of 1, and FTT a score of 2 (which means it will be started after SBI). If you set the stpage of FTT to 2, the user will be directly carried to the 2nd page of the FTT web form. This value is usually set to 1.<br/>
The endtxt field contains the text which will be displayed to the user at the end of the first action (here it could be "you now have to transfer your files")<br/>
A single action like "Modify Bibliographic Information" should have the 3 columns to 0,0 and 1.
</blockquote>
</p>
<h3><a name="behindthescenes">1.2 Behind the scenes</a></h3>
<p>This section highlights a few key behaviours of WebSubmit which are
particularly important to understand when designing a submission.</p>
<p>When a user starts a new submission, a working directory is created
on disk in order to store all the collected values. This working
directory is usually called the "<code>curdir</code>". It is located
in a subdirectory
of <code>/opt/invenio/var/data/submit/storage/</code><small><em>{action
directory}</em></small><code>/</code><small><em>{submission
code}</em></small><code>/</code><small><em>{submission access
number}</em></small> where <small><em>{submission code}</em></small>
is the short name of a submission and <small><em>{submission access
number}</em></small> is a unique submission session identifier
(displayed on the web submission interface as the <em>submission
number</em>).<small><em>{action directory}</em></small>
is <code>running</code> for SBI actions, <code>modify</code> for "MBI"
actions, <code>revise</code> for "SRV" actions, etc. (This is
configured in the "Actions" part of the WebSubmit admin
interface)<br/> Whenever the user moves from one page to the other, or
submit the form, the curdir is populated with files named after the
submission elements displayed on the page, with their content being
the user inserted values (User uploaded files can be found by default
in the <code>curdir/files/</code> directory). It is these files that
WebSubmit functions such "<code>Create_Record</code>" or
"<code>Modify_Record</code>" will use in order to create the MARCXML
to upload (Note that the output of these functions will be a file
named "<code>recmysql</code>" in the <code>curdir</code>, that will
contain the MARCXML to upload)
</p>
<p>
The curdir contains a few other additional files:
<ul>
<li><code>function_log</code>: the list of functions called by the WebSubmit engines</li>
<li><code>SuE</code>: the email of the submitter</li>
<li><code>doctype</code>: the short name (code name) of the current submission</li>
<li><code>act</code>: the current action (SBI, MBI, etc.)</li>
<li><code>step</code>: the step of the functions</li>
<li><code>curpage</code>: the current page of the submission</li>
<li><code>ln</code>: the language chosen by the user to display the web interface</li>
<li><code>combo</code><small><em>{doctype}</em></small>: for
eg. <code>comboDEMOART</code> contains the chosen category on page
"0".</li>
<li>etc.</li>
</ul>
</p>
<p>The path to the <code>curdir</code> can sometimes be slightly
different, depending on the chosen action. For eg. the SRV action will
use <code>/opt/invenio/var/data/submit/storage/revise/</code><small><em>{submission
code}</em></small><code>/</code><small><em>{submission access
number}</em></small> where <small><em>{submission
code}</em></small></p>
<p>When the functions will run they will most probably create
additional files, such as "<code>SN</code>" created by the
"<code>Create_Recid</code>" function which reserves a record id,
"<code>RN</code>" created by function
"<code>Report_Number_Generation</code>" to reserve a report number, or
the "<code>recmysql</code>" file already mentionned above. Many of
these output file then become input parameters for the next functions
to be executed. This shows the importance of running a well defined
set of functions in a well defined order.</p>
<p>The <code>curdir</code> is not removed after the end of the
submission. This gives you the opportunity to keep track of past
submissions in case something would have gone unexpected. However the
use of the "<code>Move_to_Done</code>" function will create a zipped
archive of this directory (+ rename it using the report number of the
record, found in file <code>curdir/RN</code>), and will move it to a
different
directory, <code>/opt/invenio/var/data/submit/storage/done/running/</code>.
</p>
<a name="2"></a><h2>2. Configure Submissions: a Tutorial</h2>
<p>This chapter is a quick walkthrough for creating your submission.
It is not trying to explain everything, but simply goes through the main
steps necessary to configure a submission.</p>
<a name="2.1"></a><h3>2.1 Creating the submission</h3>
<p><b><a name="2.1.1"></a>1.</a></b> Go to
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py">WebSubmit
admin interface</a> and click on the "Add New Doctype" button at the
bottom of the screen. Give your submission an ID
(eg. <code>DEMOTEST</code>. This cannot be changed later and should be
kept short. It is used in URLs to link to your submission), a name and
a description. The name and the description will be displayed on the
users end. The description can contain HTML markup. You can also
choose to clone from an already existing submission so that existing
configuration for pages, functions, elements, etc. are copied over to
your new submission (this might be not wanted if the submission you
copy from include submission specific elements).</p>
<p><b><a name="2.1.2"></a>2.</b> From the submission page, select from the "Add a new
Submission" menu the action to add to your newly created
submission. For eg. select "[SBI] Submit New Record" to create an
action that will allow users to submit new documents. Press the "Add
Submission" button to add the chosen action. You are given the
possibility to clone the configuration from another existing
submission. Start with a blank one or choose an existing one, then
press "Continue".</p>
<p><b><a name="2.1.3"></a>3.</b> On the following page, fill in the form:
<ul>
<li>Choose if the action is to be displayed on the start page of your
submission.<li>
</li>Specify the "<code>button order</code>" on the interface for this
action (to define an order between your actions buttons on splash page
(page 0) of your submission).</li>
<li> Enter the "<code>Status Text</code>": not really used in user
interface (<em>to be checked</em>): label for your action in the
WebSubmit admin interface.</li>
<li>Other fields are related to action sets (<em>chained
actions</em>). It is recommended to leave the default values.
<ul>
<li>Input the "<code>End text</code>": text displayed at the end of
the previous action to link to this one, provided that this action is
chained to another (leaving empty is recommended).</li>
<li> Choose the "<code>Stpage</code>": the page number that should be
used as starting point when reaching this action from another chained
action: leaving '0' is recommended).</li>
<li> The "<code>level</code>": the group of actions to which this one
belongs, in case it is chained with another action(s) (leaving emtpy
is recommended).</li>
<li> The "<code>score</code>": the order in which grouped actions are
chained (leaving empty is recommended).
</li>
</ul>
</li>
</ul>
Once done, press the "Save Details" button.
</p>
<p><b><a name="2.1.4"></a>4.</b> (Optional) Repeat steps 2 and 3 for any other workflow
you want to support in your submission. If the action you want to add
is not part of the list, click on
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/actionlist">available
actions</a> menu, press the "Add Action" button and enter the
"<code>action code</code>" (for eg. <code>SBI</code>),
"<code>description</code>" (displayed as the page title when going
through the submission pages), "<code>dir</code>" (in which
subdirectory of the default base submission folder the running/done
submissions for this action will be saved, for
eg. <code>submit</code>), and "<code>status text</code>" (displayed as
the label for the action button on the main submission
interface). Press Save Details, and you are ready to use this action.
</p>
<p><b><a name="2.1.5"></a>5.</b> (Optional) To propose a list of categories on the splash
page (page 0) of your submission, select your submission from the
main <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py">WebSubmit
admin interface</a>, scroll down to the "Categories" section on the
page, enter a new category, with "<code>ID</code>" being the key code
of the new category you want to add (this value will be saved in the
corresponding file in <code>curdir</code> directory of your
submission. Reminder: the file in <code>curdir</code> containing this
value will be named <code>comboDEMOTEST</code>, provided that
"<code>DEMOTEST</code>" is your submission ID) and
"<code>description</code>" being the value displayed to the user for
this category. Press "<code>Add Category</code>" to add the category.
</p>
<p><b><a name="2.1.6"></a>6.</b> (Optional) To enter the list of
persons that will be recognized as referees of a submission (for
eg. by the "<code>Is_Referee</code>" function), select your
submission from the
main <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py">WebSubmit
admin interface</a>, scroll down to the "Manage Referees" section on
the page, and click on the "Manage Referees" button.<br/> Select the
user(s) from the list (users must have an account on the system),
choose which category they manage, and click "Add". Once done, click
"Finished".
</p>
<p><b><a name="2.1.7"></a>7.</b> The skeleton of your submission is now basically
ready. You will need to add new pages to it, as well as insert
post-processing functions. These steps are defined in the next
sections. What you can do now is to make the submission visible on the
main <a href="<CFG_SITE_URL>/submit">submissions users page</a>. To do
so, click on
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/organisesubmissionpage">Organise
Main Page</a> of the main menu, select your submission in the
"Document Type Name" menu, choose from the next menu to which branch
of the submission tree you want to attach this submission, and press "Add".
Reorganize the tree as wanted from this interface.</b>
<a name="2.2"></a><h3>2.2 Building the interface</h3>
<p><b><a name="2.2.1"></a>1.</b> Go to the
main <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py">WebSubmit
admin interface</a> and select your submission. Choose the action
(SBI, MBI, etc.) for which you want to build the interface and click
on the corresponding "view interface" link.
</p>
<p><b><a name="2.2.2"></a>2.</b>If you want to add a new page, click
on the "Add a Page" button. Follow the "view page" link displayed next
to the newly created page, or the one next to the page you want to
modify.
</p>
<p><b><a name="2.2.3"></a>3.</b> To add a new field on the page, press
the "Add a Field" button (at the bottom of the screen). On the following page:
<ul>
<li>Select a field from the existing list of WebSubmit elements.</li>
<li>Enter a field label. It will be displayed just before the field
on your page. The label can contain HTML. Note that this label will
not be used in modification actions (MBI) built using the
"<code>Create_Modify_Interface</code>" function. Instead, the
"Modification Text" attribute of the element will be used.</li>
<li>Set if the field should be mandatory or not. Note that some
elements (<a href="#3.2.1">User Defined Input Elements</a>,
<a href="#3.2.5">Hidden Input Elements</a>
and <a href="#3.2.5">Response Elements</a>) should never be set
"mandatory".</li>
<li>Give a short description to the label. It will be used for
eg. to notify the user that mandatory field named <em>XXX</em> has not been filled in.</li>
<li>Select a Javascript check from the list if you want to validate
the content of the field according to some criteria.</li>
</ul>
Once done, hit the "Add Field" button.<br/> Note that this step is
simply instantiating a WebSubmit element to include on your page. If
you want to include a field that does not exist in the available
elements, you should first create it. Learn more about the creation of
WebSubmit elements in the <a href="#3"><em>WebSubmit Elements</em></a>
chapter of this guide.
</p>
<p><b><a name="2.2.4"></a>4.</b>Repeat step 3 as many times as
needed. You can reorder the fields on the page, remove them or
change their attribute. The "edit" link next to each field will let
you change its attributes. The "element" link will however let you
change the attribute of the WebSubmit element itself, i.e. affecting
all the submissions having such a field on their page.</p>
<p><b><a name="2.2.5"></a>5.</b> You can preview the page by pressing
the "View Page Preview" button at the top of the page. Note that
<a href="#3.2.5">Response Elements</a> will however not be previewed.
</p>
<p><b><a name="2.2.6"></a>6.</b> From the "page" interface you can go
back successively to the action interface and the main submission
interface by clicking on the "Finished" buttons at the bottom of the
pages.
</p>
<a name="2.3"></a><h3>2.3 Adding the functions</h3>
<p><b><a name="2.3.1"></a>1.</b> Go to the
main <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py">WebSubmit
admin interface</a> and select your submission. Choose the action
(SBI, MBI, etc.) for which you want to build the interface and click
on the corresponding "view functions" link.
</p>
<p><b><a name="2.3.2"></a>2.</b> To insert a function into the
workflow, press the "Add a Function" button at the bottom of the
screen. On the following page:
<ul>
<li>Select a function from the existing list of WebSubmit functions.</li>
<li>Enter the "<code>Step</code>" to which this function should be
added (for eg. "1").</li>
<li>Enter the "<code>Score</code>" of the function, i.e. its order
in the list of functions of the chosen step (for eg. 20). If a
function already exists for the chosen score, functions will simply
be shifted.</li>
</ul>
Once done, hit the "Save Details" button.<br/> Note that this step is
simply inserting an already existing WebSubmit function in your workflow. If
you want to include a totally new function you should first create it.
Learn more about the creation of
WebSubmit functions in the <a href="#4"><em>WebSubmit Functions</em></a>
chapter of this guide.
</p>
<p><b><a name="2.3.3"></a>3.</b> Once the function is inserted you can
change its parameters by clicking on the "View parameters" link. Each
function has a different set of parameters. Check the function
documentation (available from
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionlist">Available
Functions</a> menu of the WebSubmit admin interface) to learn more
about the offered options.
</p>
<p><b><a name="2.3.4"></a>4.</b> Repeat steps 2 and 3 as many times as
needed. You can reorder the functions on the page or remove them.
</p>
<a name="2.4"></a><h3>2.4 Restricting the submission</h3>
<p>Access to the submission interface is mostly restricted via the
WebAccess module. You can check out the <a href="#6">Access Restrictions</a>
chapter of this guide and refer to
the <a href="<CFG_SITE_URL>/help/admin/webaccess-admin-guide">WebAccess
admin</a> guide for detailed information.</p>
<p>In addition to WebAccess you can use the following functions
to restrict your submission:</p>
<p>If you have set up an action that requires to modify an existing
record (to add file, modify metadata, etc.) you can add the
"<code><a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Is_Original_Submitter">Is_Original_Submitter</a></code>"
function in order to only let the original submitter of the record
modify the record. This function must be added at the beginning of
your list of functions (usually after the
"<code><a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Get_Recid">Get_Recid</a></code>"
function), for <b>each action</b>, and <b>each step</b>. Check out
the <a href="#2.3"><em>Adding the functions</em></a> section of this
guide to learn how to add this function to your workflow.</p>
<p>You can also use the
"<code><a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=User_is_Record_Owner_or_Curator">User_is_Record_Owner_or_Curator</a></code>"
function to enable access to the original submitter of the record AND
users connected to a specific WebAccess role.</p>
<p>If you have set up an action (for eg. "APP") that requires to
approve a document by a referee (defined in the list of referees for
your submission) you can add the
"<code><a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Is_Referee">Is_Referee</a></code>"
function in order to only let the referee go through. This function
must be added at the beginning of your list of functions (usually
after the
"<code><a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Get_Recid">Get_Recid</a></code>"
function), for <b>each action</b>, and <b>each step</b>. Check out
the <a href="#2.3"><em>Adding the functions</em></a> section of this
guide to learn how to add this function to your workflow.</p>
<a name="3"></a><h2>3. WebSubmit Elements</h2>
<p>WebSubmit elements are the building blocks of submission
pages. This section focuses on how to use or create them. Refer to
the overview of this guide to learn more about the concept of
WebSubmit elements.</p>
<a name="3.1"></a><h3>3.1 Existing elements</h3>
<p>The list of existing elements can be found in
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/elementlist">"available
elements" section</a> of the WebSubmit admin interface. By default
these elements are instances used in the demo submissions. You can
reuse them, but it is recommended to create new elements to use in
your own submissions, excepted for complex "response" elements that are
generic enough.</p>
<p>Once instantiated for a submission, elements become <em>fields</em>
on the submission page. It is important to make a difference between
the fields attributes, which are submission specific, and the element
attributes, which apply to all submission using them.</p>
<a name="3.2"></a><h3>3.2 Creating a new element</h3>
<p>This section describes the creation of a customized element. It does
not show how to add an already existing element to your
submission. Refer to the <a href="#2">Tutorial</a> to learn how to add
an existing element to your submission.</p>
<p>To create a new element, go to the
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/elementlist">"available
elements" section</a> of the WebSubmit admin interface, scroll down to
the bottom of the page and press the "Add New Element" button.</p>
<p>Fill in the form:
<ul>
<li><b>Element Name</b>: The name of the element (Eg: <code>DEMO_TITLE</code>)</li>
<li><b>Modification Text</b>: The prefix to be used when the element
is used by the "<code>Create_Modify_Interface</code>" function
(i.e. in MBI actions)</li>
<li><b>Element Type</b>: The type of element:
<ul>
<li><em>User Defined Input</em>: the element is a static area
displaying the content of the field "Element Description". The
content must be HTML-escaped (or can be HTML).</li>
<li><em>File Input</em>: the element is a basic control to upload files</li>
<li><em>Hidden Input</em>: the element is an hidden form input
field, and its value is the one defined in the "Value"
field (below).</li>
<li><em>Text Input</em>: the element is a simple text field. Initial
value is the one defined in the "Value" field.</li>
<li><em>Response</em>: the element executes the Python code from the
"Element Description" field. The code is executed at runtime when
displaying the page. The element output consists in the value
assigned to the variable "<code>text</code>" in the scope of this
field at the end of the execution of the element.</li>
<li><em>Select Box</em>: a list control. The full HTML code of the
list must be given in the "Element Description" field. For eg:
<pre>
&lt;select name="DEMO_LANG"&gt;
&lt;option value="eng"&gt;English&lt;/option&gt;
&lt;option value="fre"&gt;French&lt;/option&gt;
&lt;option value="ger"&gt;German&lt;/option&gt;
&lt;/select&gt;
</pre>
The submitted value will be the one defined in the
"<code>value</code>" parameter.
</li>
<li><em>Text Area Element</em>: An HTML text area field.</li>
</ul></li>
<li><b>Marc Code</b>: the MARC code from which the value could be
retrieved when the element is used by the
"<code>Create_Modify_Interface</code>" function (i.e. in MBI
actions)</li>
<li><b>Size</b>: The size of the text input field (for "Text Input" Element Types)</li>
<li><b>No. Rows</b>: The number of rows for "Text Area" Element Types</li>
<li><b>No. Columns</b>: The number of columns for "Text Area" Element Types</li>
<li><b>Maximum Length</b>: The maximum length (in characters) for
"Text Input" Element Types. Note that it only sets a limits in the
user's browser, but is not check server-side.</li>
<li><b>Value</b>: The initial value for "Text Input" or "Hidden Input" elements</li>
<li><b>Element Description</b>: The content/code for "User Defined Input", "Select Box" and "Response" elements</li>
</ul>
</p>
<p>Once done, hit the "Save Details" button. You are done with the
creation of your element. You can then add it to your submission
page.</p>
<a name="3.2.1"></a><h4>3.2.1 User Defined Input Elements</h4>
<p>This element is simply displaying the the content defined in the
field "Element Description". The content must be HTML-escaped (or
can be HTML). This is element is not really suitable for user-input
values.
</p>
<a name="3.2.2"></a><h4>3.2.2 File Input Elements</h4>
<p>The element displays a basic control to upload files. The file
uploaded with this element can be found upon submission inside
<code>[..]/files/ELEMENT_NAME/</code>
(where <code>ELEMENT_NAME</code> is your element name, for
eg. <code>DEMOART_FILE</code>) within the submission directory.</p>
<p>You can then further process the uploaded file with relevant
WebSubmit functions (eg. stamp the file), and attach it to the record
(see <a href="#5">section 5. <em>File Management with WebSubmit</em></a>
of this guide).</p>
<a name="3.2.3"></a><h4>3.2.3 Hidden Input Elements</h4>
<p>Simply create an hidden input field, with the value defined in the
"Value" field of the element. The uploaded value can be found as any
other element in the submission directory upon submission of the
form.</p>
<p>The main usage of this field is to upload a statically defined
value in order to check if the form has already been submitted. Static
values to be part of the record would better be defined in the
BibConvert configuration file used to create the record.</p>
<a name="3.2.4"></a><h4>3.2.4 Text Input Elements</h4>
<p>A simple text input field, Nothing much to say about it excepted
that it is usually the most used of all elements.</p>
<a name="3.2.5"></a><h4>3.2.5 Response Elements</h4>
<p>Response elements are elements evaluated at runtime, which execute
the Python code they embed. These elements are useful when you need to
display complex controls that are not supported by default by
WebSubmit, or if you need to generate content dynamically. The
returned output (displayed on the submission form) of response
elements is the one defined at the end of the execution in the
"<code>text</code>" variable.<br/> For eg. to display a radio button
one would write:</p>
<pre class="code">
# Sample radio button element
text = ""
options = [1, 2, 3]
for option in options:
text += '&lt;input type="radio" name="group1" id="%(opt)i" value="%(opt)i"&gt;&lt;label for="%(opt)i"&gt;Option %(opt)i&lt;/label&gt;' % {'opt': option}
</pre>
which would display as: <br/>
<input type="radio" name="group1" id="g1" value="1"><label for="g1">Option 1</label>
<input type="radio" name="group1" id="g2" value="2"><label for="g2">Option 2</label>
<input type="radio" name="group1" id="g3" value="3"><label for="g3">Option 3</label>
<p>Upon submission of the form, a file named "<code>group1</code>"
would be created in that case with the chosen value in the submission
directory.</p>
<p>Response elements have "magically" access to some global variables,
provided that they have been set at the moment of executing the element:
<ul>
<li><b><code>sysno</code></b> the current record id</li>
<li><b><code>rn</code></b> the current report number</li>
<li><b><code>act</code></b> the current submission action (SBI, MBI, etc.)</li>
<li><b><code>curdir</code></b> the path of the current submission directory</li>
<li><b><code>uid</code></b> the user ID of the current submitter</li>
<li><b><code>uid_email</code></b> the email of the current submitter</li>
</ul>
</p>
<p>When defining a response element you should be aware of a few traps:
<ul>
<li>You must expect that the page can be reloaded. In that case
possible actions performed by your element should not be done
twice. You also have to take care of the displayed state of your
element. For eg. a list generated by a response element should not
reset to the default value when the page refreshes if the user has
already chosen a custom value. You take care of this by reading the
corresponding file in the submission directory.</li>
<li>When used in MBI (modify) actions with the
"<code>Create_Modify_Interface</code>" function (which takes care of
building the modification form by mirroring the page defined for the
initial submission, SBI), you should read the initial state from the
record (if defined in the record), or from the curdir if the page is
refreshed.</li>
<li>You should never specify a response element as "mandatory" when
including it on your page.</li>
</ul>
A possible skeleton for a response element could be: (FIXME: Check...)
</p>
<pre class="code">
import os
from invenio.websubmit_functions.ParamFile import ParamFromFile
from invenio.search_engine import get_fieldvalues
this_element_name = "DEMOART_TEST" # This would be your element name
if act == "SBI" and not os.path.exists(os.path.join(curdir, this_element_name)):
# Set initial value in case user has not already chosen one.
# This is only needed if you want to set a non-empty default
# value, for eg. retrieved from a remote service, etc.
# Otherwise the 'else' statement would be enough.
default_value = "A default value" # or any default value
elif act == "MBI" and not os.path.exists(os.path.join(curdir, this_element_name)):
# We are displaying a modification interface for the first time:
# Read initial value from the record for eg.
default_value = get_fieldvalues(sysno, '245__a')
else:
# Get user chosen value in case page is reloaded.
# The ParamFromFile() returns empty string if value was not set,
# so this is also suitable for setting empty initial values.
default_value = ParamFromFile(os.path.join(curdir, this_element_name))
# Do something ...
text = '&lt;input type="text" name="%s" value="%s"/&gt;' % (this_element_name, default_value)
</pre>
<p>Since response element needs the submission context and can
possibly have side effects, they are never executed when previewing
your submission pages from the WebSubmit admin interface.</p>
<a name="3.2.6"></a><h4>3.2.6 Select Box Elements</h4>
<p>Select Box elements are used to display lists menus (either as
dropdown menu or multiple selection list). The element is not smart
enough to save you from specifying the HTML markup of the list, but
will at least set the right intial state when reloading the submission
page or when used in MBI actions.</p>
<p>You would for eg. define the following "description" for an element
displaying a list of languages:</p>
<pre>
&lt;select name="DEMOART_LANG"&gt;
&lt;option&gt;Select:&lt;/option&gt;
&lt;option value="eng"&gt;English&lt;/option&gt;
&lt;option value="fre"&gt;French&lt;/option&gt;
&lt;option value="ger"&gt;German&lt;/option&gt;
&lt;option value="dut"&gt;Dutch&lt;/option&gt;
&lt;/select&gt;
</pre>
<p>In the above example a file named "DEMOART_LANG" will be created
with the user chosen value (for eg. "ger") in the submission
directory.</p>
<p>Note that if you set the element as being "mandatory" on your page,
the initial "Select:" value must be the first option of your list (you
can otherwise let specify the element as optional, and remove this
item if wanted). </p>
<a name="3.3"></a><h3>3.3 Creating a new check</h3>
<p>When adding an existing element to your submission page you can
associate a Javacript check to the element. You can choose from the
existing one or define your own check from
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/jschecklist">Available
Checks</a> menu of the WebSubmit admin interface.</p>
<p>From the "Available Checks" page, select "Add check", give it a
name and a "description": the description corresponds to the
Javascript code to be executed to validate the form before submitting
it. In this description you should define a Javascript function named
after your check, that takes a string (the value to validate) as
input. The function must then return <code>0</code> if the check fails
(the form cannot be submitted) or <code>1</code> if the check passes.
In addition you may want to raise an alert notifying the user about
the error.
</p>
<p>For eg. to check if the given number of a field is smaller than 10,
we create a "check" named <code>Smaller_Ten</code>:</p>
<pre class="code">
def Smaller_Ten(txt) {
/* Check if input is strictly smaller than 10 */
if (parseInt(txt) < 10 && parseInt(txt).toString()==txt) {
// Note that parseInt('9a') returns 9, hence the '.toString()==txt' test.
return 1;
} else {
alert("The given number is not smaller than 10! Please fix it.");
return 0;
}
}
</pre>
<a name="4"></a><h2>4. WebSubmit Functions</h2>
<p>This section focuses on how to create new WebSubmit functions and
use existing ones. To learn more about the concept of WebSubmit
functions, read the <a href="#shortIntro">Overview</a> section of this
guide.</p>
<a name="4.1"></a><h3>4.1 Existing functions</h3>
<p>The list of existing functions can be found in
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionlist">"available
functions" section</a> of the WebSubmit admin interface. Click on "Edit
Details" links to read more about the functions.</p>
<p>You add existing functions in the functions list of each action
(SBI, MBI, etc.) of your submission in order to post-process
user-submitted values and build your customized workflow. Some
functions have some prerequisites on the order they are run, and the
functions that must precede them. For eg. many functions expect the
"<code>Get_Recid</code>" function to run before them. You can check
the workflows provided with the Atlantis Demo installation</p>
<a name="4.2"></a><h3>4.2 Creating a new function</h3>
<p>This section describes the creation of a customized function. It
does not show how to add an already existing function to your
submission. Refer to the <a href="#2">Tutorial</a> to learn how to add
an existing function to your submission.</p>
<p>A WebSubmit function corresponds to a Python file, which must be
named after the function name (eg "<code>My_Function</code>" =&gt;
"<code>My_Function.py</code>") and placed into
the <code>/opt/invenio/lib/python/invenio/websubmit_functions/</code>
directory. The file must also contain a Python function with the same
"<code>My_Function</code>" name. This function interface must be
the following one:
<pre>
def My_Function(parameters, curdir, form, user_info=None):
</pre>
where
<ul>
<li><code>parameters</code>: a dictionary containing the parameters
and values that can be configured in the submission web interface.</li>
<li><code>curdir</code>: the path to the current working directory.</li>
<li><code>form</code>: the form passed to the current web page for possible reference from inside the function. </li>
<li><code>user_info</code>: the user_info objet reprenting the current user</li>
</ul>
The values returned by the function are printed on the last
submission page.
</p>
<p>For the function to be available from the WebSubmit admin
interface, it must be specifically inserted from
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionlist">admin
interface</a>. Scroll down to the bottom of the list, and press "Add
New Function". Insert the function name, as well as all the wished
parameters for the function.</p>
<a name="5"></a><h2>5. File Management with WebSubmit</h2>
<p>This chapters introduces different strategies to enable file upload
in WebSubmit submissions. You should already have a good understanding
of how WebSubmit works before reading further. Some practice in
WebSubmit submission implementation is also highly recommended in
order to understand the techniques introduced below. To some extent,
you might want to come back to this chapter only once you have
already set up your submission, and are about to implement file
support, as the documentation below is sometimes describing detailed
implementation steps.</p>
<p>Several techniques exists to handle files, to accommodate to
various use cases. Just read further below to choose the most
appropriate technique based on your needs.</p>
<a name="5.1"></a><h3>5.1 File Input + FFT Technique</h3>
<p>The most "basic" way of letting your users submit files is to add a
<em>File Input</em> element to your submission page(s), one for each possible
file to upload, in the same way as you add other input fields.<br/>
This technique is useful if you need to handle a well known number of
files. </p>
<b>Limitations:</b>
<ul>
<li>incompatible with function "<code>Move_to_Done</code>", as the path in the FFT tag would be wrong.</li>
<li>revision of files requires well-defined filenames</li>
<li>cannot easily delete files</li>
<li>cannot easily support file attributes (description, restriction, name, etc.) modifications</li>
</ul>
<b>Procedure:</b>
<p><b>1)</b> You can reuse an already existing <em>File Input</em> element, or create
your own. If you want to reuse an existing one, jump straight to
point 3 below. Otherwise, head to the WebSubmit admin interface,
select "6. Available Elements" in the menu, scroll down the opening
page and hit "Add New Element" button.
<p><b>2)</b> Choose a name for your new element (For e.g. "<code>DEMO_FILE</code>"). Select
the "<em>File Input</em>" item of the "Element Type" menu. Once done, click on
the "Save Detais" button.
<p><b>3)</b> Go to the main WebSubmit admin interface and select the submission
you want to edit (for e.g. "DEMOART"), then action (for e.g. "SBI"),
then the page. Scroll to the bottom of the page, and click on the "Add
a Field" button.
<p><b>4)</b> From the "Field Name" menu, select the desired input file element
(for e.g. "<code>DEMO_FILE</code>", if you have created it in previous steps). Fill
in the other usual fields, and click "Add Field". Reorder the elements
on the page as needed.
<p>At this step your users will be able to upload a file to the server
during the submission process. Indeed if you have a look at the
corresponding submission directory in
<code>/opt/invenio/var/data/submit/storage/</code> you will see the uploaded
file in the <code>/files/DEMO_FILE/</code> directory, plus a standard <code>DEMO_FILE</code>
file containing the path to the uploaded file. However the file is not
attached to the uploaded record: you must add a corresponding entry in
the BibConvert template, in a similar fashion as you would with other
input fields.</p>
<p><b>5)</b> Open your BibConvert "target" template used by the "<code>Make_Record</code>" or
"<code>Make_Modify_Record</code>" in your preferred editor. If you know where to
find your BibConvert templates, jump to point 6. Otherwise continue
reading: the BibConvert templates are used by the "<code>Make_Record</code>" and
"<code>Make_Modify_Record</code>" to create a MARCXML according to some specific
rules. From your submission page, click on "view functions" of the
action you want to edit, then "view parameters" of the
<code>Make_Record</code>/<code>Make_Modify_Record</code> function. The "create/modifyTemplate"
and "sourceTemplate" are the names of the BibConvert templates you can
find in the <code>/opt/invenio/etc/bibconvert/config/</code> directory
(Depending on the authorization on disk, you might even be able to
edit the files from the web interface). Read more about BibConvert in
the <a href="<CFG_SITE_URL>/help/admin/bibconvert-admin-guide">BibConvert admin guide</a>.
<p><b>6)</b> Add an <em>FFT</em> tag to your target BibConvert template. FFT is a special
tag interpreted by BibUpload in order to handle files. You will find
an example below, but you can read more about the FFT syntax in the
<a href="<CFG_SITE_URL>/help/admin/bibupload-admin-guide#3.5">BibUpload admin guide</a></p>
<pre style="overflow-x:auto">
FFT::REPL(EOL,)---&lt;datafield tag="FFT" ind1=" " ind2=" "&gt;&lt;subfield code="a"&gt;&lt;:curdir::curdir:&gt;/files/DEMO_FILE/&lt;:DEMO_FILE::DEMO_FILE:&gt;&lt;/subfield&gt;&lt;subfield code="n"&gt;My File&lt;/subfield&gt;&lt;subfield code="t"&gt;Main&lt;/subfield&gt;&lt;/datafield&gt;
</pre>
<p>The sample line above will rename the uploaded record to "My File",
and then attach it to the record (once the created MARCXML will be
BibUploaded). Note that you could keep the original name, or name the
file after the report number, specify a <code>doctype</code> such as "Main", or
"additional", include a comment specified in another field,
etc. Simply modify the FFT tag according to your needs. Note however
that this technique will allow to revise the file only if you can
identify it later by a well defined name. The above line is also uploading
the file in the category, or <em>doctype</em> "Main"</p>
<p><b>7)</b> One last thing not to forget is to
add <code>DEMO_FILE</code> to the source BibConvert template, as you
would for any other WebSubmit element. Open the source BibConvert
template (which is also given as parameter to
the <code>Make_Record</code>/<code>Make_Modify_Record</code>
functions, and can be found in
the <code>/opt/invenio/etc/bibconvert/config/</code> directory),
and add for example:</p>
<pre>
DEMO_FILE---<:DEMO_FILE:>
</pre>
<p>Repeat this procedure to add additional input file fields. It is
perfectly ok to have several FFT field instances in the
templates.</p>
<p>Note that if one of the <code>file input</code> fields is left empty by the
user, no file is uploaded, no <code>DEMO_FILE</code> file is created
in the submission directory, but an erroneous FFT line is still
inserted in the created output. It is why you might want to make all
the <code>File Input</code> fields mandatory, or use the
BibConvert <code>MINLW(..)</code> function to ensure that the field is
created only if the output line is at least a given number of
characters (to be computed based on the default length of an empty
line). This shows that this technique reaches its limits quite quickly
in terms of flexibility.
</p>
<a name="5.1revise"></a><h4>Revising/deleting files</h4>
<p>To revise files you would create a BibConvert template with the
adequate FFT tag. We assume below that you set up the modification
interface by using the <code>Create_Modify_Interface</code>
function/technique, so that we can reuse the submission page set up
for the "SBI" action. The key point is that the <code>Input
File</code> element name is well known ("<code>DEMO_FILE</code>" in
our case).</p>
<p><b>1)</b> Open your BibConvert "target" template used by the
"<code>Make_Modify_Record</code>" function. Note that it should not be
the same one as used in the "SBI" action of your submission, as it
must create different outputs.</p>
<p><b>2)</b> Add an FFT tag to revise your file:</p>
<pre>
&lt;datafield tag="FFT" ind1=" " ind2=" "&gt;
&lt;subfield code="a"&gt;&lt;:curdir::curdir:&gt;/files/DEMO_FILE/&lt;:DEMO_FILE::DEMO_FILE:&gt;&lt;/subfield&gt;
&lt;subfield code="n"&gt;My File&lt;/subfield&gt;
&lt;subfield code="d"&gt;KEEP-OLD-VALUE&lt;/subfield&gt;
&lt;subfield code="z"&gt;KEEP-OLD-VALUE&lt;/subfield&gt;
&lt;subfield code="r"&gt;KEEP-OLD-VALUE&lt;/subfield&gt;
&lt;/datafield&gt;
</pre>
<p><b>3)</b> The above FFT will be <em>bibuploaded</em> in
<code>--correct</code> mode, hence revising the file named "My File"
with the new one. Note in this example the use of the special keyword
<code>KEEP-OLD-VALUE</code> to keep the previous comment, description or
restriction applied to the file, if any (so that comment is not lost
for e.g. if you don't ask a new one). </p>
<p>You will notice the following limitation: you must be able to map
the uploaded file to the target file to revise by its name. This means
that you should be able to initially control your filename(s), for e.g. by
having it fixed ("Main", "additional", "figure", etc) or guessable,
for e.g. using the report number
(<code>&lt;:DEMOART_RN::DEMOART_RN:&gt;-main,
&lt;:DEMOART_RN::DEMOART_RN:&gt;-additional</code>). </p>
<p>To circumvent this limitation (as well as the impossibility to
delete files), you might combine this technique with one of the
techniques described below (For eg: with
the <code>Move_Revised_Files_To_Storage</code> function detailed in
the <a href="#2.2revise">Revising/deleting files</a> section of
the <a href="#2.2">File Input element + Move_Files_To_Storage
function</a> technique) </p>
<a name="5.2"></a><h3>5.2 File Input element + Move_Files_To_Storage function</h3>
<p>This way of doing is similar to the <a href="#2.1">technique
described above</a>. The main difference is that it leaves the job of
actually uploading/revisings the file(s) to a WebSubmit functions,
instead of the FFT in the uploaded MARCXML.</p>
<b>Limitations:</b>
<ul>
<li>revision of files requires
well-defined <code>doctype</code>. The consequence is that you can
have only one file per doctype (1 "Main", 1 "Additionnal",
etc.)</li>
<li>cannot easily delete files</li>
<li>does not support setting some additional file attributes (description, name, etc.)</li>
<li>uploaded doctypes must inherit the names of their <code>File Input</code> elements. For eg. "DEMO_FILE", instead of "Main", "Additional", "Figure", etc.</li>
</ul>
<p><b>1-4)</b> Add a file input field to your submission page as
describe in <a href="#2.1">previous technique</a>.</p>
<p>As before, the file is uploaded to the server once the user ends
the submission, but it is not attached to the created record. The
solution is to rely on the "<code>Move_Files_To_Storage</code>" function:</p>
<p><b>5)</b> Add the "<code>Move_Files_To_Storage</code>" function to your submission
functions. It is suggested to insert it after the function
"Insert_Record".</p>
<p><b>6)</b> Configure the <code>Move_Files_To_Storage</code>
function. The key parameter is <code>paths_and_suffixes</code>, which
must contain your <code>File Input</code> element names, and possibly
map to some suffixes to be added to the corresponding uploaded
files.<br/> For example, add <code>{'DEMO_FILE':'',
'DEMO_FILE2':'_additional'}</code> to have the files uploaded with
DEMO_FILE and DEMO_FILE2 elements attached to the record (with the
DEMO_FILE2 filename suffixed with
"_additional"). The <code>paths_and_restriction</code> works similarly
to set the files restrictions.
</p>
<p>Each file is simply attached to the record, with its document type
(<code>doctype</code>) being the name of your input file element (for e.g. file
uploaded with the "<code>DEMO_FILE</code>" element is attached with document type
"<code>DEMO_FILE</code>"). The filenames are kept.</p>
<a name="5.2revise"></a><h4>Revising/deleting files</h4>
<p>The "<code>Move_Revised_Files_To_Storage</code>" must be added to your modification
workflow ("MBI"). It will use the file uploaded with your "<code>DEMO_FILE</code>"
input element to revise the file with <code>doctype</code> "<code>DEMO_FILE</code>", the file
from "<code>DEMO_FILE2</code>" input element to revise file with <code>doctype</code>
"<code>DEMO_FILE2</code>", etc.</p>
<p><b>1)</b> Go to your modification workflow (MBI), and add
<code>Move_Revised_Files_To_Storage</code> to your submission
functions (usually after the "<code>Insert_Modify_Record</code>").</p>
<p><b>2)</b> Set up the <code>elementNameToDoctype</code> parameter of
this function so it maps your <code>File Input</code> field name to
the doctype to revise. For eg: "<code>DEMO_FILE=Main</code>" so that
file uploaded using the <code>DEMO_FILE</code> input field will be
used to replace the file with <code>doctype</code> "Main". This makes
the assumption that you indeed previously uploaded (for eg. with an
FFT during an SBI step) a file with this doctype.<br/> You can define
several mappings, by using character <code>|</code> as separator. For
eg:
<code>DEMO_FILE=Main|DEMO_FILE2=Additional</code>.<br/> If you have
initially uploaded your files with
the <code>Move_Files_To_Storage</code> function, you will for
eg. configure the parameter with "<code>DEMO_FILE=DEMO_FILE</code>",
so that file uploaded with <code>DEMO_FILE</code> input field will
replace the files that have been previously uploaded with doctype
"DEMO_FILE".
</p>
<p>Note that function <code>Move_Revised_Files_To_Storage</code> can
be used in combination with other techniques, as long as the mapping
in <code>elementNameToDoctype</code> can be done unambiguously.</p>
<p>Check
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Move_Revised_Files_to_Storage"><code>Move_Revised_Files_To_Storage</code>
function documentation for more detailed information.</p>
<a name="5.3"></a><h3>5.3 Create_Upload_Files_Interface +
Move_Uploaded_Files_To_Storage functions</h3>
<p>This option offers a full-featured file manager, that can be
easily configured to support file upload, revision, deletion,
commenting, restrictions, etc. It can handle an "unlimited" number of
files.</p>
<p>The strategy consists in adding a WebSubmit function
("<code>Create_Upload_Files_Interface</code>") to your submission functions list,
in order to display a file submission interface. The interface will
therefore only show up after all the submission pages have been filled
in and submitted. Once displayed, the interface lets the user upload
new/revised files: the function refreshes the interface for each
upload (runs through the functions list again and stops on the
<code>Create_Upload_Files_Interface</code>). When the user applies the
modifications, the submission "step" is incremented and executes the
submissions function of step 2, skipping the display of the
interface. In this step 2 you can perform the usual tasks of your
submission. You also must add an additional function
(<code>Move_Uploaded_Files_To_Storage</code>) to run at step 2 in order to attach
the files that have been submitted at step 1.</p>
<p>These functions are incompatible with function
"Create_Modify_Interface". It is therefore suggested to create a
dedicated submission action (in addition to "SBI" and "MBI") to let
your users edit the files independently of the bibliographic data. An
example of such setup can be found in DEMOPIC submission.</p>
<b>Limitations:</b>
<ul>
<li>Use of a WebSubmit function to draw the interface, which prevents
the interface to be used inside a submission form (is displayed at a
later step). Not as integrated as a simple input file form
element.</li>
<li>Requires Javascript to be enabled user-side (is applicable to all
submissions anyway.</li>
</ul>
<p><b>1)</b> Go to your submission in WebSubmit admin, and add a new
submission action (for e.g. "[SRV] Submit New File"). If necessary,
create your own action in <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/actionlist">WebSubmit admin "Available WebSubmit Actions"</a>
page. You can clone from another existing action (in that case move to
point 4 below), or simply create an empty action.</p>
<p><b>2)</b> Go to the new SRV action interface ("View Interface"), add a
page, open it and add fields that will allow users to specify the record
to update. Typically you will add a "<code>DEMO_RN</code>" field to enter the
report number, and "<code>DEMO_CONTINUE</code>" button to submit the form.</p>
<p><b>3)</b> Go the the new SRV action functions ("View" functions) and add
the necessary functions: for e.g. at step 1, "<code>Get_Report_Number</code>",
"<code>Get_Recid</code>" and "<code>Create_Upload_Files_Interface</code>". At step 2,
"<code>Get_Recid</code>", "<code>Move_Uploaded_Files_to_Storage</code>" and "<code>Print_Success</code>".</p>
<p><b>4)</b> Configure the <code>Create_Upload_Files_Interface</code> parameters. There
are many options available. Briefly, the most important one is the
"<code>doctype</code>" parameter, which lets you specify the document types users
are allowed to submit. Use "<code>|</code>" to separate doctypes, and "<code>=</code>" to
separate <code>doctype</code> and <code>doctype</code> description. For e.g. input "<code>Main=Main
File|Additional=Additional Document</code>" to let users choose either Main
or Additional types (which will show as "Main File" and "Additional
Document" to users). Other parameters will let you define for which
<code>doctype</code> users can revise or delete files (for e.g. specify for
<code>canDeleteDoctypes</code> "Additional" so that only these
documents can be deleted once they have been uploaded). Use
"<code>*</code>" to specify "any declared doctype", and
"<code>|</code>" as separator (for all <code>can_*_doctypes</code>
parameters).</p>
<p>To read more about the parameters available for this function, check the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Create_Upload_Files_Interface"><code>Create_Upload_Files_Interface</code> function documentation</a>.</p>
<p><b>5)</b> Configure the <code>Move_Uploaded_Files_To_Storage</code>. There are less options
than in <code>Create_Upload_Files_Interface</code> function. Specify for e.g. in
<code>createIconDoctypes</code> for which doctypes icons will be
created, or in "<code>forceFileRevision</code>" if revisions of file
attributes trigger a new file revision. For an up-to-date
documentation check
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Move_Uploaded_Files_to_Storage"><code>Move_Uploaded_Files_to_Storage</code>
function documentation</a>.</p>
<h4>Revising/deleting files</h4>
<p>File revisions and deletions comes for free with the
functions. Simply allow deletion or revision of files when
configuring <code>Create_Upload_Files_Interface<code>.</p>
<a name="5.4"></a><h3>5.4 Upload_File element instance + Move_Uploaded_Files_To_Storage function</h3>
<p>This is similar to <a href="#2.3">option 3</a>, except that instead of using a
WebSubmit function to build the interface, you use a regular WebSubmit
response element. The advantage is that you can plug the WebSubmit
element wherever you want on your submission page.</p>
<b>Limitations:</b>
<ul>
<li>Requires Javascript enabled users-side + support for JQuery library
(most "recent" browsers)</li>
</ul>
<p>To set up a file upload interface using this technique:</p>
<p><b>1)</b> Go to your submission page, and add an element: choose the
"<code>Upload_Files</code>" response element. <b>But wait!</b> Read further before:</p>
<p><b>2)</b> You most probably want to customize the upload interface (set
which types of files can be uploaded, how many, etc.). To do so, you
would have to edit the code of the <code>Upload_Files</code> response element and
change the parameters of the "<code>create_file_upload_interface(..)</code>"
function. However this would affect all submissions using this
element. The solution is to "clone" this element (by creating a new
element: "Available elements"-&gt; scroll down -&gt; "Add New
Element". Choose for e.g. name "<code>DEMO_UploadFiles</code>", Element Type-&gt;
"Response" and paste the code of the <code>Upload_Files</code> element in the
"Element Description" field). Once done, add the "<code>DEMO_UploadFiles</code>"
element to your page.</p>
<p><b>3)</b> Go to your submission functions. Add
the <code>Move_Uploaded_Files_to_Storage</code> function, and
configure it in the same way as it would be done with
the <a href="#2.3">option 3</a>, step 5.</p>
<h4>Revising/deleting files</h4>
<p>File revisions and deletions comes for free with the
this technique. Simply allow deletion or revision of files when
configuring <code>Upload_Files<code> element of the MBI or SRV steps.</p>
<a name="5.5"></a><h3>5.5 FCKeditor element instance + Move_FCKeditor_Files_To_Storage function</h3>
<p>This technique relies on the popular HTML rich text editor
"FCKeditor", which embeds an interface to upload files. As a
consequence it only makes sense to use this technique in the cases
where you want files to be uploaded as part of some HTML context.
Typical use cases are submissions for the WebJournal module, for which
you want to upload articles. The <code>DEMOJRN</code> submission is an
example of submission using this technique.</p>
<b>Limitations:</b>
<ul>
<li>Requires Javascript enabled users-side + support for the FCKeditor
(most "recent" browsers)</li>
<li>File revisions and deletions are not supported as such (must be
done through other options).</li>
</ul>
<p>Setting up a submission to use the FCKeditor is really similar to
the strategy described in <a href="#2.4">option 4</a>: the principle is
to instantiate a custom "Response Element" that will call a function taking
care of the interface, and then plug a WebSubmit function to take care
of attaching the files.</p>
<p><b>1)</b> Go to your submission page, and add an element: choose the
"<code>DEMOJRN_ABSE</code>" response element. <b>But wait!</b> Read further before:</p>
<p><b>2)</b> You will want and need to customize the behaviour of the
FCKeditor, but you don't want to alter the behaviour of other
submissions using this element. The solution is to "clone" this
element: create a new element: "Available elements"-&gt; scroll down
-&gt; "Add New Element". Choose for e.g. name
"<code>DEMO_FCKEDITOR</code>", Element Type-&gt; "Response" and paste
the code of the <code>DEMOJRN_ABSE</code> element in the "Element
Description" field). Customize the element according to your
needs. This will need some development skills and good overview of your
metadata and submission in order to have the editor correctly
initialized. Additional information can be found in
the <a href="<CFG_SITE_URL>/help/hacking/fckeditor">FCKeditor
Integration guide</a>.</p>
<p><b>3)</b> Once done, add the "<code>DEMO_FCKEDITOR</code>" element to your
page.</p>
<p><b>4)</b> Go to your submission functions. Add
the <code>Move_FCKeditor_Files_To_Storage</code> function, and
configure it so that the <code>input_fields</code> parameter list the
name(s) (separated by comma if several instances) given to the
FCKeditor instance(s) created in by the <code>DEMO_FCKEDITOR</code>
response element.</p>
<h4>Revising/deleting files</h4>
<p>The way this editor is currently used does not let you
delete/revise file right from the editor interface. To set up file
deletion/revision, combine this technique with <a href="#2.3">option
3</a> for example.</p>
<a name="5.6"></a><h3>5.6 Upload_Photo_interface element instance + Move_Photos_To_Storage function</h3>
<p>This interface is specifically dedicated to pictures: it enables
the selection of bunch of photos to upload, and let you preview and
comment them before submitting the record.</p>
<b>Limitations:</b>
<ul>
<li>Requires Javascript enabled users-side + support for the Flash plugin
(version >= 9.0.24)</li>
<li>Support for deletions, but not revisions</li>
</ul>
<p>Setting up a submission to use this interface is really similar to
the strategy described in <a href="#2.4">option 4</a>: the principle is
to instantiate a custom "Response Element" that will call a function taking
care of the interface, and then plug a WebSubmit function to take care
of attaching the files.</p>
<p><b>1)</b> Go to your submission page, and add an element: choose
the "<code>Upload_Photos</code>" response element. <b>But wait!</b>
Read further before:</p>
<p><b>2)</b>As in other strategies that use a response element to
layout the interface, you might want to customize the behaviour of the
photos uploader, but you don't want to alter the behaviour of other
submissions using this element. If so (though it is not needed in the
case of this interface), the solution is to "clone" this element:
create a new element: "Available elements"-&gt; scroll down -&gt; "Add
New Element". Choose for e.g. name "<code>DEMO_UPLOADPHOTO</code>",
Element Type-&gt; "Response" and paste the code of
the <code>Upload_Photos</code> element in the "Element Description"
field). Customize the element according to your needs. This will need
some development skills in order to have the interface correctly
customized.</a>.</p>
<p><b>3)</b> Once done, add the "<code>DEMO_UPLOADPHOTO</code>"
(or <code>Upload_Photos</code> if you kept the original file) element
to your page.</p>
<p><b>4)</b> Go to your submission functions. Add
the <code>Move_Photos_To_Storage</code> function, and
configure it according to your needs.</p>
<h4>Revising/deleting files</h4>
<p>The interface lets user add or remove files, but cannot
specifically revise a file. If needed, it can be combined with another
strategy such as <a href="#2.3">option 3</a>.</p>
<a name="5.7"></a><h3>5.7 Alternatives: BibDocFile CLI or BibDocFile Web Interface</h3>
<p>These last techniques are not meant to be used in WebSubmit
submissions, but are admin tools that can be used to manage files,
independently of any submission. They are described here for the sake
of completness.</p>
<p>The BibDocFile command line interface is describe in more details
in <a href="<CFG_SITE_URL>/help/admin/howto-fulltext">How to manage
your fulltext files through BibDocFile</a>.</p>
<p>The <a href="<CFG_SITE_URL>/submit/managedocfiles">BibDocFile admin
interface</a> gives access to some of the functionalities offered by
its command-line equivalent through a graphical web interface. Its
interface is similar to the one offered by
the <code>Upload_File</code> element or
the <code>Create_Upload_Files_Interface</code> function, but is not
tied to a specific submission (and therefore won't automatically
execute post-processing steps such a stamping).<br/> Access to the
BibDocFile admin interface is restricted via the
WebAccess <code>runbibdocfile</code> action.
</p>
<a name="6"></a><h2>6. Access restrictions</h2>
<p>This section focuses on restricting the access to the submission
themselves, not to produce content (records, files, etc.) which are
restricted. Refer to the adequate document to restrict the
collections or files.</p>
<a name="6.1"></a><h3>6.1 Admin-level</h3>
<p>Access to the WebSubmit admin interface is controlled via the
WebAccess <code>cfgwebsubmit</code> action.</p>
<a name="6.2"></a><h3>6.2 User-level</h3>
<p>Access to the submissions is controlled via the
WebAccess <code>submit</code> action. The action has the following
parameters:
<ul>
<li><b><code>doctype</code></b>: the submission code
(eg. <code>DEMOART</code>) for which you want to set
restrictions.</li>
<li><b><code>act</code></b>: the action (for eg. "SBI") for which you
want to set the restriction. Can be <b>*</b> to mean any action for
the given submission.</li>
<li><b><code>categ</code></b>: the category (for eg. "Article",
"Preprint") for which you wan to set the restriction. Can be <b>*</b>
to mean any category for the given submission.</li>
</ul>
</p>
<p>Connect for eg. a role to the <code>submit</code> action with parameters
<code>doctype=DEMOART, act=SBI, categ=*</code> to let people of this
role submit new documents in the <code>DEMOART</code> submission, in
any category.</p>
<p>
<b>If you do not add an authorization for a given submission doctype
and action (even an empty role), the submission is open to
anybody.</b> For eg. in the above example, provided that an MBI action
exists, even with a restricted SBI action anybody will be able to
modify existing documents with MBI unless the MBI action is also
connected to a role. To make it short: a submission it not restricted
until it is...
</p>
<p>Note that it is your responsibility as WebSubmit admin to <b>ensure
that your workflow is not modifying records outside the desired
scope</b>. Given that records are independant of the submission that
created them, there is no mechanism in the WebSubmit engine that
prevents the DEMOART submission to modify records created with the
DEMOBOOK submission. A check must be added at the level of WebSubmit
functions of your submission to make sure that chosen submission and
category well match the record to be modified (for eg. retrieved via
the <code>Get_Report_Number</code> function)</p>.
<p>All the above checks also do not <b>prevent any authorized user to
modify documents submitted by others</b>. To enable finer-grained
restrictions, use the WebSubmit function
"<code>Is_Original_Submitter</code>" or
"<code>User_is_Record_Owner_or_Curator</code>" in your MBI, SRV,
etc. submission workflow (for eg. just after the "Get_Recid"
function). Check also the <a href="#2.4">Restricting the
submission</a> how-to from this guide.</p>
<a name="7"></a><h2>Terminology</h2>
<a name="7.1"></a><h3>The document type of a file (<code>doctype</code>)</h3>
<p>The document type is an attribute of a file. It can be seen as a
category which lets you organize your files: "Main" file,
"Additional", "Figures", "source", whatever you need. It is not so
-much used excepted on <code>record/XXX/files/</code> pages to group
+much used excepted on <code><CFG_SITE_RECORD>/XXX/files/</code> pages to group
files by category. It can however come handy during file upload
processes, to assign different kinds of restrictions based on the
document type, or simply to make the configuration of the submission
easier, depending on which technique you use to manage files.</p>
<a name="7.2"></a><h3>The submission directory (<code>curdir</code>)</h3>
<p>The WebSubmit workflow mainly splits in two parts: data gathering
(user interface side, with WebSubmit pages and elements) and data
integration part as a second step (with WebSubmit functions involved,
plus BibConvert templates). In the middle stands the submission
directory (also called "<code>curdir</code>"). Each submission session corresponds
to a unique submission directory, which stores the values collected
from the submission pages, in the form of a series of textual files,
one for each input field. These files are named after the submission
WebSubmit elements, and their content is the value input by the
submitter. Note that uploaded files are stored in
a <code>/files/</code> subdirectory.</p>
<p>WebSubmit functions process the files in this directory. For example
"<code>Make_Record</code>" which creates the MARCXML (through BibConvert
templates), or the <code>Stamp_Uploaded_Files</code>, which will stamp
the uploaded files in the <code>/files/</code> directory. If you
happen to write a customized WebSubmit response element that writes
files to disk, or implement a WebSubmit function that must retrieve
submitted values, you will certainly use the submission directory.</p>
<p>These submission directories are also helpful to debug submissions,
and can act as a backup when something goes wrong during a submission.</p>
<p>An example of submission directory could be found at this
path <code>/opt/invenio/var/data/submit/storage/running/DEMOART/1245135338_62620</code>,
where DEMOART is your submission code,
and <code>1245135338_62620</code> is the submission session ID, as
found at the bottom of each WebSubmit web page during the submission
process. Just after the user has finished the submission, this
directory would contain all the collected values of the form. But the
life of the submission directory does not stop there. Immediately
after the user completed the submission, the WebSubmit functions are
executed: for e.g. (depending on how you have configured your
submission) creation of a report number (stored in the submission
directory too!) (Function <code>Report_Number_Generation</code>),
creation of the MARCXML (usually named "<code>recmysql</code>", in the
submission directory again!) (Function <code>Make_Record</code>),
upload of the MARCXML (Function <code>Insert_Record</code>)
and <code>Move_To_Done</code>. This last function moves the submission
directory to a new place. It could be for
e.g.: <code>/opt/invenio/var/data/submit/storage/done/DEMOART/DEMO-ARTICLE-2010-001.tar.gz</code>,
supposedly that the report number of the submitted record
is <code>ARTICLE-2010-001</code>. Some other functions will move the
submission directory to other places, and some functions will even let
you configure where to move it.</p>
<code>- End of new WebSubmit admin guide -</code>
<hr/>
<a name="oldwebsubmitguide"></a>
<div style="background-color:#bbb;border:3px dashed #444">
<p><table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
WARNING: OLD WEBSUBMIT ADMIN GUIDE FOLLOWS
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
This WebSubmit Admin Guide was written for the previous PHP-based
version of the admin tool. The submission concepts and pipeline
description remain valid, but the interface snapshot examples
would now differ. The guide is to be updated soon.
</td>
</tr>
</tbody>
</table>
<h1>Table of Contents</h1>
<ul>
<li><b>Introduction</b>
<ul>
<li><a href="#introduction">General Overview of the Manager Tool</a>
<li><a href="#example">Using the manager through an example</a>
<li><a href="#philosophy">Philosophy behind the document submission system</a>
</ul>
<li><b>The Interface</b>
<ul>
<li><a href="#description">Description</a>
</ul>
<li><b><a href="#documents">Types of Document</a></b>
<ul>
<li><a href="#documentnew">Add a New Type of Document</a>
<li><a href="#documentremove">Remove a type of document</a>
<li><a href="#documentmodify">Modify an Existing Type of Document</a>
</ul>
<li><b><a href="#actions">Actions</a></b>
<ul>
<li><a href="#actionnew">Add a New Action</a>
<li><a href="#actionremove">Remove an Action</a>
<li><a href="#actionmodify">Modify an Existing Action</a>
<li><a href="#actionimplement">Implement an Action over a Document Type</a>
<ul>
<li><a href="#implementwebform">Create and Maintain the Web Form</a>
<li><a href="#implementfunctions">Create and Maintain the Data Treatment</a>
</ul>
</ul>
<li><b><a href="#functions">Functions</a></b>
<ul>
<li><a href="#functionnew">Create a New Function</a>
<li><a href="#functiondelete">Remove a Function</a>
<li><a href="#functionedit">Edit a Function</a>
<li><a href="#functiondescription">All Functions Explained</a>
</ul>
<li><b><a href="#protection">Protection</a></b>
<li><b><a href="#catalogues">Catalogues Organisation</a></b>
<li><b><a href="#bibconvert">BibConvert</a></b>
<li><b>Notes</b>
<ul>
</ul>
<li><b><a href="#faq">FAQ</a></b>
</ul>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="introduction"></a>
<h1>General Overview of the Manager Tool</h1>
<h3>Things to know before using the Manager:</h3>
<blockquote>
<ul>
&nbsp;<span class="guideheader">T</span>his manager tool allows you to administrate all the WebSubmit
interface. With it, you will be able to create new actions, new types of documents and edit the existing ones.
<br/><br/>
&nbsp;<span class="guideheader">T</span>he main objects in webSubmit are the "action" (such as
"Submit New Record", "Submit New File", "Modify Record"...) and the "type of document" (such as "preprint",
"photo"...).<br/><br/>
&nbsp;<span class="guideheader">T</span>o one given type of document can be attached several actions.
An action is the addition of two processes:
<ul>
<li>The first one is the <a href="#implementwebform">data gathering</a>. The manager
will allow you to create new web forms corresponding to the fields the user will have to fill in when using
webSubmit.
<li>The second one is the <a href="#implementfunctions">data treatement</a>.
Basically, what the program will do with the data gathered during the first phase. The treatment appears
in this tool as a sequence of functions. This manager will allow you to add functions to an action, edit the
existing functions, and reorder the functions.
</ul>
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#example">using the manager through an example</a><br/>
<li><a href="#description">interface description</a><br/>
<li><a href="#actions">actions</a><br/>
<li><a href="#documents">document types</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="example"></a>
<h1>Using the manager through an example</h1>
<h3>what is this?</h3>
<blockquote>
<ul>
This page presents you the typical situations a user could meet using WebSubmit, and for each situation how to use the manager to configure it.
</ul>
</blockquote>
<h3>The user reaches WebSubmit main page.</h3>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-main_menu.png" alt="Main Page" class="guideimg" align="left">
&nbsp;<span class="guideheader">T</span>o add a document type to WebSubmit, you should go to the <a target=top href="<CFG_SITE_URL>/admin/websubmit/index.php">main page</a>
and click on "New Doctype" in the left blue panel.<br/><br/>
&nbsp;<span class="guideheader">E</span>ven once created, a document type will not appear automatically on this page. To configure the list of catalogues and document
types displayed on this page, the administrator shall go to the <a target=top href="<CFG_SITE_URL>/admin/websubmit/editCatalogues.php">edit catalogues</a>
page. (see the <a href="#catalogues">guide section</a>)<br clear="all"/>
<h3>The user can then click on the document type he is interested in.</h3>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-menu_doc.png" alt="Document type Page" class="guideimg" align="left">
&nbsp;<span class="guideheader">T</span>he text appearing under the header containing the name of the document
can be configured by going to the <a target=top href="websubmit-admin">main page</a>, click on
the title of the document type then on the "Edit Document Types Details" button.<br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can associate several categories to a document type which can be defined by going to the
<a target=top href="websubmit-admin">main page</a>, click on the title of the document type
then on the "View Categories" button. The selected category will be saved in a file named "comboXXX"
(where XXX is the short name of the document type) in the submission directory.<br/><br/>
&nbsp;<span class="guideheader">T</span>o add an action button to this page, first implement this action by going to the
<a target=top href="websubmit-admin">main page</a>, click on the title of the document type then
on the "Add a new submission" button. If the action is already implemented and the button still does not appear
on the submision page, then you should edit the details of this implementation: go to the
<a target=top href="websubmit-admin">main page</a>, click on the title of the document type then
on the icon in the "Edit Submission" column and in the line of the desired action. There you should set the
"Displayed" form field to "YES".<br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can also change the order of the buttons, by going to the <a target=top href="websubmit-admin">
main page</a>, click on the title of the document type then on the icon in the "Edit Submission" column and in the
line of the desired action. There you can set the "buttonorder" form field.<br/><br clear="all"/>
<h3>The user now may choose a category, then click on the action button he wishes.<br/>The submission starts, the first page of the web form appears.</h3>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-form.png" alt="Document type Page" class="guideimg" align="left">
&nbsp;<span class="guideheader">T</span>his web form is composed of several pages, on each of these
pages form fields can be found. To modify the number of pages, add or withdraw form fields and modify
the texts before each form field, you shall go to the <a target=top href="websubmit-admin">main page</a>,
click on the title of the document type then on the icon in the "Edit Submission Pages" column and in the line of the
desired action. (see the <a href="#actionimplement">guide section</a>)<br/><br clear="all"/>
<h3>On the last page of the submission, there should be a button like in the following image which will
trigger the end script</h3>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-end_action.png" alt="Document type End Page" class="guideimg" align="left">
&nbsp;<span class="guideheader">T</span>his button is defined like any other form field. Its definition should include
a <i> onclick="finish();"</i> javascript attribute.<br/><br/>
&nbsp;<span class="guideheader">A</span>fter clicking this button, WebSubmit will apply the end script functions
to the gathered data. To modify the end script, you shall go to the <a target=top href="websubmit-admin">
main page</a>, click on the title of the document type then on the icon in the "Edit Functions" column and in the line
of the desired action. (see the <a href="#implementfunctions">guide section</a>)<br clear="all"/>
<h3>See also:</h3>
<blockquote>
<a href="#description">interface description</a><br/>
<a href="#actions">actions</a><br/>
<a href="#documents">document types</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="philosophy"></a>
<h1>Philosophy behind the document submission system</h1>
<p>This page will explain some philosophical issues behind the document submission system.
<h3>On the relation between a search collection and a submission doctype:</h3>
<blockquote>
<ul>
&nbsp;<span class="guideheader">T</span>he relation
between a search collection and a submission document
type may be prone to certain confusion for Invenio
administrators. This comes from the fact that there is
no one-to-one direct mapping between them, as is usual
elsewhere. The relation is more flexible than that.<br/><br/>
&nbsp;<span class="guideheader">A</span> search
collection in Invenio is defined through a search
query. For example, "all records where field F
contains the value V belong to collection C". Several
assertions can be deduced from this definition:<br/>
&nbsp;1/ A single record can appear in several collections.<br/>
&nbsp;2/ There is no limitation to the number of
collections in which a record can appear.<br/>
&nbsp;3/ Any query can be used to build a
collection. The query can also be a complex one using
logical operators, hence can rely on the value of
several fields.<br/><br/>
&nbsp;(In addition, a search collection can be defined
via a set of its subcollections in the hierarchy tree.
Refer to the <a
href="websearch-guide">WebSearch
Admin Guide</a> for that matter.)<br/><br/>
&nbsp;<span class="guideheader">T</span>he submission
system basically creates an XML MARC record and stores
it in the database. To which collection this new
record belongs depends exclusively on the content of
the XML MARC record. This XML MARC record is created
by the <a href="#Make_Record">Make_Record</a> function. So the
secret of the matching of a submitted record to a
particular collection lies in the configuration of
this function. Some examples will clarify this
point:<br/><br/>
&nbsp;<span class="guideheader">E</span>xample 1:
Let's consider a "Preprints" collection which is
defined by this query: "980__a:PREPRINT". We want to
create a submission document type from which all
records will go to this "Preprints" collection. For
this, the Make_Record function should be configured so
that a 980__a field containing "PREPRINT" will always
be created.<br/>
&nbsp;<span class="guideheader">E</span>xample 2:
Let's still consider the same "Preprints" collection,
and an additional "Theses" collection based on a
slightly different query "980__a:THESIS". We want to
create a single submission type from which the records
will go in the "Preprints" or "Theses" collections
depending on a field chosen by the submitter. In this
case, the Make_Record function should be configured so
that a 980__a field will contain either "PREPRINT" or
"THESIS" depending on the value entered by
the submitter.<br/><br/>
&nbsp;<span class="guideheader">T</span>he apparent
disconnection between a submission document type and a
search collection allows a great flexibility, allowing
administrators to create 1 to 1, 1 to n, n to 1 or
even 1 to 0 (not very useful!) relations.
</ul>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="description"></a>
<h1>Interface Description</h1>
<h3>Welcome to webSubmit Management tool:</h3>
<blockquote>
<ul>
&nbsp;<span class="guideheader">o</span>n the websubmit admin <a href="websubmit-admin">main page</a> you will find:<br/><br/>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-main_page.png" class="guideimg"><br/><br/>
<ul>
<li>The list of all existing document type in the middle of the page. Click on one line in the list to have
access to the main document modification panel
<li>The right menu panel with the following links inside:
<ul>
<li>"<b>webSubmit Admin</b>": This links leads you back to the main page of the manager.
<li>"<b>New Doctype</b>": Click here if you wish to create a new document type.
<li>"<b>Remove Doctype</b>": Click here if you want to remove an existing document type.
<li>"<b>Available Actions</b>": Lists all existing actions
<li>"<b>Available Javascript Checks</b>": Lists all existing Javascript checking functions.
<li>"<b>Available Element Description</b>": Lists all existing html form element descriptions.
<li>"<b>Available Functions</b>": Lists all existing functions in CDS Submit.
<li>"<b>Organise Main Page</b>": Allows you to manage the appearance and order of the list of document
types on CDS Submit User main page.
</ul>
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<a href="#description">interface description</a><br/>
<a href="#actions">actions</a><br/>
<a href="#documents">document types</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="documents"></a>
<h1>Document Types</h1>
<blockquote>
<ul>
&nbsp;<span class="guideheader">W</span>ebSubmit can propose several actions on different document
types. Each of these document type may or may not implement all possible actions. The main difference
between each document type is the metadata which define each of them, and may also be the kind of fulltext
files attached to one record. <br/><br/>
&nbsp;<span class="guideheader">A</span> document type can be one of "Thesis", "Photos", "Videotapes"...
or whatever type of document you may invent. A document type is always defined by its metadata. It may or
may not have a fulltext file attached to it.<br/><br/>
&nbsp;<span class="guideheader">T</span>his tool leaves you free to create the web forms adapted to whatever type of document you want to
create (see "<a href="#implementwebform">Create and Maintain the Web Form</a>") as well as free
to determine what treatment you wish to apply to the collected data (see
"<a href="#implementfunctions">Create and Maintain the Data Treatment</a>").
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<a href="#documentnew">add a new type of document</a><br/>
<a href="#documentremove">remove a type of document</a><br/>
<a href="#documentmodify">modify a type of document</a><br/>
<a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="documentnew"></a>
<h1>Ading new type of document</h1>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "New Doctype" link in the webSubmit right menu.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">A</span> new document type is defined by 6 fields:<br/>
<ul>
<li><b>Creation Date</b> and <b>Modification Dates</b> are generated and modified automatically.<br/>
<li><b>Document Type ID</b>: This is the acronym for your new document type. We usually use a 3 letters
acronym.
<li><b>Document Type Name</b>: This is the full name of your new document. This is the text which will
appear on the list of available documents and catalogues on webSubmit main page.
<li><b>Document Type Description</b>: This is the text which will appear on the document type submission
page. This can be pure text or html.
<li><b>Doctype to clone</b>: Here you can choose to create your document type as a clone of another
existing document type. If so, the new document type will implement all actions implemented by the chosen
one. The web forms will be the same, and the functions also, as well as the values of the parameters for
these functions. Of course once cloned, you will be able to modify the implemented actions.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#documentremove">remove a type of document</a><br/>
<li><a href="#documentmodify">modify a type of document</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="documentremove"></a>
<h1>Removing a Document Type</h1>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Remove Doctype" link in the
webSubmit admin right menu
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">S</span>elect the document type to delete then click on the "Remove Doctype" button. Remember by doing this, you
will delete this document type as well as all the implementation of actions for this document type!
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#documentnew">create a type of document</a><br/>
<li><a href="#documentmodify">modify a type of document</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="documentmodify"></a>
<h1>Modifying a Document Type</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">M</span>odifying a document type in webSubmit - this will modify its
general data description, not the implementations of
the actions on this document type. For the later, please see <a href="#actionimplement">
implement an action over a type of document</a>.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the main page of the manager, click on the title of the
document type you want to modify, then click on the "Edit Document Type Details".
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">O</span>nce here, you can modify 2 fields:<br/>
<li><b>Document Type Name</b>: This is the full name of your new document. This is the text which will appear
on the list of available documents and catalogues on webSubmit main page.
<li><b>Document Type Description</b>: This is the text which will appear on the right of the screen when the user
moves the mouse over the document type title and on the document type submission page. This can be pure
text or html.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#documentremove">remove a type of document</a><br/>
<li><a href="#documentnew">create a type of document</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actions"></a>
<h1>Actions</h1>
<blockquote>
&nbsp;<span class="guideheader">I</span>n webSubmit you can create several actions (for example
"Submit New Record", "Submit a New File", "Send to a Distribution List", etc. in fact any action you can imagine
to perform on a document stored in your database). The creation of an action is very simple and consists in
filling in a name, description and associating a directory to this action. The directory parameter indicates where
the collected data will be stored when the action is carried on.<br/><br/>
&nbsp;<span class="guideheader">O</span>nce an action is created, you have to implement it over a document
type. Implementing an action means defining the web form which will be displayed to a user, and defining the
treatment (set of functions) applied to the data which have been gathered. The implementation of the same action
over two document types can be very different. The fields in the web form can be different as well as the functions
applied at the end of this action.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#actionnew">create a new action</a><br/>
<li><a href="#actionremove">remove an action</a><br/>
<li><a href="#actionmodify">modify an action</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actionnew"></a>
<h1>Adding a New Action</h1>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Available Actions" link in the websubmit right menu,
then on the "Add an Action" button.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">A</span> new action is defined by 6 fields:<br/><br/>
<ul>
<li><b>Creation Date</b> and <b>Modification Dates</b> are generated and modified automatically.<br/>
<li><b>Action Code</b>: This is the acronym for your new action. We usually use a 3 letters acronym.
<li><b>Action Description</b>: This is a short description of the new action.
<li><b>dir</b>: This is the name of the directory in which the submission data will be stored temporarily. If
the dir value is "running" as for the "Submit New Record" action (SBI), then the submission data for a
Text Document (document acronym "TEXT") will be stored in the
/opt/invenio/var/data/submit/storage/running/TEXT/9089760_90540 directory (where 9089760_90540 is what we call
the submission number. It is a string automatically generated at the beginning of each submission). Once
finished, the submission data will be moved to the
/opt/invenio/var/data/submit/storage/done/running/TEXT/ directory by the "Move_to_Done" function.
<li><b>statustext</b>: text displayed in the status bar of the browser when the user moves his mouse upon
the action button.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#actionremove">remove an action</a><br/>
<li><a href="#actionmodify">modify an action</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actionremove"></a>
<h1>Removing an Action</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">R</span>emoving the implementation of an action over a document type -
Please note the removal of the action itself is not allowed with this tool.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the websubmit admin main page, click on the title of the
relevant document type. Then click on the red cross corresponding to the line of the action you want to remove.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#actionnew">create an action</a><br/>
<li><a href="#actionmodify">modify an action</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actionmodify"></a>
<h1>Modifying an Action</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">T</span>his page is about how to modify the general data about an
action - for modifying the implementation of an action over a document type, see
<a href="#actionimplement">implement an action over a type of document</a>
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "View Actions" link in the right menu of the websubmit
admin, then on the title of the action you want to modify...
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">Y</span>ou may modify 3 fields:<br/>
<ul>
<li><b>Action Description</b>: This is a short description of the new action.
<li><b>dir</b>: This is the name of the directory in which the submission data will be stored temporarily.
See the meaning of this parameter in <a href="#actionnew">create an action</a>.
<li><b>statustext</b>: text displayed in the status bar of the browser when the user moves his mouse
upon the action button.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#actionremove">remove an action</a><br/>
<li><a href="#actionnew">create an action</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actionimplement"></a>
<h1>Implement an action over a document type</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>mplement an action over a document type. Create the web forms
and the treatment process.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the main page of the manager, click on the title of the
relevant document type.<br/>Then click on the "Add a New Submission" button.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">J</span>ust select the name of the action you want to implement. When you
select an action, the list of document which already implement this action appears. Then you can select from
this list the document from which you want to clone the implementation, or just choose "No Clone" if you want
to build this implementation from scratch.<br/><br/>
&nbsp;<span class="guideheader">A</span>fter selecting the correct fields, click on the "Add Submission"
button.<br/><br/>
&nbsp;<span class="guideheader">Y</span>ou then go back to the document type manager page where you
can see that in the bottom array your newly implemented action appears (check the acronym in the first
column).<br/><br/>
<img src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-implement.png" class="guideimg"><br/><br/>
<ul>
<li>Clicking on the action acronym will allow you to modify the general data about the action (remember
in this case that all the other implementations of this particular action will also be changed).
<li>The second column indicates whether the button representing this action will appear on the submission page.
<li>The third column shows you the number of pages composing the web form for this implementation.
(see <a href="#implementwebform">create and maintain the web form</a>).
<li>The 4th and 5th columns indicate the creation and last modification dates for this implementation.
<li>In the 6th column, you can find the order in which the button will be displayed on the submission page
of this document type.<br/>
<li>The following 4 columns (level, score, stpage, endtxt) deal with the insertion of this action in an action
set.<br/><br/>
<table border=0 bgcolor="eeeeff"><tr><td><small><br/>
An action set is a succession of actions which should be done in a given order when a user starts.<br/>
For example the submission of a document is usually composed of two actions: Submission of Bibliographic
Information (SBI) and Fulltext Transfer (FTT) which should be done one after the other.<br/>
When the user starts the submission, we want CDS Submit to get him first in SBI and when he finishes SBI to
carry him to FTT.<br/>
SBI and FTT are in this case in the same action set.<br/>
They will both have a level of 1 ("level" is a bad name, it should be "action set number"), SBI will have a
score of 1, and FTT a score of 2 (which means it will be started after SBI). If you set the stpage of FTT to 2,
the user will be directly carried to the 2nd page of the FTT web form. This value is usually set to 1.<br/>
The endtxt field contains the text which will be display to the user at the end of the first action (here
it could be "you now have to transfer your files")
<br/><br/>
A single action like "Modify Bibliographic Information" should have the 3 columns to 0,0 and 1.<br/>&nbsp;
</small></td></tr></TABLE>
<br/><br/>
<li>Click on the icon in the 12th column ("Edit Submission Pages") to
<a href="#implementwebform">create or edit the web form</a>.
<li>Click on the icon in the 13th column ("Edit Functions") to
<a href="#implementfunctions">create or edit the function list</a>.
<li>The "Edit Submission" column allows you to modify the data (level, status text...) for this implementation.
<li> Finally the last column allows you to delete this implementation.<br/>&nbsp;
</ul><br/>
&nbsp;<span class="guideheader">I</span>f you chose to clone the implementation from an existing one,
the web form as well as the functions list will already be defined. Else you will have to create them from scratch.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#implementwebform">create and maintain the web form</a><br/>
<li><a href="#implementfunctions">create and maintain the data treatment</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="implementwebform"></a>
<h1>Create and maintain the web form</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>reate and define the web form used during an action.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the main page of the manager, click on the title of the relevant
document type. Then click on the icon in the "Edit Submission Pages" column of the relevant line.
</blockquote>
<h3>List of the form pages</h3>
<blockquote>
&nbsp;<span class="guideheader">A</span> web form can be split over several pages. This is a matter
of easiness for the user: he will have an overview of all form fields present on the page without having to scroll it.
Moreover, each time the user goes from one page to the other, all entered data are saved. If he wants to stop
then come back later (or if the browser crashes!) he will be able to get back to the submission at the exact
moment he left it.<br/><br/>
&nbsp;<span class="guideheader">O</span>nce here:<br/><br/>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-menu_page.png" class="guideimg"><br/><br/>
you can see the ordered list of already existing pages in the web form. In this example there are 4 pages.
You can then:
<ul>
<li> Move one page from one place to an other, using the small blue arrows under each page number.
<li>Suppress one page by clicking on the relevant red cross.
<li>Add a page, by clicking the "ADD A PAGE" button!
<li><a href="#onepage">Edit the content of one page</a> by clicking on the page number.
<li>Go back to the document main page.
</ul>
</blockquote>
<a name="onepage"></a>
<h3>Edit one form page</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on a page number, you then arrive to a place where you can
edit this form page.<br/><br/>
&nbsp;<span class="guideheader">A</span> form page is composed of a list of form elements. Each of these
form elements is roughly made of an html template and a text displayed before the form field.<br/><br/>
&nbsp;<span class="guideheader">I</span>n the first part of the page, you have a preview of what the form
will look like to the user:<br/>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-preview.png" class="guideimg"><br/><br/>
&nbsp;<span class="guideheader">T</span>hen the second table shows you the list of the form elements
present on the page:<br/>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-elements.png" class="guideimg"><br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can then:
<ul>
<li>Move one element from one place to another using the drop-down menus in the first
column ("Item No") of the table, or the little blue arrows in the second column.
<li><a href="#edittemplate">Edit the html template of one form element</a> by clicking on the name of the
template in the 3rd column ("Name").
<li><a href="#editelement">Edit one of the form elements</a> by clicking on the icon in the 10th column.
<li>delete one form element by clicking on the relevant red cross.
<li><a href="#addelement">Add an element to the page</a> by clicking the "ADD ELEMENT TO PAGE" button.
</ul>
</blockquote>
<a name="edittemplate"></a>
<h3>Edit the html template of one form element</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>n the html template edition page, you can modify the following values:
<ul>
<li><b>Element type</b>: indicates which html form element to create
<li><b>Aleph code</b>: <font color=red>Aleph users only!</font> - This indicates in which field of the Aleph
document database to retrieve the original value when modifying this information (function
Create_Modify_Interface of action MBI).
<li><b>Marc Code</b>: <font color=red>MySQL users only!</font> - This indicates in which field of the MySQL
document database to retrieve the original value when modifying this information (function
Create_Modify_Interface of action MBI).
<li><b>Cookies</b>: indicates whether WebSubmit will set a cookie on the value filled in by the user. If yes,
next time the user will come to this submission, the value he has entered last time will be filled in
automatically. <span style="font-weight: bold;">Note:</span> <span style="color: red; font-weight: bold;">
This feature has been REMOVED.</span>
<li><b>other fields</b>: The other fields help defining the html form element.
</ul>
<font color=red>Important warning!</font> Please remember this is a template! This means it can be used in
many different web forms/implementations. When you modify this template the modification will take place in
each of the implementations this template has been used.
</blockquote>
<a name="editelement"></a>
<h3>Edit one form element</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>n the form element edition page, you may modify the following values:
<ul>
<li><b>element label</b>: This is the text displayed before the actual form field.
<li><b>level</b>: can be one of "mandatory" or "optional". If mandatory, the user won't be able to leave
this page before filling this field in.
<li><b>short desc</b>: This is the text displayed in the summary window when it is opened.
<li><b>Check</b>: Select here the <a href="#addcheck">javascript checking function</a> to be applied to
the submitted value of this field
<li><b>Modify Text</b>: This text will be displayed before the form field when modifying the value (action
"Modify Record", function "Create_Modify_Interface")
</ul>
</blockquote>
<a name="addelement"></a>
<h3>Add one form element</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "ADD ELEMENT TO PAGE" button. There you will have
to decide which <a href="#addtemplate">html template field</a> to use ("Element Description code"), and
also the field mentioned <a href="#editelement">above</a>.
</blockquote>
<a name="addtemplate"></a>
<h3>Create a new html template</h3>
<blockquote>
&nbsp;<span class="guideheader">Y</span>ou have access to the list of all existing html templates by clicking
on the "View element descriptions" link in the websubmit admin right menu.<br/>
By clicking on one of them, you will have access to its description.<br/>
If no template corresponds to the one you seek, click on the "ADD NEW ELEMENT DESCRIPTION" button to
create one.<br/>
&nbsp;<span class="guideheader">T</span>he fields you have to enter in the creation form are the one
described in the <a href="#edittemplate">Edit the html template of one form element</a> section.<br/>
You also have to choose a name for this new element.<br/>
<font color=red>IMPORTANT!</font> The name you choose for your html element is also the name of the file
in which webSubmit will save the value entered in this field. This is also the one you will use in your
<a href="#bibconvert">BibConvert</a> configuration. Bibconvert is the program which will
convert the data gathered in webSubmit in a formatted XML file for insertion in the documents database.
<br/>
&nbsp;<span class="guideheader">T</span>ips:
<li>Elements of type "select box" which are used as a mandatory field in a form must start with "&lt;option&gt;Select:&lt;/option&gt;"
</blockquote>
<a name="addcheck"></a>
<h3>Create and edit a checking function.</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "View Checks" link in the websubmit admin right menu.
You then have access to a list of all the defined javascript functions.<br/>
You can then click on the name of the function you want to modify, or click on the "ADD NEW CHECK" button
to create a new javascript function.<br/>
These functions are inserted in the web page when the user is doing his submission. When he clicks on
"next page", this function will be called with the value entered by the user as a parameter. If the function returns
false, the page does not change and an error message should be output. If the function returns true, everything
is correct, so page can be changed.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#implementfunctions">create and maintain the data treatment</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="implementfunctions"></a>
<h1>Setup the Data Treatment</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">A</span>t the end of a submission, we have to tell webSubmit what to do
with the data it has gathered. This is expressed through one or several lists of functions (we call this the
"end script").
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the main page of the manager, click on the title of the relevant
document type.<br/>Then click on the icon in the "Edit Functions" column of the relevant line.
</blockquote>
<h3>List of functions</h3>
<blockquote>
&nbsp;<span class="guideheader">H</span>ere is what you may see then (this is the end script list of functions
for a document type named "TEST" and action "FTT" - Fulltext Transfer):<br/><br/>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-list_functions.png" class="guideimg"><br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can see the ordered list of all the functions in the end script.
This end script is composed of 2 steps (see the "step" column). The functions composing the first step are called,
then there should be action from the user which would trigger step 2 - in the present case the
<a href="#Upload_Files">Upload_Files</a> function (last of step 1) allows
the user to upload additional files by creating a web form, then when the user finishes, he presses another
button created by the function, which ends the process. Functions of step 2 are then called.<br/><br/>
&nbsp;<span class="guideheader">W</span>hy implement multiple steps? The reason can vary with the task
you want to accomplish. For example with the example above (Fulltext Transfer), we use the first step to allow
the upload of multiple additional files (dynamic action) which could not be done in the
<a href="#implementwebform">static web form</a>. In the case of the
"Modify Bibliographic Information" action, the first step is used to display the fields the user wants to modify,
prefilled with the existing values. The reason is once again that the task we want to realise is dynamic.<br/><br/>
&nbsp;<span class="guideheader">T</span>he "score" column is used to order the functions. The function
which has the smallest score will be called first, and the largest score will be called last.<br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can then:
<ul>
<li> View and edit the parameters of each function by clicking on the name of the function.
<li> Move one function up and down, by using the small blue arrows.
<li> Suppress one function by clicking on the relevant red cross.
<li> Add a function to the list by clicking the "ADD FUNCTION" button.
<li> Go back to the document main page ("FINISHED" button).
</ul>
&nbsp;<span class="guideheader">P</span>lease note: To pass one function from one step to another,
you have to delete it then add it again in the proper step.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functions">all about functions</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functions"></a>
<h1>Functions</h1>
<h3>Description:</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>n webSubmit, each action process is divided into two phases: the
gathering of data (through a web form) and the treatment of the data.<br/><br/>
&nbsp;<span class="guideheader">T</span>he treatment is organised in a succession of functions, each of
which has its own input and output.<br/><br/>
&nbsp;<span class="guideheader">T</span>he functions themselves are stored in separate files (one per
function) in the /opt/invenio/lib/python/invenio/websubmit_functions directory. A file containing a function MUST
be named after the function name itself. For example, a function called "Move_to_Done" MUST be stored in a
file called Move_to_Done.py. The case is important here.<br/><br/>
&nbsp;<span class="guideheader">F</span>or a description of what should be inside the file, have a look to
the "create a new function" page of this guide.<br/><br/>
&nbsp;<span class="guideheader">T</span>o each function you can associate one or several parameters,
which may have different values according to the document type the function is used for. One parameter may
be used for different functions. For example one standard parameter used in several functions is called "edsrn".
It contains the name of the file in which the reference of the document is stored.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionnew">create a new function</a><br/>
<li><a href="#functiondelete">delete a function</a><br/>
<li><a href="#functionedit">edit a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functionnew"></a>
<h1>Creating a New Function</h1>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Available Functions" link in the websubmit admin right
menu. Then click on the "Add New Function" button.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">E</span>nter the name of the new function as well as a text description if
you wish.<br/>
&nbsp;<span class="guideheader">Y</span>ou will then reach a page where you can add parameters to your
new function.<br/><br/>
&nbsp;<span class="guideheader">D</span>on't forget to add the function file inside the
/opt/invenio/lib/python/invenio/websubmit_functions directory and to name the file after the function. Functions must
be written in Python. Here is an example implementation of a function:<br/><br/>
/opt/invenio/lib/python/invenio/websubmit_functions/Get_Report_Number.py:</small>
<table border=0 width=75% bgcolor="eeeeff"><tr><td><small><pre><br/>
def Get_Report_Number (parameters,curdir,form):
global rn
#Path of file containing report number
if os.path.exists("%s/%s" % (curdir,parameters['edsrn'])):
fp = open("%s/%s" % (curdir,parameters['edsrn']),"r")
rn = fp.read()
rn = rn.replace("/","_")
rn = re.sub("[\n\r ]+","",rn)
else:
rn = ""
return ""
<pre></small></td></tr></TABLE>
<br/>
The function parameters are passed to the function through the parameters dictionary.<br/>
The curdir parameter contains the current submission directory path.<br/>
The form parameter contains the form passed to the current web page for possible reference from inside the
function.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionedit">edit a function</a><br/>
<li><a href="#functiondelete">delete a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functiondelete"></a>
<h1>Removing a Function</h1>
<h3>Note</h3>
<blockquote>
&nbsp;<span class="guideheader">T</span>here are currently no way of deleting a function through this
interface. Use the direct MySQL command line interface for this.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionedit">edit a function</a><br/>
<li><a href="#functionnew">create a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functionedit"></a>
<h1>Editing a Function</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">E</span>dit a function, add parameters to it...
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Available Functions" link in the websubmit admin
right menu.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">O</span>n this page appears a list of all functions defined into the system.
Two columns give you access to some features:
<ul>
<li><font color=green>View function usage</font> Click here to have access to the list of all document
types and all actions in which this function is used. Then by clicking on one of the items, you will be given a
chance to modify the parameters value for the given document type.
<li><font color=green>View/Edit function details</font> There you will be able to modify the function
description, as well as add/withdraw parameters for this function.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionnew">create a new function</a><br/>
<li><a href="#functiondelete">delete a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functiondescription"></a>
<h1>All functions explained</h1>
<h3>Description:</h3>
<blockquote>
&nbsp;<span class="guideheader">T</span>his page lists and explains all the functions used in the demo
provided with the Invenio package. This list is not exhaustive since you can add any new function you need.<br/>
&nbsp;<span class="guideheader">C</span>lick on one function name to get its description.<br/>
&nbsp;<span class="guideheader">P</span>lease note in this page when we refer to [param] this means the
value of the parameter 'param' for a given document type.<br/><br/>
<table cellspacing=5><tr>
<td valign="top">
<a href="#CaseEDS">CaseEDS</a><br/>
<a href="#Create_Modify_Interface">Create_Modify_Interface</a><br/>
<a href="#Create_Recid">Create_Recid</a><br/>
<a href="#Finish_Submission">Finish_Submission</a><br/>
<a href="#Get_Info">Get_Info</a><br/>
<a href="#Get_Recid">Get_Recid</a><br/>
<a href="#Get_Report_Number">Get_Report_Number</a><br/>
<a href="#Get_Sysno">Get_Sysno</a><br/>
<a href="#Get_TFU_Files">Get_TFU_Files</a><br/>
<a href="#Insert_Modify_Record">Insert_Modify_Record</a><br/>
<a href="#Insert_Record">Insert_Record</a><br/>
</td>
<td valign="top">
<a href="#Is_Original_Submitter">Is_Original_Submitter</a><br/>
<a href="#Is_Referee">Is_Referee</a><br/>
<a href="#Mail_Submitter">Mail_Submitter</a><br/>
<a href="#Make_Modify_Record">Make_Modify_Record</a><br/>
<a href="#Make_Record">Make_Record</a><br/>
<a href="#Move_From_Pending">Move_From_Pending</a><br/>
<a href="#Move_to_Done">Move_to_Done</a><br/>
<a href="#Move_to_Pending">Move_to_Pending</a><br/>
<a href="#Print_Success">Print_Success</a><br/>
<a href="#Print_Success_APP">Print_Success_APP</a><br/>
<a href="#Print_Success_MBI">Print_Success_MBI</a><br/>
</td>
<td valign="top">
<a href="#Print_Success_SRV">Print_Success_SRV</a><br/>
<a href="#Report_Number_Generation">Report_Number_Generation</a><br/>
<a href="#Send_Approval_Request">Send_Approval_Request</a><br/>
<a href="#Send_APP_Mail">Send_APP_Mail</a><br/>
<a href="#Send_Modify_Mail">Send_Modify_Mail</a><br/>
<a href="#Send_SRV_Mail">Send_SRV_Mail</a><br/>
<a href="#Test_Status">Test_Status</a><br/>
<a href="#Update_Approval_DB">Update_Approval_DB</a><br/>
<a href="#Upload_Files">Upload_Files</a><br/>
</td>
</tr></table>
</blockquote>
<br/><br/><a name="CaseEDS">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>CaseEDS</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function may be used if the treatment to be done after a submission depends on a field entered by
the user. Typically this is used in an approval interface. If the referee approves then we do this. If he rejects,
then we do other thing.<br/>
More specifically, the function gets the value from the file named [casevariable] and compares it with the
values stored in [casevalues]. If a value matches, the function directly goes to the corresponding step stored
in [casesteps]. If no value is matched, it goes to step [casedefault].
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>casevariable</b></small></td>
<td><small>
This parameters contains the name of the file in which the function will get the chosen value.<br/>
Eg: "decision"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>casevalues</b></small></td>
<td><small>
Contains the list of recognized values to match with the chosen value. Should be a comma separated list of words.<br/>
Eg: "approve,reject"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>casesteps</b></small></td>
<td><small>
Contains the list of steps corresponding to the values matched in [casevalue]. It should be a comma
separated list of numbers<br/>
Eg: "2,3"<br/>
<i>In this example, if the value stored in the file named "decision" is "approved", then the function launches
step 2 of this action. If it is "reject", then step 3 is launched.</i>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>casedefault</b></small></td>
<td><small>
Contains the step number to go by default if no match is found.<br/>
Eg: "4"<br/>
<i>In this example, if the value stored in the file named "decision" is not "approved" nor "reject", then
step 4 is launched.</i>
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Create_Modify_Interface">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Create_Modify_Interface</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
To be used in the MBI-Modify Record action.
It displays a web form allowing the user to modify the fields he chose. The fields are prefilled with the existing
values extracted from the documents database.
This functions takes the values stored in the [fieldnameMBI] file. This file contains a list of field name separated
with "+" (it is usually generated from a multiple select form field). Then the function retrieves the corresponding
tag name (marc-21) stored in the element definition. Finally it displays the web form and fills it with the existing
values found in the documents database.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>fieldnameMBI</b></small></td>
<td><small>
Contains the name of the file in which the function will find the list of fields the user wants to modify. Depends
on the web form configuration.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Create_Recid">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Create_Recid</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function retrieves a new record id from the records database. This record id will then be used to create the
XML record afterwards, or to link with the fulltext files. The created id is stored in a file named "SN".
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</small></td>
</tr>
</TABLE>
<br/><br/><a name="Finish_Submission">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Finish_Submission</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function stops the data treatment process even if further steps exist. This is used for example in the
approval action. In the first step, the program determines whether the user approved or rejected the
document (see <a href="#CaseEDS">CaseEDS</a> function description). Then depending on the result, it
executes step 2 or step 3. If it executes step 2, then it should continue with step 3 if nothing stopped it. The
Finish_Submission function plays this role.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Get_Info">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Get_Info</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function tries to retrieve in the "pending" directory or directly in the documents database, some information
about the document: title, original submitter's email and author(s).<br/>
If found, this information is stored in 3 global variables: $emailvalue, $titlevalue, $authorvalue to be used
in other functions.<br/>
If not found, an error message is displayed.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>authorFile</b></small></td>
<td><small>
Name of the file in which the author may be found if the document has not yet been integrated (in this case
it is still in the "pending" directory).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>emailFile</b></small></td>
<td><small>
Name of the file in which the email of the original submitter may be found if the document has not yet been
integrated (in this case it is still in the "pending" directory).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>titleFile</b></small></td>
<td><small>
Name of the file in which the title may be found if the document has not yet been integrated (in this case it is
still in the "pending" directory).
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Get_Recid">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Get_Recid</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function searches for the document in the database and stores the recid of this document in the "SN" file and in a global variable "sysno".<br/>
The function conducts the search based upon the document's report-number (and relies upon the global variable "rn") so the "Get_Report_Number" function should be called before this one.<br/>
<i>This function replaces the older function "Get_Sysno".</i><br/>
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Get_Report_Number">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Get_Report_Number</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function gets the value contained in the [edsrn] file and stores it in the reference global variable.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file which stores the reference.<br/>
This value depends on the web form configuration you did. It should contain the name of the form element used for storing the reference of the document.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Get_Sysno">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Get_Sysno</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function searches for the document in the database and stores the system number of this document in the "SN" file and in a global variable.<br/>
"Get_Report_Number" should be called before.<br/>
<span style="color: red; font-style: italic;">Deprecated: Use Get_Recid instead.</span>
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Insert_Modify_Record">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Insert_Modify_Record</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function gets the output of bibconvert and uploads it into the MySQL bibliographical database.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Insert_Record">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Insert_Record</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function gets the output of bibFormat and uploads it into the MySQL bibliographical database.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Is_Original_Submitter">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Is_Original_Submitter</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
If the authentication module (login) is active in webSubmit, this function compares the current login with the email of the original submitter. If it is the same (or if the current user has superuser rights), we go on. If it differs, an error message is issued.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Is_Referee">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Is_Referee</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function checks whether the currently logged user is a referee for this document.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Mail_Submitter">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Mail_Submitter</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function send an email to the submitter to warn him the document he has just submitted has been
correctly received.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>authorfile</b></small></td>
<td><small>
Name of the file containing the authors of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>titleFile</b></small></td>
<td><small>
Name of the file containing the title of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>emailFile</b></small></td>
<td><small>
Name of the file containing the email of the submitter of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>status</b></small></td>
<td><small>
Depending on the value of this parameter, the function adds an additional text to the email.<br/>
This parameter can be one of:<br/>
<b>ADDED</b>: The file has been integrated in the database.<br/>
<b>APPROVAL</b>: The file has been sent for approval to a referee.<br/>
or can stay empty.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file containing the reference of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>newrnin</b></small></td>
<td><small>
Name of the file containing the 2nd reference of the document (if any)<br/>
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Make_Modify_Record">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Make_Modify_Record</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function creates the record file formatted for a direct insertion in the documents database. It uses the
<a href="#bibconvert">BibConvert</a> tool.<br/>
The main difference between all the Make_..._Record functions are the parameters.<br/>
As its name says, this particular function should be used for the modification of a record. (MBI- Modify
Record action).
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>modifyTemplate</b></small></td>
<td><small>
Name of bibconvert's configuration file used for creating the mysql record.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>sourceTemplate</b></small></td>
<td><small>
Name of bibconvert's source file.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Make_Record">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Make_Record</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function creates the record file formatted for a direct insertion in the documents database. It uses the
<a href="#bibconvert">BibConvert</a> tool.<br/>
The main difference between all the Make_..._Record functions are the parameters.<br/>
As its name does not say :), this particular function should be used for the submission of a document.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>createTemplate</b></small></td>
<td><small>
Name of bibconvert's configuration file used for creating the mysql record.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>sourceTemplate</b></small></td>
<td><small>
Name of bibconvert's source file.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Move_From_Pending">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Move_From_Pending</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function retrieves the data of a submission which was temporarily stored in the "pending" directory
(waiting for an approval for example), and moves it to the current action directory.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Move_to_Done">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Move_to_Done</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function moves the existing submission directory to the /opt/invenio/var/data/submit/storage/done directory. If the
Then it tars and gzips the directory.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Move_to_Pending">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Move_to_Pending</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function moves the existing submission directory to the /opt/invenio/var/data/submit/storage/pending directory. It is
used to store temporarily this data until it is approved or...
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Print_Success">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Print_Success</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function simply displays a text on the screen, telling the user the submission went fine. To be used in
the "Submit New Record" action.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>status</b></small></td>
<td><small>
Depending on the value of this parameter, the function adds an additional text to the email.<br/>
This parameter can be one of:<br/>
<b>ADDED</b>: The file has been integrated in the database.<br/>
<b>APPROVAL</b>: The file has been sent for approval to a referee.<br/>
or can stay empty.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file containing the reference of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>newrnin</b></small></td>
<td><small>
Name of the file containing the 2nd reference of the document (if any)<br/>
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Print_Success_APP">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Print_Success_APP</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function simply displays a text on the screen, telling the referee his decision has been taken into account.
To be used in the Approve (APP) action.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Print_Success_MBI">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Print_Success_MBI</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function simply displays a text on the screen, telling the user the modification went fine. To be used in
the Modify Record (MBI) action.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Print_Success_SRV">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Print_Success_SRV</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function simply displays a text on the screen, telling the user the revision went fine. To be used in the
Submit New File (SRV) action.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Report_Number_Generation">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Report_Number_Generation</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function is used to automatically generate a reference number.<br/>
After generating the reference, the function saves it into the [newrnin] file and sets the global variable
containing this reference.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>autorngen</b></small></td>
<td><small>
If set to "<b>Y</b>": The reference number is generated.<br/>
If set to "<b>N</b>": The reference number is read from a file ([newrnin])<br/>
If set to "<b>A</b>": The reference number will be the access number of the submission.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>counterpath</b></small></td>
<td><small>
indicates the file in which the program will find the counter for this reference generation.<br/>
The value of this parameter may contain one of:<br/>
"<b>&lt;PA&gt;categ&lt;/PA&gt;</b>": in this case this string is replaced with the content of the file [altrnin]<br/>
"<b>&lt;PA&gt;yy&lt;/PA&gt;</b>": in this case this string is replaced by the current year (4 digits) if [altyeargen]
is set to "AUTO", or by the content of the [altyeargen] file in any other case. (this content should be formatted
as a date (dd/mm/yyyy).<br/>
"<b>&lt;PA&gt;file:<i>name_of_file</i>&lt;/PA&gt;</b>": in this case, this string is replaced by the first line of the given file<br />
"<b>&lt;PA&gt;file*:<i>name_of_file</i>&lt;/PA&gt;</b>": in this case, this string is replaced by all the lines of the given file, separated by a dash ('-') character.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>rnformat</b></small></td>
<td><small>
This is the format used by the program to create the reference. The program computes the value of the
parameter and appends a "-" followed by the current value of the counter increased by 1.<br/>
The value of this parameter may contain one of:<br/>
"<b>&lt;PA&gt;categ&lt;/PA&gt;</b>": in this case this string is replaced with the content of the file [altrnin]<br/>
"<b>&lt;PA&gt;yy&lt;/PA&gt;</b>": in this case this string is replaced by the current year (4 digits) if [altyeargen]
is set to "AUTO", or by the content of the [altyeargen] file in any other case. (this content should be formatted
as a date (dd/mm/yyyy).
<br/>
"<b>&lt;PA&gt;file:<i>name_of_file</i>&lt;/PA&gt;</b>": in this case, this string is replaced by the first line of the given file<br />
"<b>&lt;PA&gt;file*:<i>name_of_file</i>&lt;/PA&gt;</b>": in this case, this string is replaced by all the lines of the given file, separated by a dash ('-') character.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>rnin</b></small></td>
<td><small>
This parameter contains the name of the file in which the program will find the category if needed. The content
of thif file will then replace the string &lt;PA&gt;categ&lt;/PA&gt; in the reference format or in the counter
path.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>yeargen</b></small></td>
<td><small>
This parameter can be one of:<br/>
"<b>AUTO</b>": in this case the program takes the current 4 digit year.<br/>
"<b>&lt;filename&gt;</b>": in this case the program extract the year from the file which name is
&lt;filename&gt;. This file should contain a date (dd/mm/yyyy).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file in which the created reference will be stored.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Send_Approval_Request">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Send_Approval_Request</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function sends an email to the referee in order to start the simple approval process.<br/>
This function is very CERN-specific and should be changed in case of external use.<br/>
Must be called after the <a href=#Get_Report_Number>Get_Report_Number</a> function.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>addressesDAM</b></small></td>
<td><small>
email addresses of the people who will receive this email (comma separated list). this parameter may contain the <b>&lt;CATEG&gt;</b> string. In which case the variable computed from the [categformatDAM] parameter replaces this string.<br/>
eg.: "&lt;CATEG&gt;-email@cern.ch"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>categformatDAM</b></small></td>
<td><small>
contains a regular expression used to compute the category of the document given the reference of the document.<br/>
eg.: if [categformatAFP]="TEST-&lt;CATEG&gt;-.*" and the reference of the document is "TEST-CATEGORY1-2001-001", then the computed category equals "CATEGORY1"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>authorfile</b></small></td>
<td><small>
name of the file in which the authors are stored
</small></td>
</tr>
<tr>
<td valign="top"><small><b>titlefile</b></small></td>
<td><small>
name of the file in which the title is stored.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>directory</b></small></td>
<td><small>
parameter used to create the URL to access the files.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Send_APP_Mail">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Send_APP_Mail</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
Sends an email to warn people that a document has been approved.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>addressesAPP</b></small></td>
<td><small>
email addresses of the people who will receive this email (comma separated list). this parameter may contain
the <b>&lt;CATEG&gt;</b> string. In which case the variable computed from the [categformatAFP] parameter
replaces this string.<br/>
eg.: "&lt;CATEG&gt;-email@cern.ch"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>categformatAPP</b></small></td>
<td><small>
contains a regular expression used to compute the category of the document given the reference of the
document.<br/>
eg.: if [categformatAFP]="TEST-&lt;CATEG&gt;-.*" and the reference of the document is
"TEST-CATEGORY1-2001-001", then the computed category equals "CATEGORY1"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>newrnin</b></small></td>
<td><small>
Name of the file containing the 2nd reference of the approved document (if any).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file containing the reference of the approved document.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Send_Modify_Mail">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Send_Modify_Mail</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function sends an email to warn people a document has been modified and the user his modifications
have been taken into account..
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>addressesMBI</b></small></td>
<td><small>
email addresses of the people who will receive this email (comma separated list).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>fieldnameMBI</b></small></td>
<td><small>
name of the file containing the modified fields.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>sourceDoc</b></small></td>
<td><small>
Long name for the type of document. This name will be displayed in the mail.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>emailfile</b></small></td>
<td><small>
name of the file in which the email of the modifier will be found.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Send_SRV_Mail">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Send_SRV_Mail</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function sends an email to warn people a revision has been carried out.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>notefile</b></small></td>
<td><small>
name of the file in which the note can be found
</small></td>
</tr>
<tr>
<td valign="top"><small><b>emailfile</b></small></td>
<td><small>
name of the file containing the submitter's email
</small></td>
</tr>
<tr>
<td valign="top"><small><b>addressesSRV</b></small></td>
<td><small>
email addresses of the people who will receive this email (comma separated list). this parameter may contain the <b>&lt;CATEG&gt;</b> string. In which case the variable computed from the [categformatDAM] parameter replaces this string.<br/>
eg.: "&lt;CATEG&gt;-email@cern.ch"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>categformatDAM</b></small></td>
<td><small>
contains a regular expression used to compute the category of the document given the reference of the
document.<br/>
eg.: if [categformatAFP]="TEST-&lt;CATEG&gt;-.*" and the reference of the document is
"TEST-CATEGORY1-2001-001", then the computed category equals "CATEGORY1"
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Test_Status">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Test_Status</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function checks whether the considered document has been requested for approval and is still waiting
for approval. It also checks whether the password stored in file "password" of the submission directory
corresponds to the password associated with the document..
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small><b>none</b></small></td>
</tr>
</TABLE>
<br/><br/><a name="Update_Approval_DB">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Update_Approval_DB</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function updates the approval database when a document has just been approved or rejected. It uses
the [categformatDAM] parameter to compute the category of the document.<br/>
Must be called after the <a href=#Get_Report_Number>Get_Report_Number</a> function.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>categformatDAM</b></small></td>
<td><small>
It contains the regular expression which allows the retrieval of the category from the reference number.<br/>
Eg: if [categformatDAM]="TEST-&lt;CATEG&gt;-.*" and the reference is "TEST-CATEG1-2001-001" then the
category will be recognized as "CATEG1".
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Upload_Files">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Upload_Files</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function displays the list of already transfered files (main and additional ones), and also outputs an html
form for uploading other files (pictures or fulltexts).
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>maxsize</b></small></td>
<td><small>
Maximum allowed size for the transfered files (size in bits)
</small></td>
</tr>
<tr>
<td valign="top"><small><b>minsize</b></small></td>
<td><small>
Minimum allowed size for the transfered files (size in bits)
</small></td>
</tr>
<tr>
<td valign="top"><small><b>iconsize</b></small></td>
<td><small>
In case the transfered files are pictures (jpg, gif or pdf), the function will automatically try to create icons from them.
This parameter indicates the size in pixel of the created icon.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>type</b></small></td>
<td><small>
This can be one of "fulltext" or "picture". If the type is set to "picture" then the function will try to create icons
(uses the ImageMagick's "convert" tool)
</small></td>
</tr>
</TABLE>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionnew">create a new function</a><br/>
<li><a href="#functiondelete">delete a function</a><br/>
<li><a href="#functionedit">edit a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="protection"></a>
<h1>Protection and Restriction</h1>
<h3>Description:</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>n webSubmit, you can restrict the use of some actions on a given
document type to a list of users. You can use the <a href="webaccess-admin">webAccess</a>
manager for this.<br/><br/>
&nbsp;<span class="guideheader">L</span>et's say you want to restrict the submission of new TEXT documents
to a given user. You should then create a role in webAccess which will authorize the action "submit" over doctype
"TEXT" and act "SBI" (Submit new record). You can call this role "submitter_TEXT_SBI" for example.
Then link the role to the proper users.<br/>
&nbsp;<span class="guideheader">A</span>nother example: if you wish to authorize a user to Modify the
bibliographic data of PICT documents, you have to create a role which authorize the action "submit" over doctype
"PICT" and act "MBI". This role can be called "submitter_PICT_MBI" or whatever you want.<br/><br/>
&nbsp;<span class="guideheader">I</span>f no role is defined for a given action and a given document type,
then all users will be allowed to use it.
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="catalogues"></a>
<h1>Submission Catalogue Organisation</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">T</span>his feature allows you to organise the way webSubmit main page
will look like. You will be able to group document types inside catalogues and order the catalogues the way you
wish.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Organisation" link in the websubmit admin right menu.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">O</span>nce on the "Edit Catalogues page", you will find the currently
defined organisation chart in the middle of the page. To the right, one form allows you to create a new catalogue
("Add a Catalogue") and one to add a document type to an existing catalogue ("Add a document type").
<br/>&nbsp;<br/>
<ul>
<li><font color=green>To add a catalogue:</font> Enter the name of your new catalogue in the
"Catalogue Name" free text field then choose to which existing catalogue this one will be attached to. If you
attach the new one to an already existing catalogue, you can create a sub-catalogue. To actually create it,
click on "ADD".
<li><font color=green>To add a document type to a catalogue:</font> Choose in the list of existing
"Document type names" the one you want to add to the chart. Then choose to which catalogue the document
type will be associated. Click on "ADD" to finalise this action.
<li><font color=green>To withdraw a document type or a catalogue from the chart:</font> Click on the
red cross next to the item you want to withdraw. If you withdraw a catalogue all document types attached to
it will be withdrawn also (of course the actual document types in webSubmit won't be destroyed!).
<li><font color=green>To move a document type or a catalogue in the chart:</font> Use the small up
and down arrows next to the document type/catalogue title.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#newtype">Create a New Document Type</a><br/>
<li><a href="#documents">document types</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="bibconvert"></a>
<h1>BibConvert</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">W</span>ebSubmit stores the data gathered during a submission in a
directory. In this directory each file corresponds to a field saved during the submission. <br/>
&nbsp;<span class="guideheader">B</span>ibConvert is used to create a formatted file which will be easy to
upload in the bibliographical database from this directory.<br/>
&nbsp;<span class="guideheader">T</span>his BibConvert program is called from the
<a href="#Make_Record">Make_Record</a> and
<a href="#Make_Modify_Record">Make_Modify_Record</a> functions
from the <a href="#functions">end script</a> system of webSubmit.<br/>
&nbsp;<span class="guideheader">T</span>he BibConvert configuration files used by webSubmit are in the
<ETCDIR>/bibconvert/config directory.<br/><br/>
&nbsp;<span class="guideheader">F</span>or more info about bibconvert, please see the dedicated
<a href="bibconvert-admin-guide">guide</a>.
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="faq"></a>
<h1>FAQ</h1>
&nbsp;<span class="guideheader">Q</span>1. <a href="#Q1">I'd like to be warned each time there is an error, or an important
action is made through the manager. Is this possible?
</a><br/>
&nbsp;<span class="guideheader">Q</span>2. <a href="#Q2">Where are all the files stored in this system?
</a><br/>
&nbsp;<span class="guideheader">Q</span>3. <a href="#Q3">How is the documents archive organised?
</a><br/><br/><br/><br/>
<a name="Q1"></a>
<i>&nbsp;<span class="guideheader">Q</span>1. I'd like to be warned each time there is an error, or an important
action is made through the manager. Is this possible?
</i>
<blockquote>
Yes, it is. Edit the invenio-local.conf file, the "CFG_SITE_ADMIN_EMAIL" definition and set it to your email
address. You will then receive all the warning emails issued by the manager.
</blockquote>
<a name="Q2"></a>
<i>&nbsp;<span class="guideheader">Q</span>2. Where are all the files stored in this system?
</i>
<blockquote>
<li>the counter files are here: /opt/invenio/var/data/submit/counters. There are used by the
<a href="#Report_Number_Generation">Report_Number_Generation</a>
function.
<li>all running and completed submissions are stored here: /opt/invenio/var/data/submit/storage.
<li>all the document files attached to records are stored here: /opt/invenio/var/data/files.
<li>all python functions used by webSubmit are stored here: /opt/invenio/lib/python/invenio/websubmit_functions
</blockquote>
<a name="Q3"></a>
<i>&nbsp;<span class="guideheader">Q</span>3. How is the documents archive organised?
</i>
<blockquote>
First of all, the documents files attached to records are stored here: /opt/invenio/var/data/files. <br/><br/>
The <a href="#Upload_Files">Upload_Files</a> webSubmit function is used
to link a document with a record.<br/><br/>
All documents get an id from the system and are stored in the "bibdoc" table in the database. The link between a
document and a record is stored using the "bibdoc_bibrec" table.<br/><br/>
The document id is used to determine where the files are stored. For example the files of document #14 will be
stored here: /opt/invenio/var/data/files/g0/14<br/><br/>
The subdirectory g0 is used to split the documents accross the filesystem. The CFG_FILE_DIR_SIZE variable from
invenio.conf determines how many documents will be stored under one subdirectory.<br/><br/>
Several files may be stored under the same document directory: they are the different formats and versions of the
same document. Versions are indicated by a string of the form ";1.0" concatenated to the name of the file.<br/><br/>
Please see the <href="/help/admin/howto-fulltext">HOWTO Manage Fulltext Files</a> for more information on the administrative command line tools available to manipulate fulltext files.
</blockquote>
<h3>See also:</h3>
<blockquote>
notes
</blockquote>
</div>
diff --git a/modules/websubmit/lib/bibdocfile.py b/modules/websubmit/lib/bibdocfile.py
index a908c7df5..146a9332b 100644
--- a/modules/websubmit/lib/bibdocfile.py
+++ b/modules/websubmit/lib/bibdocfile.py
@@ -1,3763 +1,3764 @@
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
This module implements the low-level API for dealing with fulltext files.
- All the files associated to a I{record} (identified by a I{recid}) can be
managed via an instance of the C{BibRecDocs} class.
- A C{BibRecDocs} is a wrapper of the list of I{documents} attached to the
record.
- Each document is represented by an instance of the C{BibDoc} class.
- A document is identified by a C{docid} and name (C{docname}). The docname
must be unique within the record. A document is the set of all the
formats and revisions of a piece of information.
- A document has a type called C{doctype} and can have a restriction.
- Each physical file, i.e. the concretization of a document into a
particular I{version} and I{format} is represented by an instance of the
C{BibDocFile} class.
- The format is infact the extension of the physical file.
- A comment and a description and other information can be associated to a
BibDocFile.
- A C{bibdoc} is a synonim for a document, while a C{bibdocfile} is a
synonim for a physical file.
@group Main classes: BibRecDocs,BibDoc,BibDocFile
@group Other classes: BibDocMoreInfo,Md5Folder,InvenioWebSubmitFileError
@group Main functions: decompose_file,stream_file,bibdocfile_*,download_url
@group Configuration Variables: CFG_*
"""
__revision__ = "$Id$"
import os
import re
import shutil
import filecmp
import time
import random
import socket
import urllib2
import urllib
import tempfile
import cPickle
import base64
import binascii
import cgi
import sys
if sys.hexversion < 0x2060000:
from md5 import md5
else:
from hashlib import md5
try:
import magic
if not hasattr(magic, "open"):
raise ImportError
CFG_HAS_MAGIC = True
except ImportError:
CFG_HAS_MAGIC = False
## The above flag controls whether HTTP range requests are supported or not
## when serving static files via Python. This is disabled by default as
## it currently breaks support for opening PDF files on Windows platforms
## using Acrobat reader brower plugin.
CFG_ENABLE_HTTP_RANGE_REQUESTS = False
from datetime import datetime
from mimetypes import MimeTypes
from thread import get_ident
from invenio import webinterface_handler_config as apache
## Let's set a reasonable timeout for URL request (e.g. FFT)
socket.setdefaulttimeout(40)
if sys.hexversion < 0x2040000:
# pylint: disable=W0622
from sets import Set as set
# pylint: enable=W0622
from invenio.shellutils import escape_shell_arg
from invenio.dbquery import run_sql, DatabaseError, blob_to_string
from invenio.errorlib import register_exception
from invenio.bibrecord import record_get_field_instances, \
field_get_subfield_values, field_get_subfield_instances, \
encode_for_xml
from invenio.urlutils import create_url
from invenio.textutils import nice_size
from invenio.access_control_engine import acc_authorize_action
from invenio.webuser import collect_user_info
from invenio.access_control_admin import acc_is_user_in_role, acc_get_role_id
from invenio.access_control_firerole import compile_role_definition, acc_firerole_check_user
from invenio.access_control_config import SUPERADMINROLE, CFG_WEBACCESS_WARNING_MSGS
from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, \
CFG_WEBDIR, CFG_WEBSUBMIT_FILEDIR,\
CFG_WEBSUBMIT_ADDITIONAL_KNOWN_FILE_EXTENSIONS, \
CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT, CFG_SITE_SECURE_URL, \
CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS, \
CFG_TMPDIR, CFG_TMPSHAREDDIR, CFG_PATH_MD5SUM, \
CFG_WEBSUBMIT_STORAGEDIR, \
CFG_BIBDOCFILE_USE_XSENDFILE, \
- CFG_BIBDOCFILE_MD5_CHECK_PROBABILITY
+ CFG_BIBDOCFILE_MD5_CHECK_PROBABILITY, \
+ CFG_SITE_RECORD
from invenio.websubmit_config import CFG_WEBSUBMIT_ICON_SUBFORMAT_RE, \
CFG_WEBSUBMIT_DEFAULT_ICON_SUBFORMAT
import invenio.template
websubmit_templates = invenio.template.load('websubmit')
websearch_templates = invenio.template.load('websearch')
#: block size when performing I/O.
CFG_BIBDOCFILE_BLOCK_SIZE = 1024 * 8
#: threshold used do decide when to use Python MD5 of CLI MD5 algorithm.
CFG_BIBDOCFILE_MD5_THRESHOLD = 256 * 1024
#: chunks loaded by the Python MD5 algorithm.
CFG_BIBDOCFILE_MD5_BUFFER = 1024 * 1024
#: whether to normalize e.g. ".JPEG" and ".jpg" into .jpeg.
CFG_BIBDOCFILE_STRONG_FORMAT_NORMALIZATION = False
#: flags that can be associated to files.
CFG_BIBDOCFILE_AVAILABLE_FLAGS = (
'PDF/A',
'STAMPED',
'PDFOPT',
'HIDDEN',
'CONVERTED',
'PERFORM_HIDE_PREVIOUS',
'OCRED'
)
#: constant used if FFT correct with the obvious meaning.
KEEP_OLD_VALUE = 'KEEP-OLD-VALUE'
_mimes = MimeTypes(strict=False)
_mimes.suffix_map.update({'.tbz2' : '.tar.bz2'})
_mimes.encodings_map.update({'.bz2' : 'bzip2'})
_magic_cookies = {}
def _get_magic_cookies():
"""
@return: a tuple of magic object.
@rtype: (MAGIC_NONE, MAGIC_COMPRESS, MAGIC_MIME, MAGIC_COMPRESS + MAGIC_MIME)
@note: ... not real magic. Just see: man file(1)
"""
thread_id = get_ident()
if thread_id not in _magic_cookies:
_magic_cookies[thread_id] = {
magic.MAGIC_NONE : magic.open(magic.MAGIC_NONE),
magic.MAGIC_COMPRESS : magic.open(magic.MAGIC_COMPRESS),
magic.MAGIC_MIME : magic.open(magic.MAGIC_MIME),
magic.MAGIC_COMPRESS + magic.MAGIC_MIME : magic.open(magic.MAGIC_COMPRESS + magic.MAGIC_MIME)
}
for key in _magic_cookies[thread_id].keys():
_magic_cookies[thread_id][key].load()
return _magic_cookies[thread_id]
def _generate_extensions():
"""
Generate the regular expression to match all the known extensions.
@return: the regular expression.
@rtype: regular expression object
"""
_tmp_extensions = _mimes.encodings_map.keys() + \
_mimes.suffix_map.keys() + \
_mimes.types_map[1].keys() + \
CFG_WEBSUBMIT_ADDITIONAL_KNOWN_FILE_EXTENSIONS
extensions = []
for ext in _tmp_extensions:
if ext.startswith('.'):
extensions.append(ext)
else:
extensions.append('.' + ext)
extensions.sort()
extensions.reverse()
extensions = set([ext.lower() for ext in extensions])
extensions = '\\' + '$|\\'.join(extensions) + '$'
extensions = extensions.replace('+', '\\+')
return re.compile(extensions, re.I)
#: Regular expression to recognized extensions.
_extensions = _generate_extensions()
class InvenioWebSubmitFileError(Exception):
"""
Exception raised in case of errors related to fulltext files.
"""
pass
def file_strip_ext(afile, skip_version=False, only_known_extensions=False, allow_subformat=True):
"""
Strip in the best way the extension from a filename.
>>> file_strip_ext("foo.tar.gz")
'foo'
>>> file_strip_ext("foo.buz.gz")
'foo.buz'
>>> file_strip_ext("foo.buz")
'foo'
>>> file_strip_ext("foo.buz", only_known_extensions=True)
'foo.buz'
>>> file_strip_ext("foo.buz;1", skip_version=False,
... only_known_extensions=True)
'foo.buz;1'
>>> file_strip_ext("foo.gif;icon")
'foo'
>>> file_strip_ext("foo.gif:icon", allow_subformat=False)
'foo.gif:icon'
@param afile: the path/name of a file.
@type afile: string
@param skip_version: whether to skip a trailing ";version".
@type skip_version: bool
@param only_known_extensions: whether to strip out only known extensions or
to consider as extension anything that follows a dot.
@type only_known_extensions: bool
@param allow_subformat: whether to consider also subformats as part of
the extension.
@type allow_subformat: bool
@return: the name/path without the extension (and version).
@rtype: string
"""
if skip_version or allow_subformat:
afile = afile.split(';')[0]
nextfile = _extensions.sub('', afile)
if nextfile == afile and not only_known_extensions:
nextfile = os.path.splitext(afile)[0]
while nextfile != afile:
afile = nextfile
nextfile = _extensions.sub('', afile)
return nextfile
def normalize_format(format, allow_subformat=True):
"""
Normalize the format, e.g. by adding a dot in front.
@param format: the format/extension to be normalized.
@type format: string
@param allow_subformat: whether to consider also subformats as part of
the extension.
@type allow_subformat: bool
@return: the normalized format.
@rtype; string
"""
if allow_subformat:
subformat = format[format.rfind(';'):]
format = format[:format.rfind(';')]
else:
subformat = ''
if format and format[0] != '.':
format = '.' + format
if CFG_BIBDOCFILE_STRONG_FORMAT_NORMALIZATION:
if format not in ('.Z', '.H', '.C', '.CC'):
format = format.lower()
format = {
'.jpg' : '.jpeg',
'.htm' : '.html',
'.tif' : '.tiff'
}.get(format, format)
return format + subformat
def guess_format_from_url(url):
"""
Given a URL tries to guess it's extension.
Different method will be used, including HTTP HEAD query,
downloading the resource and using mime
@param url: the URL for which the extension shuld be guessed.
@type url: string
@return: the recognized extension or empty string if it's impossible to
recognize it.
@rtype: string
"""
def parse_content_disposition(text):
for item in text.split(';'):
item = item.strip()
if item.strip().startswith('filename='):
return item[len('filename="'):-len('"')]
def parse_content_type(text):
return text.split(';')[0].strip()
## Let's try to guess the extension by considering the URL as a filename
ext = decompose_file(url, skip_version=True, only_known_extensions=True)[2]
if ext.startswith('.'):
return ext
if is_url_a_local_file(url) and CFG_HAS_MAGIC:
## if the URL corresponds to a local file, let's try to use
## the Python magic library to guess it
try:
magic_cookie = _get_magic_cookies()[magic.MAGIC_MIME]
mimetype = magic_cookie.file(url)
ext = _mimes.guess_extension(mimetype)
if ext:
return normalize_format(ext)
except Exception:
pass
else:
## Since the URL is remote, let's try to perform a HEAD request
## and see the corresponding headers
info = urllib2.urlopen(url).info()
content_disposition = info.getheader('Content-Disposition')
if content_disposition:
filename = parse_content_disposition(content_disposition)
if filename:
return decompose_file(filename)[2]
content_type = info.getheader('Content-Type')
if content_type:
content_type = parse_content_type(content_type)
ext = _mimes.guess_extension(content_type)
if ext:
return normalize_format(ext)
if CFG_HAS_MAGIC:
## Last solution: let's download the remote resource
## and use the Python magic library to guess the extension
try:
filename = download_url(url, format='')
magic_cookie = _get_magic_cookies()[magic.MAGIC_MIME]
mimetype = magic_cookie.file(filename)
os.remove(filename)
ext = _mimes.guess_extension(mimetype)
if ext:
return normalize_format(ext)
except Exception:
pass
return ""
_docname_re = re.compile(r'[^-\w.]*')
def normalize_docname(docname):
"""
Normalize the docname.
At the moment the normalization is just returning the same string.
@param docname: the docname to be normalized.
@type docname: string
@return: the normalized docname.
@rtype: string
"""
#return _docname_re.sub('', docname)
return docname
def normalize_version(version):
"""
Normalize the version.
The version can be either an integer or the keyword 'all'. Any other
value will be transformed into the empty string.
@param version: the version (either a number or 'all').
@type version: integer or string
@return: the normalized version.
@rtype: string
"""
try:
int(version)
except ValueError:
if version.lower().strip() == 'all':
return 'all'
else:
return ''
return str(version)
def compose_file(dirname, docname, extension, subformat=None, version=None):
"""
Construct back a fullpath given the separate components.
"""
if version:
version = ";%i" % int(version)
else:
version = ""
if subformat:
if not subformat.startswith(";"):
subformat = ";%s" % subformat
else:
subformat = ""
if extension and not extension.startswith("."):
extension = ".%s" % extension
return os.path.join(dirname, docname + extension + subformat + version)
def decompose_file(afile, skip_version=False, only_known_extensions=False,
allow_subformat=True):
"""
Decompose a file/path into its components dirname, basename and extension.
>>> decompose_file('/tmp/foo.tar.gz')
('/tmp', 'foo', '.tar.gz')
>>> decompose_file('/tmp/foo.tar.gz;1', skip_version=True)
('/tmp', 'foo', '.tar.gz')
>>> decompose_file('http://www.google.com/index.html')
('http://www.google.com', 'index', '.html')
@param afile: the path/name of a file.
@type afile: string
@param skip_version: whether to skip a trailing ";version".
@type skip_version: bool
@param only_known_extensions: whether to strip out only known extensions or
to consider as extension anything that follows a dot.
@type only_known_extensions: bool
@param allow_subformat: whether to consider also subformats as part of
the extension.
@type allow_subformat: bool
@return: a tuple with the directory name, the docname and extension.
@rtype: (dirname, docname, extension)
@note: if a URL is provided, the scheme will be part of the dirname.
@see: L{file_strip_ext} for the algorithm used to retrieve the extension.
"""
if skip_version:
version = afile.split(';')[-1]
try:
int(version)
afile = afile[:-len(version)-1]
except ValueError:
pass
basename = os.path.basename(afile)
dirname = afile[:-len(basename)-1]
base = file_strip_ext(
basename,
only_known_extensions=only_known_extensions,
allow_subformat=allow_subformat)
extension = basename[len(base) + 1:]
if extension:
extension = '.' + extension
return (dirname, base, extension)
def decompose_file_with_version(afile):
"""
Decompose a file into dirname, basename, extension and version.
>>> decompose_file_with_version('/tmp/foo.tar.gz;1')
('/tmp', 'foo', '.tar.gz', 1)
@param afile: the path/name of a file.
@type afile: string
@return: a tuple with the directory name, the docname, extension and
version.
@rtype: (dirname, docname, extension, version)
@raise ValueError: in case version does not exist it will.
@note: if a URL is provided, the scheme will be part of the dirname.
"""
version_str = afile.split(';')[-1]
version = int(version_str)
afile = afile[:-len(version_str)-1]
basename = os.path.basename(afile)
dirname = afile[:-len(basename)-1]
base = file_strip_ext(basename)
extension = basename[len(base) + 1:]
if extension:
extension = '.' + extension
return (dirname, base, extension, version)
def get_subformat_from_format(format):
"""
@return the subformat if any.
@rtype: string
>>> get_superformat_from_format('foo;bar')
'bar'
>>> get_superformat_from_format('foo')
''
"""
try:
return format[format.rindex(';') + 1:]
except ValueError:
return ''
def get_superformat_from_format(format):
"""
@return the superformat if any.
@rtype: string
>>> get_superformat_from_format('foo;bar')
'foo'
>>> get_superformat_from_format('foo')
'foo'
"""
try:
return format[:format.rindex(';')]
except ValueError:
return format
def propose_next_docname(docname):
"""
Given a I{docname}, suggest a new I{docname} (useful when trying to generate
a unique I{docname}).
>>> propose_next_docname('foo')
'foo_1'
>>> propose_next_docname('foo_1')
'foo_2'
>>> propose_next_docname('foo_10')
'foo_11'
@param docname: the base docname.
@type docname: string
@return: the next possible docname based on the given one.
@rtype: string
"""
if '_' in docname:
split_docname = docname.split('_')
try:
split_docname[-1] = str(int(split_docname[-1]) + 1)
docname = '_'.join(split_docname)
except ValueError:
docname += '_1'
else:
docname += '_1'
return docname
class BibRecDocs:
"""
This class represents all the files attached to one record.
@param recid: the record identifier.
@type recid: integer
@param deleted_too: whether to consider deleted documents as normal
documents (useful when trying to recover deleted information).
@type deleted_too: bool
@param human_readable: whether numbers should be printed in human readable
format (e.g. 2048 bytes -> 2Kb)
@ivar id: the record identifier as passed to the constructor.
@type id: integer
@ivar human_readable: the human_readable flag as passed to the constructor.
@type human_readable: bool
@ivar deleted_too: the deleted_too flag as passed to the constructor.
@type deleted_too: bool
@ivar bibdocs: the list of documents attached to the record.
@type bibdocs: list of BibDoc
"""
def __init__(self, recid, deleted_too=False, human_readable=False):
self.id = recid
self.human_readable = human_readable
self.deleted_too = deleted_too
self.bibdocs = []
self.build_bibdoc_list()
def __repr__(self):
"""
@return: the canonical string representation of the C{BibRecDocs}.
@rtype: string
"""
return 'BibRecDocs(%s%s%s)' % (self.id,
self.deleted_too and ', True' or '',
self.human_readable and ', True' or ''
)
def __str__(self):
"""
@return: an easy to be I{grepped} string representation of the
whole C{BibRecDocs} content.
@rtype: string
"""
out = '%i::::total bibdocs attached=%i\n' % (self.id, len(self.bibdocs))
out += '%i::::total size latest version=%s\n' % (self.id, nice_size(self.get_total_size_latest_version()))
out += '%i::::total size all files=%s\n' % (self.id, nice_size(self.get_total_size()))
for bibdoc in self.bibdocs:
out += str(bibdoc)
return out
def empty_p(self):
"""
@return: True when the record has no attached documents.
@rtype: bool
"""
return len(self.bibdocs) == 0
def deleted_p(self):
"""
@return: True if the corresponding record has been deleted.
@rtype: bool
"""
from invenio.search_engine import record_exists
return record_exists(self.id) == -1
def get_xml_8564(self):
"""
Return a snippet of I{MARCXML} representing the I{8564} fields
corresponding to the current state.
@return: the MARCXML representation.
@rtype: string
"""
from invenio.search_engine import get_record
out = ''
record = get_record(self.id)
fields = record_get_field_instances(record, '856', '4', ' ')
for field in fields:
urls = field_get_subfield_values(field, 'u')
if urls and not bibdocfile_url_p(urls[0]):
out += '\t<datafield tag="856" ind1="4" ind2=" ">\n'
for subfield, value in field_get_subfield_instances(field):
out += '\t\t<subfield code="%s">%s</subfield>\n' % (subfield, encode_for_xml(value))
out += '\t</datafield>\n'
for afile in self.list_latest_files(list_hidden=False):
out += '\t<datafield tag="856" ind1="4" ind2=" ">\n'
url = afile.get_url()
description = afile.get_description()
comment = afile.get_comment()
if url:
out += '\t\t<subfield code="u">%s</subfield>\n' % encode_for_xml(url)
if description:
out += '\t\t<subfield code="y">%s</subfield>\n' % encode_for_xml(description)
if comment:
out += '\t\t<subfield code="z">%s</subfield>\n' % encode_for_xml(comment)
out += '\t</datafield>\n'
return out
def get_total_size_latest_version(self):
"""
Returns the total size used on disk by all the files belonging
to this record and corresponding to the latest version.
@return: the total size.
@rtype: integer
"""
size = 0
for bibdoc in self.bibdocs:
size += bibdoc.get_total_size_latest_version()
return size
def get_total_size(self):
"""
Return the total size used on disk of all the files belonging
to this record of any version (not only the last as in
L{get_total_size_latest_version}).
@return: the total size.
@rtype: integer
"""
size = 0
for bibdoc in self.bibdocs:
size += bibdoc.get_total_size()
return size
def build_bibdoc_list(self):
"""
This method must be called everytime a I{bibdoc} is added, removed or
modified.
"""
self.bibdocs = []
if self.deleted_too:
res = run_sql("""SELECT id_bibdoc, type FROM bibrec_bibdoc JOIN
bibdoc ON id=id_bibdoc WHERE id_bibrec=%s
ORDER BY docname ASC""", (self.id,))
else:
res = run_sql("""SELECT id_bibdoc, type FROM bibrec_bibdoc JOIN
bibdoc ON id=id_bibdoc WHERE id_bibrec=%s AND
status<>'DELETED' ORDER BY docname ASC""", (self.id,))
for row in res:
cur_doc = BibDoc(docid=row[0], recid=self.id, doctype=row[1], human_readable=self.human_readable)
self.bibdocs.append(cur_doc)
def list_bibdocs(self, doctype=''):
"""
Returns the list all bibdocs object belonging to a recid.
If C{doctype} is set, it returns just the bibdocs of that doctype.
@param doctype: the optional doctype.
@type doctype: string
@return: the list of bibdocs.
@rtype: list of BibDoc
"""
if not doctype:
return self.bibdocs
else:
return [bibdoc for bibdoc in self.bibdocs if doctype == bibdoc.doctype]
def get_bibdoc_names(self, doctype=''):
"""
Returns all the names of the documents associated with the bibdoc.
If C{doctype} is set, restrict the result to all the matching doctype.
@param doctype: the optional doctype.
@type doctype: string
@return: the list of document names.
@rtype: list of string
"""
return [bibdoc.docname for bibdoc in self.list_bibdocs(doctype)]
def check_file_exists(self, path):
"""
Check if a file with the same content of the file pointed in C{path}
is already attached to this record.
@param path: the file to be checked against.
@type path: string
@return: True if a file with the requested content is already attached
to the record.
@rtype: bool
"""
size = os.path.getsize(path)
# Let's consider all the latest files
files = self.list_latest_files()
# Let's consider all the latest files with same size
potential = [afile for afile in files if afile.get_size() == size]
if potential:
checksum = calculate_md5(path)
# Let's consider all the latest files with the same size and the
# same checksum
potential = [afile for afile in potential if afile.get_checksum() == checksum]
if potential:
potential = [afile for afile in potential if filecmp.cmp(afile.get_full_path(), path)]
if potential:
return True
else:
# Gosh! How unlucky, same size, same checksum but not same
# content!
pass
return False
def propose_unique_docname(self, docname):
"""
Given C{docname}, return a new docname that is not already attached to
the record.
@param docname: the reference docname.
@type docname: string
@return: a docname not already attached.
@rtype: string
"""
docname = normalize_docname(docname)
goodname = docname
i = 1
while goodname in self.get_bibdoc_names():
i += 1
goodname = "%s_%s" % (docname, i)
return goodname
def merge_bibdocs(self, docname1, docname2):
"""
This method merge C{docname2} into C{docname1}.
1. Given all the formats of the latest version of the files
attached to C{docname2}, these files are added as new formats
into C{docname1}.
2. C{docname2} is marked as deleted.
@raise InvenioWebSubmitFileError: if at least one format in C{docname2}
already exists in C{docname1}. (In this case the two bibdocs are
preserved)
@note: comments and descriptions are also copied.
@note: if C{docname2} has a I{restriction}(i.e. if the I{status} is
set) and C{docname1} doesn't, the restriction is imported.
"""
bibdoc1 = self.get_bibdoc(docname1)
bibdoc2 = self.get_bibdoc(docname2)
## Check for possibility
for bibdocfile in bibdoc2.list_latest_files():
format = bibdocfile.get_format()
if bibdoc1.format_already_exists_p(format):
raise InvenioWebSubmitFileError('Format %s already exists in bibdoc %s of record %s. It\'s impossible to merge bibdoc %s into it.' % (format, docname1, self.id, docname2))
## Importing restriction if needed.
restriction1 = bibdoc1.get_status()
restriction2 = bibdoc2.get_status()
if restriction2 and not restriction1:
bibdoc1.set_status(restriction2)
## Importing formats
for bibdocfile in bibdoc2.list_latest_files():
format = bibdocfile.get_format()
comment = bibdocfile.get_comment()
description = bibdocfile.get_description()
bibdoc1.add_file_new_format(bibdocfile.get_full_path(), description=description, comment=comment, format=format)
## Finally deleting old bibdoc2
bibdoc2.delete()
self.build_bibdoc_list()
def get_docid(self, docname):
"""
@param docname: the document name.
@type docname: string
@return: the identifier corresponding to the given C{docname}.
@rtype: integer
@raise InvenioWebSubmitFileError: if the C{docname} does not
corresponds to a document attached to this record.
"""
for bibdoc in self.bibdocs:
if bibdoc.docname == docname:
return bibdoc.id
raise InvenioWebSubmitFileError, "Recid '%s' is not connected with a " \
"docname '%s'" % (self.id, docname)
def get_docname(self, docid):
"""
@param docid: the document identifier.
@type docid: integer
@return: the name of the document corresponding to the given document
identifier.
@rtype: string
@raise InvenioWebSubmitFileError: if the C{docid} does not
corresponds to a document attached to this record.
"""
for bibdoc in self.bibdocs:
if bibdoc.id == docid:
return bibdoc.docname
raise InvenioWebSubmitFileError, "Recid '%s' is not connected with a " \
"docid '%s'" % (self.id, docid)
def has_docname_p(self, docname):
"""
@param docname: the document name,
@type docname: string
@return: True if a document with the given name is attached to this
record.
@rtype: bool
"""
for bibdoc in self.bibdocs:
if bibdoc.docname == docname:
return True
return False
def get_bibdoc(self, docname):
"""
@return: the bibdoc with a particular docname associated with
this recid"""
for bibdoc in self.bibdocs:
if bibdoc.docname == docname:
return bibdoc
raise InvenioWebSubmitFileError, "Recid '%s' is not connected with " \
" docname '%s'" % (self.id, docname)
def delete_bibdoc(self, docname):
"""
Deletes the document with the specified I{docname}.
@param docname: the document name.
@type docname: string
"""
for bibdoc in self.bibdocs:
if bibdoc.docname == docname:
bibdoc.delete()
self.build_bibdoc_list()
def add_bibdoc(self, doctype="Main", docname='file', never_fail=False):
"""
Add a new empty document object (a I{bibdoc}) to the list of
documents of this record.
@param doctype: the document type.
@type doctype: string
@param docname: the document name.
@type docname: string
@param never_fail: if True, this procedure will not fail, even if
a document with the given name is already attached to this
record. In this case a new name will be generated (see
L{propose_unique_docname}).
@type never_fail: bool
@return: the newly created document object.
@rtype: BibDoc
@raise InvenioWebSubmitFileError: in case of any error.
"""
try:
docname = normalize_docname(docname)
if never_fail:
docname = self.propose_unique_docname(docname)
if docname in self.get_bibdoc_names():
raise InvenioWebSubmitFileError, "%s has already a bibdoc with docname %s" % (self.id, docname)
else:
bibdoc = BibDoc(recid=self.id, doctype=doctype, docname=docname, human_readable=self.human_readable)
self.build_bibdoc_list()
return bibdoc
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError(str(e))
def add_new_file(self, fullpath, doctype="Main", docname=None, never_fail=False, description=None, comment=None, format=None, flags=None):
"""
Directly add a new file to this record.
Adds a new file with the following policy:
- if the C{docname} is not set it is retrieved from the name of the
file.
- If a bibdoc with the given docname doesn't already exist, it is
created and the file is added to it.
- It it exist but it doesn't contain the format that is being
added, the new format is added.
- If the format already exists then if C{never_fail} is True a new
bibdoc is created with a similar name but with a progressive
number as a suffix and the file is added to it (see
L{propose_unique_docname}).
@param fullpath: the filesystme path of the document to be added.
@type fullpath: string
@param doctype: the type of the document.
@type doctype: string
@param docname: the document name.
@type docname: string
@param never_fail: if True, this procedure will not fail, even if
a document with the given name is already attached to this
record. In this case a new name will be generated (see
L{propose_unique_docname}).
@type never_fail: bool
@param description: an optional description of the file.
@type description: string
@param comment: an optional comment to the file.
@type comment: string
@param format: the extension of the file. If not specified it will
be guessed (see L{guess_format_from_url}).
@type format: string
@param flags: a set of flags to be associated with the file (see
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS})
@type flags: list of string
@return: the elaborated document object.
@rtype: BibDoc
@raise InvenioWebSubmitFileError: in case of error.
"""
if docname is None:
docname = decompose_file(fullpath)[1]
if format is None:
format = decompose_file(fullpath)[2]
docname = normalize_docname(docname)
try:
bibdoc = self.get_bibdoc(docname)
except InvenioWebSubmitFileError:
# bibdoc doesn't already exists!
bibdoc = self.add_bibdoc(doctype, docname, False)
bibdoc.add_file_new_version(fullpath, description=description, comment=comment, format=format, flags=flags)
self.build_bibdoc_list()
else:
try:
bibdoc.add_file_new_format(fullpath, description=description, comment=comment, format=format, flags=flags)
self.build_bibdoc_list()
except InvenioWebSubmitFileError, e:
# Format already exist!
if never_fail:
bibdoc = self.add_bibdoc(doctype, docname, True)
bibdoc.add_file_new_version(fullpath, description=description, comment=comment, format=format, flags=flags)
self.build_bibdoc_list()
else:
raise
return bibdoc
def add_new_version(self, fullpath, docname=None, description=None, comment=None, format=None, flags=None):
"""
Adds a new file to an already existent document object as a new
version.
@param fullpath: the filesystem path of the file to be added.
@type fullpath: string
@param docname: the document name. If not specified it will be
extracted from C{fullpath} (see L{decompose_file}).
@type docname: string
@param description: an optional description for the file.
@type description: string
@param comment: an optional comment to the file.
@type comment: string
@param format: the extension of the file. If not specified it will
be guessed (see L{guess_format_from_url}).
@type format: string
@param flags: a set of flags to be associated with the file (see
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS})
@type flags: list of string
@return: the elaborated document object.
@rtype: BibDoc
@raise InvenioWebSubmitFileError: in case of error.
@note: previous files associated with the same document will be
considered obsolete.
"""
if docname is None:
docname = decompose_file(fullpath)[1]
if format is None:
format = decompose_file(fullpath)[2]
if flags is None:
flags = []
if 'pdfa' in get_subformat_from_format(format).split(';') and not 'PDF/A' in flags:
flags.append('PDF/A')
bibdoc = self.get_bibdoc(docname=docname)
bibdoc.add_file_new_version(fullpath, description=description, comment=comment, format=format, flags=flags)
self.build_bibdoc_list()
return bibdoc
def add_new_format(self, fullpath, docname=None, description=None, comment=None, format=None, flags=None):
"""
Adds a new file to an already existent document object as a new
format.
@param fullpath: the filesystem path of the file to be added.
@type fullpath: string
@param docname: the document name. If not specified it will be
extracted from C{fullpath} (see L{decompose_file}).
@type docname: string
@param description: an optional description for the file.
@type description: string
@param comment: an optional comment to the file.
@type comment: string
@param format: the extension of the file. If not specified it will
be guessed (see L{guess_format_from_url}).
@type format: string
@param flags: a set of flags to be associated with the file (see
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS})
@type flags: list of string
@return: the elaborated document object.
@rtype: BibDoc
@raise InvenioWebSubmitFileError: in case the same format already
exists.
"""
if docname is None:
docname = decompose_file(fullpath)[1]
if format is None:
format = decompose_file(fullpath)[2]
if flags is None:
flags = []
if 'pdfa' in get_subformat_from_format(format).split(';') and not 'PDF/A' in flags:
flags.append('PDF/A')
bibdoc = self.get_bibdoc(docname=docname)
bibdoc.add_file_new_format(fullpath, description=description, comment=comment, format=format, flags=flags)
self.build_bibdoc_list()
return bibdoc
def list_latest_files(self, doctype='', list_hidden=True):
"""
Returns a list of the latest files.
@param doctype: if set, only document of the given type will be listed.
@type doctype: string
@param list_hidden: if True, will list also files with the C{HIDDEN}
flag being set.
@type list_hidden: bool
@return: the list of latest files.
@rtype: list of BibDocFile
"""
docfiles = []
for bibdoc in self.list_bibdocs(doctype):
docfiles += bibdoc.list_latest_files(list_hidden=list_hidden)
return docfiles
def display(self, docname="", version="", doctype="", ln=CFG_SITE_LANG, verbose=0, display_hidden=True):
"""
Returns an HTML representation of the the attached documents.
@param docname: if set, include only the requested document.
@type docname: string
@param version: if not set, only the last version will be displayed. If
'all', all versions will be displayed.
@type version: string (integer or 'all')
@param doctype: is set, include only documents of the requested type.
@type doctype: string
@param ln: the language code.
@type ln: string
@param verbose: if greater than 0, includes debug information.
@type verbose: integer
@param display_hidden: whether to include hidden files as well.
@type display_hidden: bool
@return: the formatted representation.
@rtype: HTML string
"""
t = ""
if docname:
try:
bibdocs = [self.get_bibdoc(docname)]
except InvenioWebSubmitFileError:
bibdocs = self.list_bibdocs(doctype)
else:
bibdocs = self.list_bibdocs(doctype)
if bibdocs:
types = list_types_from_array(bibdocs)
fulltypes = []
for mytype in types:
if mytype in ('Plot', 'PlotMisc'):
# FIXME: quick hack to ignore plot-like doctypes
# on Files tab
continue
fulltype = {
'name' : mytype,
'content' : [],
}
for bibdoc in bibdocs:
if mytype == bibdoc.get_type():
fulltype['content'].append(bibdoc.display(version,
ln=ln, display_hidden=display_hidden))
fulltypes.append(fulltype)
if verbose >= 9:
verbose_files = str(self)
else:
verbose_files = ''
t = websubmit_templates.tmpl_bibrecdoc_filelist(
ln=ln,
types = fulltypes,
verbose_files=verbose_files
)
return t
def fix(self, docname):
"""
Algorithm that transform a broken/old bibdoc into a coherent one.
Think of it as being the fsck of BibDocs.
- All the files in the bibdoc directory will be renamed according
to the document name. Proper .recid, .type, .md5 files will be
created/updated.
- In case of more than one file with the same format version a new
bibdoc will be created in order to put does files.
@param docname: the document name that need to be fixed.
@type docname: string
@return: the list of newly created bibdocs if any.
@rtype: list of BibDoc
@raise InvenioWebSubmitFileError: in case of issues that can not be
fixed automatically.
"""
bibdoc = self.get_bibdoc(docname)
versions = {}
res = []
new_bibdocs = [] # List of files with the same version/format of
# existing file which need new bibdoc.
counter = 0
zero_version_bug = False
if os.path.exists(bibdoc.basedir):
for filename in os.listdir(bibdoc.basedir):
if filename[0] != '.' and ';' in filename:
name, version = filename.split(';')
try:
version = int(version)
except ValueError:
# Strange name
register_exception()
raise InvenioWebSubmitFileError, "A file called %s exists under %s. This is not a valid name. After the ';' there must be an integer representing the file version. Please, manually fix this file either by renaming or by deleting it." % (filename, bibdoc.basedir)
if version == 0:
zero_version_bug = True
format = name[len(file_strip_ext(name)):]
format = normalize_format(format)
if not versions.has_key(version):
versions[version] = {}
new_name = 'FIXING-%s-%s' % (str(counter), name)
try:
shutil.move('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, new_name))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in renaming '%s' to '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, new_name), e)
if versions[version].has_key(format):
new_bibdocs.append((new_name, version))
else:
versions[version][format] = new_name
counter += 1
elif filename[0] != '.':
# Strange name
register_exception()
raise InvenioWebSubmitFileError, "A file called %s exists under %s. This is not a valid name. There should be a ';' followed by an integer representing the file version. Please, manually fix this file either by renaming or by deleting it." % (filename, bibdoc.basedir)
else:
# we create the corresponding storage directory
old_umask = os.umask(022)
os.makedirs(bibdoc.basedir)
# and save the father record id if it exists
try:
if self.id != "":
recid_fd = open("%s/.recid" % bibdoc.basedir, "w")
recid_fd.write(str(self.id))
recid_fd.close()
if bibdoc.doctype != "":
type_fd = open("%s/.type" % bibdoc.basedir, "w")
type_fd.write(str(bibdoc.doctype))
type_fd.close()
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, e
os.umask(old_umask)
if not versions:
bibdoc.delete()
else:
for version, formats in versions.iteritems():
if zero_version_bug:
version += 1
for format, filename in formats.iteritems():
destination = '%s%s;%i' % (docname, format, version)
try:
shutil.move('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, destination))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in renaming '%s' to '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, destination), e)
try:
recid_fd = open("%s/.recid" % bibdoc.basedir, "w")
recid_fd.write(str(self.id))
recid_fd.close()
type_fd = open("%s/.type" % bibdoc.basedir, "w")
type_fd.write(str(bibdoc.doctype))
type_fd.close()
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in creating .recid and .type file for '%s' folder: '%s'" % (bibdoc.basedir, e)
self.build_bibdoc_list()
res = []
for (filename, version) in new_bibdocs:
if zero_version_bug:
version += 1
new_bibdoc = self.add_bibdoc(doctype=bibdoc.doctype, docname=docname, never_fail=True)
new_bibdoc.add_file_new_format('%s/%s' % (bibdoc.basedir, filename), version)
res.append(new_bibdoc)
try:
os.remove('%s/%s' % (bibdoc.basedir, filename))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in removing '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), e)
Md5Folder(bibdoc.basedir).update(only_new=False)
bibdoc._build_file_list()
self.build_bibdoc_list()
for bibdoc in self.bibdocs:
if not run_sql('SELECT more_info FROM bibdoc WHERE id=%s', (bibdoc.id,)):
## Import from MARC only if the bibdoc has never had
## its more_info initialized.
try:
bibdoc.import_descriptions_and_comments_from_marc()
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in importing description and comment from %s for record %s: %s" % (repr(bibdoc), self.id, e)
return res
def check_format(self, docname):
"""
Check for any format related issue.
In case L{CFG_WEBSUBMIT_ADDITIONAL_KNOWN_FILE_EXTENSIONS} is
altered or Python version changes, it might happen that a docname
contains files which are no more docname + .format ; version, simply
because the .format is now recognized (and it was not before, so
it was contained into the docname).
This algorithm verify if it is necessary to fix (seel L{fix_format}).
@param docname: the document name whose formats should be verified.
@type docname: string
@return: True if format is correct. False if a fix is needed.
@rtype: bool
@raise InvenioWebSubmitFileError: in case of any error.
"""
bibdoc = self.get_bibdoc(docname)
correct_docname = decompose_file(docname + '.pdf')[1]
if docname != correct_docname:
return False
for filename in os.listdir(bibdoc.basedir):
if not filename.startswith('.'):
try:
dummy, dummy, format, version = decompose_file_with_version(filename)
except Exception:
raise InvenioWebSubmitFileError('Incorrect filename "%s" for docname %s for recid %i' % (filename, docname, self.id))
if '%s%s;%i' % (correct_docname, format, version) != filename:
return False
return True
def check_duplicate_docnames(self):
"""
Check wethever the record is connected with at least tho documents
with the same name.
@return: True if everything is fine.
@rtype: bool
"""
docnames = set()
for docname in self.get_bibdoc_names():
if docname in docnames:
return False
else:
docnames.add(docname)
return True
def uniformize_bibdoc(self, docname):
"""
This algorithm correct wrong file name belonging to a bibdoc.
@param docname: the document name whose formats should be verified.
@type docname: string
"""
bibdoc = self.get_bibdoc(docname)
for filename in os.listdir(bibdoc.basedir):
if not filename.startswith('.'):
try:
dummy, dummy, format, version = decompose_file_with_version(filename)
except ValueError:
register_exception(alert_admin=True, prefix= "Strange file '%s' is stored in %s" % (filename, bibdoc.basedir))
else:
os.rename(os.path.join(bibdoc.basedir, filename), os.path.join(bibdoc.basedir, '%s%s;%i' % (docname, format, version)))
Md5Folder(bibdoc.basedir).update()
bibdoc.touch()
bibdoc._build_file_list('rename')
def fix_format(self, docname, skip_check=False):
"""
Fixes format related inconsistencies.
@param docname: the document name whose formats should be verified.
@type docname: string
@param skip_check: if True assume L{check_format} has already been
called and the need for fix has already been found.
If False, will implicitly call L{check_format} and skip fixing
if no error is found.
@type skip_check: bool
@return: in case merging two bibdocs is needed but it's not possible.
@rtype: bool
"""
if not skip_check:
if self.check_format(docname):
return True
bibdoc = self.get_bibdoc(docname)
correct_docname = decompose_file(docname + '.pdf')[1]
need_merge = False
if correct_docname != docname:
need_merge = self.has_docname_p(correct_docname)
if need_merge:
proposed_docname = self.propose_unique_docname(correct_docname)
run_sql('UPDATE bibdoc SET docname=%s WHERE id=%s', (proposed_docname, bibdoc.id))
self.build_bibdoc_list()
self.uniformize_bibdoc(proposed_docname)
try:
self.merge_bibdocs(docname, proposed_docname)
except InvenioWebSubmitFileError:
return False
else:
run_sql('UPDATE bibdoc SET docname=%s WHERE id=%s', (correct_docname, bibdoc.id))
self.build_bibdoc_list()
self.uniformize_bibdoc(correct_docname)
else:
self.uniformize_bibdoc(docname)
return True
def fix_duplicate_docnames(self, skip_check=False):
"""
Algotirthm to fix duplicate docnames.
If a record is connected with at least two bibdoc having the same
docname, the algorithm will try to merge them.
@param skip_check: if True assume L{check_duplicate_docnames} has
already been called and the need for fix has already been found.
If False, will implicitly call L{check_duplicate_docnames} and skip
fixing if no error is found.
@type skip_check: bool
"""
if not skip_check:
if self.check_duplicate_docnames():
return
docnames = set()
for bibdoc in self.list_bibdocs():
docname = bibdoc.docname
if docname in docnames:
new_docname = self.propose_unique_docname(bibdoc.docname)
bibdoc.change_name(new_docname)
self.merge_bibdocs(docname, new_docname)
docnames.add(docname)
class BibDoc:
"""
This class represents one document (i.e. a set of files with different
formats and with versioning information that consitutes a piece of
information.
To instanciate a new document, the recid and the docname are mandatory.
To instanciate an already existing document, either the recid and docname
or the docid alone are sufficient to retrieve it.
@param docid: the document identifier.
@type docid: integer
@param recid: the record identifier of the record to which this document
belongs to. If the C{docid} is specified the C{recid} is automatically
retrieven from the database.
@type recid: integer
@param docname: the document name.
@type docname: string
@param doctype: the document type (used when instanciating a new document).
@type doctype: string
@param human_readable: whether sizes should be represented in a human
readable format.
@type human_readable: bool
@raise InvenioWebSubmitFileError: in case of error.
"""
def __init__ (self, docid=None, recid=None, docname=None, doctype='Main', human_readable=False):
"""Constructor of a bibdoc. At least the docid or the recid/docname
pair is needed."""
# docid is known, the document already exists
if docname:
docname = normalize_docname(docname)
self.docfiles = []
self.md5s = None
self.related_files = []
self.human_readable = human_readable
if docid:
if not recid:
res = run_sql("SELECT id_bibrec,type FROM bibrec_bibdoc WHERE id_bibdoc=%s LIMIT 1", (docid,), 1)
if res:
recid = res[0][0]
doctype = res[0][1]
else:
res = run_sql("SELECT id_bibdoc1,type FROM bibdoc_bibdoc WHERE id_bibdoc2=%s LIMIT 1", (docid,), 1)
if res:
main_docid = res[0][0]
doctype = res[0][1]
res = run_sql("SELECT id_bibrec,type FROM bibrec_bibdoc WHERE id_bibdoc=%s LIMIT 1", (main_docid,), 1)
if res:
recid = res[0][0]
else:
raise InvenioWebSubmitFileError, "The docid %s associated with docid %s is not associated with any record" % (main_docid, docid)
else:
raise InvenioWebSubmitFileError, "The docid %s is not associated to any recid or other docid" % docid
else:
res = run_sql("SELECT type FROM bibrec_bibdoc WHERE id_bibrec=%s AND id_bibdoc=%s LIMIT 1", (recid, docid,), 1)
if res:
doctype = res[0][0]
else:
#this bibdoc isn't associated with the corresponding bibrec.
raise InvenioWebSubmitFileError, "Docid %s is not associated with the recid %s" % (docid, recid)
# gather the other information
res = run_sql("SELECT id,status,docname,creation_date,modification_date,text_extraction_date,more_info FROM bibdoc WHERE id=%s LIMIT 1", (docid,), 1)
if res:
self.cd = res[0][3]
self.md = res[0][4]
self.td = res[0][5]
self.recid = recid
self.docname = res[0][2]
self.id = docid
self.status = res[0][1]
self.more_info = BibDocMoreInfo(docid, blob_to_string(res[0][6]))
self.basedir = _make_base_dir(self.id)
self.doctype = doctype
else:
# this bibdoc doesn't exist
raise InvenioWebSubmitFileError, "The docid %s does not exist." % docid
# else it is a new document
else:
if not docname:
raise InvenioWebSubmitFileError, "You should specify the docname when creating a new bibdoc"
else:
self.recid = recid
self.doctype = doctype
self.docname = docname
self.status = ''
if recid:
res = run_sql("SELECT b.id FROM bibrec_bibdoc bb JOIN bibdoc b on bb.id_bibdoc=b.id WHERE bb.id_bibrec=%s AND b.docname=%s LIMIT 1", (recid, docname), 1)
if res:
raise InvenioWebSubmitFileError, "A bibdoc called %s already exists for recid %s" % (docname, recid)
self.id = run_sql("INSERT INTO bibdoc (status,docname,creation_date,modification_date) "
"values(%s,%s,NOW(),NOW())", (self.status, docname))
if self.id:
# we link the document to the record if a recid was
# specified
self.more_info = BibDocMoreInfo(self.id)
res = run_sql("SELECT creation_date, modification_date, text_extraction_date FROM bibdoc WHERE id=%s", (self.id,))
self.cd = res[0][0]
self.md = res[0][1]
self.td = res[0][2]
else:
raise InvenioWebSubmitFileError, "New docid cannot be created"
try:
self.basedir = _make_base_dir(self.id)
# we create the corresponding storage directory
if not os.path.exists(self.basedir):
old_umask = os.umask(022)
os.makedirs(self.basedir)
# and save the father record id if it exists
try:
if self.recid:
recid_fd = open("%s/.recid" % self.basedir, "w")
recid_fd.write(str(self.recid))
recid_fd.close()
if self.doctype:
type_fd = open("%s/.type" % self.basedir, "w")
type_fd.write(str(self.doctype))
type_fd.close()
except Exception, e:
register_exception(alert_admin=True)
raise InvenioWebSubmitFileError, e
os.umask(old_umask)
if self.recid:
run_sql("INSERT INTO bibrec_bibdoc (id_bibrec, id_bibdoc, type) VALUES (%s,%s,%s)",
(recid, self.id, self.doctype,))
except Exception, e:
run_sql('DELETE FROM bibdoc WHERE id=%s', (self.id, ))
run_sql('DELETE FROM bibrec_bibdoc WHERE id_bibdoc=%s', (self.id, ))
register_exception(alert_admin=True)
raise InvenioWebSubmitFileError, e
# build list of attached files
self._build_file_list('init')
# link with related_files
self._build_related_file_list()
def __repr__(self):
"""
@return: the canonical string representation of the C{BibDoc}.
@rtype: string
"""
return 'BibDoc(%s, %s, %s, %s, %s)' % (repr(self.id), repr(self.recid), repr(self.docname), repr(self.doctype), repr(self.human_readable))
def __str__(self):
"""
@return: an easy to be I{grepped} string representation of the
whole C{BibDoc} content.
@rtype: string
"""
out = '%s:%i:::docname=%s\n' % (self.recid or '', self.id, self.docname)
out += '%s:%i:::doctype=%s\n' % (self.recid or '', self.id, self.doctype)
out += '%s:%i:::status=%s\n' % (self.recid or '', self.id, self.status)
out += '%s:%i:::basedir=%s\n' % (self.recid or '', self.id, self.basedir)
out += '%s:%i:::creation date=%s\n' % (self.recid or '', self.id, self.cd)
out += '%s:%i:::modification date=%s\n' % (self.recid or '', self.id, self.md)
out += '%s:%i:::text extraction date=%s\n' % (self.recid or '', self.id, self.td)
out += '%s:%i:::total file attached=%s\n' % (self.recid or '', self.id, len(self.docfiles))
if self.human_readable:
out += '%s:%i:::total size latest version=%s\n' % (self.recid or '', self.id, nice_size(self.get_total_size_latest_version()))
out += '%s:%i:::total size all files=%s\n' % (self.recid or '', self.id, nice_size(self.get_total_size()))
else:
out += '%s:%i:::total size latest version=%s\n' % (self.recid or '', self.id, self.get_total_size_latest_version())
out += '%s:%i:::total size all files=%s\n' % (self.recid or '', self.id, self.get_total_size())
for docfile in self.docfiles:
out += str(docfile)
return out
def format_already_exists_p(self, format):
"""
@param format: a format to be checked.
@type format: string
@return: True if a file of the given format already exists among the
latest files.
@rtype: bool
"""
format = normalize_format(format)
for afile in self.list_latest_files():
if format == afile.get_format():
return True
return False
def get_status(self):
"""
@return: the status information.
@rtype: string
"""
return self.status
def get_text(self, version=None):
"""
@param version: the requested version. If not set, the latest version
will be used.
@type version: integer
@return: the textual content corresponding to the specified version
of the document.
@rtype: string
"""
if version is None:
version = self.get_latest_version()
if self.has_text(version):
return open(os.path.join(self.basedir, '.text;%i' % version)).read()
else:
return ""
def get_text_path(self, version=None):
"""
@param version: the requested version. If not set, the latest version
will be used.
@type version: int
@return: the full path to the textual content corresponding to the specified version
of the document.
@rtype: string
"""
if version is None:
version = self.get_latest_version()
if self.has_text(version):
return os.path.join(self.basedir, '.text;%i' % version)
else:
return ""
def extract_text(self, version=None, perform_ocr=False, ln='en'):
"""
Try what is necessary to extract the textual information of a document.
@param version: the version of the document for which text is required.
If not specified the text will be retrieved from the last version.
@type version: integer
@param perform_ocr: whether to perform OCR.
@type perform_ocr: bool
@param ln: a two letter language code to give as a hint to the OCR
procedure.
@type ln: string
@raise InvenioWebSubmitFileError: in case of error.
@note: the text is extracted and cached for later use. Use L{get_text}
to retrieve it.
"""
from invenio.websubmit_file_converter import get_best_format_to_extract_text_from, convert_file, InvenioWebSubmitFileConverterError
if version is None:
version = self.get_latest_version()
docfiles = self.list_version_files(version)
## We try to extract text only from original or OCRed documents.
filenames = [docfile.get_full_path() for docfile in docfiles if 'CONVERTED' not in docfile.flags or 'OCRED' in docfile.flags]
try:
filename = get_best_format_to_extract_text_from(filenames)
except InvenioWebSubmitFileConverterError:
## We fall back on considering all the documents
filenames = [docfile.get_full_path() for docfile in docfiles]
try:
filename = get_best_format_to_extract_text_from(filenames)
except InvenioWebSubmitFileConverterError:
open(os.path.join(self.basedir, '.text;%i' % version), 'w').write('')
return
try:
convert_file(filename, os.path.join(self.basedir, '.text;%i' % version), '.txt', perform_ocr=perform_ocr, ln=ln)
if version == self.get_latest_version():
run_sql("UPDATE bibdoc SET text_extraction_date=NOW() WHERE id=%s", (self.id, ))
except InvenioWebSubmitFileConverterError, e:
register_exception(alert_admin=True, prefix="Error in extracting text from bibdoc %i, version %i" % (self.id, version))
raise InvenioWebSubmitFileError, str(e)
def touch(self):
"""
Update the modification time of the bibdoc (as in the UNIX command
C{touch}).
"""
run_sql('UPDATE bibdoc SET modification_date=NOW() WHERE id=%s', (self.id, ))
#if self.recid:
#run_sql('UPDATE bibrec SET modification_date=NOW() WHERE id=%s', (self.recid, ))
def set_status(self, new_status):
"""
Set a new status. A document with a status information is a restricted
document that can be accessed only to user which as an authorization
to the I{viewrestrdoc} WebAccess action with keyword status with value
C{new_status}.
@param new_status: the new status. If empty the document will be
unrestricted.
@type new_status: string
@raise InvenioWebSubmitFileError: in case the reserved word
'DELETED' is used.
"""
if new_status != KEEP_OLD_VALUE:
if new_status == 'DELETED':
raise InvenioWebSubmitFileError('DELETED is a reserved word and can not be used for setting the status')
run_sql('UPDATE bibdoc SET status=%s WHERE id=%s', (new_status, self.id))
self.status = new_status
self.touch()
self._build_file_list()
self._build_related_file_list()
def add_file_new_version(self, filename, description=None, comment=None, format=None, flags=None):
"""
Add a new version of a file. If no physical file is already attached
to the document a the given file will have version 1. Otherwise the
new file will have the current version number plus one.
@param filename: the local path of the file.
@type filename: string
@param description: an optional description for the file.
@type description: string
@param comment: an optional comment to the file.
@type comment: string
@param format: the extension of the file. If not specified it will
be retrieved from the filename (see L{decompose_file}).
@type format: string
@param flags: a set of flags to be associated with the file (see
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS})
@type flags: list of string
@raise InvenioWebSubmitFileError: in case of error.
"""
try:
latestVersion = self.get_latest_version()
if latestVersion == 0:
myversion = 1
else:
myversion = latestVersion + 1
if os.path.exists(filename):
if not os.path.getsize(filename) > 0:
raise InvenioWebSubmitFileError, "%s seems to be empty" % filename
if format is None:
format = decompose_file(filename)[2]
else:
format = normalize_format(format)
destination = "%s/%s%s;%i" % (self.basedir, self.docname, format, myversion)
try:
shutil.copyfile(filename, destination)
os.chmod(destination, 0644)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while copying '%s' to '%s': '%s'" % (filename, destination, e)
self.more_info.set_description(description, format, myversion)
self.more_info.set_comment(comment, format, myversion)
if flags is None:
flags = []
if 'pdfa' in get_subformat_from_format(format).split(';') and not 'PDF/A' in flags:
flags.append('PDF/A')
for flag in flags:
if flag == 'PERFORM_HIDE_PREVIOUS':
for afile in self.list_all_files():
format = afile.get_format()
version = afile.get_version()
if version < myversion:
self.more_info.set_flag('HIDDEN', format, myversion)
else:
self.more_info.set_flag(flag, format, myversion)
else:
raise InvenioWebSubmitFileError, "'%s' does not exists!" % filename
finally:
self.touch()
Md5Folder(self.basedir).update()
self._build_file_list()
def add_file_new_format(self, filename, version=None, description=None, comment=None, format=None, flags=None):
"""
Add a file as a new format.
@param filename: the local path of the file.
@type filename: string
@param version: an optional specific version to which the new format
should be added. If None, the last version will be used.
@type version: integer
@param description: an optional description for the file.
@type description: string
@param comment: an optional comment to the file.
@type comment: string
@param format: the extension of the file. If not specified it will
be retrieved from the filename (see L{decompose_file}).
@type format: string
@param flags: a set of flags to be associated with the file (see
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS})
@type flags: list of string
@raise InvenioWebSubmitFileError: if the given format already exists.
"""
try:
if version is None:
version = self.get_latest_version()
if version == 0:
version = 1
if os.path.exists(filename):
if not os.path.getsize(filename) > 0:
raise InvenioWebSubmitFileError, "%s seems to be empty" % filename
if format is None:
format = decompose_file(filename)[2]
else:
format = normalize_format(format)
destination = "%s/%s%s;%i" % (self.basedir, self.docname, format, version)
if os.path.exists(destination):
raise InvenioWebSubmitFileError, "A file for docname '%s' for the recid '%s' already exists for the format '%s'" % (self.docname, self.recid, format)
try:
shutil.copyfile(filename, destination)
os.chmod(destination, 0644)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while copying '%s' to '%s': '%s'" % (filename, destination, e)
self.more_info.set_comment(comment, format, version)
self.more_info.set_description(description, format, version)
if flags is None:
flags = []
if 'pdfa' in get_subformat_from_format(format).split(';') and not 'PDF/A' in flags:
flags.append('PDF/A')
for flag in flags:
if flag != 'PERFORM_HIDE_PREVIOUS':
self.more_info.set_flag(flag, format, version)
else:
raise InvenioWebSubmitFileError, "'%s' does not exists!" % filename
finally:
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list()
def purge(self):
"""
Physically removes all the previous version of the given bibdoc.
Everything but the last formats will be erased.
"""
version = self.get_latest_version()
if version > 1:
for afile in self.docfiles:
if afile.get_version() < version:
self.more_info.unset_comment(afile.get_format(), afile.get_version())
self.more_info.unset_description(afile.get_format(), afile.get_version())
for flag in CFG_BIBDOCFILE_AVAILABLE_FLAGS:
self.more_info.unset_flag(flag, afile.get_format(), afile.get_version())
try:
os.remove(afile.get_full_path())
except Exception, e:
register_exception()
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list()
def expunge(self):
"""
Physically remove all the traces of a given document.
@note: an expunged BibDoc object shouldn't be used anymore or the
result might be unpredicted.
"""
del self.md5s
del self.more_info
os.system('rm -rf %s' % escape_shell_arg(self.basedir))
run_sql('DELETE FROM bibrec_bibdoc WHERE id_bibdoc=%s', (self.id, ))
run_sql('DELETE FROM bibdoc_bibdoc WHERE id_bibdoc1=%s OR id_bibdoc2=%s', (self.id, self.id))
run_sql('DELETE FROM bibdoc WHERE id=%s', (self.id, ))
run_sql('INSERT DELAYED INTO hstDOCUMENT(action, id_bibdoc, docname, doctimestamp) VALUES("EXPUNGE", %s, %s, NOW())', (self.id, self.docname))
del self.docfiles
del self.id
del self.cd
del self.md
del self.td
del self.basedir
del self.recid
del self.doctype
del self.docname
def revert(self, version):
"""
Revert the document to a given version. All the formats corresponding
to that version are copied forward to a new version.
@param version: the version to revert to.
@type version: integer
@raise InvenioWebSubmitFileError: in case of errors
"""
try:
version = int(version)
new_version = self.get_latest_version() + 1
for docfile in self.list_version_files(version):
destination = "%s/%s%s;%i" % (self.basedir, self.docname, docfile.get_format(), new_version)
if os.path.exists(destination):
raise InvenioWebSubmitFileError, "A file for docname '%s' for the recid '%s' already exists for the format '%s'" % (self.docname, self.recid, docfile.get_format())
try:
shutil.copyfile(docfile.get_full_path(), destination)
os.chmod(destination, 0644)
self.more_info.set_comment(self.more_info.get_comment(docfile.get_format(), version), docfile.get_format(), new_version)
self.more_info.set_description(self.more_info.get_description(docfile.get_format(), version), docfile.get_format(), new_version)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while copying '%s' to '%s': '%s'" % (docfile.get_full_path(), destination, e)
finally:
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list()
def import_descriptions_and_comments_from_marc(self, record=None):
"""
Import descriptions and comments from the corresponding MARC metadata.
@param record: the record (if None it will be calculated).
@type record: bibrecord recstruct
@note: If record is passed it is directly used, otherwise it is retrieved
from the MARCXML stored in the database.
"""
## Let's get the record
from invenio.search_engine import get_record
if record is None:
record = get_record(self.id)
fields = record_get_field_instances(record, '856', '4', ' ')
global_comment = None
global_description = None
local_comment = {}
local_description = {}
for field in fields:
url = field_get_subfield_values(field, 'u')
if url:
## Given a url
url = url[0]
- if url == '%s/record/%s/files/' % (CFG_SITE_URL, self.recid):
- ## If it is a traditional /record/1/files/ one
+ if url == '%s/%s/%s/files/' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid):
+ ## If it is a traditional /CFG_SITE_RECORD/1/files/ one
## We have global description/comment for all the formats
description = field_get_subfield_values(field, 'y')
if description:
global_description = description[0]
comment = field_get_subfield_values(field, 'z')
if comment:
global_comment = comment[0]
elif bibdocfile_url_p(url):
## Otherwise we have description/comment per format
dummy, docname, format = decompose_bibdocfile_url(url)
if docname == self.docname:
description = field_get_subfield_values(field, 'y')
if description:
local_description[format] = description[0]
comment = field_get_subfield_values(field, 'z')
if comment:
local_comment[format] = comment[0]
## Let's update the tables
version = self.get_latest_version()
for docfile in self.list_latest_files():
format = docfile.get_format()
if format in local_comment:
self.set_comment(local_comment[format], format, version)
else:
self.set_comment(global_comment, format, version)
if format in local_description:
self.set_description(local_description[format], format, version)
else:
self.set_description(global_description, format, version)
self._build_file_list('init')
def get_icon(self, subformat_re=CFG_WEBSUBMIT_ICON_SUBFORMAT_RE, display_hidden=True):
"""
@param subformat_re: by default the convention is that
L{CFG_WEBSUBMIT_ICON_SUBFORMAT_RE} is used as a subformat indicator to
mean that a particular format is to be used as an icon.
Specifiy a different subformat if you need to use a different
convention.
@type subformat_re: compiled regular expression
@return: the bibdocfile corresponding to the icon of this document, or
None if any icon exists for this document.
@rtype: BibDocFile
@warning: before I{subformat} were introduced this method was
returning a BibDoc, while now is returning a BibDocFile. Check
if your client code is compatible with this.
"""
for docfile in self.list_latest_files(list_hidden=display_hidden):
if subformat_re.match(docfile.get_subformat()):
return docfile
return None
def add_icon(self, filename, format=None, subformat=CFG_WEBSUBMIT_DEFAULT_ICON_SUBFORMAT):
"""
Attaches icon to this document.
@param filename: the local filesystem path to the icon.
@type filename: string
@param format: an optional format for the icon. If not specified it
will be calculated after the filesystem path.
@type format: string
@param subformat: by default the convention is that
CFG_WEBSUBMIT_DEFAULT_ICON_SUBFORMAT is used as a subformat indicator to
mean that a particular format is to be used as an icon.
Specifiy a different subformat if you need to use a different
convention.
@type subformat: string
@raise InvenioWebSubmitFileError: in case of errors.
"""
#first check if an icon already exists
if not format:
format = decompose_file(filename)[2]
if subformat:
format += ";%s" % subformat
self.add_file_new_format(filename, format=format)
def delete_icon(self, subformat_re=CFG_WEBSUBMIT_ICON_SUBFORMAT_RE):
"""
@param subformat_re: by default the convention is that
L{CFG_WEBSUBMIT_ICON_SUBFORMAT_RE} is used as a subformat indicator to
mean that a particular format is to be used as an icon.
Specifiy a different subformat if you need to use a different
convention.
@type subformat: compiled regular expression
Removes the icon attached to the document if it exists.
"""
for docfile in self.list_latest_files():
if subformat_re.match(docfile.get_subformat()):
self.delete_file(docfile.get_format(), docfile.get_version())
def display(self, version="", ln=CFG_SITE_LANG, display_hidden=True):
"""
Returns an HTML representation of the this document.
@param version: if not set, only the last version will be displayed. If
'all', all versions will be displayed.
@type version: string (integer or 'all')
@param ln: the language code.
@type ln: string
@param display_hidden: whether to include hidden files as well.
@type display_hidden: bool
@return: the formatted representation.
@rtype: HTML string
"""
t = ""
if version == "all":
docfiles = self.list_all_files(list_hidden=display_hidden)
elif version != "":
version = int(version)
docfiles = self.list_version_files(version, list_hidden=display_hidden)
else:
docfiles = self.list_latest_files(list_hidden=display_hidden)
icon = self.get_icon(display_hidden=display_hidden)
if icon:
imageurl = icon.get_url()
else:
imageurl = "%s/img/smallfiles.gif" % CFG_SITE_URL
versions = []
for version in list_versions_from_array(docfiles):
currversion = {
'version' : version,
'previous' : 0,
'content' : []
}
if version == self.get_latest_version() and version != 1:
currversion['previous'] = 1
for docfile in docfiles:
if docfile.get_version() == version:
currversion['content'].append(docfile.display(ln = ln))
versions.append(currversion)
if versions:
return websubmit_templates.tmpl_bibdoc_filelist(
ln = ln,
versions = versions,
imageurl = imageurl,
docname = self.docname,
recid = self.recid
)
else:
return ""
def change_name(self, newname):
"""
Renames this document name.
@param newname: the new name.
@type newname: string
@raise InvenioWebSubmitFileError: if the new name corresponds to
a document already attached to the record owning this document.
"""
try:
newname = normalize_docname(newname)
res = run_sql("SELECT b.id FROM bibrec_bibdoc bb JOIN bibdoc b on bb.id_bibdoc=b.id WHERE bb.id_bibrec=%s AND b.docname=%s", (self.recid, newname))
if res:
raise InvenioWebSubmitFileError, "A bibdoc called %s already exists for recid %s" % (newname, self.recid)
try:
for f in os.listdir(self.basedir):
if not f.startswith('.'):
try:
(dummy, base, extension, version) = decompose_file_with_version(f)
except ValueError:
register_exception(alert_admin=True, prefix="Strange file '%s' is stored in %s" % (f, self.basedir))
else:
shutil.move(os.path.join(self.basedir, f), os.path.join(self.basedir, '%s%s;%i' % (newname, extension, version)))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError("Error in renaming the bibdoc %s to %s for recid %s: %s" % (self.docname, newname, self.recid, e))
run_sql("update bibdoc set docname=%s where id=%s", (newname, self.id,))
self.docname = newname
finally:
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list('rename')
self._build_related_file_list()
def set_comment(self, comment, format, version=None):
"""
Updates the comment of a specific format/version of the document.
@param comment: the new comment.
@type comment: string
@param format: the specific format for which the comment should be
updated.
@type format: string
@param version: the specific version for which the comment should be
updated. If not specified the last version will be used.
@type version: integer
"""
if version is None:
version = self.get_latest_version()
format = normalize_format(format)
self.more_info.set_comment(comment, format, version)
self.touch()
self._build_file_list('init')
def set_description(self, description, format, version=None):
"""
Updates the description of a specific format/version of the document.
@param description: the new description.
@type description: string
@param format: the specific format for which the description should be
updated.
@type format: string
@param version: the specific version for which the description should be
updated. If not specified the last version will be used.
@type version: integer
"""
if version is None:
version = self.get_latest_version()
format = normalize_format(format)
self.more_info.set_description(description, format, version)
self.touch()
self._build_file_list('init')
def set_flag(self, flagname, format, version=None):
"""
Sets a flag for a specific format/version of the document.
@param flagname: a flag from L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}.
@type flagname: string
@param format: the specific format for which the flag should be
set.
@type format: string
@param version: the specific version for which the flag should be
set. If not specified the last version will be used.
@type version: integer
"""
if version is None:
version = self.get_latest_version()
format = normalize_format(format)
self.more_info.set_flag(flagname, format, version)
self.touch()
self._build_file_list('init')
def has_flag(self, flagname, format, version=None):
"""
Checks if a particular flag for a format/version is set.
@param flagname: a flag from L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}.
@type flagname: string
@param format: the specific format for which the flag should be
set.
@type format: string
@param version: the specific version for which the flag should be
set. If not specified the last version will be used.
@type version: integer
@return: True if the flag is set.
@rtype: bool
"""
if version is None:
version = self.get_latest_version()
format = normalize_format(format)
return self.more_info.has_flag(flagname, format, version)
def unset_flag(self, flagname, format, version=None):
"""
Unsets a flag for a specific format/version of the document.
@param flagname: a flag from L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}.
@type flagname: string
@param format: the specific format for which the flag should be
unset.
@type format: string
@param version: the specific version for which the flag should be
unset. If not specified the last version will be used.
@type version: integer
"""
if version is None:
version = self.get_latest_version()
format = normalize_format(format)
self.more_info.unset_flag(flagname, format, version)
self.touch()
self._build_file_list('init')
def get_comment(self, format, version=None):
"""
Retrieve the comment of a specific format/version of the document.
@param format: the specific format for which the comment should be
retrieved.
@type format: string
@param version: the specific version for which the comment should be
retrieved. If not specified the last version will be used.
@type version: integer
@return: the comment.
@rtype: string
"""
if version is None:
version = self.get_latest_version()
format = normalize_format(format)
return self.more_info.get_comment(format, version)
def get_description(self, format, version=None):
"""
Retrieve the description of a specific format/version of the document.
@param format: the specific format for which the description should be
retrieved.
@type format: string
@param version: the specific version for which the description should
be retrieved. If not specified the last version will be used.
@type version: integer
@return: the description.
@rtype: string
"""
if version is None:
version = self.get_latest_version()
format = normalize_format(format)
return self.more_info.get_description(format, version)
def hidden_p(self, format, version=None):
"""
Returns True if the file specified by the given format/version is
hidden.
@param format: the specific format for which the description should be
retrieved.
@type format: string
@param version: the specific version for which the description should
be retrieved. If not specified the last version will be used.
@type version: integer
@return: True if hidden.
@rtype: bool
"""
if version is None:
version = self.get_latest_version()
return self.more_info.has_flag('HIDDEN', format, version)
def get_docname(self):
"""
@return: the name of this document.
@rtype: string
"""
return self.docname
def get_base_dir(self):
"""
@return: the base directory on the local filesystem for this document
(e.g. C{/soft/cdsweb/var/data/files/g0/123})
@rtype: string
"""
return self.basedir
def get_type(self):
"""
@return: the type of this document.
@rtype: string"""
return self.doctype
def get_recid(self):
"""
@return: the record id of the record to which this document is
attached.
@rtype: integer
"""
return self.recid
def get_id(self):
"""
@return: the id of this document.
@rtype: integer
"""
return self.id
def pdf_a_p(self):
"""
@return: True if this document contains a PDF in PDF/A format.
@rtype: bool"""
return self.has_flag('PDF/A', 'pdf')
def has_text(self, require_up_to_date=False, version=None):
"""
Return True if the text of this document has already been extracted.
@param require_up_to_date: if True check the text was actually
extracted after the most recent format of the given version.
@type require_up_to_date: bool
@param version: a version for which the text should have been
extracted. If not specified the latest version is considered.
@type version: integer
@return: True if the text has already been extracted.
@rtype: bool
"""
if version is None:
version = self.get_latest_version()
if os.path.exists(os.path.join(self.basedir, '.text;%i' % version)):
if not require_up_to_date:
return True
else:
docfiles = self.list_version_files(version)
text_md = datetime.fromtimestamp(os.path.getmtime(os.path.join(self.basedir, '.text;%i' % version)))
for docfile in docfiles:
if text_md <= docfile.md:
return False
return True
return False
def get_file(self, format, version=""):
"""
Returns a L{BibDocFile} instance of this document corresponding to the
specific format and version.
@param format: the specific format.
@type format: string
@param version: the specific version for which the description should
be retrieved. If not specified the last version will be used.
@type version: integer
@return: the L{BibDocFile} instance.
@rtype: BibDocFile
"""
if version == "":
docfiles = self.list_latest_files()
else:
version = int(version)
docfiles = self.list_version_files(version)
format = normalize_format(format)
for docfile in docfiles:
if (docfile.get_format()==format or not format):
return docfile
## Let's skip the subformat specification and consider just the
## superformat
superformat = get_superformat_from_format(format)
for docfile in docfiles:
if get_superformat_from_format(docfile.get_format()) == superformat:
return docfile
raise InvenioWebSubmitFileError, "No file called '%s' of format '%s', version '%s'" % (self.docname, format, version)
def list_versions(self):
"""
@return: the list of existing version numbers for this document.
@rtype: list of integer
"""
versions = []
for docfile in self.docfiles:
if not docfile.get_version() in versions:
versions.append(docfile.get_version())
versions.sort()
return versions
def delete(self):
"""
Delete this document.
@see: L{undelete} for how to undelete the document.
@raise InvenioWebSubmitFileError: in case of errors.
"""
try:
today = datetime.today()
self.change_name('DELETED-%s%s-%s' % (today.strftime('%Y%m%d%H%M%S'), today.microsecond, self.docname))
run_sql("UPDATE bibdoc SET status='DELETED' WHERE id=%s", (self.id,))
self.status = 'DELETED'
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "It's impossible to delete bibdoc %s: %s" % (self.id, e)
def deleted_p(self):
"""
@return: True if this document has been deleted.
@rtype: bool
"""
return self.status == 'DELETED'
def empty_p(self):
"""
@return: True if this document is empty, i.e. it has no bibdocfile
connected.
@rtype: bool
"""
return len(self.docfiles) == 0
def undelete(self, previous_status=''):
"""
Undelete a deleted file (only if it was actually deleted via L{delete}).
The previous C{status}, i.e. the restriction key can be provided.
Otherwise the undeleted document will be public.
@param previous_status: the previous status the should be restored.
@type previous_status: string
@raise InvenioWebSubmitFileError: in case of any error.
"""
bibrecdocs = BibRecDocs(self.recid)
try:
run_sql("UPDATE bibdoc SET status=%s WHERE id=%s AND status='DELETED'", (previous_status, self.id))
except Exception, e:
raise InvenioWebSubmitFileError, "It's impossible to undelete bibdoc %s: %s" % (self.id, e)
if self.docname.startswith('DELETED-'):
try:
# Let's remove DELETED-20080214144322- in front of the docname
original_name = '-'.join(self.docname.split('-')[2:])
original_name = bibrecdocs.propose_unique_docname(original_name)
self.change_name(original_name)
except Exception, e:
raise InvenioWebSubmitFileError, "It's impossible to restore the previous docname %s. %s kept as docname because: %s" % (original_name, self.docname, e)
else:
raise InvenioWebSubmitFileError, "Strange just undeleted docname isn't called DELETED-somedate-docname but %s" % self.docname
def delete_file(self, format, version):
"""
Delete a specific format/version of this document on the filesystem.
@param format: the particular format to be deleted.
@type format: string
@param version: the particular version to be deleted.
@type version: integer
@note: this operation is not reversible!"""
try:
afile = self.get_file(format, version)
except InvenioWebSubmitFileError:
return
try:
os.remove(afile.get_full_path())
except OSError:
pass
self.touch()
self._build_file_list()
def get_history(self):
"""
@return: a human readable and parsable string that represent the
history of this document.
@rtype: string
"""
ret = []
hst = run_sql("""SELECT action, docname, docformat, docversion,
docsize, docchecksum, doctimestamp
FROM hstDOCUMENT
WHERE id_bibdoc=%s ORDER BY doctimestamp ASC""", (self.id, ))
for row in hst:
ret.append("%s %s '%s', format: '%s', version: %i, size: %s, checksum: '%s'" % (row[6].strftime('%Y-%m-%d %H:%M:%S'), row[0], row[1], row[2], row[3], nice_size(row[4]), row[5]))
return ret
def _build_file_list(self, context=''):
"""
Lists all files attached to the bibdoc. This function should be
called everytime the bibdoc is modified.
As a side effect it log everything that has happened to the bibdocfiles
in the log facility, according to the context:
"init": means that the function has been called;
for the first time by a constructor, hence no logging is performed
"": by default means to log every deleted file as deleted and every
added file as added;
"rename": means that every appearently deleted file is logged as
renamef and every new file as renamet.
"""
def log_action(action, docid, docname, format, version, size, checksum, timestamp=''):
"""Log an action into the bibdoclog table."""
try:
if timestamp:
run_sql('INSERT DELAYED INTO hstDOCUMENT(action, id_bibdoc, docname, docformat, docversion, docsize, docchecksum, doctimestamp) VALUES(%s, %s, %s, %s, %s, %s, %s, %s)', (action, docid, docname, format, version, size, checksum, timestamp))
else:
run_sql('INSERT DELAYED INTO hstDOCUMENT(action, id_bibdoc, docname, docformat, docversion, docsize, docchecksum, doctimestamp) VALUES(%s, %s, %s, %s, %s, %s, %s, NOW())', (action, docid, docname, format, version, size, checksum))
except DatabaseError:
register_exception()
def make_removed_added_bibdocfiles(previous_file_list):
"""Internal function for build the log of changed files."""
# Let's rebuild the previous situation
old_files = {}
for bibdocfile in previous_file_list:
old_files[(bibdocfile.name, bibdocfile.format, bibdocfile.version)] = (bibdocfile.size, bibdocfile.checksum, bibdocfile.md)
# Let's rebuild the new situation
new_files = {}
for bibdocfile in self.docfiles:
new_files[(bibdocfile.name, bibdocfile.format, bibdocfile.version)] = (bibdocfile.size, bibdocfile.checksum, bibdocfile.md)
# Let's subtract from added file all the files that are present in
# the old list, and let's add to deleted files that are not present
# added file.
added_files = dict(new_files)
deleted_files = {}
for key, value in old_files.iteritems():
if added_files.has_key(key):
del added_files[key]
else:
deleted_files[key] = value
return (added_files, deleted_files)
if context != 'init':
previous_file_list = list(self.docfiles)
res = run_sql("SELECT status,docname,creation_date,"
"modification_date,more_info FROM bibdoc WHERE id=%s", (self.id,))
self.cd = res[0][2]
self.md = res[0][3]
self.docname = res[0][1]
self.status = res[0][0]
self.more_info = BibDocMoreInfo(self.id, blob_to_string(res[0][4]))
self.docfiles = []
if os.path.exists(self.basedir):
self.md5s = Md5Folder(self.basedir)
files = os.listdir(self.basedir)
files.sort()
for afile in files:
if not afile.startswith('.'):
try:
filepath = os.path.join(self.basedir, afile)
dirname, basename, format, fileversion = decompose_file_with_version(filepath)
checksum = self.md5s.get_checksum(afile)
# we can append file:
self.docfiles.append(BibDocFile(filepath, self.doctype,
fileversion, basename, format,
self.recid, self.id, self.status, checksum,
self.more_info, human_readable=self.human_readable))
except Exception, e:
register_exception()
if context == 'init':
return
else:
added_files, deleted_files = make_removed_added_bibdocfiles(previous_file_list)
deletedstr = "DELETED"
addedstr = "ADDED"
if context == 'rename':
deletedstr = "RENAMEDFROM"
addedstr = "RENAMEDTO"
for (docname, format, version), (size, checksum, md) in added_files.iteritems():
if context == 'rename':
md = '' # No modification time
log_action(addedstr, self.id, docname, format, version, size, checksum, md)
for (docname, format, version), (size, checksum, md) in deleted_files.iteritems():
if context == 'rename':
md = '' # No modification time
log_action(deletedstr, self.id, docname, format, version, size, checksum, md)
def _build_related_file_list(self):
"""Lists all files attached to the bibdoc. This function should be
called everytime the bibdoc is modified within e.g. its icon.
@deprecated: use subformats instead.
"""
self.related_files = {}
res = run_sql("SELECT ln.id_bibdoc2,ln.type,bibdoc.status FROM "
"bibdoc_bibdoc AS ln,bibdoc WHERE id=ln.id_bibdoc2 AND "
"ln.id_bibdoc1=%s", (self.id,))
for row in res:
docid = row[0]
doctype = row[1]
if row[2] != 'DELETED':
if not self.related_files.has_key(doctype):
self.related_files[doctype] = []
cur_doc = BibDoc(docid=docid, human_readable=self.human_readable)
self.related_files[doctype].append(cur_doc)
def get_total_size_latest_version(self):
"""Return the total size used on disk of all the files belonging
to this bibdoc and corresponding to the latest version."""
ret = 0
for bibdocfile in self.list_latest_files():
ret += bibdocfile.get_size()
return ret
def get_total_size(self):
"""Return the total size used on disk of all the files belonging
to this bibdoc."""
ret = 0
for bibdocfile in self.list_all_files():
ret += bibdocfile.get_size()
return ret
def list_all_files(self, list_hidden=True):
"""Returns all the docfiles linked with the given bibdoc."""
if list_hidden:
return self.docfiles
else:
return [afile for afile in self.docfiles if not afile.hidden_p()]
def list_latest_files(self, list_hidden=True):
"""Returns all the docfiles within the last version."""
return self.list_version_files(self.get_latest_version(), list_hidden=list_hidden)
def list_version_files(self, version, list_hidden=True):
"""Return all the docfiles of a particular version."""
version = int(version)
return [docfile for docfile in self.docfiles if docfile.get_version() == version and (list_hidden or not docfile.hidden_p())]
def get_latest_version(self):
""" Returns the latest existing version number for the given bibdoc.
If no file is associated to this bibdoc, returns '0'.
"""
version = 0
for bibdocfile in self.docfiles:
if bibdocfile.get_version() > version:
version = bibdocfile.get_version()
return version
def get_file_number(self):
"""Return the total number of files."""
return len(self.docfiles)
def register_download(self, ip_address, version, format, userid=0):
"""Register the information about a download of a particular file."""
format = normalize_format(format)
if format[:1] == '.':
format = format[1:]
format = format.upper()
return run_sql("INSERT INTO rnkDOWNLOADS "
"(id_bibrec,id_bibdoc,file_version,file_format,"
"id_user,client_host,download_time) VALUES "
"(%s,%s,%s,%s,%s,INET_ATON(%s),NOW())",
(self.recid, self.id, version, format,
userid, ip_address,))
class BibDocFile:
"""This class represents a physical file in the Invenio filesystem.
It should never be instantiated directly"""
def __init__(self, fullpath, doctype, version, name, format, recid, docid, status, checksum, more_info, human_readable=False):
self.fullpath = os.path.abspath(fullpath)
self.doctype = doctype
self.docid = docid
self.recid = recid
self.version = version
self.status = status
self.checksum = checksum
self.human_readable = human_readable
self.description = more_info.get_description(format, version)
self.format = normalize_format(format)
self.superformat = get_superformat_from_format(self.format)
self.subformat = get_subformat_from_format(self.format)
if format == "":
self.mime = "application/octet-stream"
self.encoding = ""
self.fullname = name
else:
self.fullname = "%s%s" % (name, self.superformat)
(self.mime, self.encoding) = _mimes.guess_type(self.fullname)
if self.mime is None:
self.mime = "application/octet-stream"
self.more_info = more_info
self.comment = more_info.get_comment(format, version)
self.flags = more_info.get_flags(format, version)
self.hidden = 'HIDDEN' in self.flags
self.size = os.path.getsize(fullpath)
self.md = datetime.fromtimestamp(os.path.getmtime(fullpath))
try:
self.cd = datetime.fromtimestamp(os.path.getctime(fullpath))
except OSError:
self.cd = self.md
self.name = name
self.dir = os.path.dirname(fullpath)
if self.subformat:
- self.url = create_url('%s/record/%s/files/%s%s' % (CFG_SITE_URL, self.recid, self.name, self.superformat), {'subformat' : self.subformat})
- self.fullurl = create_url('%s/record/%s/files/%s%s' % (CFG_SITE_URL, self.recid, self.name, self.superformat), {'subformat' : self.subformat, 'version' : self.version})
+ self.url = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, self.name, self.superformat), {'subformat' : self.subformat})
+ self.fullurl = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, self.name, self.superformat), {'subformat' : self.subformat, 'version' : self.version})
else:
- self.url = create_url('%s/record/%s/files/%s%s' % (CFG_SITE_URL, self.recid, self.name, self.superformat), {})
- self.fullurl = create_url('%s/record/%s/files/%s%s' % (CFG_SITE_URL, self.recid, self.name, self.superformat), {'version' : self.version})
+ self.url = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, self.name, self.superformat), {})
+ self.fullurl = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, self.name, self.superformat), {'version' : self.version})
self.etag = '"%i%s%i"' % (self.docid, self.format, self.version)
self.magic = None
def __repr__(self):
return ('BibDocFile(%s, %s, %i, %s, %s, %i, %i, %s, %s, %s, %s)' % (repr(self.fullpath), repr(self.doctype), self.version, repr(self.name), repr(self.format), self.recid, self.docid, repr(self.status), repr(self.checksum), repr(self.more_info), repr(self.human_readable)))
def __str__(self):
out = '%s:%s:%s:%s:fullpath=%s\n' % (self.recid, self.docid, self.version, self.format, self.fullpath)
out += '%s:%s:%s:%s:fullname=%s\n' % (self.recid, self.docid, self.version, self.format, self.fullname)
out += '%s:%s:%s:%s:name=%s\n' % (self.recid, self.docid, self.version, self.format, self.name)
out += '%s:%s:%s:%s:subformat=%s\n' % (self.recid, self.docid, self.version, self.format, get_subformat_from_format(self.format))
out += '%s:%s:%s:%s:status=%s\n' % (self.recid, self.docid, self.version, self.format, self.status)
out += '%s:%s:%s:%s:checksum=%s\n' % (self.recid, self.docid, self.version, self.format, self.checksum)
if self.human_readable:
out += '%s:%s:%s:%s:size=%s\n' % (self.recid, self.docid, self.version, self.format, nice_size(self.size))
else:
out += '%s:%s:%s:%s:size=%s\n' % (self.recid, self.docid, self.version, self.format, self.size)
out += '%s:%s:%s:%s:creation time=%s\n' % (self.recid, self.docid, self.version, self.format, self.cd)
out += '%s:%s:%s:%s:modification time=%s\n' % (self.recid, self.docid, self.version, self.format, self.md)
out += '%s:%s:%s:%s:magic=%s\n' % (self.recid, self.docid, self.version, self.format, self.get_magic())
out += '%s:%s:%s:%s:mime=%s\n' % (self.recid, self.docid, self.version, self.format, self.mime)
out += '%s:%s:%s:%s:encoding=%s\n' % (self.recid, self.docid, self.version, self.format, self.encoding)
out += '%s:%s:%s:%s:url=%s\n' % (self.recid, self.docid, self.version, self.format, self.url)
out += '%s:%s:%s:%s:fullurl=%s\n' % (self.recid, self.docid, self.version, self.format, self.fullurl)
out += '%s:%s:%s:%s:description=%s\n' % (self.recid, self.docid, self.version, self.format, self.description)
out += '%s:%s:%s:%s:comment=%s\n' % (self.recid, self.docid, self.version, self.format, self.comment)
out += '%s:%s:%s:%s:hidden=%s\n' % (self.recid, self.docid, self.version, self.format, self.hidden)
out += '%s:%s:%s:%s:flags=%s\n' % (self.recid, self.docid, self.version, self.format, self.flags)
out += '%s:%s:%s:%s:etag=%s\n' % (self.recid, self.docid, self.version, self.format, self.etag)
return out
def display(self, ln = CFG_SITE_LANG):
"""Returns a formatted representation of this docfile."""
return websubmit_templates.tmpl_bibdocfile_filelist(
ln = ln,
recid = self.recid,
version = self.version,
md = self.md,
name = self.name,
superformat = self.superformat,
subformat = self.subformat,
nice_size = nice_size(self.size),
description = self.description or ''
)
def is_restricted(self, user_info):
"""Returns restriction state. (see acc_authorize_action return values)"""
if self.status not in ('', 'DELETED'):
return check_bibdoc_authorization(user_info, status=self.status)
elif self.status == 'DELETED':
return (1, 'File has ben deleted')
else:
return (0, '')
def is_icon(self, subformat_re=CFG_WEBSUBMIT_ICON_SUBFORMAT_RE):
"""
@param subformat_re: by default the convention is that
L{CFG_WEBSUBMIT_ICON_SUBFORMAT_RE} is used as a subformat indicator to
mean that a particular format is to be used as an icon.
Specifiy a different subformat if you need to use a different
convention.
@type subformat: compiled regular expression
@return: True if this file is an icon.
@rtype: bool
"""
return bool(subformat_re.match(self.subformat))
def hidden_p(self):
return self.hidden
def get_url(self):
return self.url
def get_type(self):
return self.doctype
def get_path(self):
return self.fullpath
def get_bibdocid(self):
return self.docid
def get_name(self):
return self.name
def get_full_name(self):
return self.fullname
def get_full_path(self):
return self.fullpath
def get_format(self):
return self.format
def get_subformat(self):
return self.subformat
def get_superformat(self):
return self.superformat
def get_size(self):
return self.size
def get_version(self):
return self.version
def get_checksum(self):
return self.checksum
def get_description(self):
return self.description
def get_comment(self):
return self.comment
def get_content(self):
"""Returns the binary content of the file."""
content_fd = open(self.fullpath, 'rb')
content = content_fd.read()
content_fd.close()
return content
def get_recid(self):
"""Returns the recid connected with the bibdoc of this file."""
return self.recid
def get_status(self):
"""Returns the status of the file, i.e. either '', 'DELETED' or a
restriction keyword."""
return self.status
def get_magic(self):
"""Return all the possible guesses from the magic library about
the content of the file."""
if self.magic is None and CFG_HAS_MAGIC:
magic_cookies = _get_magic_cookies()
magic_result = []
for key in magic_cookies.keys():
magic_result.append(magic_cookies[key].file(self.fullpath))
self.magic = tuple(magic_result)
return self.magic
def check(self):
"""Return True if the checksum corresponds to the file."""
return calculate_md5(self.fullpath) == self.checksum
def stream(self, req):
"""Stream the file. Note that no restriction check is being
done here, since restrictions have been checked previously
inside websubmit_webinterface.py."""
if os.path.exists(self.fullpath):
if random.random() < CFG_BIBDOCFILE_MD5_CHECK_PROBABILITY and calculate_md5(self.fullpath) != self.checksum:
raise InvenioWebSubmitFileError, "File %s, version %i, for record %s is corrupted!" % (self.fullname, self.version, self.recid)
stream_file(req, self.fullpath, "%s%s" % (self.name, self.superformat), self.mime, self.encoding, self.etag, self.checksum, self.fullurl)
raise apache.SERVER_RETURN, apache.DONE
else:
req.status = apache.HTTP_NOT_FOUND
raise InvenioWebSubmitFileError, "%s does not exists!" % self.fullpath
_RE_STATUS_PARSER = re.compile(r'^(?P<type>email|group|egroup|role|firerole|status):\s*(?P<value>.*)$', re.S + re.I)
def check_bibdoc_authorization(user_info, status):
"""
Check if the user is authorized to access a document protected with the given status.
L{status} is a string of the form::
auth_type: auth_value
where C{auth_type} can have values in::
email, group, role, firerole, status
and C{auth_value} has a value interpreted againsta C{auth_type}:
- C{email}: the user can access the document if his/her email matches C{auth_value}
- C{group}: the user can access the document if one of the groups (local or
external) of which he/she is member matches C{auth_value}
- C{role}: the user can access the document if he/she belongs to the WebAccess
role specified in C{auth_value}
- C{firerole}: the user can access the document if he/she is implicitly matched
by the role described by the firewall like role definition in C{auth_value}
- C{status}: the user can access the document if he/she is authorized to
for the action C{viewrestrdoc} with C{status} paramter having value
C{auth_value}
@note: If no C{auth_type} is specified or if C{auth_type} is not one of the
above, C{auth_value} will be set to the value contained in the
parameter C{status}, and C{auth_type} will be considered to be C{status}.
@param user_info: the user_info dictionary
@type: dict
@param status: the status of the document.
@type status: string
@return: a tuple, of the form C{(auth_code, auth_message)} where auth_code is 0
if the authorization is granted and greater than 0 otherwise.
@rtype: (int, string)
@raise ValueError: in case of unexpected parsing error.
"""
def parse_status(status):
g = _RE_STATUS_PARSER.match(status)
if g:
return (g.group('type').lower(), g.group('value'))
else:
return ('status', status)
if acc_is_user_in_role(user_info, acc_get_role_id(SUPERADMINROLE)):
return (0, CFG_WEBACCESS_WARNING_MSGS[0])
auth_type, auth_value = parse_status(status)
if auth_type == 'status':
return acc_authorize_action(user_info, 'viewrestrdoc', status=auth_value)
elif auth_type == 'email':
if not auth_value.lower().strip() == user_info['email'].lower().strip():
return (1, 'You must be member of the group %s in order to access this document' % repr(auth_value))
elif auth_type == 'group':
if not auth_value in user_info['group']:
return (1, 'You must be member of the group %s in order to access this document' % repr(auth_value))
elif auth_type == 'role':
if not acc_is_user_in_role(user_info, acc_get_role_id(auth_value)):
return (1, 'You must be member in the role %s in order to access this document' % repr(auth_value))
elif auth_type == 'firerole':
if not acc_firerole_check_user(user_info, compile_role_definition(auth_value)):
return (1, 'You must be authorized in order to access this document')
else:
raise ValueError, 'Unexpected authorization type %s for %s' % (repr(auth_type), repr(auth_value))
return (0, CFG_WEBACCESS_WARNING_MSGS[0])
def stream_file(req, fullpath, fullname=None, mime=None, encoding=None, etag=None, md5=None, location=None):
"""This is a generic function to stream a file to the user.
If fullname, mime, encoding, and location are not provided they will be
guessed based on req and fullpath.
md5 should be passed as an hexadecimal string.
"""
def normal_streaming(size):
req.set_content_length(size)
req.send_http_header()
if not req.header_only:
req.sendfile(fullpath)
return ""
def single_range(size, the_range):
req.set_content_length(the_range[1])
req.headers_out['Content-Range'] = 'bytes %d-%d/%d' % (the_range[0], the_range[0] + the_range[1] - 1, size)
req.status = apache.HTTP_PARTIAL_CONTENT
req.send_http_header()
if not req.header_only:
req.sendfile(fullpath, the_range[0], the_range[1])
return ""
def multiple_ranges(size, ranges, mime):
req.status = apache.HTTP_PARTIAL_CONTENT
boundary = '%s%04d' % (time.strftime('THIS_STRING_SEPARATES_%Y%m%d%H%M%S'), random.randint(0, 9999))
req.content_type = 'multipart/byteranges; boundary=%s' % boundary
content_length = 0
for arange in ranges:
content_length += len('--%s\r\n' % boundary)
content_length += len('Content-Type: %s\r\n' % mime)
content_length += len('Content-Range: bytes %d-%d/%d\r\n' % (arange[0], arange[0] + arange[1] - 1, size))
content_length += len('\r\n')
content_length += arange[1]
content_length += len('\r\n')
content_length += len('--%s--\r\n' % boundary)
req.set_content_length(content_length)
req.send_http_header()
if not req.header_only:
for arange in ranges:
req.write('--%s\r\n' % boundary, 0)
req.write('Content-Type: %s\r\n' % mime, 0)
req.write('Content-Range: bytes %d-%d/%d\r\n' % (arange[0], arange[0] + arange[1] - 1, size), 0)
req.write('\r\n', 0)
req.sendfile(fullpath, arange[0], arange[1])
req.write('\r\n', 0)
req.write('--%s--\r\n' % boundary)
req.flush()
return ""
def parse_date(date):
"""According to <http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3>
a date can come in three formats (in order of preference):
Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
Moreover IE is adding some trailing information after a ';'.
Wrong dates should be simpled ignored.
This function return the time in seconds since the epoch GMT or None
in case of errors."""
if not date:
return None
try:
date = date.split(';')[0].strip() # Because of IE
## Sun, 06 Nov 1994 08:49:37 GMT
return time.mktime(time.strptime(date, '%a, %d %b %Y %X %Z'))
except:
try:
## Sun, 06 Nov 1994 08:49:37 GMT
return time.mktime(time.strptime(date, '%A, %d-%b-%y %H:%M:%S %Z'))
except:
try:
## Sun, 06 Nov 1994 08:49:37 GMT
return time.mktime(date)
except:
return None
def parse_ranges(ranges):
"""According to <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
a (multiple) range request comes in the form:
bytes=20-30,40-60,70-,-80
with the meaning:
from byte to 20 to 30 inclusive (11 bytes)
from byte to 40 to 60 inclusive (21 bytes)
from byte 70 to (size - 1) inclusive (size - 70 bytes)
from byte size - 80 to (size - 1) inclusive (80 bytes)
This function will return the list of ranges in the form:
[[first_byte, last_byte], ...]
If first_byte or last_byte aren't specified they'll be set to None
If the list is not well formatted it will return None
"""
try:
if ranges.startswith('bytes') and '=' in ranges:
ranges = ranges.split('=')[1].strip()
else:
return None
ret = []
for arange in ranges.split(','):
arange = arange.strip()
if arange.startswith('-'):
ret.append([None, int(arange[1:])])
elif arange.endswith('-'):
ret.append([int(arange[:-1]), None])
else:
ret.append(map(int, arange.split('-')))
return ret
except:
return None
def parse_tags(tags):
"""Return a list of tags starting from a comma separated list."""
return [tag.strip() for tag in tags.split(',')]
def fix_ranges(ranges, size):
"""Complementary to parse_ranges it will transform all the ranges
into (first_byte, length), adjusting all the value based on the
actual size provided.
"""
ret = []
for arange in ranges:
if (arange[0] is None and arange[1] > 0) or arange[0] < size:
if arange[0] is None:
arange[0] = size - arange[1]
elif arange[1] is None:
arange[1] = size - arange[0]
else:
arange[1] = arange[1] - arange[0] + 1
arange[0] = max(0, arange[0])
arange[1] = min(size - arange[0], arange[1])
if arange[1] > 0:
ret.append(arange)
return ret
def get_normalized_headers(headers):
"""Strip and lowerize all the keys of the headers dictionary plus
strip, lowerize and transform known headers value into their value."""
ret = {
'if-match' : None,
'unless-modified-since' : None,
'if-modified-since' : None,
'range' : None,
'if-range' : None,
'if-none-match' : None,
}
for key, value in req.headers_in.iteritems():
key = key.strip().lower()
value = value.strip()
if key in ('unless-modified-since', 'if-modified-since'):
value = parse_date(value)
elif key == 'range':
value = parse_ranges(value)
elif key == 'if-range':
value = parse_date(value) or parse_tags(value)
elif key in ('if-match', 'if-none-match'):
value = parse_tags(value)
if value:
ret[key] = value
return ret
if CFG_BIBDOCFILE_USE_XSENDFILE:
## If XSendFile is supported by the server, let's use it.
if os.path.exists(fullpath):
if fullname is None:
fullname = os.path.basename(fullpath)
req.headers_out["Content-Disposition"] = 'inline; filename="%s"' % fullname.replace('"', '\\"')
req.headers_out["X-Sendfile"] = fullpath
if mime is None:
format = decompose_file(fullpath)[2]
(mime, encoding) = _mimes.guess_type(fullpath)
if mime is None:
mime = "application/octet-stream"
req.content_type = mime
return ""
else:
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
headers = get_normalized_headers(req.headers_in)
if headers['if-match']:
if etag is not None and etag not in headers['if-match']:
raise apache.SERVER_RETURN, apache.HTTP_PRECONDITION_FAILED
if os.path.exists(fullpath):
mtime = os.path.getmtime(fullpath)
if fullname is None:
fullname = os.path.basename(fullpath)
if mime is None:
(mime, encoding) = _mimes.guess_type(fullpath)
if mime is None:
mime = "application/octet-stream"
if location is None:
location = req.uri
req.content_type = mime
req.encoding = encoding
req.filename = fullname
req.headers_out["Last-Modified"] = time.strftime('%a, %d %b %Y %X GMT', time.gmtime(mtime))
if CFG_ENABLE_HTTP_RANGE_REQUESTS:
req.headers_out["Accept-Ranges"] = "bytes"
else:
req.headers_out["Accept-Ranges"] = "none"
req.headers_out["Content-Location"] = location
if etag is not None:
req.headers_out["ETag"] = etag
if md5 is not None:
req.headers_out["Content-MD5"] = base64.encodestring(binascii.unhexlify(md5.upper()))[:-1]
req.headers_out["Content-Disposition"] = 'inline; filename="%s"' % fullname.replace('"', '\\"')
size = os.path.getsize(fullpath)
if not size:
try:
raise Exception, '%s exists but is empty' % fullpath
except Exception:
register_exception(req=req, alert_admin=True)
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
if headers['if-modified-since'] and headers['if-modified-since'] >= mtime:
raise apache.SERVER_RETURN, apache.HTTP_NOT_MODIFIED
if headers['if-none-match']:
if etag is not None and etag in headers['if-none-match']:
raise apache.SERVER_RETURN, apache.HTTP_NOT_MODIFIED
if headers['unless-modified-since'] and headers['unless-modified-since'] < mtime:
return normal_streaming(size)
if CFG_ENABLE_HTTP_RANGE_REQUESTS and headers['range']:
try:
if headers['if-range']:
if etag is None or etag not in headers['if-range']:
return normal_streaming(size)
ranges = fix_ranges(headers['range'], size)
except:
return normal_streaming(size)
if len(ranges) > 1:
return multiple_ranges(size, ranges, mime)
elif ranges:
return single_range(size, ranges[0])
else:
raise apache.SERVER_RETURN, apache.HTTP_RANGE_NOT_SATISFIABLE
else:
return normal_streaming(size)
else:
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
def stream_restricted_icon(req):
"""Return the content of the "Restricted Icon" file."""
stream_file(req, '%s/img/restricted.gif' % CFG_WEBDIR)
raise apache.SERVER_RETURN, apache.DONE
def list_types_from_array(bibdocs):
"""Retrieves the list of types from the given bibdoc list."""
types = []
for bibdoc in bibdocs:
if not bibdoc.get_type() in types:
types.append(bibdoc.get_type())
types.sort()
if 'Main' in types:
## Move 'Main' at the beginning
types.remove('Main')
types.insert(0, 'Main')
return types
def list_versions_from_array(docfiles):
"""Retrieve the list of existing versions from the given docfiles list."""
versions = []
for docfile in docfiles:
if not docfile.get_version() in versions:
versions.append(docfile.get_version())
versions.sort()
versions.reverse()
return versions
def _make_base_dir(docid):
"""Given a docid it returns the complete path that should host its files."""
group = "g" + str(int(int(docid) / CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT))
return os.path.join(CFG_WEBSUBMIT_FILEDIR, group, str(docid))
class Md5Folder:
"""Manage all the Md5 checksum about a folder"""
def __init__(self, folder):
"""Initialize the class from the md5 checksum of a given path"""
self.folder = folder
try:
self.load()
except InvenioWebSubmitFileError:
self.md5s = {}
self.update()
def update(self, only_new = True):
"""Update the .md5 file with the current files. If only_new
is specified then only not already calculated file are calculated."""
if not only_new:
self.md5s = {}
if os.path.exists(self.folder):
for filename in os.listdir(self.folder):
if filename not in self.md5s and not filename.startswith('.'):
self.md5s[filename] = calculate_md5(os.path.join(self.folder, filename))
self.store()
def store(self):
"""Store the current md5 dictionary into .md5"""
try:
old_umask = os.umask(022)
md5file = open(os.path.join(self.folder, ".md5"), "w")
for key, value in self.md5s.items():
md5file.write('%s *%s\n' % (value, key))
md5file.close()
os.umask(old_umask)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while storing .md5 for folder '%s': '%s'" % (self.folder, e)
def load(self):
"""Load .md5 into the md5 dictionary"""
self.md5s = {}
try:
md5file = open(os.path.join(self.folder, ".md5"), "r")
for row in md5file:
md5hash = row[:32]
filename = row[34:].strip()
self.md5s[filename] = md5hash
md5file.close()
except IOError:
self.update()
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while loading .md5 for folder '%s': '%s'" % (self.folder, e)
def check(self, filename = ''):
"""Check the specified file or all the files for which it exists a hash
for being coherent with the stored hash."""
if filename and filename in self.md5s.keys():
try:
return self.md5s[filename] == calculate_md5(os.path.join(self.folder, filename))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while loading '%s': '%s'" % (os.path.join(self.folder, filename), e)
else:
for filename, md5hash in self.md5s.items():
try:
if calculate_md5(os.path.join(self.folder, filename)) != md5hash:
return False
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while loading '%s': '%s'" % (os.path.join(self.folder, filename), e)
return True
def get_checksum(self, filename):
"""Return the checksum of a physical file."""
md5hash = self.md5s.get(filename, None)
if md5hash is None:
self.update()
# Now it should not fail!
md5hash = self.md5s[filename]
return md5hash
def calculate_md5_external(filename):
"""Calculate the md5 of a physical file through md5sum Command Line Tool.
This is suitable for file larger than 256Kb."""
try:
md5_result = os.popen(CFG_PATH_MD5SUM + ' -b %s' % escape_shell_arg(filename))
ret = md5_result.read()[:32]
md5_result.close()
if len(ret) != 32:
# Error in running md5sum. Let's fallback to internal
# algorithm.
return calculate_md5(filename, force_internal=True)
else:
return ret
except Exception, e:
raise InvenioWebSubmitFileError, "Encountered an exception while calculating md5 for file '%s': '%s'" % (filename, e)
def calculate_md5(filename, force_internal=False):
"""Calculate the md5 of a physical file. This is suitable for files smaller
than 256Kb."""
if not CFG_PATH_MD5SUM or force_internal or os.path.getsize(filename) < CFG_BIBDOCFILE_MD5_THRESHOLD:
try:
to_be_read = open(filename, "rb")
computed_md5 = md5()
while True:
buf = to_be_read.read(CFG_BIBDOCFILE_MD5_BUFFER)
if buf:
computed_md5.update(buf)
else:
break
to_be_read.close()
return computed_md5.hexdigest()
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while calculating md5 for file '%s': '%s'" % (filename, e)
else:
return calculate_md5_external(filename)
def bibdocfile_url_to_bibrecdocs(url):
- """Given an URL in the form CFG_SITE_[SECURE_]URL/record/xxx/files/... it returns
+ """Given an URL in the form CFG_SITE_[SECURE_]URL/CFG_SITE_RECORD/xxx/files/... it returns
a BibRecDocs object for the corresponding recid."""
recid = decompose_bibdocfile_url(url)[0]
return BibRecDocs(recid)
def bibdocfile_url_to_bibdoc(url):
- """Given an URL in the form CFG_SITE_[SECURE_]URL/record/xxx/files/... it returns
+ """Given an URL in the form CFG_SITE_[SECURE_]URL/CFG_SITE_RECORD/xxx/files/... it returns
a BibDoc object for the corresponding recid/docname."""
docname = decompose_bibdocfile_url(url)[1]
return bibdocfile_url_to_bibrecdocs(url).get_bibdoc(docname)
def bibdocfile_url_to_bibdocfile(url):
- """Given an URL in the form CFG_SITE_[SECURE_]URL/record/xxx/files/... it returns
+ """Given an URL in the form CFG_SITE_[SECURE_]URL/CFG_SITE_RECORD/xxx/files/... it returns
a BibDocFile object for the corresponding recid/docname/format."""
dummy, dummy, format = decompose_bibdocfile_url(url)
return bibdocfile_url_to_bibdoc(url).get_file(format)
def bibdocfile_url_to_fullpath(url):
- """Given an URL in the form CFG_SITE_[SECURE_]URL/record/xxx/files/... it returns
+ """Given an URL in the form CFG_SITE_[SECURE_]URL/CFG_SITE_RECORD/xxx/files/... it returns
the fullpath for the corresponding recid/docname/format."""
return bibdocfile_url_to_bibdocfile(url).get_full_path()
def bibdocfile_url_p(url):
"""Return True when the url is a potential valid url pointing to a
fulltext owned by a system."""
if url.startswith('%s/getfile.py' % CFG_SITE_URL) or url.startswith('%s/getfile.py' % CFG_SITE_SECURE_URL):
return True
- if not (url.startswith('%s/record/' % CFG_SITE_URL) or url.startswith('%s/record/' % CFG_SITE_SECURE_URL)):
+ if not (url.startswith('%s/%s/' % (CFG_SITE_URL, CFG_SITE_RECORD)) or url.startswith('%s/%s/' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD))):
return False
splitted_url = url.split('/files/')
return len(splitted_url) == 2 and splitted_url[0] != '' and splitted_url[1] != ''
def get_docid_from_bibdocfile_fullpath(fullpath):
"""Given a bibdocfile fullpath (e.g. "CFG_WEBSUBMIT_FILEDIR/g0/123/bar.pdf;1")
returns the docid (e.g. 123)."""
if not fullpath.startswith(os.path.join(CFG_WEBSUBMIT_FILEDIR, 'g')):
raise InvenioWebSubmitFileError, "Fullpath %s doesn't correspond to a valid bibdocfile fullpath" % fullpath
dirname, base, extension, version = decompose_file_with_version(fullpath)
try:
return int(dirname.split('/')[-1])
except:
raise InvenioWebSubmitFileError, "Fullpath %s doesn't correspond to a valid bibdocfile fullpath" % fullpath
def decompose_bibdocfile_fullpath(fullpath):
"""Given a bibdocfile fullpath (e.g. "CFG_WEBSUBMIT_FILEDIR/g0/123/bar.pdf;1")
returns a quadruple (recid, docname, format, version)."""
if not fullpath.startswith(os.path.join(CFG_WEBSUBMIT_FILEDIR, 'g')):
raise InvenioWebSubmitFileError, "Fullpath %s doesn't correspond to a valid bibdocfile fullpath" % fullpath
dirname, base, extension, version = decompose_file_with_version(fullpath)
try:
docid = int(dirname.split('/')[-1])
bibdoc = BibDoc(docid)
recid = bibdoc.get_recid()
docname = bibdoc.get_docname()
return recid, docname, extension, version
except:
raise InvenioWebSubmitFileError, "Fullpath %s doesn't correspond to a valid bibdocfile fullpath" % fullpath
def decompose_bibdocfile_url(url):
"""Given a bibdocfile_url return a triple (recid, docname, format)."""
if url.startswith('%s/getfile.py' % CFG_SITE_URL) or url.startswith('%s/getfile.py' % CFG_SITE_SECURE_URL):
return decompose_bibdocfile_very_old_url(url)
- if url.startswith('%s/record/' % CFG_SITE_URL):
- recid_file = url[len('%s/record/' % CFG_SITE_URL):]
- elif url.startswith('%s/record/' % CFG_SITE_SECURE_URL):
- recid_file = url[len('%s/record/' % CFG_SITE_SECURE_URL):]
+ if url.startswith('%s/%s/' % (CFG_SITE_URL, CFG_SITE_RECORD)):
+ recid_file = url[len('%s/%s/' % (CFG_SITE_URL, CFG_SITE_RECORD)):]
+ elif url.startswith('%s/%s/' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD)):
+ recid_file = url[len('%s/%s/' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD)):]
else:
raise InvenioWebSubmitFileError, "Url %s doesn't correspond to a valid record inside the system." % url
recid_file = recid_file.replace('/files/', '/')
recid, docname, format = decompose_file(urllib.unquote(recid_file))
if not recid and docname.isdigit():
- ## If the URL was something similar to CFG_SITE_URL/record/123
+ ## If the URL was something similar to CFG_SITE_URL/CFG_SITE_RECORD/123
return (int(docname), '', '')
return (int(recid), docname, format)
-re_bibdocfile_old_url = re.compile(r'/record/(\d*)/files/')
+re_bibdocfile_old_url = re.compile(r'/%s/(\d*)/files/' % CFG_SITE_RECORD)
def decompose_bibdocfile_old_url(url):
- """Given a bibdocfile old url (e.g. CFG_SITE_URL/record/123/files)
+ """Given a bibdocfile old url (e.g. CFG_SITE_URL/CFG_SITE_RECORD/123/files)
it returns the recid."""
g = re_bibdocfile_old_url.search(url)
if g:
return int(g.group(1))
raise InvenioWebSubmitFileError('%s is not a valid old bibdocfile url' % url)
def decompose_bibdocfile_very_old_url(url):
"""Decompose an old /getfile.py? URL"""
if url.startswith('%s/getfile.py' % CFG_SITE_URL) or url.startswith('%s/getfile.py' % CFG_SITE_SECURE_URL):
params = urllib.splitquery(url)[1]
if params:
try:
params = cgi.parse_qs(params)
if 'docid' in params:
docid = int(params['docid'][0])
bibdoc = BibDoc(docid)
recid = bibdoc.get_recid()
docname = bibdoc.get_docname()
elif 'recid' in params:
recid = int(params['recid'][0])
if 'name' in params:
docname = params['name'][0]
else:
docname = ''
else:
raise InvenioWebSubmitFileError('%s has not enough params to correspond to a bibdocfile.' % url)
format = normalize_format(params.get('format', [''])[0])
return (recid, docname, format)
except Exception, e:
raise InvenioWebSubmitFileError('Problem with %s: %s' % (url, e))
else:
raise InvenioWebSubmitFileError('%s has no params to correspond to a bibdocfile.' % url)
else:
raise InvenioWebSubmitFileError('%s is not a valid very old bibdocfile url' % url)
def get_docname_from_url(url):
"""Return a potential docname given a url"""
path = urllib2.urlparse.urlsplit(urllib.unquote(url))[2]
filename = os.path.split(path)[-1]
return file_strip_ext(filename)
def get_format_from_url(url):
"""Return a potential format given a url"""
path = urllib2.urlparse.urlsplit(urllib.unquote(url))[2]
filename = os.path.split(path)[-1]
return filename[len(file_strip_ext(filename)):]
def clean_url(url):
"""Given a local url e.g. a local path it render it a realpath."""
protocol = urllib2.urlparse.urlsplit(url)[0]
if protocol in ('', 'file'):
path = urllib2.urlparse.urlsplit(urllib.unquote(url))[2]
return os.path.abspath(path)
else:
return url
def is_url_a_local_file(url):
"""Return True if the given URL is pointing to a local file."""
protocol = urllib2.urlparse.urlsplit(url)[0]
return protocol in ('', 'file')
def check_valid_url(url):
"""Check for validity of a url or a file."""
try:
if is_url_a_local_file(url):
path = urllib2.urlparse.urlsplit(urllib.unquote(url))[2]
if os.path.abspath(path) != path:
raise StandardError, "%s is not a normalized path (would be %s)." % (path, os.path.normpath(path))
for allowed_path in CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS + [CFG_TMPDIR, CFG_TMPSHAREDDIR, CFG_WEBSUBMIT_STORAGEDIR]:
if path.startswith(allowed_path):
dummy_fd = open(path)
dummy_fd.close()
return
raise StandardError, "%s is not in one of the allowed paths." % path
else:
urllib2.urlopen(url)
except Exception, e:
raise StandardError, "%s is not a correct url: %s" % (url, e)
def safe_mkstemp(suffix):
"""Create a temporary filename that don't have any '.' inside a part
from the suffix."""
tmpfd, tmppath = tempfile.mkstemp(suffix=suffix, dir=CFG_TMPDIR)
if '.' not in suffix:
# Just in case format is empty
return tmpfd, tmppath
while '.' in os.path.basename(tmppath)[:-len(suffix)]:
os.close(tmpfd)
os.remove(tmppath)
tmpfd, tmppath = tempfile.mkstemp(suffix=suffix, dir=CFG_TMPDIR)
return (tmpfd, tmppath)
def download_url(url, format=None, sleep=2):
"""Download a url (if it corresponds to a remote file) and return a local url
to it."""
if format is None:
format = decompose_file(url)[2]
else:
format = normalize_format(format)
protocol = urllib2.urlparse.urlsplit(url)[0]
tmpfd, tmppath = safe_mkstemp(format)
try:
try:
if protocol in ('', 'file'):
path = urllib2.urlparse.urlsplit(urllib.unquote(url))[2]
if os.path.abspath(path) != path:
raise StandardError, "%s is not a normalized path (would be %s)." % (path, os.path.normpath(path))
for allowed_path in CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS + [CFG_TMPDIR, CFG_TMPSHAREDDIR, CFG_WEBSUBMIT_STORAGEDIR]:
if path.startswith(allowed_path):
shutil.copy(path, tmppath)
if os.path.getsize(tmppath) > 0:
return tmppath
else:
raise StandardError, "%s seems to be empty" % url
raise StandardError, "%s is not in one of the allowed paths." % path
else:
try:
from_file = urllib2.urlopen(url)
to_file = open(tmppath, 'w')
while True:
block = from_file.read(CFG_BIBDOCFILE_BLOCK_SIZE)
if not block:
break
to_file.write(block)
to_file.close()
from_file.close()
except Exception, e:
raise StandardError, "Error when downloading %s into %s: %s" % (url, tmppath, e)
if os.path.getsize(tmppath) > 0:
return tmppath
else:
raise StandardError, "%s seems to be empty" % url
except:
os.remove(tmppath)
raise
finally:
os.close(tmpfd)
class BibDocMoreInfo:
"""
This class wraps contextual information of the documents, such as the
- comments
- descriptions
- flags.
Such information is kept separately per every format/version instance of
the corresponding document and is searialized in the database, ready
to be retrieved (but not searched).
@param docid: the document identifier.
@type docid: integer
@param more_info: a serialized version of an already existing more_info
object. If not specified this information will be readed from the
database, and othewise an empty dictionary will be allocated.
@raise ValueError: if docid is not a positive integer.
@ivar docid: the document identifier as passed to the constructor.
@type docid: integer
@ivar more_info: the more_info dictionary that will hold all the
additional document information.
@type more_info: dict of dict of dict
@note: in general this class is never instanciated in client code and
never used outside bibdocfile module.
@note: this class will be extended in the future to hold all the new auxiliary
information about a document.
"""
def __init__(self, docid, more_info=None):
if not (type(docid) in (long, int) and docid > 0):
raise ValueError("docid is not a positive integer, but %s." % docid)
self.docid = docid
if more_info is None:
res = run_sql('SELECT more_info FROM bibdoc WHERE id=%s', (docid, ))
if res and res[0][0]:
self.more_info = cPickle.loads(blob_to_string(res[0][0]))
else:
self.more_info = {}
else:
self.more_info = cPickle.loads(more_info)
if 'descriptions' not in self.more_info:
self.more_info['descriptions'] = {}
if 'comments' not in self.more_info:
self.more_info['comments'] = {}
if 'flags' not in self.more_info:
self.more_info['flags'] = {}
def __repr__(self):
"""
@return: the canonical string representation of the C{BibDocMoreInfo}.
@rtype: string
"""
return 'BibDocMoreInfo(%i, %s)' % (self.docid, repr(cPickle.dumps(self.more_info)))
def flush(self):
"""
Flush this object to the database.
"""
run_sql('UPDATE bibdoc SET more_info=%s WHERE id=%s', (cPickle.dumps(self.more_info), self.docid))
def set_flag(self, flagname, format, version):
"""
Sets a flag.
@param flagname: the flag to set (see
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}).
@type flagname: string
@param format: the format for which the flag should set.
@type format: string
@param version: the version for which the flag should set:
@type version: integer
@raise ValueError: if the flag is not in
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}
"""
if flagname in CFG_BIBDOCFILE_AVAILABLE_FLAGS:
if not flagname in self.more_info['flags']:
self.more_info['flags'][flagname] = {}
if not version in self.more_info['flags'][flagname]:
self.more_info['flags'][flagname][version] = {}
if not format in self.more_info['flags'][flagname][version]:
self.more_info['flags'][flagname][version][format] = {}
self.more_info['flags'][flagname][version][format] = True
self.flush()
else:
raise ValueError, "%s is not in %s" % (flagname, CFG_BIBDOCFILE_AVAILABLE_FLAGS)
def get_comment(self, format, version):
"""
Returns the specified comment.
@param format: the format for which the comment should be
retrieved.
@type format: string
@param version: the version for which the comment should be
retrieved.
@type version: integer
@return: the specified comment.
@rtype: string
"""
try:
assert(type(version) is int)
format = normalize_format(format)
return self.more_info['comments'].get(version, {}).get(format)
except:
register_exception()
raise
def get_description(self, format, version):
"""
Returns the specified description.
@param format: the format for which the description should be
retrieved.
@type format: string
@param version: the version for which the description should be
retrieved.
@type version: integer
@return: the specified description.
@rtype: string
"""
try:
assert(type(version) is int)
format = normalize_format(format)
return self.more_info['descriptions'].get(version, {}).get(format)
except:
register_exception()
raise
def has_flag(self, flagname, format, version):
"""
Return True if the corresponding has been set.
@param flagname: the name of the flag (see
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}).
@type flagname: string
@param format: the format for which the flag should be checked.
@type format: string
@param version: the version for which the flag should be checked.
@type version: integer
@return: True if the flag is set for the given format/version.
@rtype: bool
@raise ValueError: if the flagname is not in
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}
"""
if flagname in CFG_BIBDOCFILE_AVAILABLE_FLAGS:
return self.more_info['flags'].get(flagname, {}).get(version, {}).get(format, False)
else:
raise ValueError, "%s is not in %s" % (flagname, CFG_BIBDOCFILE_AVAILABLE_FLAGS)
def get_flags(self, format, version):
"""
Return the list of all the enabled flags.
@param format: the format for which the list should be returned.
@type format: string
@param version: the version for which the list should be returned.
@type version: integer
@return: the list of enabled flags (from
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}).
@rtype: list of string
"""
return [flag for flag in self.more_info['flags'] if format in self.more_info['flags'][flag].get(version, {})]
def set_comment(self, comment, format, version):
"""
Set a comment.
@param comment: the comment to be set.
@type comment: string
@param format: the format for which the comment should be set.
@type format: string
@param version: the version for which the comment should be set:
@type version: integer
"""
try:
assert(type(version) is int and version > 0)
format = normalize_format(format)
if comment == KEEP_OLD_VALUE:
comment = self.get_comment(format, version) or self.get_comment(format, version - 1)
if not comment:
self.unset_comment(format, version)
self.flush()
return
if not version in self.more_info['comments']:
self.more_info['comments'][version] = {}
self.more_info['comments'][version][format] = comment
self.flush()
except:
register_exception()
raise
def set_description(self, description, format, version):
"""
Set a description.
@param description: the description to be set.
@type description: string
@param format: the format for which the description should be set.
@type format: string
@param version: the version for which the description should be set:
@type version: integer
"""
try:
assert(type(version) is int and version > 0)
format = normalize_format(format)
if description == KEEP_OLD_VALUE:
description = self.get_description(format, version) or self.get_description(format, version - 1)
if not description:
self.unset_description(format, version)
self.flush()
return
if not version in self.more_info['descriptions']:
self.more_info['descriptions'][version] = {}
self.more_info['descriptions'][version][format] = description
self.flush()
except:
register_exception()
raise
def unset_comment(self, format, version):
"""
Unset a comment.
@param format: the format for which the comment should be unset.
@type format: string
@param version: the version for which the comment should be unset:
@type version: integer
"""
try:
assert(type(version) is int and version > 0)
del self.more_info['comments'][version][format]
self.flush()
except KeyError:
pass
except:
register_exception()
raise
def unset_description(self, format, version):
"""
Unset a description.
@param format: the format for which the description should be unset.
@type format: string
@param version: the version for which the description should be unset:
@type version: integer
"""
try:
assert(type(version) is int and version > 0)
del self.more_info['descriptions'][version][format]
self.flush()
except KeyError:
pass
except:
register_exception()
raise
def unset_flag(self, flagname, format, version):
"""
Unset a flag.
@param flagname: the flag to be unset (see
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}).
@type flagname: string
@param format: the format for which the flag should be unset.
@type format: string
@param version: the version for which the flag should be unset:
@type version: integer
@raise ValueError: if the flag is not in
L{CFG_BIBDOCFILE_AVAILABLE_FLAGS}
"""
if flagname in CFG_BIBDOCFILE_AVAILABLE_FLAGS:
try:
del self.more_info['flags'][flagname][version][format]
self.flush()
except KeyError:
pass
else:
raise ValueError, "%s is not in %s" % (flagname, CFG_BIBDOCFILE_AVAILABLE_FLAGS)
def serialize(self):
"""
@return: the serialized version of this object.
@rtype: string
"""
return cPickle.dumps(self.more_info)
def readfile(filename):
"""
Read a file.
@param filename: the name of the file to be read.
@type filename: string
@return: the text contained in the file.
@rtype: string
@note: Returns empty string in case of any error.
@note: this function is useful for quick implementation of websubmit
functions.
"""
try:
return open(filename).read()
except Exception:
return ''
diff --git a/modules/websubmit/lib/bibdocfile_regression_tests.py b/modules/websubmit/lib/bibdocfile_regression_tests.py
index 0e1c11b22..33febed9b 100644
--- a/modules/websubmit/lib/bibdocfile_regression_tests.py
+++ b/modules/websubmit/lib/bibdocfile_regression_tests.py
@@ -1,270 +1,271 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibDocFile Regression Test Suite."""
__revision__ = "$Id$"
import unittest
from invenio.testutils import make_test_suite, run_test_suite
from invenio.bibdocfile import BibRecDocs, check_bibdoc_authorization
from invenio.access_control_config import CFG_WEBACCESS_WARNING_MSGS
from invenio.config import \
CFG_SITE_URL, \
CFG_PREFIX, \
- CFG_WEBSUBMIT_FILEDIR
+ CFG_WEBSUBMIT_FILEDIR, \
+ CFG_SITE_RECORD
class BibRecDocsTest(unittest.TestCase):
"""regression tests about BibRecDocs"""
def test_BibRecDocs(self):
"""bibdocfile - BibRecDocs functions"""
my_bibrecdoc = BibRecDocs(2)
#add bibdoc
my_bibrecdoc.add_new_file(CFG_PREFIX + '/lib/webtest/invenio/test.jpg', 'Main', 'img_test', False, 'test add new file', 'test', '.jpg')
my_bibrecdoc.add_bibdoc(doctype='Main', docname='file', never_fail=False)
self.assertEqual(len(my_bibrecdoc.list_bibdocs()), 3)
my_added_bibdoc = my_bibrecdoc.get_bibdoc('file')
#add bibdocfile in empty bibdoc
my_added_bibdoc.add_file_new_version(CFG_PREFIX + '/lib/webtest/invenio/test.gif', \
description= 'added in empty bibdoc', comment=None, format=None, flags=['PERFORM_HIDE_PREVIOUS'])
#propose unique docname
self.assertEqual(my_bibrecdoc.propose_unique_docname('file'), 'file_2')
#has docname
self.assertEqual(my_bibrecdoc.has_docname_p('file'), True)
#merge 2 bibdocs
my_bibrecdoc.merge_bibdocs('img_test', 'file')
self.assertEqual(len(my_bibrecdoc.get_bibdoc("img_test").list_all_files()), 2)
#check file exists
self.assertEqual(my_bibrecdoc.check_file_exists(CFG_PREFIX + '/lib/webtest/invenio/test.jpg'), True)
#get bibdoc names
self.assertEqual(my_bibrecdoc.get_bibdoc_names('Main')[0], '0104007_02')
self.assertEqual(my_bibrecdoc.get_bibdoc_names('Main')[1],'img_test')
#get total size
self.assertEqual(my_bibrecdoc.get_total_size(), 1647591)
#get total size latest version
self.assertEqual(my_bibrecdoc.get_total_size_latest_version(), 1647591)
#display
value = my_bibrecdoc.display(docname='img_test', version='', doctype='', ln='en', verbose=0, display_hidden=True)
self.assert_("<small><b>Main</b>" in value)
#get xml 8564
value = my_bibrecdoc.get_xml_8564()
- self.assert_('/record/2/files/img_test.jpg</subfield>' in value)
+ self.assert_('/'+ CFG_SITE_RECORD +'/2/files/img_test.jpg</subfield>' in value)
#check duplicate docnames
self.assertEqual(my_bibrecdoc.check_duplicate_docnames(), True)
def tearDown(self):
my_bibrecdoc = BibRecDocs(2)
#delete
my_bibrecdoc.delete_bibdoc('img_test')
my_bibrecdoc.delete_bibdoc('file')
class BibDocsTest(unittest.TestCase):
"""regression tests about BibDocs"""
def test_BibDocs(self):
"""bibdocfile - BibDocs functions"""
#add file
my_bibrecdoc = BibRecDocs(2)
my_bibrecdoc.add_new_file(CFG_PREFIX + '/lib/webtest/invenio/test.jpg', 'Main', 'img_test', False, 'test add new file', 'test', '.jpg')
my_new_bibdoc = my_bibrecdoc.get_bibdoc("img_test")
value = my_bibrecdoc.list_bibdocs()
self.assertEqual(len(value), 2)
#get total file (bibdoc)
self.assertEqual(my_new_bibdoc.get_total_size(), 91750)
#get recid
self.assertEqual(my_new_bibdoc.get_recid(), 2)
#change name
my_new_bibdoc.change_name('new_name')
#get docname
self.assertEqual(my_new_bibdoc.get_docname(), 'new_name')
#get type
self.assertEqual(my_new_bibdoc.get_type(), 'Main')
#get id
self.assert_(my_new_bibdoc.get_id() > 80)
#set status
my_new_bibdoc.set_status('new status')
#get status
self.assertEqual(my_new_bibdoc.get_status(), 'new status')
#get base directory
self.assert_(my_new_bibdoc.get_base_dir().startswith(CFG_WEBSUBMIT_FILEDIR))
#get file number
self.assertEqual(my_new_bibdoc.get_file_number(), 1)
#add file new version
my_new_bibdoc.add_file_new_version(CFG_PREFIX + '/lib/webtest/invenio/test.jpg', description= 'the new version', comment=None, format=None, flags=["PERFORM_HIDE_PREVIOUS"])
self.assertEqual(my_new_bibdoc.list_versions(), [1, 2])
#revert
my_new_bibdoc.revert(1)
self.assertEqual(my_new_bibdoc.list_versions(), [1, 2, 3])
self.assertEqual(my_new_bibdoc.get_description('.jpg', version=3), 'test add new file')
#get total size latest version
self.assertEqual(my_new_bibdoc.get_total_size_latest_version(), 91750)
#get latest version
self.assertEqual(my_new_bibdoc.get_latest_version(), 3)
#list latest files
self.assertEqual(len(my_new_bibdoc.list_latest_files()), 1)
self.assertEqual(my_new_bibdoc.list_latest_files()[0].get_version(), 3)
#list version files
self.assertEqual(len(my_new_bibdoc.list_version_files(1, list_hidden=True)), 1)
#display
value = my_new_bibdoc.display(version='', ln='en', display_hidden=True)
self.assert_('>test add new file<' in value)
#format already exist
self.assertEqual(my_new_bibdoc.format_already_exists_p('.jpg'), True)
#get file
self.assertEqual(my_new_bibdoc.get_file('.jpg', version='1').get_version(), 1)
#set description
my_new_bibdoc.set_description('new description', '.jpg', version=1)
#get description
self.assertEqual(my_new_bibdoc.get_description('.jpg', version=1), 'new description')
#set comment
my_new_bibdoc.set_description('new comment', '.jpg', version=1)
#get comment
self.assertEqual(my_new_bibdoc.get_description('.jpg', version=1), 'new comment')
#get history
assert len(my_new_bibdoc.get_history()) > 0
#delete file
my_new_bibdoc.delete_file('.jpg', 2)
#list all files
self.assertEqual(len(my_new_bibdoc.list_all_files()), 2)
#delete file
my_new_bibdoc.delete_file('.jpg', 3)
#add new format
my_new_bibdoc.add_file_new_format(CFG_PREFIX + '/lib/webtest/invenio/test.gif', version=None, description=None, comment=None, format=None)
self.assertEqual(len(my_new_bibdoc.list_all_files()), 2)
#delete file
my_new_bibdoc.delete_file('.jpg', 1)
#delete file
my_new_bibdoc.delete_file('.gif', 1)
#empty bibdoc
self.assertEqual(my_new_bibdoc.empty_p(), True)
#hidden?
self.assertEqual(my_new_bibdoc.hidden_p('.jpg', version=1), False)
#hide
my_new_bibdoc.set_flag('HIDDEN', '.jpg', version=1)
#hidden?
self.assertEqual(my_new_bibdoc.hidden_p('.jpg', version=1), True)
#add and get icon
my_new_bibdoc.add_icon( CFG_PREFIX + '/lib/webtest/invenio/icon-test.gif')
value = my_bibrecdoc.list_bibdocs()[1]
self.assertEqual(value.get_icon(), my_new_bibdoc.get_icon())
#delete icon
my_new_bibdoc.delete_icon()
#get icon
self.assertEqual(my_new_bibdoc.get_icon(), None)
#delete
my_new_bibdoc.delete()
self.assertEqual(my_new_bibdoc.deleted_p(), True)
#undelete
my_new_bibdoc.undelete(previous_status='')
#expunging
my_new_bibdoc.expunge()
my_bibrecdoc.build_bibdoc_list()
self.failIf('new_name' in my_bibrecdoc.get_bibdoc_names())
self.failUnless(my_bibrecdoc.get_bibdoc_names())
def tearDown(self):
my_bibrecdoc = BibRecDocs(2)
#delete
my_bibrecdoc.delete_bibdoc('img_test')
my_bibrecdoc.delete_bibdoc('new_name')
class BibDocFilesTest(unittest.TestCase):
"""regression tests about BibDocFiles"""
def test_BibDocFiles(self):
"""bibdocfile - BibDocFile functions """
#add bibdoc
my_bibrecdoc = BibRecDocs(2)
my_bibrecdoc.add_new_file(CFG_PREFIX + '/lib/webtest/invenio/test.jpg', 'Main', 'img_test', False, 'test add new file', 'test', '.jpg')
my_new_bibdoc = my_bibrecdoc.get_bibdoc("img_test")
my_new_bibdocfile = my_new_bibdoc.list_all_files()[0]
#get url
- self.assertEqual(my_new_bibdocfile.get_url(), CFG_SITE_URL + '/record/2/files/img_test.jpg')
+ self.assertEqual(my_new_bibdocfile.get_url(), CFG_SITE_URL + '/%s/2/files/img_test.jpg' % CFG_SITE_RECORD)
#get type
self.assertEqual(my_new_bibdocfile.get_type(), 'Main')
#get path
self.assert_(my_new_bibdocfile.get_path().startswith(CFG_WEBSUBMIT_FILEDIR))
self.assert_(my_new_bibdocfile.get_path().endswith('/img_test.jpg;1'))
#get bibdocid
self.assertEqual(my_new_bibdocfile.get_bibdocid(), my_new_bibdoc.get_id())
#get name
self.assertEqual(my_new_bibdocfile.get_name() , 'img_test')
#get full name
self.assertEqual(my_new_bibdocfile.get_full_name() , 'img_test.jpg')
#get full path
self.assert_(my_new_bibdocfile.get_full_path().startswith(CFG_WEBSUBMIT_FILEDIR))
self.assert_(my_new_bibdocfile.get_full_path().endswith('/img_test.jpg;1'))
#get format
self.assertEqual(my_new_bibdocfile.get_format(), '.jpg')
#get version
self.assertEqual(my_new_bibdocfile.get_version(), 1)
#get description
self.assertEqual(my_new_bibdocfile.get_description(), my_new_bibdoc.get_description('.jpg', version=1))
#get comment
self.assertEqual(my_new_bibdocfile.get_comment(), my_new_bibdoc.get_comment('.jpg', version=1))
#get recid
self.assertEqual(my_new_bibdocfile.get_recid(), 2)
#get status
self.assertEqual(my_new_bibdocfile.get_status(), '')
#get size
self.assertEqual(my_new_bibdocfile.get_size(), 91750)
#get checksum
self.assertEqual(my_new_bibdocfile.get_checksum(), '28ec893f9da735ad65de544f71d4ad76')
#check
self.assertEqual(my_new_bibdocfile.check(), True)
#display
value = my_new_bibdocfile.display(ln='en')
assert 'files/img_test.jpg?version=1">' in value
#hidden?
self.assertEqual(my_new_bibdocfile.hidden_p(), False)
#delete
my_new_bibdoc.delete()
self.assertEqual(my_new_bibdoc.deleted_p(), True)
class CheckBibDocAuthorization(unittest.TestCase):
"""Regression tests for check_bibdoc_authorization function."""
def test_check_bibdoc_authorization(self):
"""bibdocfile - check_bibdoc_authorization function"""
from invenio.webuser import collect_user_info, get_uid_from_email
jekyll = collect_user_info(get_uid_from_email('jekyll@cds.cern.ch'))
self.assertEqual(check_bibdoc_authorization(jekyll, 'role:thesesviewer'), (0, CFG_WEBACCESS_WARNING_MSGS[0]))
self.assertEqual(check_bibdoc_authorization(jekyll, 'role: thesesviewer'), (0, CFG_WEBACCESS_WARNING_MSGS[0]))
self.assertEqual(check_bibdoc_authorization(jekyll, 'role: thesesviewer'), (0, CFG_WEBACCESS_WARNING_MSGS[0]))
self.assertEqual(check_bibdoc_authorization(jekyll, 'Role: thesesviewer'), (0, CFG_WEBACCESS_WARNING_MSGS[0]))
self.assertEqual(check_bibdoc_authorization(jekyll, 'email: jekyll@cds.cern.ch'), (0, CFG_WEBACCESS_WARNING_MSGS[0]))
self.assertEqual(check_bibdoc_authorization(jekyll, 'email: jekyll@cds.cern.ch'), (0, CFG_WEBACCESS_WARNING_MSGS[0]))
juliet = collect_user_info(get_uid_from_email('juliet.capulet@cds.cern.ch'))
self.assertEqual(check_bibdoc_authorization(juliet, 'restricted_picture'), (0, CFG_WEBACCESS_WARNING_MSGS[0]))
self.assertEqual(check_bibdoc_authorization(juliet, 'status: restricted_picture'), (0, CFG_WEBACCESS_WARNING_MSGS[0]))
self.assertNotEqual(check_bibdoc_authorization(juliet, 'restricted_video')[0], 0)
self.assertNotEqual(check_bibdoc_authorization(juliet, 'status: restricted_video')[0], 0)
TEST_SUITE = make_test_suite(BibRecDocsTest, \
BibDocsTest, \
BibDocFilesTest, \
CheckBibDocAuthorization)
if __name__ == "__main__":
run_test_suite(TEST_SUITE, warn_user=True)
diff --git a/modules/websubmit/lib/bibdocfilecli.py b/modules/websubmit/lib/bibdocfilecli.py
index 8f6e4db9b..dd5073e8d 100644
--- a/modules/websubmit/lib/bibdocfilecli.py
+++ b/modules/websubmit/lib/bibdocfilecli.py
@@ -1,1107 +1,1108 @@
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
BibDocAdmin CLI administration tool
"""
__revision__ = "$Id$"
import sys
import re
import os
import time
import fnmatch
import time
from datetime import datetime
from logging import getLogger, debug, DEBUG
from optparse import OptionParser, OptionGroup, OptionValueError
from tempfile import mkstemp
from invenio.errorlib import register_exception
-from invenio.config import CFG_TMPDIR, CFG_SITE_URL, CFG_WEBSUBMIT_FILEDIR
+from invenio.config import CFG_TMPDIR, CFG_SITE_URL, CFG_WEBSUBMIT_FILEDIR, \
+ CFG_SITE_RECORD
from invenio.bibdocfile import BibRecDocs, BibDoc, InvenioWebSubmitFileError, \
nice_size, check_valid_url, clean_url, get_docname_from_url, \
guess_format_from_url, KEEP_OLD_VALUE, decompose_bibdocfile_fullpath, \
bibdocfile_url_to_bibdoc, decompose_bibdocfile_url
from invenio.intbitset import intbitset
from invenio.search_engine import perform_request_search
from invenio.textutils import wrap_text_in_a_box, wait_for_user
from invenio.dbquery import run_sql
from invenio.bibtask import task_low_level_submission
from invenio.textutils import encode_for_xml
from invenio.websubmit_file_converter import can_perform_ocr
def _xml_mksubfield(key, subfield, fft):
return fft.get(key, None) is not None and '\t\t<subfield code="%s">%s</subfield>\n' % (subfield, encode_for_xml(str(fft[key]))) or ''
def _xml_mksubfields(key, subfield, fft):
ret = ""
for value in fft.get(key, []):
ret += '\t\t<subfield code="%s">%s</subfield>\n' % (subfield, encode_for_xml(str(value)))
return ret
def _xml_fft_creator(fft):
"""Transform an fft dictionary (made by keys url, docname, format,
new_docname, comment, description, restriction, doctype, into an xml
string."""
debug('Input FFT structure: %s' % fft)
out = '\t<datafield tag ="FFT" ind1=" " ind2=" ">\n'
out += _xml_mksubfield('url', 'a', fft)
out += _xml_mksubfield('docname', 'n', fft)
out += _xml_mksubfield('format', 'f', fft)
out += _xml_mksubfield('new_docname', 'm', fft)
out += _xml_mksubfield('doctype', 't', fft)
out += _xml_mksubfield('description', 'd', fft)
out += _xml_mksubfield('comment', 'z', fft)
out += _xml_mksubfield('restriction', 'r', fft)
out += _xml_mksubfields('options', 'o', fft)
out += _xml_mksubfield('version', 'v', fft)
out += '\t</datafield>\n'
debug('FFT created: %s' % out)
return out
def ffts_to_xml(ffts_dict):
"""Transform a dictionary: recid -> ffts where ffts is a list of fft dictionary
into xml.
"""
debug('Input FFTs dictionary: %s' % ffts_dict)
out = ''
recids = ffts_dict.keys()
recids.sort()
for recid in recids:
ffts = ffts_dict[recid]
if ffts:
out += '<record>\n'
out += '\t<controlfield tag="001">%i</controlfield>\n' % recid
for fft in ffts:
out += _xml_fft_creator(fft)
out += '</record>\n'
debug('MARC to Upload: %s' % out)
return out
_shift_re = re.compile("([-\+]{0,1})([\d]+)([dhms])")
def _parse_datetime(var):
"""Returns a date string according to the format string.
It can handle normal date strings and shifts with respect
to now."""
if not var:
return None
date = time.time()
factors = {"d":24*3600, "h":3600, "m":60, "s":1}
m = _shift_re.match(var)
if m:
sign = m.groups()[0] == "-" and -1 or 1
factor = factors[m.groups()[2]]
value = float(m.groups()[1])
return datetime.fromtimestamp(date + sign * factor * value)
else:
return datetime(*(time.strptime(var, "%Y-%m-%d %H:%M:%S")[0:6]))
# The code above is Python 2.4 compatible. The following is the 2.5
# version.
# return datetime.strptime(var, "%Y-%m-%d %H:%M:%S")
def _parse_date_range(var):
"""Returns the two dates contained as a low,high tuple"""
limits = var.split(",")
if len(limits)==1:
low = _parse_datetime(limits[0])
return low, None
if len(limits)==2:
low = _parse_datetime(limits[0])
high = _parse_datetime(limits[1])
return low, high
return None, None
def cli_quick_match_all_recids(options):
"""Return an quickly an approximate but (by excess) list of good recids."""
url = getattr(options, 'url', None)
if url:
return intbitset([decompose_bibdocfile_url(url)[0]])
path = getattr(options, 'path', None)
if path:
return intbitset([decompose_bibdocfile_fullpath(path)[0]])
collection = getattr(options, 'collection', None)
pattern = getattr(options, 'pattern', None)
recids = getattr(options, 'recids', None)
md_rec = getattr(options, 'md_rec', None)
cd_rec = getattr(options, 'cd_rec', None)
tmp_date_query = []
tmp_date_params = []
if recids is None:
debug('Initially considering all the recids')
recids = intbitset(run_sql('SELECT id FROM bibrec'))
if not recids:
print >> sys.stderr, 'WARNING: No record in the database'
if md_rec[0] is not None:
tmp_date_query.append('modification_date>=%s')
tmp_date_params.append(md_rec[0])
if md_rec[1] is not None:
tmp_date_query.append('modification_date<=%s')
tmp_date_params.append(md_rec[1])
if cd_rec[0] is not None:
tmp_date_query.append('creation_date>=%s')
tmp_date_params.append(cd_rec[0])
if cd_rec[1] is not None:
tmp_date_query.append('creation_date<=%s')
tmp_date_params.append(cd_rec[1])
if tmp_date_query:
tmp_date_query = ' AND '.join(tmp_date_query)
tmp_date_params = tuple(tmp_date_params)
query = 'SELECT id FROM bibrec WHERE %s' % tmp_date_query
debug('Query: %s, param: %s' % (query, tmp_date_params))
recids &= intbitset(run_sql(query % tmp_date_query, tmp_date_params))
debug('After applying dates we obtain recids: %s' % recids)
if not recids:
print >> sys.stderr, 'WARNING: Time constraints for records are too strict'
if collection or pattern:
recids &= intbitset(perform_request_search(cc=collection or '', p=pattern or ''))
debug('After applyings pattern and collection we obtain recids: %s' % recids)
debug('Quick recids: %s' % recids)
return recids
def cli_quick_match_all_docids(options, recids=None):
"""Return an quickly an approximate but (by excess) list of good docids."""
url = getattr(options, 'url', None)
if url:
return intbitset([bibdocfile_url_to_bibdoc(url).get_id()])
path = getattr(options, 'path', None)
if path:
return intbitset([decompose_bibdocfile_fullpath(path)[0]])
deleted_docs = getattr(options, 'deleted_docs', None)
action_undelete = getattr(options, 'action', None) == 'undelete'
docids = getattr(options, 'docids', None)
md_doc = getattr(options, 'md_doc', None)
cd_doc = getattr(options, 'cd_doc', None)
if docids is None:
debug('Initially considering all the docids')
docids = intbitset(run_sql('SELECT id_bibdoc FROM bibrec_bibdoc'))
else:
debug('Initially considering this docids: %s' % docids)
tmp_query = []
tmp_params = []
if deleted_docs is None and action_undelete:
deleted_docs = 'only'
if deleted_docs == 'no':
tmp_query.append('status<>"DELETED"')
elif deleted_docs == 'only':
tmp_query.append('status="DELETED"')
if md_doc[0] is not None:
tmp_query.append('modification_date>=%s')
tmp_params.append(md_doc[0])
if md_doc[1] is not None:
tmp_query.append('modification_date<=%s')
tmp_params.append(md_doc[1])
if cd_doc[0] is not None:
tmp_query.append('creation_date>=%s')
tmp_params.append(cd_doc[0])
if cd_doc[1] is not None:
tmp_query.append('creation_date<=%s')
tmp_params.append(cd_doc[1])
if tmp_query:
tmp_query = ' AND '.join(tmp_query)
tmp_params = tuple(tmp_params)
query = 'SELECT id FROM bibdoc WHERE %s' % tmp_query
debug('Query: %s, param: %s' % (query, tmp_params))
docids &= intbitset(run_sql(query, tmp_params))
debug('After applying dates we obtain docids: %s' % docids)
return docids
def cli_slow_match_single_recid(options, recid, recids=None, docids=None):
"""Apply all the given queries in order to assert wethever a recid
match or not.
if with_docids is True, the recid is matched if it has at least one docid that is matched"""
debug('cli_slow_match_single_recid checking: %s' % recid)
deleted_docs = getattr(options, 'deleted_docs', None)
deleted_recs = getattr(options, 'deleted_recs', None)
empty_recs = getattr(options, 'empty_recs', None)
docname = cli2docname(options)
bibrecdocs = BibRecDocs(recid, deleted_too=(deleted_docs != 'no'))
if bibrecdocs.deleted_p() and (deleted_recs == 'no'):
return False
elif not bibrecdocs.deleted_p() and (deleted_recs != 'only'):
if docids:
for bibdoc in bibrecdocs.list_bibdocs():
if bibdoc.get_id() in docids:
break
else:
return False
if docname:
for other_docname in bibrecdocs.get_bibdoc_names():
if docname and fnmatch.fnmatchcase(other_docname, docname):
break
else:
return False
if bibrecdocs.empty_p() and (empty_recs != 'no'):
return True
elif not bibrecdocs.empty_p() and (empty_recs != 'only'):
return True
return False
def cli_slow_match_single_docid(options, docid, recids=None, docids=None):
"""Apply all the given queries in order to assert wethever a recid
match or not."""
debug('cli_slow_match_single_docid checking: %s' % docid)
empty_docs = getattr(options, 'empty_docs', None)
docname = cli2docname(options)
if recids is None:
recids = cli_quick_match_all_recids(options)
bibdoc = BibDoc(docid)
if docname and not fnmatch.fnmatchcase(bibdoc.get_docname(), docname):
debug('docname %s does not match the pattern %s' % (repr(bibdoc.get_docname()), repr(docname)))
return False
elif bibdoc.get_recid() and bibdoc.get_recid() not in recids:
debug('recid %s is not in pattern %s' % (repr(bibdoc.get_recid()), repr(recids)))
return False
elif empty_docs == 'no' and bibdoc.empty_p():
debug('bibdoc is empty')
return False
elif empty_docs == 'only' and not bibdoc.empty_p():
debug('bibdoc is not empty')
return False
else:
return True
def cli2recid(options, recids=None, docids=None):
"""Given the command line options return a recid."""
recids = list(cli_recids_iterator(options, recids=recids, docids=docids))
if len(recids) == 1:
return recids[0]
if recids:
raise StandardError, "More than one recid has been matched: %s" % recids
else:
raise StandardError, "No recids matched"
def cli2docid(options, recids=None, docids=None):
"""Given the command line options return a docid."""
docids = list(cli_docids_iterator(options, recids=recids, docids=docids))
if len(docids) == 1:
return docids[0]
if docids:
raise StandardError, "More than one docid has been matched: %s" % docids
else:
raise StandardError, "No docids matched"
def cli2description(options):
"""Return a good value for the description."""
description = getattr(options, 'set_description', None)
if description is None:
description = KEEP_OLD_VALUE
return description
def cli2restriction(options):
"""Return a good value for the restriction."""
restriction = getattr(options, 'set_restriction', None)
if restriction is None:
restriction = KEEP_OLD_VALUE
return restriction
def cli2comment(options):
"""Return a good value for the comment."""
comment = getattr(options, 'set_comment', None)
if comment is None:
comment = KEEP_OLD_VALUE
return comment
def cli2doctype(options):
"""Return a good value for the doctype."""
doctype = getattr(options, 'set_doctype', None)
if not doctype:
return 'Main'
return doctype
def cli2docname(options, docid=None, url=None):
"""Given the command line options and optional precalculated docid
returns the corresponding docname."""
if docid:
bibdoc = BibDoc(docid=docid)
return bibdoc.get_docname()
docname = getattr(options, 'docname', None)
if docname is not None:
return docname
if url is not None:
return get_docname_from_url(url)
else:
return None
def cli2format(options, url=None):
"""Given the command line options returns the corresponding format."""
format = getattr(options, 'format', None)
if format is not None:
return format
elif url is not None:
## FIXME: to deploy once conversion-tools branch is merged
#return guess_format_from_url(url)
return guess_format_from_url(url)
else:
raise OptionValueError("Not enough information to retrieve a valid format")
def cli_recids_iterator(options, recids=None, docids=None):
"""Slow iterator over all the matched recids.
if with_docids is True, the recid must be attached to at least a matched docid"""
debug('cli_recids_iterator')
if recids is None:
recids = cli_quick_match_all_recids(options)
debug('working on recids: %s, docids: %s' % (recids, docids))
for recid in recids:
if cli_slow_match_single_recid(options, recid, recids, docids):
yield recid
raise StopIteration
def cli_docids_iterator(options, recids=None, docids=None):
"""Slow iterator over all the matched docids."""
if recids is None:
recids = cli_quick_match_all_recids(options)
if docids is None:
docids = cli_quick_match_all_docids(options)
for docid in docids:
if cli_slow_match_single_docid(options, docid, recids, docids):
yield docid
raise StopIteration
class OptionParserSpecial(OptionParser):
def format_help(self, *args, **kwargs):
result = OptionParser.format_help(self, *args, **kwargs)
if hasattr(self, 'trailing_text'):
return "%s\n%s\n" % (result, self.trailing_text)
else:
return result
def prepare_option_parser():
"""Parse the command line options."""
def _ids_ranges_callback(option, opt, value, parser):
"""Callback for optparse to parse a set of ids ranges in the form
nnn1-nnn2,mmm1-mmm2... returning the corresponding intbitset.
"""
try:
debug('option: %s, opt: %s, value: %s, parser: %s' % (option, opt, value, parser))
debug('Parsing range: %s' % value)
value = ranges2ids(value)
setattr(parser.values, option.dest, value)
except Exception, e:
raise OptionValueError("It's impossible to parse the range '%s' for option %s: %s" % (value, opt, e))
def _date_range_callback(option, opt, value, parser):
"""Callback for optparse to parse a range of dates in the form
[date1],[date2]. Both date1 and date2 could be optional.
the date can be expressed absolutely ("%Y-%m-%d %H:%M:%S")
or relatively (([-\+]{0,1})([\d]+)([dhms])) to the current time."""
try:
value = _parse_date_range(value)
setattr(parser.values, option.dest, value)
except Exception, e:
raise OptionValueError("It's impossible to parse the range '%s' for option %s: %s" % (value, opt, e))
parser = OptionParserSpecial(usage="usage: %prog [options]",
#epilog="""With <query> you select the range of record/docnames/single files to work on. Note that some actions e.g. delete, append, revise etc. works at the docname level, while others like --set-comment, --set-description, at single file level and other can be applied in an iterative way to many records in a single run. Note that specifing docid(2) takes precedence over recid(2) which in turns takes precedence over pattern/collection search.""",
version=__revision__)
parser.trailing_text = """
Examples:
$ bibdocfile --append foo.tar.gz --recid=1
$ bibdocfile --revise http://foo.com?search=123 --with-docname='sam'
--format=pdf --recid=3 --set-docname='pippo' # revise for record 3
# the document sam, renaming it to pippo.
$ bibdocfile --delete --with-docname="*sam" --all # delete all documents
# starting ending
# with "sam"
$ bibdocfile --undelete -c "Test Collection" # undelete documents for
# the collection
$ bibdocfile --get-info --recids=1-4,6-8 # obtain informations
$ bibdocfile -r 1 --with-docname=foo --set-docname=bar # Rename a document
$ bibdocfile -r 1 --set-restriction "firerole: deny until '2011-01-01'
allow any" # set an embargo to all the documents attached to record 1
# (note the ^M or \\n before 'allow any')
# See also $r subfield in <%(site)s/help/admin/bibupload-admin-guide#3.6>
# and Firerole in <%(site)s/help/admin/webaccess-admin-guide#6>
""" % {'site': CFG_SITE_URL}
query_options = OptionGroup(parser, 'Query options')
query_options.add_option('-r', '--recids', action="callback", callback=_ids_ranges_callback, type='string', dest='recids', help='matches records by recids, e.g.: --recids=1-3,5-7')
query_options.add_option('-d', '--docids', action="callback", callback=_ids_ranges_callback, type='string', dest='docids', help='matches documents by docids, e.g.: --docids=1-3,5-7')
query_options.add_option('-a', '--all', action='store_true', dest='all', help='Select all the records')
query_options.add_option("--with-deleted-recs", choices=['yes', 'no', 'only'], type="choice", dest="deleted_recs", help="'Yes' to also match deleted records, 'no' to exclude them, 'only' to match only deleted ones", metavar="yes/no/only", default='no')
query_options.add_option("--with-deleted-docs", choices=['yes', 'no', 'only'], type="choice", dest="deleted_docs", help="'Yes' to also match deleted documents, 'no' to exclude them, 'only' to match only deleted ones (e.g. for undeletion)", metavar="yes/no/only", default='no')
query_options.add_option("--with-empty-recs", choices=['yes', 'no', 'only'], type="choice", dest="empty_recs", help="'Yes' to also match records without attached documents, 'no' to exclude them, 'only' to consider only such records (e.g. for statistics)", metavar="yes/no/only", default='no')
query_options.add_option("--with-empty-docs", choices=['yes', 'no', 'only'], type="choice", dest="empty_docs", help="'Yes' to also match documents without attached files, 'no' to exclude them, 'only' to consider only such documents (e.g. for sanity checking)", metavar="yes/no/only", default='no')
query_options.add_option("--with-record-modification-date", action="callback", callback=_date_range_callback, dest="md_rec", nargs=1, type="string", default=(None, None), help="matches records modified date1 and date2; dates can be expressed relatively, e.g.:\"-5m,2030-2-23 04:40\" # matches records modified since 5 minutes ago until the 2030...", metavar="date1,date2")
query_options.add_option("--with-record-creation-date", action="callback", callback=_date_range_callback, dest="cd_rec", nargs=1, type="string", default=(None, None), help="matches records created between date1 and date2; dates can be expressed relatively", metavar="date1,date2")
query_options.add_option("--with-document-modification-date", action="callback", callback=_date_range_callback, dest="md_doc", nargs=1, type="string", default=(None, None), help="matches documents modified between date1 and date2; dates can be expressed relatively", metavar="date1,date2")
query_options.add_option("--with-document-creation-date", action="callback", callback=_date_range_callback, dest="cd_doc", nargs=1, type="string", default=(None, None), help="matches documents created between date1 and date2; dates can be expressed relatively", metavar="date1,date2")
- query_options.add_option("--url", dest="url", help='matches the document referred by the URL, e.g. "%s/record/1/files/foobar.pdf?version=2"' % CFG_SITE_URL)
+ query_options.add_option("--url", dest="url", help='matches the document referred by the URL, e.g. "%s/%s/1/files/foobar.pdf?version=2"' % (CFG_SITE_URL, CFG_SITE_RECORD))
query_options.add_option("--path", dest="path", help='matches the document referred by the internal filesystem path, e.g. %s/g0/1/foobar.pdf\\;1' % CFG_WEBSUBMIT_FILEDIR)
query_options.add_option("--with-docname", dest="docname", help='matches documents with the given docname (accept wildcards)')
query_options.add_option("--with-doctype", dest="doctype", help='matches documents with the given doctype')
query_options.add_option('-p', '--pattern', dest='pattern', help='matches records by pattern')
query_options.add_option('-c', '--collection', dest='collection', help='matches records by collection')
query_options.add_option('--force', dest='force', help='force an action even when it\'s not necessary e.g. textify on an already textified bibdoc.', action='store_true', default=False)
parser.add_option_group(query_options)
getting_information_options = OptionGroup(parser, 'Actions for getting information')
getting_information_options.add_option('--get-info', dest='action', action='store_const', const='get-info', help='print all the informations about the matched record/documents')
getting_information_options.add_option('--get-disk-usage', dest='action', action='store_const', const='get-disk-usage', help='print disk usage statistics of the matched documents')
getting_information_options.add_option('--get-history', dest='action', action='store_const', const='get-history', help='print the matched documents history')
parser.add_option_group(getting_information_options)
setting_information_options = OptionGroup(parser, 'Actions for setting information')
setting_information_options.add_option('--set-doctype', dest='set_doctype', help='specify the new doctype', metavar='doctype')
setting_information_options.add_option('--set-description', dest='set_description', help='specify a description', metavar='description')
setting_information_options.add_option('--set-comment', dest='set_comment', help='specify a comment', metavar='comment')
setting_information_options.add_option('--set-restriction', dest='set_restriction', help='specify a restriction tag', metavar='restriction')
setting_information_options.add_option('--set-docname', dest='new_docname', help='specifies a new docname for renaming', metavar='docname')
setting_information_options.add_option("--unset-comment", action="store_const", const='', dest="set_comment", help="remove any comment")
setting_information_options.add_option("--unset-descriptions", action="store_const", const='', dest="set_description", help="remove any description")
setting_information_options.add_option("--unset-restrictions", action="store_const", const='', dest="set_restriction", help="remove any restriction")
setting_information_options.add_option("--hide", dest="action", action='store_const', const='hide', help="hides matched documents and revisions")
setting_information_options.add_option("--unhide", dest="action", action='store_const', const='unhide', help="hides matched documents and revisions")
parser.add_option_group(setting_information_options)
revising_options = OptionGroup(parser, 'Action for revising content')
revising_options.add_option("--append", dest='append_path', help='specify the URL/path of the file that will appended to the bibdoc (implies --with-empty-recs=yes)', metavar='PATH/URL')
revising_options.add_option("--revise", dest='revise_path', help='specify the URL/path of the file that will revise the bibdoc', metavar='PATH/URL')
revising_options.add_option("--revert", dest='action', action='store_const', const='revert', help='reverts a document to the specified version')
revising_options.add_option("--delete", action='store_const', const='delete', dest='action', help='soft-delete the matched documents')
revising_options.add_option("--hard-delete", action='store_const', const='hard-delete', dest='action', help='hard-delete the single matched document with a specific format and a specific revision (this operation is not revertible)')
revising_options.add_option("--undelete", action='store_const', const='undelete', dest='action', help='undelete previosuly soft-deleted documents')
revising_options.add_option("--purge", action='store_const', const='purge', dest='action', help='purge (i.e. hard-delete any format of any version prior to the latest version of) the matched documents')
revising_options.add_option("--expunge", action='store_const', const='expunge', dest='action', help='expunge (i.e. hard-delete any version and formats of) the matched documents')
revising_options.add_option("--with-versions", dest="version", help="specifies the version(s) to be used with hard-delete, hide, revert, e.g.: 1-2,3 or all")
revising_options.add_option("--with-format", dest="format", help='to specify a format when appending/revising/deleting/reverting a document, e.g. "pdf"', metavar='FORMAT')
revising_options.add_option("--with-hide-previous", dest='hide_previous', action='store_true', help='when revising, hides previous versions', default=False)
parser.add_option_group(revising_options)
housekeeping_options = OptionGroup(parser, 'Actions for housekeeping')
housekeeping_options.add_option("--check-md5", action='store_const', const='check-md5', dest='action', help='check md5 checksum validity of files')
housekeeping_options.add_option("--check-format", action='store_const', const='check-format', dest='action', help='check if any format-related inconsistences exists')
housekeeping_options.add_option("--check-duplicate-docnames", action='store_const', const='check-duplicate-docnames', dest='action', help='check for duplicate docnames associated with the same record')
housekeeping_options.add_option("--update-md5", action='store_const', const='update-md5', dest='action', help='update md5 checksum of files')
housekeeping_options.add_option("--fix-all", action='store_const', const='fix-all', dest='action', help='fix inconsistences in filesystem vs database vs MARC')
housekeeping_options.add_option("--fix-marc", action='store_const', const='fix-marc', dest='action', help='synchronize MARC after filesystem/database')
housekeeping_options.add_option("--fix-format", action='store_const', const='fix-format', dest='action', help='fix format related inconsistences')
housekeeping_options.add_option("--fix-duplicate-docnames", action='store_const', const='fix-duplicate-docnames', dest='action', help='fix duplicate docnames associated with the same record')
parser.add_option_group(housekeeping_options)
experimental_options = OptionGroup(parser, 'Experimental options (do not expect to find them in the next release)')
experimental_options.add_option('--textify', dest='action', action='store_const', const='textify', help='extract text from matched documents and store it for later indexing')
experimental_options.add_option('--with-ocr', dest='perform_ocr', action='store_true', default=False, help='when used with --textify, wether to perform OCR')
parser.add_option_group(experimental_options)
parser.add_option('-D', '--debug', action='store_true', dest='debug', default=False)
parser.add_option('-H', '--human-readable', dest='human_readable', action='store_true', default=False, help='print sizes in human readable format (e.g., 1KB 234MB 2GB)')
parser.add_option('--yes-i-know', action='store_true', dest='yes-i-know', help='use with care!')
return parser
def print_info(recid, docid, info):
"""Nicely print info about a recid, docid pair."""
print '%i:%i:%s' % (recid, docid, info)
def bibupload_ffts(ffts, append=False, debug=False, interactive=True):
"""Given an ffts dictionary it creates the xml and submit it."""
xml = ffts_to_xml(ffts)
if xml:
if interactive:
print xml
tmp_file_fd, tmp_file_name = mkstemp(suffix='.xml', prefix="bibdocfile_%s" % time.strftime("%Y-%m-%d_%H:%M:%S"), dir=CFG_TMPDIR)
os.write(tmp_file_fd, xml)
os.close(tmp_file_fd)
os.chmod(tmp_file_name, 0644)
if append:
if interactive:
wait_for_user("This will be appended via BibUpload")
if debug:
task = task_low_level_submission('bibupload', 'bibdocfile', '-a', tmp_file_name, '-N', 'FFT', '-S2', '-v9')
else:
task = task_low_level_submission('bibupload', 'bibdocfile', '-a', tmp_file_name, '-N', 'FFT', '-S2')
if interactive:
print "BibUpload append submitted with id %s" % task
else:
if interactive:
wait_for_user("This will be corrected via BibUpload")
if debug:
task = task_low_level_submission('bibupload', 'bibdocfile', '-c', tmp_file_name, '-N', 'FFT', '-S2', '-v9')
else:
task = task_low_level_submission('bibupload', 'bibdocfile', '-c', tmp_file_name, '-N', 'FFT', '-S2')
if interactive:
print "BibUpload correct submitted with id %s" % task
elif interactive:
print >> sys.stderr, "WARNING: no MARC to upload."
return True
def ranges2ids(parse_string):
"""Parse a string and return the intbitset of the corresponding ids."""
ids = intbitset()
ranges = parse_string.split(",")
for arange in ranges:
tmp_ids = arange.split("-")
if len(tmp_ids)==1:
ids.add(int(tmp_ids[0]))
else:
if int(tmp_ids[0]) > int(tmp_ids[1]): # sanity check
tmp = tmp_ids[0]
tmp_ids[0] = tmp_ids[1]
tmp_ids[1] = tmp
ids += xrange(int(tmp_ids[0]), int(tmp_ids[1]) + 1)
return ids
def cli_append(options, append_path):
"""Create a bibupload FFT task submission for appending a format."""
recid = cli2recid(options)
comment = cli2comment(options)
description = cli2description(options)
restriction = cli2restriction(options)
doctype = cli2doctype(options)
docname = cli2docname(options, url=append_path)
if not docname:
raise OptionValueError, 'Not enough information to retrieve a valid docname'
format = cli2format(options, append_path)
url = clean_url(append_path)
check_valid_url(url)
bibrecdocs = BibRecDocs(recid)
if bibrecdocs.has_docname_p(docname) and bibrecdocs.get_bibdoc(docname).format_already_exists_p(format):
new_docname = bibrecdocs.propose_unique_docname(docname)
wait_for_user("WARNING: a document with name %s and format %s already exists for recid %s. A new document with name %s will be created instead." % (repr(docname), repr(format), repr(recid), repr(new_docname)))
docname = new_docname
ffts = {recid: [{
'docname' : docname,
'comment' : comment,
'description' : description,
'restriction' : restriction,
'doctype' : doctype,
'format' : format,
'url' : url
}]}
return bibupload_ffts(ffts, append=True)
def cli_revise(options, revise_path):
"""Create a bibupload FFT task submission for appending a format."""
recid = cli2recid(options)
comment = cli2comment(options)
description = cli2description(options)
restriction = cli2restriction(options)
docname = cli2docname(options, url=revise_path)
hide_previous = getattr(options, 'hide_previous', None)
if not docname:
raise OptionValueError, 'Not enough information to retrieve a valid docname'
format = cli2format(options, revise_path)
doctype = cli2doctype(options)
url = clean_url(revise_path)
new_docname = getattr(options, 'new_docname', None)
check_valid_url(url)
ffts = {recid : [{
'docname' : docname,
'new_docname' : new_docname,
'comment' : comment,
'description' : description,
'restriction' : restriction,
'doctype' : doctype,
'format' : format,
'url' : url,
'options' : hide_previous and ['PERFORM_HIDE_PREVIOUS'] or None
}]}
return bibupload_ffts(ffts)
def cli_set_batch(options):
"""Change in batch the doctype, description, comment and restriction."""
ffts = {}
doctype = getattr(options, 'set_doctype', None)
description = cli2description(options)
comment = cli2comment(options)
restriction = cli2restriction(options)
with_format = getattr(options, 'format', None)
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
recid = bibdoc.get_recid()
docname = bibdoc.get_docname()
fft = []
if description is not None or comment is not None:
for bibdocfile in bibdoc.list_latest_files():
format = bibdocfile.get_format()
if not with_format or with_format == format:
fft.append({
'docname': docname,
'restriction': restriction,
'comment': comment,
'description': description,
'format': format,
'doctype': doctype
})
else:
fft.append({
'docname': docname,
'restriction': restriction,
'doctype': doctype,
})
ffts[recid] = fft
return bibupload_ffts(ffts, append=False)
def cli_textify(options):
"""Extract text to let indexing on fulltext be possible."""
force = getattr(options, 'force', None)
perform_ocr = getattr(options, 'perform_ocr', None)
if perform_ocr:
if not can_perform_ocr():
print >> sys.stderr, "WARNING: OCR requested but OCR is not possible"
perform_ocr = False
if perform_ocr:
additional = ' using OCR (this might take some time)'
else:
additional = ''
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
print 'Extracting text for docid %s%s...' % (docid, additional),
sys.stdout.flush()
if force or not bibdoc.has_text(require_up_to_date=True):
try:
bibdoc.extract_text(perform_ocr=perform_ocr)
print "DONE"
except InvenioWebSubmitFileError, e:
print >> sys.stderr, "WARNING: %s" % e
else:
print "not needed"
def cli_rename(options):
"""Rename a docname within a recid."""
new_docname = getattr(options, 'new_docname', None)
docid = cli2docid(options)
bibdoc = BibDoc(docid)
docname = bibdoc.get_docname()
recid = bibdoc.get_recid()
ffts = {recid : [{'docname' : docname, 'new_docname' : new_docname}]}
return bibupload_ffts(ffts, append=False)
def cli_fix_all(options):
"""Fix all the records of a recid_set."""
ffts = {}
for recid in cli_recids_iterator(options):
ffts[recid] = []
for docname in BibRecDocs(recid).get_bibdoc_names():
ffts[recid].append({'docname' : docname, 'doctype' : 'FIX-ALL'})
return bibupload_ffts(ffts, append=False)
def cli_fix_marc(options, explicit_recid_set=None, interactive=True):
"""Fix all the records of a recid_set."""
ffts = {}
if explicit_recid_set is not None:
for recid in explicit_recid_set:
ffts[recid] = [{'doctype' : 'FIX-MARC'}]
else:
for recid in cli_recids_iterator(options):
ffts[recid] = [{'doctype' : 'FIX-MARC'}]
return bibupload_ffts(ffts, append=False, interactive=interactive)
def cli_check_format(options):
"""Check if any format-related inconsistences exists."""
count = 0
tot = 0
duplicate = False
for recid in cli_recids_iterator(options):
tot += 1
bibrecdocs = BibRecDocs(recid)
if not bibrecdocs.check_duplicate_docnames():
print >> sys.stderr, "recid %s has duplicate docnames!"
broken = True
duplicate = True
else:
broken = False
for docname in bibrecdocs.get_bibdoc_names():
if not bibrecdocs.check_format(docname):
print >> sys.stderr, "recid %s with docname %s need format fixing" % (recid, docname)
broken = True
if broken:
count += 1
if count:
result = "%d out of %d records need their formats to be fixed." % (count, tot)
else:
result = "All records appear to be correct with respect to formats."
if duplicate:
result += " Note however that at least one record appear to have duplicate docnames. You should better fix this situation by using --fix-duplicate-docnames."
print wrap_text_in_a_box(result, style="conclusion")
return not(duplicate or count)
def cli_check_duplicate_docnames(options):
"""Check if some record is connected with bibdoc having the same docnames."""
count = 0
tot = 0
for recid in cli_recids_iterator(options):
tot += 1
bibrecdocs = BibRecDocs(recid)
if bibrecdocs.check_duplicate_docnames():
count += 1
print sys.stderr, "recid %s has duplicate docnames!"
if count:
print "%d out of %d records have duplicate docnames." % (count, tot)
return False
else:
print "All records appear to be correct with respect to duplicate docnames."
return True
def cli_fix_format(options):
"""Fix format-related inconsistences."""
fixed = intbitset()
tot = 0
for recid in cli_recids_iterator(options):
tot += 1
bibrecdocs = BibRecDocs(recid)
for docname in bibrecdocs.get_bibdoc_names():
if not bibrecdocs.check_format(docname):
if bibrecdocs.fix_format(docname, skip_check=True):
print >> sys.stderr, "%i has been fixed for docname %s" % (recid, docname)
else:
print >> sys.stderr, "%i has been fixed for docname %s. However note that a new bibdoc might have been created." % (recid, docname)
fixed.add(recid)
if fixed:
print "Now we need to synchronize MARC to reflect current changes."
cli_fix_marc(options, explicit_recid_set=fixed)
print wrap_text_in_a_box("%i out of %i record needed to be fixed." % (tot, len(fixed)), style="conclusion")
return not fixed
def cli_fix_duplicate_docnames(options):
"""Fix duplicate docnames."""
fixed = intbitset()
tot = 0
for recid in cli_recids_iterator(options):
tot += 1
bibrecdocs = BibRecDocs(recid)
if not bibrecdocs.check_duplicate_docnames():
bibrecdocs.fix_duplicate_docnames(skip_check=True)
print >> sys.stderr, "%i has been fixed for duplicate docnames." % recid
fixed.add(recid)
if fixed:
print "Now we need to synchronize MARC to reflect current changes."
cli_fix_marc(options, explicit_recid_set=fixed)
print wrap_text_in_a_box("%i out of %i record needed to be fixed." % (tot, len(fixed)), style="conclusion")
return not fixed
def cli_delete(options):
"""Delete the given docid_set."""
ffts = {}
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
docname = bibdoc.get_docname()
recid = bibdoc.get_recid()
if recid not in ffts:
ffts[recid] = [{'docname' : docname, 'doctype' : 'DELETE'}]
else:
ffts[recid].append({'docname' : docname, 'doctype' : 'DELETE'})
return bibupload_ffts(ffts)
def cli_delete_file(options):
"""Delete the given file irreversibely."""
docid = cli2docid(options)
recid = cli2recid(options, docids=intbitset([docid]))
format = cli2format(options)
docname = BibDoc(docid).get_docname()
version = getattr(options, 'version', None)
ffts = {recid : [{'docname' : docname, 'version' : version, 'format' : format, 'doctype' : 'DELETE-FILE'}]}
return bibupload_ffts(ffts)
def cli_revert(options):
"""Revert a bibdoc to a given version."""
docid = cli2docid(options)
recid = cli2recid(options, docids=intbitset([docid]))
docname = BibDoc(docid).get_docname()
version = getattr(options, 'version', None)
try:
version = int(version)
if 0 >= version:
raise ValueError
except ValueError:
raise OptionValueError, 'when reverting, version should be valid positive integer, not %s' % version
ffts = {recid : [{'docname' : docname, 'version' : version, 'doctype' : 'REVERT'}]}
return bibupload_ffts(ffts)
def cli_undelete(options):
"""Delete the given docname"""
docname = cli2docname(options)
restriction = getattr(options, 'restriction', None)
count = 0
if not docname:
docname = 'DELETED-*-*'
if not docname.startswith('DELETED-'):
docname = 'DELETED-*-' + docname
to_be_undeleted = intbitset()
fix_marc = intbitset()
setattr(options, 'deleted_docs', 'only')
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
if bibdoc.get_status() == 'DELETED' and fnmatch.fnmatch(bibdoc.get_docname(), docname):
to_be_undeleted.add(docid)
fix_marc.add(bibdoc.get_recid())
count += 1
print '%s (docid %s from recid %s) will be undeleted to restriction: %s' % (bibdoc.get_docname(), docid, bibdoc.get_recid(), restriction)
wait_for_user("I'll proceed with the undeletion")
for docid in to_be_undeleted:
bibdoc = BibDoc(docid)
bibdoc.undelete(restriction)
cli_fix_marc(options, explicit_recid_set=fix_marc)
print wrap_text_in_a_box("%s bibdoc successfuly undeleted with status '%s'" % (count, restriction), style="conclusion")
def cli_get_info(options):
"""Print all the info of the matched docids or recids."""
debug('Getting info!')
human_readable = bool(getattr(options, 'human_readable', None))
debug('human_readable: %s' % human_readable)
deleted_docs = getattr(options, 'deleted_docs', None) in ('yes', 'only')
debug('deleted_docs: %s' % deleted_docs)
if getattr(options, 'docids', None):
for docid in cli_docids_iterator(options):
sys.stdout.write(str(BibDoc(docid, human_readable=human_readable)))
else:
for recid in cli_recids_iterator(options):
sys.stdout.write(str(BibRecDocs(recid, deleted_too=deleted_docs, human_readable=human_readable)))
def cli_purge(options):
"""Purge the matched docids."""
ffts = {}
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
recid = bibdoc.get_recid()
docname = bibdoc.get_docname()
if recid:
if recid not in ffts:
ffts[recid] = []
ffts[recid].append({
'docname' : docname,
'doctype' : 'PURGE',
})
return bibupload_ffts(ffts)
def cli_expunge(options):
"""Expunge the matched docids."""
ffts = {}
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
recid = bibdoc.get_recid()
docname = bibdoc.get_docname()
if recid:
if recid not in ffts:
ffts[recid] = []
ffts[recid].append({
'docname' : docname,
'doctype' : 'EXPUNGE',
})
return bibupload_ffts(ffts)
def cli_get_history(options):
"""Print the history of a docid_set."""
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
history = bibdoc.get_history()
for row in history:
print_info(bibdoc.get_recid(), docid, row)
def cli_get_disk_usage(options):
"""Print the space usage of a docid_set."""
human_readable = getattr(options, 'human_readable', None)
total_size = 0
total_latest_size = 0
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
size = bibdoc.get_total_size()
total_size += size
latest_size = bibdoc.get_total_size_latest_version()
total_latest_size += latest_size
if human_readable:
print_info(bibdoc.get_recid(), docid, 'size=%s' % nice_size(size))
print_info(bibdoc.get_recid(), docid, 'latest version size=%s' % nice_size(latest_size))
else:
print_info(bibdoc.get_recid(), docid, 'size=%s' % size)
print_info(bibdoc.get_recid(), docid, 'latest version size=%s' % latest_size)
if human_readable:
print wrap_text_in_a_box('total size: %s\n\nlatest version total size: %s'
% (nice_size(total_size), nice_size(total_latest_size)),
style='conclusion')
else:
print wrap_text_in_a_box('total size: %s\n\nlatest version total size: %s'
% (total_size, total_latest_size),
style='conclusion')
def cli_check_md5(options):
"""Check the md5 sums of a docid_set."""
failures = 0
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
if bibdoc.md5s.check():
print_info(bibdoc.get_recid(), docid, 'checksum OK')
else:
for afile in bibdoc.list_all_files():
if not afile.check():
failures += 1
print_info(bibdoc.get_recid(), docid, '%s failing checksum!' % afile.get_full_path())
if failures:
print wrap_text_in_a_box('%i files failing' % failures , style='conclusion')
else:
print wrap_text_in_a_box('All files are correct', style='conclusion')
def cli_update_md5(options):
"""Update the md5 sums of a docid_set."""
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
if bibdoc.md5s.check():
print_info(bibdoc.get_recid(), docid, 'checksum OK')
else:
for afile in bibdoc.list_all_files():
if not afile.check():
print_info(bibdoc.get_recid(), docid, '%s failing checksum!' % afile.get_full_path())
wait_for_user('Updating the md5s of this document can hide real problems.')
bibdoc.md5s.update(only_new=False)
def cli_hide(options):
"""Hide the matched versions of documents."""
documents_to_be_hidden = {}
to_be_fixed = intbitset()
versions = getattr(options, 'versions', 'all')
if versions != 'all':
try:
versions = ranges2ids(versions)
except:
raise OptionValueError, 'You should specify correct versions. Not %s' % versions
else:
versions = intbitset(trailing_bits=True)
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
recid = bibdoc.get_recid()
if recid:
for bibdocfile in bibdoc.list_all_files():
this_version = bibdocfile.get_version()
this_format = bibdocfile.get_format()
if this_version in versions:
if docid not in documents_to_be_hidden:
documents_to_be_hidden[docid] = []
documents_to_be_hidden[docid].append((this_version, this_format))
to_be_fixed.add(recid)
print '%s (docid: %s, recid: %s) will be hidden' % (bibdocfile.get_full_name(), docid, recid)
wait_for_user('Proceeding to hide the matched documents...')
for docid, documents in documents_to_be_hidden.iteritems():
bibdoc = BibDoc(docid)
for version, format in documents:
bibdoc.set_flag('HIDDEN', format, version)
return cli_fix_marc(options, to_be_fixed)
def cli_unhide(options):
"""Unhide the matched versions of documents."""
documents_to_be_unhidden = {}
to_be_fixed = intbitset()
versions = getattr(options, 'versions', 'all')
if versions != 'all':
try:
versions = ranges2ids(versions)
except:
raise OptionValueError, 'You should specify correct versions. Not %s' % versions
else:
versions = intbitset(trailing_bits=True)
for docid in cli_docids_iterator(options):
bibdoc = BibDoc(docid)
recid = bibdoc.get_recid()
if recid:
for bibdocfile in bibdoc.list_all_files():
this_version = bibdocfile.get_version()
this_format = bibdocfile.get_format()
if this_version in versions:
if docid not in documents_to_be_unhidden:
documents_to_be_unhidden[docid] = []
documents_to_be_unhidden[docid].append((this_version, this_format))
to_be_fixed.add(recid)
print '%s (docid: %s, recid: %s) will be unhidden' % (bibdocfile.get_full_name(), docid, recid)
wait_for_user('Proceeding to unhide the matched documents...')
for docid, documents in documents_to_be_unhidden.iteritems():
bibdoc = BibDoc(docid)
for version, format in documents:
bibdoc.unset_flag('HIDDEN', format, version)
return cli_fix_marc(options, to_be_fixed)
def main():
parser = prepare_option_parser()
(options, args) = parser.parse_args()
if getattr(options, 'debug', None):
getLogger().setLevel(DEBUG)
debug('test')
debug('options: %s, args: %s' % (options, args))
try:
if not getattr(options, 'action', None) and \
not getattr(options, 'append_path', None) and \
not getattr(options, 'revise_path', None):
if getattr(options, 'set_doctype', None) is not None or \
getattr(options, 'set_comment', None) is not None or \
getattr(options, 'set_description', None) is not None or \
getattr(options, 'set_restriction', None) is not None:
cli_set_batch(options)
elif getattr(options, 'new_docname', None):
cli_rename(options)
else:
print >> sys.stderr, "ERROR: no action specified"
sys.exit(1)
elif getattr(options, 'append_path', None):
options.empty_recs = 'yes'
options.empty_docs = 'yes'
cli_append(options, getattr(options, 'append_path', None))
elif getattr(options, 'revise_path', None):
cli_revise(options, getattr(options, 'revise_path', None))
elif options.action == 'textify':
cli_textify(options)
elif getattr(options, 'action', None) == 'get-history':
cli_get_history(options)
elif getattr(options, 'action', None) == 'get-info':
cli_get_info(options)
elif getattr(options, 'action', None) == 'get-disk-usage':
cli_get_disk_usage(options)
elif getattr(options, 'action', None) == 'check-md5':
cli_check_md5(options)
elif getattr(options, 'action', None) == 'update-md5':
cli_update_md5(options)
elif getattr(options, 'action', None) == 'fix-all':
cli_fix_all(options)
elif getattr(options, 'action', None) == 'fix-marc':
cli_fix_marc(options)
elif getattr(options, 'action', None) == 'delete':
cli_delete(options)
elif getattr(options, 'action', None) == 'hard-delete':
cli_delete_file(options)
elif getattr(options, 'action', None) == 'fix-duplicate-docnames':
cli_fix_duplicate_docnames(options)
elif getattr(options, 'action', None) == 'fix-format':
cli_fix_format(options)
elif getattr(options, 'action', None) == 'check-duplicate-docnames':
cli_check_duplicate_docnames(options)
elif getattr(options, 'action', None) == 'check-format':
cli_check_format(options)
elif getattr(options, 'action', None) == 'undelete':
cli_undelete(options)
elif getattr(options, 'action', None) == 'purge':
cli_purge(options)
elif getattr(options, 'action', None) == 'expunge':
cli_expunge(options)
elif getattr(options, 'action', None) == 'revert':
cli_revert(options)
elif getattr(options, 'action', None) == 'hide':
cli_hide(options)
elif getattr(options, 'action', None) == 'unhide':
cli_unhide(options)
else:
print >> sys.stderr, "ERROR: Action %s is not valid" % getattr(options, 'action', None)
sys.exit(1)
except Exception, e:
register_exception()
print >> sys.stderr, 'ERROR: %s' % e
sys.exit(1)
if __name__ == '__main__':
main()
diff --git a/modules/websubmit/lib/functions/Mail_Approval_Request_to_Committee_Chair.py b/modules/websubmit/lib/functions/Mail_Approval_Request_to_Committee_Chair.py
index 8931d0a4d..38bb3a7d0 100644
--- a/modules/websubmit/lib/functions/Mail_Approval_Request_to_Committee_Chair.py
+++ b/modules/websubmit/lib/functions/Mail_Approval_Request_to_Committee_Chair.py
@@ -1,162 +1,163 @@
## $id: Mail_Approval_Request_to_Committee_Chair.py,v 0.01 2008/07/25 18:33:44 tibor Exp $
## This file is part of Invenio.
## Copyright (C) 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
##
## Name: Mail_Approval_Request_to_Committee_Chair.py
## Description: function Mail_Approval_Request_to_Committee_Chair.py
## This function sends a confirmation email to the Committee Chair
## when approval for a document is requested.
## Author: T.Baron (first); C.Parker
##
## PARAMETERS: authorfile: name of the file containing the author
## titleFile: name of the file containing the title
## emailFile: name of the file containing the email
## status: one of "ADDED" (the document has been integrated
## into the database) or "APPROVAL" (an email has
## been sent to a referee - simple approval)
## edsrn: name of the file containing the reference
## newrnin: name of the file containing the 2nd reference
## (if any)
##
from invenio.config import CFG_SITE_NAME, \
CFG_SITE_URL, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.mailutils import send_email
from invenio.access_control_admin import acc_get_role_id, acc_get_role_users
from invenio.search_engine import search_pattern, get_fieldvalues
from invenio.dbquery import run_sql
#Copied from publiline
def get_brief_doc_details_from_repository(reportnumber):
"""Try to get some brief details about the submission that is awaiting
the referee's decision.
Details sought are:
title
+ Authors
+ recid (why?)
+ report-number (why?)
This function searches in the Invenio repository, based on
"reportnumber" for a record and then pulls the interesting fields
from it.
@param reportnumber: (string) - the report number of the item for
which details are to be recovered. It is used in the search.
@return: (dictionary or None) - If details are found for the item,
they will be returned in a dictionary structured as follows:
{ 'title' : '-', ## String - the item's title
'recid' : '', ## String - recid taken from the SN file
'report-number' : '', ## String - the item's report number
'authors' : [], ## List - the item's authors
}
If no details were found a NoneType is returned.
"""
## Details of the pending document, as found in the repository:
pending_doc_details = None
## Search for records matching this "report number"
found_record_ids = list(search_pattern(req=None, \
p=reportnumber, \
f="reportnumber", \
m="e"))
## How many records were found?
if len(found_record_ids) == 1:
## Found only 1 record. Get the fields of interest:
pending_doc_details = { 'title' : '-',
'recid' : '',
'report-number' : '',
'authors' : [],
}
recid = found_record_ids[0]
## Authors:
first_author = get_fieldvalues(recid, "100__a")
for author in first_author:
pending_doc_details['authors'].append(author)
other_authors = get_fieldvalues(recid, "700__a")
for author in other_authors:
pending_doc_details['authors'].append(author)
## Title:
title = get_fieldvalues(recid, "245__a")
if len(title) > 0:
pending_doc_details['title'] = title[0]
else:
## There was no value for title - check for an alternative title:
alt_title = get_fieldvalues(recid, "2641_a")
if len(alt_title) > 0:
pending_doc_details['title'] = alt_title[0]
## Record ID:
pending_doc_details['recid'] = recid
## Report Number:
reptnum = get_fieldvalues(recid, "037__a")
if len(reptnum) > 0:
pending_doc_details['report-number'] = reptnum[0]
elif len(found_record_ids) > 1:
## Oops. This is unexpected - there shouldn't be me multiple matches
## for this item. The old "getInAlice" function would have simply
## taken the first record in the list. That's not very nice though.
## Some kind of warning or error should be raised here. FIXME.
pass
return pending_doc_details
def Mail_Approval_Request_to_Committee_Chair(parameters, curdir, form, user_info=None):
"""
This function sends a confirmation email to the Committee Chair
when approval for a document is requested.
"""
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
# retrieve useful information from webSubmit configuration
res = run_sql("select * from sbmCPLXAPPROVAL where rn=%s", (rn, ))
categ = res[0][1]
pubcomchair_address = ""
# Try to retrieve the committee chair's email from the referee's database
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_%s" % (res[0][0],categ))):
pubcomchair_address += user[1]
#Get the document details from the repository - use the function in publiline.py
item_details = get_brief_doc_details_from_repository(rn)
#Generate the author list
authors = ""
for element in item_details['authors']:
authors += element + ", "
message = """
The document %s has been published as a Communication.
Please select an appropriate referee for this document.
Title: %s
Author(s): %s
To access the document(s), select the file(s) from the location:
- <%s/record/%s>
+ <%s/%s/%s>
To select a referee, please go to:
<%s/publiline.py?flow=cplx&doctype=%s&categ=%s&apptype=%s&RN=%s&ln=en>
---------------------------------------------
Best regards.
- The submission team.""" % (rn,item_details['title'],authors,CFG_SITE_URL,sysno,CFG_SITE_URL,res[0][0],res[0][1],res[0][3],rn)
+ The submission team.""" % (rn,item_details['title'],authors,CFG_SITE_URL,CFG_SITE_RECORD,sysno,CFG_SITE_URL,res[0][0],res[0][1],res[0][3],rn)
# send the mail
send_email(FROMADDR,pubcomchair_address,"Request for Referee Selection : Document %s" % rn, message,footer="")
return ""
diff --git a/modules/websubmit/lib/functions/Mail_Approval_Request_to_Referee.py b/modules/websubmit/lib/functions/Mail_Approval_Request_to_Referee.py
index 9bef09123..481262d40 100644
--- a/modules/websubmit/lib/functions/Mail_Approval_Request_to_Referee.py
+++ b/modules/websubmit/lib/functions/Mail_Approval_Request_to_Referee.py
@@ -1,407 +1,409 @@
## This file is part of Invenio.
## Copyright (C) 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Mail_Approval_Request_to_Referee: A function to send an email to the referee
of a document informing him/her that a request for its approval has been
submitted by the user.
"""
__revision__ = "$Id$"
import os
import re
import sre_constants
from invenio.websubmit_dblayer import get_approval_request_notes
from invenio.websubmit_config import InvenioWebSubmitFunctionError, \
CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.config import CFG_CERN_SITE, \
CFG_SITE_NAME, \
CFG_SITE_URL, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.access_control_admin import acc_get_role_users, acc_get_role_id
from invenio.websubmit_functions.Shared_Functions import ParamFromFile
from invenio.errorlib import register_exception
from invenio.search_engine import print_record
from invenio.mailutils import send_email
CFG_MAIL_BODY = """
A request for the approval of a document in the %(site-name)s has been
made and requires your attention as a referee. The details are as
follows:
Reference Number: [%(report-number)s]
Title: %(title)s
Author(s): %(authors)s
You can see the details of the record at the following address:
- <%(site-url)s/record/%(record-id)s>
+ <%(site-url)s/%(CFG_SITE_RECORD)s/%(record-id)s>
Please register your decision by following the instructions at the
following address:
<%(site-url)s/submit/direct?%(report-number-fieldname)s=%(report-number)s&sub=%(approval-action)s%(doctype)s&combo%(doctype)s=%(category)s>
Below, you may find some additional information about the approval request:
%(notes)s
"""
def Mail_Approval_Request_to_Referee(parameters, curdir, form, user_info=None):
"""
This function sends an email to the referee of a document informing
him/her that a request for its approval has been submitted by the
user.
@param categ_file_appreq: (string) - some document types are
separated into different categories, each of which has its own
referee(s).
In such document types, it's necessary to know the document-
type's category in order to choose the referee.
This parameter provides a means by which the category information
can be extracted from a file in the current submission's working
directory. It should therefore be a filename.
@param categ_rnseek_appreq: (string) - some document types are
separated into different categories, each of which has its own
referee(s).
In such document types, it's necessary to know the document-
type's category in order to choose the referee.
This parameter provides a means by which the category information
can be extracted from the document's reference number.
It is infact a string that will be compiled into a regexp and
an attempt will be made to match it agains the document's reference
number starting from the left-most position.
The only pre-requisite is that the segment in which the category is
sought should be indicated with <CATEGORY>.
Thus, an example might be as follows:
ATL(-COM)?-<CATEGORY>-.+
This would allow "PHYS" in the following reference number to be
recognised as the category:
ATL-COM-PHYS-2008-001
@param edsrn: (string) - the name of the field in which the report
number should be placed when the referee visits the form for making
a decision.
@return: (string) - empty string.
"""
## Get the reference number (as global rn - sorry!) and the document type:
global sysno, rn
doctype = form['doctype']
########
## Get the parameters from the list:
########
## Get the name of the report-number file:
########
try:
edsrn_file = parameters["edsrn"]
except KeyError:
## No value given for the edsrn file:
msg = "Error in Mail_Approval_Request_to_Referee function: unable " \
"to determine the name of the file in which the document's " \
"report number should be stored."
raise InvenioWebSubmitFunctionError(msg)
else:
edsrn_file = str(edsrn_file)
edsrn_file = os.path.basename(edsrn_file).strip()
if edsrn_file == "":
msg = "Error in Mail_Approval_Request_to_Referee function: " \
"unable to determine the name of the file in which " \
"the document's report number should be stored."
raise InvenioWebSubmitFunctionError(msg)
########
## Get the name of the category file:
#######
try:
## If it has been provided, get the name of the file in which the
## category is stored:
category_file = parameters["categ_file_appreq"]
except KeyError:
## No value given for the category file:
category_file = None
else:
if category_file is not None:
category_file = str(category_file)
category_file = os.path.basename(category_file).strip()
if category_file == "":
category_file = None
########
## Get the regexp that is used to find the category in the report number:
########
try:
## If it has been provided, get the regexp used for identifying
## a document-type's category from its reference number:
category_rn_regexp = parameters["categ_rnseek_appreq"]
except KeyError:
## No value given for the category regexp:
category_rn_regexp = None
else:
if category_rn_regexp is not None:
category_rn_regexp = str(category_rn_regexp).strip()
if category_rn_regexp == "":
category_rn_regexp = None
#######
## Resolve the document type's category:
##
## This is a long process. The end result is that the category is extracted
## either from a file in curdir, or from the report number.
## If it's taken from the report number, the admin must configure the
## function to accept a regular expression that is used to find the
## category in the report number.
##
if category_file is not None and category_rn_regexp is not None:
## It is not valid to have both a category file and a pattern
## describing how to extract the category from a report number.
## raise an InvenioWebSubmitFunctionError
msg = "Error in Register_Approval_Request function: received " \
"instructions to search for the document's category in " \
"both its report number AND in a category file. Could " \
"not determine which to use - please notify the " \
"administrator."
raise InvenioWebSubmitFunctionError(msg)
elif category_file is not None:
## Attempt to recover the category information from a file in the
## current submission's working directory:
category = ParamFromFile("%s/%s" % (curdir, category_file))
if category is not None:
category = category.strip()
if category in (None, ""):
## The category cannot be resolved.
msg = "Error in Register_Approval_Request function: received " \
"instructions to search for the document's category in " \
"a category file, but could not recover the category " \
"from that file. An approval request therefore cannot " \
"be registered for the document."
raise InvenioWebSubmitFunctionError(msg)
elif category_rn_regexp is not None:
## Attempt to recover the category information from the document's
## reference number using the regexp in category_rn_regexp:
##
## Does the category regexp contain the key-phrase "<CATEG>"?
if category_rn_regexp.find("<CATEG>") != -1:
## Yes. Replace "<CATEG>" with "(?P<category>.+?)".
## For example, this:
## ATL(-COM)?-<CATEG>-
## Will be transformed into this:
## ATL(-COM)?-(?P<category>.+?)-
category_rn_final_regexp = \
category_rn_regexp.replace("<CATEG>", r"(?P<category>.+?)", 1)
else:
## The regexp for category didn't contain "<CATEG>", but this is
## mandatory.
msg = "Error in Register_Approval_Request function: The " \
"[%(doctype)s] submission has been configured to search " \
"for the document type's category in its reference number, " \
"using a poorly formed search expression (no marker for " \
"the category was present.) Since the document's category " \
"therefore cannot be retrieved, an approval request cannot " \
"be registered for it. Please report this problem to the " \
"administrator." \
% { 'doctype' : doctype, }
raise InvenioWebSubmitFunctionError(msg)
##
try:
## Attempt to compile the regexp for finding the category:
re_categ_from_rn = re.compile(category_rn_final_regexp)
except sre_constants.error:
## The expression passed to this function could not be compiled
## into a regexp. Register this exception and raise an
## InvenioWebSubmitFunctionError:
exception_prefix = "Error in Register_Approval_Request function: " \
"The [%(doctype)s] submission has been " \
"configured to search for the document type's " \
"category in its reference number, using the " \
"following regexp: /%(regexp)s/. This regexp, " \
"however, could not be compiled correctly " \
"(created it from %(categ-search-term)s.)" \
% { 'doctype' : doctype, \
'regexp' : category_rn_final_regexp, \
'categ-search-term' : category_rn_regexp, }
register_exception(prefix=exception_prefix)
msg = "Error in Register_Approval_Request function: The " \
"[%(doctype)s] submission has been configured to search " \
"for the document type's category in its reference number, " \
"using a poorly formed search expression. Since the " \
"document's category therefore cannot be retrieved, an " \
"approval request cannot be registered for it. Please " \
"report this problem to the administrator." \
% { 'doctype' : doctype, }
raise InvenioWebSubmitFunctionError(msg)
else:
## Now attempt to recover the category from the RN string:
m_categ_from_rn = re_categ_from_rn.match(rn)
if m_categ_from_rn is not None:
## The pattern matched in the string.
## Extract the category from the match:
try:
category = m_categ_from_rn.group("category")
except IndexError:
## There was no "category" group. That group is mandatory.
exception_prefix = \
"Error in Register_Approval_Request function: The " \
"[%(doctype)s] submission has been configured to " \
"search for the document type's category in its " \
"reference number using the following regexp: " \
"/%(regexp)s/. The search produced a match, but " \
"there was no \"category\" group in the match " \
"object although this group is mandatory. The " \
"regexp was compiled from the following string: " \
"[%(categ-search-term)s]." \
% { 'doctype' : doctype, \
'regexp' : category_rn_final_regexp, \
'categ-search-term' : category_rn_regexp, }
register_exception(prefix=exception_prefix)
msg = "Error in Register_Approval_Request function: The " \
"[%(doctype)s] submission has been configured to " \
"search for the document type's category in its " \
"reference number, using a poorly formed search " \
"expression (there was no category marker). Since " \
"the document's category therefore cannot be " \
"retrieved, an approval request cannot be " \
"registered for it. Please report this problem to " \
"the administrator." \
% { 'doctype' : doctype, }
raise InvenioWebSubmitFunctionError(msg)
else:
category = category.strip()
if category == "":
msg = "Error in Register_Approval_Request function: " \
"The [%(doctype)s] submission has been " \
"configured to search for the document type's " \
"category in its reference number, but no " \
"category was found. The request for approval " \
"cannot be registered. Please report this " \
"problem to the administrator." \
% { 'doctype' : doctype, }
raise InvenioWebSubmitFunctionError(msg)
else:
## No match. Cannot find the category and therefore cannot
## continue:
msg = "Error in Register_Approval_Request function: The " \
"[%(doctype)s] submission has been configured to " \
"search for the document type's category in its " \
"reference number, but no match was made. The request " \
"for approval cannot be registered. Please report " \
"this problem to the administrator." \
% { 'doctype' : doctype, }
raise InvenioWebSubmitFunctionError(msg)
else:
## The document type has no category.
category = ""
##
## End of category recovery
#######
#######
## Get the title and author(s) from the record:
#######
## Author(s):
rec_authors = ""
rec_first_author = print_record(int(sysno), 'tm', "100__a")
rec_other_authors = print_record(int(sysno), 'tm', "700__a")
if rec_first_author != "":
rec_authors += "".join(["%s\n" % author.strip() for \
author in rec_first_author.split("\n")])
if rec_other_authors != "":
rec_authors += "".join(["%s\n" % author.strip() for \
author in rec_other_authors.split("\n")])
## Title:
rec_title = "".join(["%s\n" % title.strip() for title in \
print_record(int(sysno), 'tm', "245__a").split("\n")])
##
#######
## the normal approval action
approve_act = 'APP'
## Get notes about the approval request:
approval_notes = get_approval_request_notes(doctype, rn)
## Get the referee email address:
if CFG_CERN_SITE:
## The referees system in CERN now works with listbox membership.
## List names should take the format
## "service-cds-referee-doctype-category@cern.ch"
## Make sure that your list exists!
## FIXME - to be replaced by a mailing alias in webaccess in the
## future.
if doctype == 'ATN': ## Special case of 'RPR' action for doctype ATN
action = ParamFromFile("%s/%s" % (curdir,'act')).strip()
if action == 'RPR':
notetype = ParamFromFile("%s/%s" % (curdir,'ATN_NOTETYPE')).strip()
if notetype not in ('SLIDE','PROC'):
raise InvenioWebSubmitFunctionError('ERROR function Mail_Approval_Request_to_Referee:: do not recognize notetype ' + notetype)
if notetype == 'PROC':
approve_act = 'APR' # RPR PROC requires APR action to approve
referee_listname = "service-cds-referee-atn-proc@cern.ch"
elif notetype == 'SLIDE': ## SLIDES approval
approve_act = 'APS' # RPR SLIDE requires APS action to approve
referee_listname = "atlas-speakers-comm@cern.ch"
else:
raise InvenioWebSubmitFunctionError('ERROR function Mail_Approval_Request_to_Referee:: do not understand notetype: ' +notetype)
else:
referee_listname = "service-cds-referee-%s" % doctype.lower()
if category != "":
referee_listname += "-%s" % category.lower()
mailto_addresses = referee_listname + "@cern.ch"
if category == 'CDSTEST':
referee_listname = "service-cds-referee-%s" % doctype.lower()
referee_listname += "-%s" % category.lower()
mailto_addresses = referee_listname + "@cern.ch"
else:
referee_address = ""
## Try to retrieve the referee's email from the referee's database:
for user in \
acc_get_role_users(acc_get_role_id("referee_%s_%s" \
% (doctype, category))):
referee_address += user[1] + ","
## And if there are general referees:
for user in \
acc_get_role_users(acc_get_role_id("referee_%s_*" % doctype)):
referee_address += user[1] + ","
referee_address = re.sub(",$", "", referee_address)
# Creation of the mail for the referee
mailto_addresses = ""
if referee_address != "":
mailto_addresses = referee_address + ","
else:
mailto_addresses = re.sub(",$", "", mailto_addresses)
##
## Send the email:
mail_subj = "Request for approval of [%s]" % rn
mail_body = CFG_MAIL_BODY % \
{ 'site-name' : CFG_SITE_NAME,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'report-number-fieldname' : edsrn_file,
'report-number' : rn,
'title' : rec_title,
'authors' : rec_authors,
'site-url' : CFG_SITE_URL,
'record-id' : sysno,
'approval-action' : approve_act,
'doctype' : doctype,
'notes' : approval_notes,
'category' : category,
}
send_email(CFG_SITE_SUPPORT_EMAIL,
mailto_addresses,
mail_subj,
mail_body,
copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
##
return ""
diff --git a/modules/websubmit/lib/functions/Mail_New_Record_Notification.py b/modules/websubmit/lib/functions/Mail_New_Record_Notification.py
index 4d7d2a2a2..73e8aa2bf 100644
--- a/modules/websubmit/lib/functions/Mail_New_Record_Notification.py
+++ b/modules/websubmit/lib/functions/Mail_New_Record_Notification.py
@@ -1,307 +1,309 @@
# -*- coding: utf-8 -*-
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""This module contains the WebSubmit function "Mail_New_Record_Notification",
which should be called when a new record has been submitted to the repository
and notified of the fact should be sent by mail to the submitters/requester/
admins/other general managers.
"""
__revision__ = "$Id$"
-from invenio.config import CFG_SITE_NAME, CFG_SITE_SUPPORT_EMAIL, CFG_SITE_URL, CFG_SITE_ADMIN_EMAIL
+from invenio.config import CFG_SITE_NAME, CFG_SITE_SUPPORT_EMAIL, CFG_SITE_URL, CFG_SITE_ADMIN_EMAIL, \
+ CFG_SITE_RECORD
from invenio.webuser import email_valid_p
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
CFG_EMAIL_FROM_ADDRESS = '%s Submission Engine <%s>' % (CFG_SITE_NAME, CFG_SITE_SUPPORT_EMAIL)
def Mail_New_Record_Notification(parameters, curdir, form, user_info=None):
"""
This function sends a mail giving notification about the submission
of a new item to the relevant recipients, including:
+ The record's Submitter(s);
+ The site ADMIN;
+ The record-type's "managers" (signified by the "submit_managers"
parameter);
The mail contains details of the new item's reference number(s), its
title and its author(s). It also contains a link to the item in the
Invenio repository.
@param parameters: (dictionary) - contains the following parameter
strings used by this function:
+ item_status: (string) - the status of the new item. It can be
either "ADDED" (in which case the new item has been integrated
into the repository), or "APPROVAL" (in which case the item is
awaiting a referee's approval before being integrated into the
repository, and the mail should state this fact);
+ mail_submitters: (string) - a flag containing "Y" or "N" (defaulting
to "Y"). Determines whether or not the notification mail will be
sent to the submitters;
+ item_managers: (string) - a comma-separated list of email
addresses, each of which corresponds to a "manager" for the class
of item that has been submitted. These managers will receive the
notification message sent by this function;
+ author_file: (string) - the name of a file that contains the names
of the item's authors (one author per line);
+ title_file: (string) - the name of a file that contains the title
of the new item;
+ owners_file: (string) - the name of a file that contains the email
addresses of the "owners" of the submitted item. I.e. those who
will be classed as "submitters" of the item and will therefore
have modification rights over it. The mail will be sent to these
people. There should be one email-address per line in this file;
+ rn_file1: (string) - the name of the the file containing the item's
principal reference number;
+ rn_file2: (string) - the name of the file containing the item's
additional reference number(s) (e.g. sometimes two reference numbers
are allocated during the submission process;
@param curdir: (string) - the current submission's working directory. All
files containing data related to the submission are stored here and
therefore all of the files referred to in the "parameters" dictionary
are considered to be within "curdir";
@param form: (string) - a dictionary-like structure containing the fields
that were present in the WebSubmit submission form;
@return: (string) - an empty string;
"""
global sysno ## (I'm really sorry for that! :-O )
## Read items from the parameters array into local vars:
item_status = parameters["item_status"]
mail_submitters = parameters["mail_submitters"]
item_managers = parameters["item_managers"]
author_file = parameters["author_file"]
title_file = parameters["title_file"]
owners_file = parameters["owners_file"]
rn_file1 = parameters["rn_file1"]
rn_file2 = parameters["rn_file2"]
## Now wash the parameters' values:
##
## item_status:
try:
## If item_status isn't "added" or "approval", make it "added" by
## default. Else, keep its value:
item_status = (item_status.upper() in ("ADDED", "APPROVAL") \
and item_status.upper()) or "ADDED"
except AttributeError:
## Oops - item_status wasn't a string (NoneType?) Anyway, default
## it to "ADDED".
item_status = "ADDED"
## mail_submitters:
try:
## If mail_submitters isn't "Y" or "N", make it "Y" by
## default. Else, keep its value:
mail_submitters = (mail_submitters.upper() in ("Y", "N") \
and mail_submitters.upper()) or "Y"
except AttributeError:
## Oops - mail_submitters wasn't a string (NoneType?) Anyway, default
## it to "Y".
mail_submitters = "Y"
## item_managers:
## A string in which the item_managers' email addresses will be stored:
managers_email = ""
try:
## We assume that the email addresses of item managers are
## separated by commas.
item_managers_list = item_managers.split(",")
for manager in item_managers_list:
manager_address = manager.strip()
## Test that this manager's email address is OK, adding it if so:
if email_valid_p(manager_address):
## This address is OK - add it to the string of manager
## addresses:
managers_email += "%s," % manager_address
## Strip the trailing comma from managers_email (if there is one):
managers_email = managers_email.strip().rstrip(",")
except AttributeError:
## Oops - item_managers doesn't seem to be a string? Treat it as
## though it were empty:
managers_email = ""
## author_file:
authors = ""
try:
## Read in the authors from author_file, putting them into the "authors"
## variable, one per line:
fp_author_file = open("%s/%s" % (curdir, author_file), "r")
for author in fp_author_file:
authors += "%s\n" % author.strip()
fp_author_file.close()
except IOError:
## Unable to correctly read from "author_file", Skip it as though
## there were no authors:
authors = "-"
## title_file:
title = ""
try:
## Read in the lines from title_file, putting them into the "title"
## variable on one line:
fp_title_file = open("%s/%s" % (curdir, title_file), "r")
for line in fp_title_file:
title += "%s " % line.strip()
fp_title_file.close()
title = title.strip()
except IOError:
## Unable to correctly read from "title_file", Skip it as though
## there were no title:
title = "-"
## owners_file:
## A string in which the item_owners' email addresses will be stored:
owners_email = ""
try:
fp_owners_file = open("%s/%s" % (curdir, owners_file), "r")
for line in fp_owners_file:
owner_address = line.strip()
## Test that this owner's email address is OK, adding it if so:
if email_valid_p(owner_address):
## This address is OK - add it to the string of item owner
## addresses:
owners_email += "%s," % owner_address
## Strip the trailing comma from owners_email (if there is one):
owners_email = owners_email.strip().rstrip(",")
except IOError:
## Unable to correctly read from "owners_file", Skip it as though
## there were no title:
owners_email = ""
## Add "SuE" (the submitter) into the list of document "owners":
try:
fp_sue = open("%s/SuE" % curdir, "r")
sue = fp_sue.readline()
fp_sue.close()
except IOError:
sue = ""
else:
if sue.lower() not in owners_email.lower().split(","):
## The submitter is not listed in the "owners" mails,
## add her:
owners_email = "%s,%s" % (sue, owners_email)
owners_email = owners_email.strip().rstrip(",")
## rn_file1 & rn_file2:
reference_numbers = ""
try:
fp_rnfile1 = open("%s/%s" % (curdir, rn_file1), "r")
for line in fp_rnfile1:
reference_number = line.strip()
reference_number = \
reference_number.replace("\n", "").replace("\r", "").\
replace(" ", "")
if reference_number != "":
## Add this reference number into the "reference numbers"
## variable:
reference_numbers += "%s " % reference_number
fp_rnfile1.close()
except IOError:
reference_numbers = ""
try:
fp_rnfile2 = open("%s/%s" % (curdir, rn_file2), "r")
for line in fp_rnfile2:
reference_number = line.strip()
reference_number = \
reference_number.replace("\n", "").replace("\r", "").\
replace(" ", "")
if reference_number != "":
## Add this reference number into the "reference numbers"
## variable:
reference_numbers += "%s " % reference_number
fp_rnfile2.close()
except IOError:
pass
## Strip any trailing whitespace from the reference numbers:
reference_numbers = reference_numbers.strip()
## Now build the email from the information we've collected:
email_txt = """
The following item has been submitted to %(sitename)s:
Reference(s): %(reference)s
Title: %(title)s
Author(s): %(author)s
""" % { 'sitename' : CFG_SITE_NAME,
'reference' : reference_numbers,
'title' : title,
'author' : authors,
}
if item_status == "ADDED":
## The item has been added into the repository.
email_txt += """
It will soon be made available and you will be able to check it at the
following URL:
- <%(siteurl)s/record/%(record-id)s>
+ <%(siteurl)s/%(CFG_SITE_RECORD)s/%(record-id)s>
Please report any problems to <%(sitesupportemail)s>.
""" % { 'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD' : CFG_SITE_RECORD,
'record-id' : sysno,
'sitesupportemail' : CFG_SITE_SUPPORT_EMAIL,
}
else:
## The item has not yet been added - instead it awaits the
## approval of a referee. Let the email reflect this detail:
email_txt += """
The item is now awaiting a referee's approval before being integrated
into the repository. You will be alerted by email as soon as a decision
has been taken.
"""
## Finish the message with a signature:
email_txt += """
Thank you for submitting your item into %(sitename)s.
""" % { 'sitename' : CFG_SITE_NAME, }
## Send the email:
if mail_submitters == "Y" and len(owners_email) != "":
## Mail-to is "owners_email":
if managers_email != "":
## Managers should also be copied into the mail:
owners_email += ",%s" % managers_email
## Post the mail:
send_email(CFG_EMAIL_FROM_ADDRESS, owners_email, \
"[%s] Submitted" % reference_numbers, \
email_txt, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
elif managers_email != "":
## Although it's not desirable to mail the submitters, if "managers"
## have been given, it is reasonable to mail them:
send_email(CFG_EMAIL_FROM_ADDRESS, managers_email, \
"[%s] Submitted" % reference_numbers, \
email_txt, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
elif CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN:
## We don't want to mail the "owners". Let's mail the admin instead:
send_email(CFG_EMAIL_FROM_ADDRESS, CFG_SITE_ADMIN_EMAIL, \
"[%s] Submitted" % reference_numbers, email_txt)
## Return an empty string
return ""
diff --git a/modules/websubmit/lib/functions/Mail_Submitter.py b/modules/websubmit/lib/functions/Mail_Submitter.py
index c8ca1b5b2..7bd60d4f3 100644
--- a/modules/websubmit/lib/functions/Mail_Submitter.py
+++ b/modules/websubmit/lib/functions/Mail_Submitter.py
@@ -1,130 +1,131 @@
## This file is part of Invenio.
## Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
##
## Name: Mail_Submitter.py
## Description: function Mail_Submitter
## This function sends a confirmation email to the submitter
## of the document
## Author: T.Baron
##
## PARAMETERS: authorfile: name of the file containing the author
## titleFile: name of the file containing the title
## emailFile: name of the file containing the email
## status: one of "ADDED" (the document has been integrated
## into the database) or "APPROVAL" (an email has
## been sent to a referee - simple approval)
## edsrn: name of the file containing the reference
## newrnin: name of the file containing the 2nd reference
## (if any)
## OUTPUT: HTML
##
import os
import re
from invenio.config import CFG_SITE_NAME, \
CFG_SITE_URL, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
from invenio.websubmit_functions.Shared_Functions import get_nice_bibsched_related_message
def Mail_Submitter(parameters, curdir, form, user_info=None):
"""
This function send an email to the submitter to warn him the
document he has just submitted has been correctly received.
Parameters:
* authorfile: Name of the file containing the authors of the
document
* titleFile: Name of the file containing the title of the
document
* emailFile: Name of the file containing the email of the
submitter of the document
* status: Depending on the value of this parameter, the function
adds an additional text to the email. This parameter
can be one of: ADDED: The file has been integrated in
the database. APPROVAL: The file has been sent for
approval to a referee. or can stay empty.
* edsrn: Name of the file containing the reference of the
document
* newrnin: Name of the file containing the 2nd reference of the
document (if any)
"""
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
# retrieve report number
edsrn = parameters['edsrn']
newrnin = parameters['newrnin']
fp = open("%s/%s" % (curdir,edsrn),"r")
rn = fp.read()
fp.close()
rn = re.sub("[\n\r]+","",rn)
if newrnin != "" and os.path.exists("%s/%s" % (curdir,newrnin)):
fp = open("%s/%s" % (curdir,newrnin),"r")
additional_rn = fp.read()
fp.close()
additional_rn = re.sub("[\n\r]+","",additional_rn)
fullrn = "%s and %s" % (additional_rn,rn)
else:
fullrn = rn
fullrn = fullrn.replace("\n"," ")
# The title is read from the file specified by 'titlefile'
try:
fp = open("%s/%s" % (curdir,parameters['titleFile']),"r")
m_title = fp.read().replace("\n"," ")
fp.close()
except:
m_title = "-"
# The name of the author is read from the file specified by 'authorfile'
try:
fp = open("%s/%s" % (curdir,parameters['authorfile']),"r")
m_author = fp.read().replace("\n"," ")
fp.close()
except:
m_author = "-"
# The submitters email address is read from the file specified by 'emailFile'
try:
fp = open("%s/%s" % (curdir,parameters['emailFile']),"r")
m_recipient = fp.read().replace ("\n"," ")
fp.close()
except:
m_recipient = ""
# create email body
email_txt = "The document %s\nTitle: %s\nAuthor(s): %s\n\nhas been correctly received\n\n" % (fullrn,m_title,m_author)
# The user is either informed that the document has been added to the database, or sent for approval
if parameters['status'] == "APPROVAL":
email_txt = email_txt + "An email has been sent to the referee. You will be warned by email as soon as the referee takes his/her decision regarding your document.\n\n"
elif parameters['status'] == "ADDED":
- email_txt = email_txt + "It will be soon added to our Document Server.\n\nOnce inserted, you will be able to check the bibliographic information and the quality of the electronic documents at this URL:\n<%s/record/%s>\nIf you detect an error please let us know by sending an email to %s. \n\n" % (CFG_SITE_URL,sysno,CFG_SITE_SUPPORT_EMAIL)
+ email_txt = email_txt + "It will be soon added to our Document Server.\n\nOnce inserted, you will be able to check the bibliographic information and the quality of the electronic documents at this URL:\n<%s/%s/%s>\nIf you detect an error please let us know by sending an email to %s. \n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno,CFG_SITE_SUPPORT_EMAIL)
email_txt += get_nice_bibsched_related_message(curdir)
email_txt = email_txt + "Thank you for using %s Submission Interface.\n" % CFG_SITE_NAME
# send the mail
send_email(FROMADDR, m_recipient.strip(), "%s: Document Received" % fullrn, email_txt, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/functions/Move_FCKeditor_Files_to_Storage.py b/modules/websubmit/lib/functions/Move_FCKeditor_Files_to_Storage.py
index cf4c782dc..bd1b191e7 100644
--- a/modules/websubmit/lib/functions/Move_FCKeditor_Files_to_Storage.py
+++ b/modules/websubmit/lib/functions/Move_FCKeditor_Files_to_Storage.py
@@ -1,182 +1,182 @@
## This file is part of Invenio.
## Copyright (C) 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
WebSubmit function - Replaces the links that have been created by the
FCKeditor
"""
__revision__ = "$Id$"
import re
import os
import urllib
from invenio.bibdocfile import decompose_file
from invenio.config import \
CFG_SITE_URL, \
- CFG_PREFIX
+ CFG_PREFIX, CFG_SITE_RECORD
re_fckeditor_link = re.compile('"' + CFG_SITE_URL + \
r'/submit/getattachedfile/(?P<uid>\d+)/(?P<type>(image|file|media|flash))/(?P<filename>.*?)"')
def Move_FCKeditor_Files_to_Storage(parameters, curdir, form, user_info=None):
"""
Moves the files uploaded via the FCKeditor that are linked to
the given field. Replace these links with URLs 'local' to the
record (recid/files/).
When attaching a file, the editor post the file to a temporary
drop box accessible via a URL for previews. We want to fetch
these files (via FFT) to integrate them to the record, and change
the links in the record to point to the integrated files.
The function *MUST* be run BEFORE the record has been created
(with Make_Record.py or Make_Modify_Record.py).
You *HAVE* to include the created FFT field (output of this
function) in your BibConvert template.
Parameters:
input_fields - *str* a comma separated list of file names that
should be processed by this element. Eg:
'ABSE,ABSF' in order to process values of the
English and French abstracts
"""
input_filenames = [input_filename for input_filename in \
parameters['input_fields'].split(',') if \
os.path.exists(curdir + os.sep + input_filename)]
processed_paths = []
for input_filename in input_filenames:
input_file = file(curdir + os.sep + input_filename)
input_string = input_file.read()
input_file.close()
def translate_link(match_obj):
"""Replace FCKeditor link by 'local' record link. Also
create the FFT for that link"""
file_type = match_obj.group('type')
file_name = match_obj.group('filename')
uid = match_obj.group('uid')
dummy, name, extension = decompose_file(file_name)
new_url = build_url(sysno, name, file_type, extension)
original_location = match_obj.group()[1:-1]
icon_location = original_location
# Prepare FFT that will fetch the file (+ the original
# file in the case of images)
if file_type == 'image':
# Does original file exists, or do we just have the
# icon? We expect the original file at a well defined
# location
possible_original_path = os.path.join(CFG_PREFIX,
'var', 'tmp',
'attachfile',
uid,
file_type,
'original',
file_name)
if os.path.exists(possible_original_path):
icon_location = original_location
original_location = possible_original_path
new_url = build_url(sysno, name,
file_type, extension, is_icon=True)
docname = build_docname(name, file_type, extension)
if original_location not in processed_paths:
# Must create an FFT only if we have not yet processed
# the file. This can happen if same image exists on
# the same page (either in two different FCKeditor
# instances, or twice in the HTML)
processed_paths.append(original_location)
write_fft(original_location,
docname,
icon_location,
doctype=file_type)
return '"' + new_url + '"'
output_string = re_fckeditor_link.sub(translate_link, input_string)
output_file = file(curdir + os.sep + input_filename, 'w')
output_file.write(output_string)
output_file.close()
def build_url(sysno, name, file_type, extension, is_icon=False):
"""
Build the local URL to the file with given parameters
@param sysno: record ID
@name name: base name of the file
@param file_type: as chosen by FCKeditor: 'File', 'Image', 'Flash', 'Media'
@param extension: file extension, including '.'
"""
- return CFG_SITE_URL + '/record/' + str(sysno) + \
+ return CFG_SITE_URL + '/'+ CFG_SITE_RECORD +'/' + str(sysno) + \
'/files/' + build_docname(name, file_type, extension) + \
(is_icon and '?subformat=icon' or '')
def build_docname(name, file_type, extension):
"""
Build the docname of the file.
In order to ensure uniqueness of the docname, we have to prefix
the filename with the filetype: FCKeditor takes care of filenames
uniqueness for each diffrent filetype, but not that files in
different filetypes will not have the same name
"""
return name + '_' + file_type + extension
def write_fft(file_location, docname, icon_location=None, doctype="image"):
"""
Append a new FFT for the record. Write the result to the FFT file on disk.
May only be used for files attached with FCKeditor (i.e. URLs
matching re_fckeditor_link)
"""
if file_location.startswith(CFG_SITE_URL):
# FCKeditor does not url-encode filenames, and FFT does not
# like URLs that are not quoted. So do it now (but only for
# file name, in URL context!)
url_parts = file_location.split("/")
try:
file_location = "/".join(url_parts[:-1]) + \
'/' + urllib.quote(url_parts[-1])
except:
pass
if icon_location.startswith(CFG_SITE_URL):
# Ditto quote file name
url_parts = icon_location.split("/")
try:
icon_location = "/".join(url_parts[:-1]) + \
'/' + urllib.quote(url_parts[-1])
except:
pass
icon_subfield = ''
if icon_location:
icon_subfield = '<subfield code="x">%s</subfield>' % icon_location
fft_file = file(os.path.join(curdir, 'FFT'), 'a')
fft_file.write("""
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(location)s</subfield>
<subfield code="n">%(docname)s</subfield>
<subfield code="t">%(doctype)s</subfield>
%(icon_subfield)s
</datafield>""" % {'location': file_location,
'icon_subfield': icon_subfield,
'doctype': doctype,
'docname': docname})
fft_file.close()
diff --git a/modules/websubmit/lib/functions/Send_APP_Mail.py b/modules/websubmit/lib/functions/Send_APP_Mail.py
index 9badef76d..984c3c984 100644
--- a/modules/websubmit/lib/functions/Send_APP_Mail.py
+++ b/modules/websubmit/lib/functions/Send_APP_Mail.py
@@ -1,266 +1,267 @@
## This file is part of Invenio.
## Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
## Description: function Send_APP_Mail
## This function send an email informing the original
## submitter of a document that the referee has approved/
## rejected the document. The email is also sent to the
## referee for checking.
## Author: T.Baron
## PARAMETERS:
## newrnin: name of the file containing the 2nd reference
## addressesAPP: email addresses to which the email will
## be sent (additionally to the author)
## categformatAPP: variable needed to derive the addresses
## mentioned above
import os
import re
from invenio.config import CFG_SITE_NAME, \
CFG_SITE_URL, \
CFG_SITE_SUPPORT_EMAIL, \
- CFG_CERN_SITE
+ CFG_CERN_SITE, \
+ CFG_SITE_RECORD
from invenio.access_control_admin import acc_get_role_users, acc_get_role_id
from invenio.dbquery import run_sql
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
from invenio.errorlib import register_exception
from invenio.search_engine import print_record
## The field in which to search for the record submitter/owner's email address:
if CFG_CERN_SITE:
## This is a CERN site - we use 859__f for submitter/record owner's email:
CFG_WEBSUBMIT_RECORD_OWNER_EMAIL = "859__f"
else:
## Non-CERN site. Use 8560_f for submitter/record owner's email:
CFG_WEBSUBMIT_RECORD_OWNER_EMAIL = "8560_f"
def Send_APP_Mail (parameters, curdir, form, user_info=None):
"""
This function send an email informing the original submitter of a
document that the referee has approved/ rejected the document. The
email is also sent to the referee for checking.
Parameters:
* addressesAPP: email addresses of the people who will receive
this email (comma separated list). this parameter may contain
the <CATEG> string. In which case the variable computed from
the [categformatAFP] parameter replaces this string.
eg.: "<CATEG>-email@cern.ch"
* categformatAPP contains a regular expression used to compute
the category of the document given the reference of the
document.
eg.: if [categformatAFP]="TEST-<CATEG>-.*" and the reference
of the document is "TEST-CATEGORY1-2001-001", then the computed
category equals "CATEGORY1"
* newrnin: Name of the file containing the 2nd reference of the
approved document (if any).
* edsrn: Name of the file containing the reference of the
approved document.
"""
global titlevalue,authorvalue,sysno,rn
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
doctype = form['doctype']
titlevalue = titlevalue.replace("\n"," ")
authorvalue = authorvalue.replace("\n","; ")
# variables declaration
categformat = parameters['categformatAPP']
otheraddresses = parameters['addressesAPP']
newrnpath = parameters['newrnin']
## Get the name of the decision file:
try:
decision_filename = parameters['decision_file']
except KeyError:
decision_filename = ""
## Get the name of the comments file:
try:
comments_filename = parameters['comments_file']
except KeyError:
comments_filename = ""
## Now try to read the comments from the comments_filename:
if comments_filename in (None, "", "NULL"):
## We don't have a name for the comments file.
## For backward compatibility reasons, try to read the comments from
## a file called 'COM' in curdir:
if os.path.exists("%s/COM" % curdir):
try:
fh_comments = open("%s/COM" % curdir, "r")
comment = fh_comments.read()
fh_comments.close()
except IOError:
## Unable to open the comments file
exception_prefix = "Error in WebSubmit function " \
"Send_APP_Mail. Tried to open " \
"comments file [%s/COM] but was " \
"unable to." % curdir
register_exception(prefix=exception_prefix)
comment = ""
else:
comment = comment.strip()
else:
comment = ""
else:
## Try to read the comments from the comments file:
if os.path.exists("%s/%s" % (curdir, comments_filename)):
try:
fh_comments = open("%s/%s" % (curdir, comments_filename), "r")
comment = fh_comments.read()
fh_comments.close()
except IOError:
## Oops, unable to open the comments file.
comment = ""
exception_prefix = "Error in WebSubmit function " \
"Send_APP_Mail. Tried to open comments " \
"file [%s/%s] but was unable to." \
% (curdir, comments_filename)
register_exception(prefix=exception_prefix)
else:
comment = comment.strip()
else:
comment = ""
## Now try to read the decision from the decision_filename:
if decision_filename in (None, "", "NULL"):
## We don't have a name for the decision file.
## For backward compatibility reasons, try to read the decision from
## a file called 'decision' in curdir:
if os.path.exists("%s/decision" % curdir):
try:
fh_decision = open("%s/decision" % curdir, "r")
decision = fh_decision.read()
fh_decision.close()
except IOError:
## Unable to open the decision file
exception_prefix = "Error in WebSubmit function " \
"Send_APP_Mail. Tried to open " \
"decision file [%s/decision] but was " \
"unable to." % curdir
register_exception(prefix=exception_prefix)
decision = ""
else:
decision = decision.strip()
else:
decision = ""
else:
## Try to read the decision from the decision file:
try:
fh_decision = open("%s/%s" % (curdir, decision_filename), "r")
decision = fh_decision.read()
fh_decision.close()
except IOError:
## Oops, unable to open the decision file.
decision = ""
exception_prefix = "Error in WebSubmit function " \
"Send_APP_Mail. Tried to open decision " \
"file [%s/%s] but was unable to." \
% (curdir, decision_filename)
register_exception(prefix=exception_prefix)
else:
decision = decision.strip()
if os.path.exists("%s/%s" % (curdir,newrnpath)):
fp = open("%s/%s" % (curdir,newrnpath) , "r")
newrn = fp.read()
fp.close()
else:
newrn = ""
# Document name
res = run_sql("SELECT ldocname FROM sbmDOCTYPE WHERE sdocname=%s", (doctype,))
docname = res[0][0]
# retrieve category
categformat = categformat.replace("<CATEG>", "([^-]*)")
m_categ_search = re.match(categformat, rn)
if m_categ_search is not None:
if len(m_categ_search.groups()) > 0:
## Found a match for the category of this document. Get it:
category = m_categ_search.group(1)
else:
## This document has no category.
category = "unknown"
else:
category = "unknown"
## Get the referee email address:
if CFG_CERN_SITE:
## The referees system in CERN now works with listbox membership.
## List names should take the format
## "service-cds-referee-doctype-category@cern.ch"
## Make sure that your list exists!
## FIXME - to be replaced by a mailing alias in webaccess in the
## future.
referee_listname = "service-cds-referee-%s" % doctype.lower()
if category != "":
referee_listname += "-%s" % category.lower()
referee_listname += "@cern.ch"
addresses = referee_listname
else:
# Build referee's email address
refereeaddress = ""
# Try to retrieve the referee's email from the referee's database
for user in acc_get_role_users(acc_get_role_id("referee_%s_%s" % (doctype,category))):
refereeaddress += user[1] + ","
# And if there is a general referee
for user in acc_get_role_users(acc_get_role_id("referee_%s_*" % doctype)):
refereeaddress += user[1] + ","
refereeaddress = re.sub(",$","",refereeaddress)
# Creation of the mail for the referee
otheraddresses = otheraddresses.replace("<CATEG>",category)
addresses = ""
if refereeaddress != "":
addresses = refereeaddress + ","
if otheraddresses != "":
addresses += otheraddresses
else:
addresses = re.sub(",$","",addresses)
## Add the record's submitter(s) into the list of recipients:
## Get the email address(es) of the record submitter(s)/owner(s) from
## the record itself:
record_owners = print_record(sysno, 'tm', \
[CFG_WEBSUBMIT_RECORD_OWNER_EMAIL]).strip()
if record_owners != "":
record_owners_list = record_owners.split("\n")
record_owners_list = [email.lower().strip() \
for email in record_owners_list]
else:
record_owners_list = []
record_owners = ",".join([owner for owner in record_owners_list])
if record_owners != "":
addresses += ",%s" % record_owners
if decision == "approve":
mailtitle = "%s has been approved" % rn
mailbody = "The %s %s has been approved." % (docname,rn)
- mailbody += "\nIt will soon be accessible here:\n\n<%s/record/%s>" % (CFG_SITE_URL,sysno)
+ mailbody += "\nIt will soon be accessible here:\n\n<%s/%s/%s>" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
else:
mailtitle = "%s has been rejected" % rn
mailbody = "The %s %s has been rejected." % (docname,rn)
if rn != newrn and decision == "approve" and newrn != "":
mailbody += "\n\nIts new reference number is: %s" % newrn
mailbody += "\n\nTitle: %s\n\nAuthor(s): %s\n\n" % (titlevalue,authorvalue)
if comment != "":
mailbody += "Comments from the referee:\n%s\n" % comment
# Send mail to referee
send_email(FROMADDR,addresses,mailtitle,mailbody, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/functions/Send_Approval_Request.py b/modules/websubmit/lib/functions/Send_Approval_Request.py
index d2ed06d2a..891dfcec6 100644
--- a/modules/websubmit/lib/functions/Send_Approval_Request.py
+++ b/modules/websubmit/lib/functions/Send_Approval_Request.py
@@ -1,145 +1,146 @@
## This file is part of Invenio.
## Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
## Description: function Send_Approval_Request
## This function sends an email to the referee asking him/her
## to approve/reject a document
## Author: T.Baron
## PARAMETERS: directory: parameter to the link manager program
## addressesDAM: address of the referee(s)
## categformatDAM: variable needed to extract the category
## of the document and use it to derive the
## address.
## authorfile: name of the file containing the author list
## titleFile: name of the file containing the title
import os
import re
from invenio.config import CFG_SITE_NAME, \
CFG_SITE_URL, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.dbquery import run_sql
from invenio.access_control_admin import acc_get_role_users,acc_get_role_id
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
def Send_Approval_Request (parameters, curdir, form, user_info=None):
"""
This function sends an email to the referee in order to start the
simple approval process. This function is very CERN-specific and
should be changed in case of external use. Must be called after
the Get_Report_Number function.
Parameters:
* addressesDAM: email addresses of the people who will receive
this email (comma separated list). this
parameter may contain the <CATEG> string. In
which case the variable computed from the
[categformatDAM] parameter replaces this
string.
eg.:"<CATEG>-email@cern.ch"
* categformatDAM: contains a regular expression used to compute
the category of the document given the
reference of the document.
eg.: if [categformatAFP]="TEST-<CATEG>-.*"
and the reference of the document is
"TEST-CATEGORY1-2001-001", then the computed
category equals "CATEGORY1"
* authorfile: name of the file in which the authors are stored
* titlefile: name of the file in which the title is stored.
* directory: parameter used to create the URL to access the
files.
"""
global rn,sysno
# variables declaration
doctype = re.search(".*/([^/]*)/([^/]*)/[^/]*$",curdir).group(2)
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
otheraddresses = parameters['addressesDAM']
categformat = parameters['categformatDAM']
# retrieve category
categformat = categformat.replace("<CATEG>","([^-]*)")
m_categ_search = re.match(categformat, rn)
if m_categ_search is not None:
if len(m_categ_search.groups()) > 0:
## Found a match for the category of this document. Get it:
category = m_categ_search.group(1)
else:
## This document has no category.
category = "unknown"
else:
category = "unknown"
# create TI
if os.path.exists("%s/date" % curdir):
fp = open("%s/date" % curdir, "r")
date = fp.read()
fp.close()
else:
date = ""
if os.path.exists("%s/%s" % (curdir,parameters['titleFile'])):
fp = open("%s/%s" % (curdir,parameters['titleFile']),"r")
title = fp.read()
fp.close()
title = title.replace("\n","")
else:
title = ""
title += " - %s" % date
# create AU
if os.path.exists("%s/%s" % (curdir,parameters['authorfile'])):
fp = open("%s/%s" % (curdir,parameters['authorfile']), "r")
author = fp.read()
fp.close()
else:
author = ""
# we get the referee password
sth = run_sql("SELECT access FROM sbmAPPROVAL WHERE rn=%s", (rn,))
if len(sth) >0:
access = sth[0][0]
# Build referee's email address
refereeaddress = ""
# Try to retrieve the referee's email from the referee's database
for user in acc_get_role_users(acc_get_role_id("referee_%s_%s" % (doctype,category))):
refereeaddress += user[1] + ","
# And if there are general referees
for user in acc_get_role_users(acc_get_role_id("referee_%s_*" % doctype)):
refereeaddress += user[1] + ","
refereeaddress = re.sub(",$","",refereeaddress)
# Creation of the mail for the referee
addresses = ""
if refereeaddress != "":
addresses = refereeaddress + ","
if otheraddresses != "":
addresses += otheraddresses
else:
addresses = re.sub(",$","",addresses)
title_referee = "Request for approval of %s" % rn
mail_referee = "The document %s has been submitted to the %s Server..\nYour approval is requested on it.\n\n" % (rn,CFG_SITE_NAME)
mail_referee +="Title: %s\n\nAuthor(s): %s\n\n" % (title,author)
- mail_referee +="To access the document(s), select the file(s) from the location:<%s/record/%s/files/>\n\n" % (CFG_SITE_URL,sysno)
+ mail_referee +="To access the document(s), select the file(s) from the location:<%s/%s/%s/files/>\n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
mail_referee +="To approve/reject the document, you should go to this URL:\n<%s/approve.py?%s>\n" % (CFG_SITE_URL,access)
mail_referee +="---------------------------------------------\nBest regards.\nThe submission team.\n"
#Send mail to referee
send_email(FROMADDR, addresses, title_referee, mail_referee, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/functions/Send_Modify_Mail.py b/modules/websubmit/lib/functions/Send_Modify_Mail.py
index 268bca21e..9034fe0be 100644
--- a/modules/websubmit/lib/functions/Send_Modify_Mail.py
+++ b/modules/websubmit/lib/functions/Send_Modify_Mail.py
@@ -1,92 +1,93 @@
## This file is part of Invenio.
## Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
## Description: function Send_Modify_Mail
## This function sends an email saying the document has been
## correctly updated
## Author: T.Baron
## PARAMETERS: addressesMBI: email addresses to which the mail is sent
## fieldnameMBI: name of the file containing the modified
## fields
## sourceDoc: name of the type of document
## emailFile: name of the file containing the email of the
## user
import os
import re
from invenio.config import CFG_SITE_URL, \
CFG_SITE_NAME, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
def Send_Modify_Mail (parameters, curdir, form, user_info=None):
"""
This function sends an email to warn people a document has been
modified and the user his modifications have been taken into
account..
Parameters:
* addressesMBI: email addresses of the people who will receive
this email (comma separated list).
* fieldnameMBI: name of the file containing the modified
fields.
* sourceDoc: Long name for the type of document. This name will
be displayed in the mail.
* emailfile: name of the file in which the email of the modifier
will be found.
"""
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
global sysno,rn
if parameters['emailFile'] is not None and parameters['emailFile']!= "" and os.path.exists("%s/%s" % (curdir,parameters['emailFile'])):
fp = open("%s/%s" % (curdir,parameters['emailFile']),"r")
sub = fp.read()
fp.close()
sub = sub.replace ("\n","")
else:
sub = ""
# Copy mail to:
addresses = parameters['addressesMBI']
addresses = addresses.strip()
m_fields = parameters['fieldnameMBI']
type = parameters['sourceDoc']
rn = re.sub("[\n\r ]+","",rn)
if os.path.exists("%s/%s" % (curdir,m_fields)):
fp = open("%s/%s" % (curdir,m_fields),"r")
fields = fp.read()
fp.close()
fields = fields.replace ("\n"," | ")
fields = re.sub("[| \n\r]+$","",fields)
else:
fields = ""
email_txt = "Dear Sir or Madam, \n%s %s has just been modified.\nModified fields: %s\n\n" % (type,rn,fields)
if CFG_SITE_URL != "" and sysno != "":
email_txt += "You can check the modified document here:\n"
- email_txt += "<%s/record/%s>\n\n" % (CFG_SITE_URL,sysno)
+ email_txt += "<%s/%s/%s>\n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
email_txt += "Please note that the modifications will be taken into account in a couple of minutes.\n\nBest regards,\nThe %s Server support Team" % CFG_SITE_NAME
# send the mail
send_email(FROMADDR,sub,"%s modified" % rn,email_txt,copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/functions/Send_Request_For_Direct_Approval.py b/modules/websubmit/lib/functions/Send_Request_For_Direct_Approval.py
index 3bb4b0521..5d94f6c1d 100644
--- a/modules/websubmit/lib/functions/Send_Request_For_Direct_Approval.py
+++ b/modules/websubmit/lib/functions/Send_Request_For_Direct_Approval.py
@@ -1,123 +1,124 @@
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
## Description: function Send_Approval_Request
## This function sends an email to the referee asking him/her
## to approve/reject a document
## Author: T.Baron
## PARAMETERS: directory: parameter to the link manager program
## addressesDAM: address of the referee(s)
## categformatDAM: variable needed to extract the category
## of the document and use it to derive the
## address.
## authorfile: name of the file containing the author list
## titleFile: name of the file containing the title
import os
import re
from invenio.config import CFG_SITE_NAME, \
CFG_SITE_URL, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.access_control_admin import acc_get_role_users,acc_get_role_id
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
def Send_Request_For_Direct_Approval(parameters, curdir, form, user_info=None):
"""
This function sends an email to the referee asking him/her to
approve/reject a document
Parameters:
* directory: parameter to the link manager program
* addressesDAM: address of the referee(s)
* categformatDAM: variable needed to extract the category of
the document and use it to derive the
address.
* authorfile: name of the file containing the author list
* titleFile: name of the file containing the title
"""
global rn,sysno
# variables declaration
doctype = re.search(".*/([^/]*)/([^/]*)/[^/]*$",curdir).group(2)
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
otheraddresses = parameters['addressesDAM']
categformat = parameters['categformatDAM']
# retrieve category
categformat = categformat.replace("<CATEG>","([^-]*)")
categs = re.match(categformat,rn)
if categs is not None:
category = categs.group(1)
else:
category = "unknown"
# create TI
if os.path.exists("%s/date" % curdir):
fp = open("%s/date" % curdir, "r")
date = fp.read()
fp.close()
else:
date = ""
if os.path.exists("%s/%s" % (curdir,parameters['titleFile'])):
fp = open("%s/%s" % (curdir,parameters['titleFile']),"r")
title = fp.read()
fp.close()
title = title.replace("\n","")
else:
title = ""
title += " - %s" % date
# create AU
if os.path.exists("%s/%s" % (curdir,parameters['authorfile'])):
fp = open("%s/%s" % (curdir,parameters['authorfile']), "r")
author = fp.read()
fp.close()
else:
author = ""
# we get the referee password
#sth = run_sql("SELECT access FROM sbmAPPROVAL WHERE rn=%s", (rn,))
#if len(sth) >0:
#access = sth[0][0]
# Build referee's email address
refereeaddress = ""
# Try to retrieve the publication committee chair's email from the role database
for user in acc_get_role_users(acc_get_role_id("projectleader_%s_%s" % (doctype,category))):
refereeaddress += user[1] + ","
# And if there are general referees
for user in acc_get_role_users(acc_get_role_id("projectleader_%s_*" % doctype)):
refereeaddress += user[1] + ","
refereeaddress = re.sub(",$","",refereeaddress)
# Creation of the mail for the referee
addresses = ""
if refereeaddress != "":
addresses = refereeaddress + ","
if otheraddresses != "":
addresses += otheraddresses
else:
addresses = re.sub(",$","",addresses)
title_referee = "Request for direct approval of %s" % rn
mail_referee = "The document %s has been asked direct approval to the %s Server..\nYour approval is requested on it.\n\n" % (rn,CFG_SITE_NAME)
mail_referee +="Title: %s\n\nAuthor(s): %s\n\n" % (title,author)
- mail_referee +="To access the document(s), select the file(s) from the location:<%s/record/%s/files/>\n\n" % (CFG_SITE_URL,sysno)
+ mail_referee +="To access the document(s), select the file(s) from the location:<%s/%s/%s/files/>\n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
mail_referee +="To approve/reject the document, you should go to this URL:\n<%s/publiline.py?doctype=%s&categ=%s&RN=%s>\n" % (CFG_SITE_URL,doctype,category,rn)
mail_referee +="---------------------------------------------\nBest regards.\nThe submission team.\n"
#Send mail to referee
send_email(FROMADDR, addresses, title_referee, mail_referee, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/functions/Send_Request_For_Publication.py b/modules/websubmit/lib/functions/Send_Request_For_Publication.py
index 194c49391..ebfa48524 100644
--- a/modules/websubmit/lib/functions/Send_Request_For_Publication.py
+++ b/modules/websubmit/lib/functions/Send_Request_For_Publication.py
@@ -1,94 +1,95 @@
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
import os
import re
from invenio.config import CFG_SITE_NAME, \
CFG_SITE_URL, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.access_control_admin import acc_get_role_users,acc_get_role_id
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
def Send_Request_For_Publication(parameters, curdir, form, user_info=None):
global rn,sysno
# variables declaration
doctype = re.search(".*/([^/]*)/([^/]*)/[^/]*$",curdir).group(2)
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
otheraddresses = parameters['addressesDAM']
categformat = parameters['categformatDAM']
# retrieve category
categformat = categformat.replace("<CATEG>","([^-]*)")
categs = re.match(categformat,rn)
if categs is not None:
category = categs.group(1)
else:
category = "unknown"
# create TI
if os.path.exists("%s/date" % curdir):
fp = open("%s/date" % curdir, "r")
date = fp.read()
fp.close()
else:
date = ""
if os.path.exists("%s/%s" % (curdir,parameters['titleFile'])):
fp = open("%s/%s" % (curdir,parameters['titleFile']),"r")
title = fp.read()
fp.close()
title = title.replace("\n","")
else:
title = ""
title += " - %s" % date
# create AU
if os.path.exists("%s/%s" % (curdir,parameters['authorfile'])):
fp = open("%s/%s" % (curdir,parameters['authorfile']), "r")
author = fp.read()
fp.close()
else:
author = ""
# we get the referee password
#sth = run_sql("SELECT access FROM sbmAPPROVAL WHERE rn=%s", (rn,))
#if len(sth) >0:
#access = sth[0][0]
# Build referee's email address
refereeaddress = ""
# Try to retrieve the publication committee chair's email from the role database
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_%s" % (doctype,category))):
refereeaddress += user[1] + ","
# And if there are general referees
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_*" % doctype)):
refereeaddress += user[1] + ","
refereeaddress = re.sub(",$","",refereeaddress)
# Creation of the mail for the referee
addresses = ""
if refereeaddress != "":
addresses = refereeaddress + ","
if otheraddresses != "":
addresses += otheraddresses
else:
addresses = re.sub(",$","",addresses)
title_referee = "Request for publication of %s" % rn
mail_referee = "The document %s has been asked for publication to the %s Server..\nYour have to select an editorial board for it.\n\n" % (rn,CFG_SITE_NAME)
mail_referee +="Title: %s\n\nAuthor(s): %s\n\n" % (title,author)
- mail_referee +="To access the document(s), select the file(s) from the location:<%s/record/%s/files/>\n\n" % (CFG_SITE_URL,sysno)
+ mail_referee +="To access the document(s), select the file(s) from the location:<%s/%s/%s/files/>\n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
mail_referee +="To select an editorial board, you should go to this URL:\n<%s/publiline.py?doctype=%s&categ=%s&RN=%s>\n" % (CFG_SITE_URL,doctype,category,rn)
mail_referee +="---------------------------------------------\nBest regards.\nThe submission team.\n"
#Send mail to referee
send_email(FROMADDR, addresses, title_referee, mail_referee, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/functions/Send_Request_For_Refereeing_Process.py b/modules/websubmit/lib/functions/Send_Request_For_Refereeing_Process.py
index dd896b7a7..7c6f9558e 100644
--- a/modules/websubmit/lib/functions/Send_Request_For_Refereeing_Process.py
+++ b/modules/websubmit/lib/functions/Send_Request_For_Refereeing_Process.py
@@ -1,106 +1,107 @@
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
## Description: function Send_Approval_Request
## This function sends an email to the referee asking him/her
## to approve/reject a document
## Author: T.Baron
## PARAMETERS: directory: parameter to the link manager program
## addressesDAM: address of the referee(s)
## categformatDAM: variable needed to extract the category
## of the document and use it to derive the
## address.
## authorfile: name of the file containing the author list
## titleFile: name of the file containing the title
import os
import re
from invenio.config import \
CFG_SITE_NAME, \
CFG_SITE_URL, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.access_control_admin import acc_get_role_users,acc_get_role_id
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
def Send_Request_For_Refereeing_Process(parameters, curdir, form, user_info=None):
global rn,sysno
# variables declaration
doctype = re.search(".*/([^/]*)/([^/]*)/[^/]*$",curdir).group(2)
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
otheraddresses = parameters['addressesDAM']
categformat = parameters['categformatDAM']
# retrieve category
categformat = categformat.replace("<CATEG>","([^-]*)")
categs = re.match(categformat,rn)
if categs is not None:
category = categs.group(1)
else:
category = "unknown"
# create TI
if os.path.exists("%s/date" % curdir):
fp = open("%s/date" % curdir, "r")
date = fp.read()
fp.close()
else:
date = ""
if os.path.exists("%s/%s" % (curdir,parameters['titleFile'])):
fp = open("%s/%s" % (curdir,parameters['titleFile']),"r")
title = fp.read()
fp.close()
title = title.replace("\n","")
else:
title = ""
title += " - %s" % date
# create AU
if os.path.exists("%s/%s" % (curdir,parameters['authorfile'])):
fp = open("%s/%s" % (curdir,parameters['authorfile']), "r")
author = fp.read()
fp.close()
else:
author = ""
# we get the referee password
#sth = run_sql("SELECT access FROM sbmAPPROVAL WHERE rn=%s", (rn,))
#if len(sth) >0:
#access = sth[0][0]
# Build referee's email address
refereeaddress = ""
# Try to retrieve the publication committee chair's email from the role database
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_%s" % (doctype,category))):
refereeaddress += user[1] + ","
# And if there are general referees
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_*" % doctype)):
refereeaddress += user[1] + ","
refereeaddress = re.sub(",$","",refereeaddress)
# Creation of the mail for the referee
addresses = ""
if refereeaddress != "":
addresses = refereeaddress + ","
if otheraddresses != "":
addresses += otheraddresses
else:
addresses = re.sub(",$","",addresses)
title_referee = "Request for refereeing process of %s" % rn
mail_referee = "The document %s has been asked for refereing process to the %s Server..\nYour have to select an editorial board for it.\n\n" % (rn,CFG_SITE_NAME)
mail_referee +="Title: %s\n\nAuthor(s): %s\n\n" % (title,author)
- mail_referee +="To access the document(s), select the file(s) from the location:<%s/record/%s/files/>\n\n" % (CFG_SITE_URL,sysno)
+ mail_referee +="To access the document(s), select the file(s) from the location:<%s/%s/%s/files/>\n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
mail_referee +="To select an editorial board, you should go to this URL:\n<%s/publiline.py?doctype=%s&categ=%s&RN=%s>\n" % (CFG_SITE_URL,doctype,category,rn)
mail_referee +="---------------------------------------------\nBest regards.\nThe submission team.\n"
#Send mail to referee
send_email(FROMADDR, addresses, title_referee, mail_referee, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/functions/Send_SRV_Mail.py b/modules/websubmit/lib/functions/Send_SRV_Mail.py
index 0f281b527..78dc595e7 100644
--- a/modules/websubmit/lib/functions/Send_SRV_Mail.py
+++ b/modules/websubmit/lib/functions/Send_SRV_Mail.py
@@ -1,95 +1,96 @@
## This file is part of Invenio.
## Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
## Description: function Send_SRV_Mail
## This function sends an email confirming the revision
## has been carried on with success
## Author: T.Baron
## PARAMETERS: addressesSRV: list of addresses to send this email to.
## categformatDAM: variable used to derive the category of
## the document from its reference. This value might then
## be used to derive the list of addresses
## emailFile: name of the file in which the user's email is
## noteFile: name of the file containing a note from the user
import os
from invenio.config import CFG_SITE_URL, \
CFG_SITE_NAME, \
- CFG_SITE_SUPPORT_EMAIL
+ CFG_SITE_SUPPORT_EMAIL, \
+ CFG_SITE_RECORD
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
from invenio.websubmit_functions.Retrieve_Data import Get_Field
def Send_SRV_Mail(parameters, curdir, form, user_info=None):
"""
This function sends an email to warn people a revision has been
carried out.
Parameters:
* notefile: name of the file in which the note can be found
* emailfile: name of the file containing the submitter's email
* addressesSRV: email addresses of the people who will receive
this email (comma separated list). this
parameter may contain the <CATEG> string. In
which case the variable computed from the
[categformatDAM] parameter replaces this
string.
eg.:"<CATEG>-email@cern.ch"
* categformatDAM: contains a regular expression used to compute
the category of the document given the
reference of the document.
eg.: if [categformatAFP]="TEST-<CATEG>-.*"
and the reference of the document is
"TEST-CATEGORY1-2001-001", then the computed
category equals "CATEGORY1"
"""
global rn,doctype,sysno
# variables declaration
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
addresses = parameters['addressesSRV']
addresses = addresses.strip()
if parameters['emailFile'] is not None and parameters['emailFile']!="" and os.path.exists("%s/%s" % (curdir,parameters['emailFile'])):
fp = open("%s/%s" % (curdir,parameters['emailFile']), "r")
SuE = fp.read()
fp.close()
else:
SuE = ""
SuE = SuE.replace("\n",",")
if parameters['noteFile'] is not None and parameters['noteFile']!= "" and os.path.exists("%s/%s" % (curdir,parameters['noteFile'])):
fp = open("%s/%s" % (curdir,parameters['noteFile']), "r")
note = fp.read()
fp.close()
else:
note = ""
title = Get_Field("245__a",sysno)
author = Get_Field('100__a',sysno)
author += Get_Field('700__a',sysno)
# create message
- message = "A revised version of document %s has been submitted.\n\nTitle: %s\nAuthor(s): %s\nURL: <%s/record/%s>%s" % (rn,title,author,CFG_SITE_URL,sysno,note)
+ message = "A revised version of document %s has been submitted.\n\nTitle: %s\nAuthor(s): %s\nURL: <%s/%s/%s>%s" % (rn,title,author,CFG_SITE_URL,CFG_SITE_RECORD,sysno,note)
# send the email
send_email(FROMADDR, SuE, "%s revised" % rn, message, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/websubmit_templates.py b/modules/websubmit/lib/websubmit_templates.py
index f445a52b4..ff7598ae2 100644
--- a/modules/websubmit/lib/websubmit_templates.py
+++ b/modules/websubmit/lib/websubmit_templates.py
@@ -1,3085 +1,3091 @@
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import urllib
import cgi
import re
import operator
from invenio.config import CFG_SITE_URL, \
- CFG_SITE_LANG
+ CFG_SITE_LANG, CFG_SITE_RECORD
from invenio.messages import gettext_set_language
from invenio.dateutils import convert_datetext_to_dategui, convert_datestruct_to_dategui
from invenio.urlutils import create_html_link
from invenio.webmessage_mailutils import email_quoted_txt2html
from invenio.htmlutils import escape_html
from websubmit_config import \
CFG_WEBSUBMIT_CHECK_USER_LEAVES_SUBMISSION
class Template:
# Parameters allowed in the web interface for fetching files
files_default_urlargd = {
'version': (str, ""), # version "" means "latest"
'docname': (str, ""), # the docname (optional)
'format' : (str, ""), # the format
'verbose' : (int, 0), # the verbosity
'subformat' : (str, ""), # the subformat
}
def tmpl_submit_home_page(self, ln, catalogues):
"""
The content of the home page of the submit engine
Parameters:
- 'ln' *string* - The language to display the interface in
- 'catalogues' *string* - The HTML code for the catalogues list
"""
# load the right message language
_ = gettext_set_language(ln)
return """
<script type="text/javascript" language="Javascript1.2">
var allLoaded = 1;
</script>
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(document_types)s:</th>
</tr>
<tr>
<td class="portalboxbody">
<br />
%(please_select)s:
<br /><br />
<table width="100%%">
<tr>
<td width="50%%" class="narrowsearchboxbody">
%(catalogues)s
</td>
</tr>
</table>
</td>
</tr>
</table>""" % {
'document_types' : _("Document types available for submission"),
'please_select' : _("Please select the type of document you want to submit"),
'catalogues' : catalogues,
'ln' : ln,
}
def tmpl_submit_home_catalog_no_content(self, ln):
"""
The content of the home page of submit in case no doctypes are available
Parameters:
- 'ln' *string* - The language to display the interface in
"""
# load the right message language
_ = gettext_set_language(ln)
out = "<h3>" + _("No document types available.") + "</h3>\n"
return out
def tmpl_submit_home_catalogs(self, ln, catalogs):
"""
Produces the catalogs' list HTML code
Parameters:
- 'ln' *string* - The language to display the interface in
- 'catalogs' *array* - The catalogs of documents, each one a hash with the properties:
- 'id' - the internal id
- 'name' - the name
- 'sons' - sub-catalogs
- 'docs' - the contained document types, in the form:
- 'id' - the internal id
- 'name' - the name
There is at least one catalog
"""
# load the right message language
_ = gettext_set_language(ln)
# import pprint
# out = "<pre>" + pprint.pformat(catalogs)
out = ""
for catalog in catalogs:
out += "\n<ul>"
out += self.tmpl_submit_home_catalogs_sub(ln, catalog)
out += "\n</ul>\n"
return out
def tmpl_print_warning(self, msg, type, prologue, epilogue):
"""Prints warning message and flushes output.
Parameters:
- 'msg' *string* - The message string
- 'type' *string* - the warning type
- 'prologue' *string* - HTML code to display before the warning
- 'epilogue' *string* - HTML code to display after the warning
"""
out = '\n%s<span class="quicknote">' % (prologue)
if type:
out += '%s: ' % type
out += '%s</span>%s' % (msg, epilogue)
return out
def tmpl_submit_home_catalogs_sub(self, ln, catalog):
"""
Recursive function that produces a catalog's HTML display
Parameters:
- 'ln' *string* - The language to display the interface in
- 'catalog' *array* - A catalog of documents, with the properties:
- 'id' - the internal id
- 'name' - the name
- 'sons' - sub-catalogs
- 'docs' - the contained document types, in the form:
- 'id' - the internal id
- 'name' - the name
"""
# load the right message language
_ = gettext_set_language(ln)
if catalog['level'] == 1:
out = "<li><font size=\"+1\"><strong>%s</strong></font>\n" % catalog['name']
else:
if catalog['level'] == 2:
out = "<li>%s\n" % cgi.escape(catalog['name'])
else:
if catalog['level'] > 2:
out = "<li>%s\n" % cgi.escape(catalog['name'])
if len(catalog['docs']) or len(catalog['sons']):
out += "<ul>\n"
if len(catalog['docs']) != 0:
for row in catalog['docs']:
out += self.tmpl_submit_home_catalogs_doctype(ln, row)
if len(catalog['sons']) != 0:
for row in catalog['sons']:
out += self.tmpl_submit_home_catalogs_sub(ln, row)
if len(catalog['docs']) or len(catalog['sons']):
out += "</ul></li>"
else:
out += "</li>"
return out
def tmpl_submit_home_catalogs_doctype(self, ln, doc):
"""
Recursive function that produces a catalog's HTML display
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doc' *array* - A catalog of documents, with the properties:
- 'id' - the internal id
- 'name' - the name
"""
# load the right message language
_ = gettext_set_language(ln)
return """<li>%s</li>""" % create_html_link('%s/submit' % CFG_SITE_URL, {'doctype' : doc['id'], 'ln' : ln}, doc['name'])
def tmpl_action_page(self, ln, uid, guest, pid, now, doctype,
description, docfulldesc, snameCateg,
lnameCateg, actionShortDesc, indir,
statustext):
"""
Recursive function that produces a catalog's HTML display
Parameters:
- 'ln' *string* - The language to display the interface in
- 'guest' *boolean* - If the user is logged in or not
- 'pid' *string* - The current process id
- 'now' *string* - The current time (security control features)
- 'doctype' *string* - The selected doctype
- 'description' *string* - The description of the doctype
- 'docfulldesc' *string* - The title text of the page
- 'snameCateg' *array* - The short names of all the categories of documents
- 'lnameCateg' *array* - The long names of all the categories of documents
- 'actionShortDesc' *array* - The short names (codes) for the different actions
- 'indir' *array* - The directories for each of the actions
- 'statustext' *array* - The names of the different action buttons
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
out += """
<script language="JavaScript" type="text/javascript">
var checked = 0;
function tester() {
"""
if (guest):
out += "alert(\"%(please_login_js)s\");return false;\n" % {
'please_login_js' : _("Please log in first.") + '\\n' + _("Use the top-right menu to log in.")
}
out += """
if (checked == 0) {
alert ("%(select_cat)s");
return false;
} else {
return true;
}
}
function clicked() {
checked=1;
}
function selectdoctype(nb) {
document.forms[0].act.value = docname[nb];
}
</script>
<form method="get" action="/submit">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="indir" />
<input type="hidden" name="access" value="%(now)i_%(pid)s" />
<input type="hidden" name="act" />
<input type="hidden" name="startPg" value="1" />
<input type="hidden" name="mainmenu" value="/submit?doctype=%(doctype)s&amp;ln=%(ln)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(docfulldesc)s</th>
</tr>
<tr>
<td class="portalboxbody">%(description)s
<br />
<script language="JavaScript" type="text/javascript">
var nbimg = document.images.length + 1;
</script>
<br />
<table align="center" cellpadding="0" cellspacing="0" border="0">
<tr valign="top">
""" % {
'select_cat' : _("Please select a category"),
'doctype' : doctype,
'now' : now,
'pid' : pid,
'docfulldesc' : docfulldesc,
'description' : description,
'ln' : ln,
}
if len(snameCateg) :
out += """<td align="right">"""
for i in range(0, len(snameCateg)):
out += """<label for="combo%(shortname)s">%(longname)s</label><input type="radio" name="combo%(doctype)s" id="combo%(shortname)s" value="%(shortname)s" onclick="clicked();" />&nbsp;<br />""" % {
'longname' : lnameCateg[i],
'doctype' : doctype,
'shortname' : snameCateg[i],
}
out += "</td><td>"
else:
out += '<td><script type="text/javascript">checked=1;</script>'
out += """&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>
<table><tr><td>
"""
#display list of actions
for i in range(0, len(actionShortDesc)):
out += """<input type="submit" class="adminbutton" value="%(status)s" onclick="if (tester()) { document.forms[0].indir.value='%(indir)s';document.forms[0].act.value='%(act)s';document.forms[0].submit();}; return false;" /><br />""" % {
'status' : statustext[i],
'indir' : indir[i],
'act' : actionShortDesc[i]
}
out += """ </td></tr></table>
</td>
</tr>
</table>
<br />"""
if len(snameCateg) :
out += """<strong class="headline">%(notice)s:</strong><br />
%(select_cat)s""" % {
'notice' : _("Notice"),
'select_cat' : _("Select a category and then click on an action button."),
}
out += """
<br /><br />
</td>
</tr>
</table>
</form>
<form action="/submit"><hr />
<font color="black"><small>%(continue_explain)s</small></font>
<table border="0" bgcolor="#CCCCCC" width="100%%"><tr>
<td width="100%%">
<small>Access Number: <input size="15" name="AN" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<input class="adminbutton" type="submit" value=" %(go)s " />
</small>
</td></tr>
</table>
<hr />
</form>
""" % {
'continue_explain' : _("To continue with a previously interrupted submission, enter an access number into the box below:"),
'doctype' : doctype,
'go' : _("GO"),
'ln' : ln,
}
return out
def tmpl_warning_message(self, ln, msg):
"""
Produces a warning message for the specified text
Parameters:
- 'ln' *string* - The language to display the interface in
- 'msg' *string* - The message to display
"""
# load the right message language
_ = gettext_set_language(ln)
return """<center><font color="red">%s</font></center>""" % msg
def tmpl_page_interface(self, ln, docname, actname, curpage, nbpages, nextPg, access, nbPg, doctype, act, fields, javascript, mainmenu):
"""
Produces a page with the specified fields (in the submit chain)
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The document type
- 'docname' *string* - The document type name
- 'actname' *string* - The action name
- 'act' *string* - The action
- 'curpage' *int* - The current page of submitting engine
- 'nbpages' *int* - The total number of pages
- 'nextPg' *int* - The next page
- 'access' *string* - The submission number
- 'nbPg' *string* - ??
- 'fields' *array* - the fields to display in the page, with each record having the structure:
- 'fullDesc' *string* - the description of the field
- 'text' *string* - the HTML code of the field
- 'javascript' *string* - if the field has some associated javascript code
- 'type' *string* - the type of field (T, F, I, H, D, S, R)
- 'name' *string* - the name of the field
- 'rows' *string* - the number of rows for textareas
- 'cols' *string* - the number of columns for textareas
- 'val' *string* - the default value of the field
- 'size' *string* - the size for text fields
- 'maxlength' *string* - the maximum length for text fields
- 'htmlcode' *string* - the complete HTML code for user-defined fields
- 'typename' *string* - the long name of the type
- 'javascript' *string* - the javascript code to insert in the page
- 'mainmenu' *string* - the url of the main menu
"""
# load the right message language
_ = gettext_set_language(ln)
# top menu
out = """
<form method="post" action="/submit" enctype="multipart/form-data" onsubmit="return tester();" accept-charset="UTF-8">
<center><table cellspacing="0" cellpadding="0" border="0">
<tr>
<td class="submitHeader"><b>%(docname)s&nbsp;</b></td>
<td class="submitHeader"><small>&nbsp;%(actname)s&nbsp;</small></td>
<td valign="bottom">
<table cellspacing="0" cellpadding="0" border="0" width="100%%">
<tr><td class="submitEmptyPage">&nbsp;&nbsp;</td>
""" % {
'docname' : docname,
'actname' : actname,
}
for i in range(1, nbpages+1):
if i == int(curpage):
out += """<td class="submitCurrentPage"><small>&nbsp;page: %s&nbsp;</small></td>""" % curpage
else:
out += """<td class="submitPage"><small>&nbsp;<a href='' onclick="if (tester2() == 1){document.forms[0].curpage.value=%s;user_must_confirm_before_leaving_page = false;document.forms[0].submit();return false;} else { return false; }">%s</a>&nbsp;</small></td>""" % (i, i)
out += """ <td class="submitEmptyPage">&nbsp;&nbsp;
</td></tr></table>
</td>
<td class="submitHeader" align="right">&nbsp;<a href="" onclick="window.open('/submit/summary?doctype=%(doctype)s&amp;act=%(act)s&amp;access=%(access)s&amp;ln=%(ln)s','summary','scrollbars=yes,menubar=no,width=500,height=250');return false;"><font color="white"><small>%(summary)s(2)</small></font></a>&nbsp;</td>
</tr>
<tr><td colspan="5" class="submitHeader">
<table border="0" cellspacing="0" cellpadding="15" width="100%%" class="submitBody"><tr><td>
<br />
<input type="hidden" name="nextPg" value="%(nextPg)s" />
<input type="hidden" name="access" value="%(access)s" />
<input type="hidden" name="curpage" value="%(curpage)s" />
<input type="hidden" name="nbPg" value="%(nbPg)s" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="act" value="%(act)s" />
<input type="hidden" name="mode" value="U" />
<input type="hidden" name="step" value="0" />
<input type="hidden" name="ln" value="%(ln)s" />
""" % {
'summary' : _("SUMMARY"),
'doctype' : cgi.escape(doctype),
'act' : cgi.escape(act),
'access' : cgi.escape(access),
'nextPg' : cgi.escape(nextPg),
'curpage' : cgi.escape(curpage),
'nbPg' : cgi.escape(nbPg),
'ln' : cgi.escape(ln),
}
for field in fields:
if field['javascript']:
out += """<script language="JavaScript1.1" type="text/javascript">
%s
</script>
""" % field['javascript']
# now displays the html form field(s)
out += "%s\n%s\n" % (field['fullDesc'], field['text'])
out += javascript
out += "<br />&nbsp;<br />&nbsp;</td></tr></table></td></tr>\n"
# Display the navigation cell
# Display "previous page" navigation arrows
out += """<tr><td colspan="5"><table border="0" cellpadding="0" cellspacing="0" width="100%%"><tr>"""
if int(curpage) != 1:
out += """ <td class="submitHeader" align="left">&nbsp;
<a href='' onclick="if (tester2() == 1) {document.forms[0].curpage.value=%(prpage)s;user_must_confirm_before_leaving_page = false;document.forms[0].submit();return false;} else { return false; }">
<img src="%(images)s/left-trans.gif" alt="%(prevpage)s" border="0" />
<strong><font color="white">%(prevpage)s</font></strong>
</a>
</td>
""" % {
'prpage' : int(curpage) - 1,
'images' : CFG_SITE_URL + '/img',
'prevpage' : _("Previous page"),
}
else:
out += """ <td class="submitHeader">&nbsp;</td>"""
# Display the submission number
out += """ <td class="submitHeader" align="center"><small>%(submission)s: %(access)s</small></td>\n""" % {
'submission' : _("Submission number") + '(1)',
'access' : cgi.escape(access),
}
# Display the "next page" navigation arrow
if int(curpage) != int(nbpages):
out += """ <td class="submitHeader" align="right">
<a href='' onclick="if (tester2()){document.forms[0].curpage.value=%(nxpage)s;user_must_confirm_before_leaving_page = false;document.forms[0].submit();return false;} else {return false;}; return false;">
<strong><font color="white">%(nextpage)s</font></strong>
<img src="%(images)s/right-trans.gif" alt="%(nextpage)s" border="0" />
</a>
</td>
""" % {
'nxpage' : int(curpage) + 1,
'images' : CFG_SITE_URL + '/img',
'nextpage' : _("Next page"),
}
else:
out += """ <td class="submitHeader">&nbsp;</td>"""
out += """</tr></table></td></tr></table></center></form>
<br />
<br />
<a href="%(mainmenu)s" onclick="if (%(check_not_already_enabled)s){return confirm('%(surequit)s')}">
<img src="%(images)s/mainmenu.gif" border="0" alt="%(back)s" align="right" /></a>
<br /><br />
<hr />
<small>%(take_note)s</small><br />
<small>%(explain_summary)s</small><br />
""" % {
'surequit' : _("Are you sure you want to quit this submission?"),
'check_not_already_enabled': CFG_WEBSUBMIT_CHECK_USER_LEAVES_SUBMISSION and 'false' or 'true',
'back' : _("Back to main menu"),
'mainmenu' : cgi.escape(mainmenu),
'images' : CFG_SITE_URL + '/img',
'take_note' : '(1) ' + _("This is your submission access number. It can be used to continue with an interrupted submission in case of problems."),
'explain_summary' : '(2) ' + _("Mandatory fields appear in red in the SUMMARY window."),
}
return out
def tmpl_submit_field(self, ln, field):
"""
Produces the HTML code for the specified field
Parameters:
- 'ln' *string* - The language to display the interface in
- 'field' *array* - the field to display in the page, with the following structure:
- 'javascript' *string* - if the field has some associated javascript code
- 'type' *string* - the type of field (T, F, I, H, D, S, R)
- 'name' *string* - the name of the field
- 'rows' *string* - the number of rows for textareas
- 'cols' *string* - the number of columns for textareas
- 'val' *string* - the default value of the field
- 'size' *string* - the size for text fields
- 'maxlength' *string* - the maximum length for text fields
- 'htmlcode' *string* - the complete HTML code for user-defined fields
- 'typename' *string* - the long name of the type
"""
# load the right message language
_ = gettext_set_language(ln)
# If the field is a textarea
if field['type'] == 'T':
## Field is a textarea:
text = "<textarea name=\"%s\" rows=\"%s\" cols=\"%s\">%s</textarea>" \
% (field['name'], field['rows'], field['cols'], cgi.escape(str(field['val']), 1))
# If the field is a file upload
elif field['type'] == 'F':
## the field is a file input:
text = """<input type="file" name="%s" size="%s"%s />""" \
% (field['name'], field['size'], "%s" \
% ((field['maxlength'] in (0, None) and " ") or (""" maxlength="%s\"""" % field['maxlength'])) )
# If the field is a text input
elif field['type'] == 'I':
## Field is a text input:
text = """<input type="text" name="%s" size="%s" value="%s"%s />""" \
% (field['name'], field['size'], field['val'], "%s" \
% ((field['maxlength'] in (0, None) and " ") or (""" maxlength="%s\"""" % field['maxlength'])) )
# If the field is a hidden input
elif field['type'] == 'H':
text = "<input type=\"hidden\" name=\"%s\" value=\"%s\" />" % (field['name'], field['val'])
# If the field is user-defined
elif field['type'] == 'D':
text = field['htmlcode']
# If the field is a select box
elif field['type'] == 'S':
text = field['htmlcode']
# If the field type is not recognized
else:
text = "%s: unknown field type" % field['typename']
return text
def tmpl_page_interface_js(self, ln, upload, field, fieldhtml, txt, check, level, curdir, values, select, radio, curpage, nbpages, returnto):
"""
Produces the javascript for validation and value filling for a submit interface page
Parameters:
- 'ln' *string* - The language to display the interface in
- 'upload' *array* - booleans if the field is a <input type="file"> field
- 'field' *array* - the fields' names
- 'fieldhtml' *array* - the fields' HTML representation
- 'txt' *array* - the fields' long name
- 'check' *array* - if the fields should be checked (in javascript)
- 'level' *array* - strings, if the fields should be filled (M) or not (O)
- 'curdir' *array* - the current directory of the submission
- 'values' *array* - the current values of the fields
- 'select' *array* - booleans, if the controls are "select" controls
- 'radio' *array* - booleans, if the controls are "radio" controls
- 'curpage' *int* - the current page
- 'nbpages' *int* - the total number of pages
- 'returnto' *array* - a structure with 'field' and 'page', if a mandatory field on antoher page was not completed
"""
# load the right message language
_ = gettext_set_language(ln)
nbFields = len(upload)
# if there is a file upload field, we change the encoding type
out = """<script language="JavaScript1.1" type="text/javascript">
"""
for i in range(0,nbFields):
if upload[i] == 1:
out += "document.forms[0].encoding = \"multipart/form-data\";\n"
break
# we don't want the form to be submitted if the user enters 'Return'
# tests if mandatory fields are well filled
out += """function tester(){
return false;
}
function tester2() {
"""
for i in range(0,nbFields):
if re.search("%s\[\]" % field[i],fieldhtml[i]):
fieldname = "%s[]" % field[i]
else:
fieldname = field[i]
out += " el = document.forms[0].elements['%s'];\n" % fieldname
# If the field must be checked we call the checking function
if check[i] != "":
out += """if (%(check)s(el.value) == 0) {
el.focus();
return 0;
} """ % {
'check' : check[i]
}
# If the field is mandatory, we check a value has been selected
if level[i] == 'M':
if select[i] != 0:
# If the field is a select box
out += """if ((el.selectedIndex == -1)||(el.selectedIndex == 0)){
alert("%(field_mandatory)s");
return 0;
} """ % {
'field_mandatory' : _("The field %s is mandatory.") % txt[i] + '\\n' + _("Please make a choice in the select box")
}
elif radio[i] != 0:
# If the field is a radio buttonset
out += """var check=0;
for (var j = 0; j < el.length; j++) {
if (el.options[j].checked){
check++;
}
}
if (check == 0) {
alert("%(press_button)s");
return 0;
}""" % {
'press_button':_("Please press a button.")
}
else:
# If the field is a text input
out += """if (el.value == '') {
alert("%(field_mandatory)s");
return 0;
}""" % {
'field_mandatory' : _("The field %s is mandatory. Please fill it in.") % txt[i]
}
out += """ return 1;
}
<!-- Fill the fields in with the previous saved values-->
"""
# # # # # # # # # # # # # # # # # # # # # # # # #
# Fill the fields with the previously saved values
# # # # # # # # # # # # # # # # # # # # # # # # #
for i in range(0,nbFields):
if re.search("%s\[\]"%field[i],fieldhtml[i]):
fieldname = "%s[]" % field[i]
else:
fieldname = field[i]
text = values[i]
if text != '':
if select[i] != 0:
# If the field is a SELECT element
vals = text.split("\n")
tmp=""
for val in vals:
if tmp != "":
tmp = tmp + " || "
tmp = tmp + "el.options[j].value == \"%s\" || el.options[j].text == \"%s\"" % (val,val)
if tmp != "":
out += """
<!--SELECT field found-->
el = document.forms[0].elements['%(fieldname)s'];
for (var j = 0; j < el.length; j++){
if (%(tmp)s){
el.options[j].selected = true;
}
}""" % {
'fieldname' : fieldname,
'tmp' : tmp,
}
elif radio[i] != 0:
# If the field is a RADIO element
out += """<!--RADIO field found-->
el = document.forms[0].elements['%(fieldname)s'];
if (el.value == "%(text)s"){
el.checked=true;
}""" % {
'fieldname' : fieldname,
'text' : cgi.escape(str(text)).replace('"', '\\"'),
}
elif upload[i] == 0:
text = text.replace('"','\"')
text = text.replace("\n","\\n")
# If the field is not an upload element
out += """<!--input field found-->
el = document.forms[0].elements['%(fieldname)s'];
el.value="%(text)s";
""" % {
'fieldname' : fieldname,
'text' : cgi.escape(str(text)).replace('"', '\\"'),
}
out += """<!--End Fill in section-->
"""
# JS function finish
# This function tests each mandatory field in the whole submission and checks whether
# the field has been correctly filled in or not
# This function is called when the user presses the "End
# Submission" button
if int(curpage) == int(nbpages):
out += """function finish() {
"""
if returnto:
out += """alert ("%(msg)s");
document.forms[0].curpage.value="%(page)s";
user_must_confirm_before_leaving_page = false;
document.forms[0].submit();
}
""" % {
'msg' : _("The field %(field)s is mandatory.") + '\n' \
+ _("Going back to page") \
+ str(returnto['page']),
'page' : returnto['page']
}
else:
out += """ if (tester2()) {
document.forms[0].action="/submit";
document.forms[0].step.value=1;
user_must_confirm_before_leaving_page = false;
document.forms[0].submit();
} else {
return false;
}
}"""
out += """</script>"""
return out
def tmpl_page_do_not_leave_submission_js(self, ln, enabled=CFG_WEBSUBMIT_CHECK_USER_LEAVES_SUBMISSION):
"""
Code to ask user confirmation when leaving the page, so that the
submission is not interrupted by mistake.
All submission functions should set the Javascript variable
'user_must_confirm_before_leaving_page' to 'false' before
programmatically submitting the submission form.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'enabled' *bool* - If the check applies or not
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''
<script language="JavaScript">
var user_must_confirm_before_leaving_page = %s;
window.onbeforeunload = confirmExit;
function confirmExit() {
if (user_must_confirm_before_leaving_page)
return "%s";
}
</script>
''' % (enabled and 'true' or 'false',
_('Your modifications will not be saved.').replace('"', '\\"'))
return out
def tmpl_page_endaction(self, ln, nextPg, startPg, access, curpage, nbPg, nbpages, doctype, act, docname, actname, mainmenu, finished, function_content, next_action):
"""
Produces the pages after all the fields have been submitted.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The document type
- 'act' *string* - The action
- 'docname' *string* - The document type name
- 'actname' *string* - The action name
- 'curpage' *int* - The current page of submitting engine
- 'startPg' *int* - The start page
- 'nextPg' *int* - The next page
- 'access' *string* - The submission number
- 'nbPg' *string* - total number of pages
- 'nbpages' *string* - number of pages (?)
- 'mainmenu' *string* - the url of the main menu
- 'finished' *bool* - if the submission is finished
- 'function_content' *string* - HTML code produced by some function executed
- 'next_action' *string* - if there is another action to be completed, the HTML code for linking to it
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<form ENCTYPE="multipart/form-data" action="/submit" onsubmit="user_must_confirm_before_leaving_page=false;" method="post" accept-charset="UTF-8">
<input type="hidden" name="nextPg" value="%(nextPg)s" />
<input type="hidden" name="startPg" value="%(startPg)s" />
<input type="hidden" name="access" value="%(access)s" />
<input type="hidden" name="curpage" value="%(curpage)s" />
<input type="hidden" name="nbPg" value="%(nbPg)s" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="act" value="%(act)s" />
<input type="hidden" name="fromdir" value="" />
<input type="hidden" name="mainmenu" value="%(mainmenu)s" />
<input type="hidden" name="mode" value="U" />
<input type="hidden" name="step" value="1" />
<input type="hidden" name="deleted" value="no" />
<input type="hidden" name="file_path" value="" />
<input type="hidden" name="userfile_name" value="" />
<input type="hidden" name="ln" value="%(ln)s" />
<center><table cellspacing="0" cellpadding="0" border="0"><tr>
<td class="submitHeader"><b>%(docname)s&nbsp;</b></td>
<td class="submitHeader"><small>&nbsp;%(actname)s&nbsp;</small></td>
<td valign="bottom">
<table cellspacing="0" cellpadding="0" border="0" width="100%%">
<tr><td class="submitEmptyPage">&nbsp;&nbsp;</td>
""" % {
'nextPg' : cgi.escape(nextPg),
'startPg' : cgi.escape(startPg),
'access' : cgi.escape(access),
'curpage' : cgi.escape(curpage),
'nbPg' : cgi.escape(nbPg),
'doctype' : cgi.escape(doctype),
'act' : cgi.escape(act),
'docname' : docname,
'actname' : actname,
'mainmenu' : cgi.escape(mainmenu),
'ln' : cgi.escape(ln),
}
if finished == 1:
out += """<td class="submitCurrentPage">%(finished)s</td>
<td class="submitEmptyPage">&nbsp;&nbsp;</td>
</tr></table>
</td>
<td class="submitEmptyPage" align="right">&nbsp;</td>
""" % {
'finished' : _("finished!"),
}
else:
for i in range(1, nbpages + 1):
out += """<td class="submitPage"><small>&nbsp;
<a href='' onclick="document.forms[0].curpage.value=%s;document.forms[0].action='/submit';document.forms[0].step.value=0;user_must_confirm_before_leaving_page = false;document.forms[0].submit();return false;">%s</a>&nbsp;</small></td>""" % (i,i)
out += """<td class="submitCurrentPage">%(end_action)s</td><td class="submitEmptyPage">&nbsp;&nbsp;</td></tr></table></td>
<td class="submitHeader" align="right">&nbsp;<a href='' onclick="window.open('/submit/summary?doctype=%(doctype)s&amp;act=%(act)s&amp;access=%(access)s&amp;ln=%(ln)s','summary','scrollbars=yes,menubar=no,width=500,height=250');return false;"><font color="white"><small>%(summary)s(2)</small></font></a>&nbsp;</td>""" % {
'end_action' : _("end of action"),
'summary' : _("SUMMARY"),
'doctype' : cgi.escape(doctype),
'act' : cgi.escape(act),
'access' : cgi.escape(access),
'ln' : cgi.escape(ln),
}
out += """</tr>
<tr>
<td colspan="5" class="submitBody">
<small><br /><br />
%(function_content)s
%(next_action)s
<br /><br />
</td>
</tr>
<tr class="submitHeader">
<td class="submitHeader" colspan="5" align="center">""" % {
'function_content' : function_content,
'next_action' : next_action,
}
if finished == 0:
out += """<small>%(submission)s</small>&sup2;:
<small>%(access)s</small>""" % {
'submission' : _("Submission no"),
'access' : cgi.escape(access),
}
else:
out += "&nbsp;\n"
out += """
</td>
</tr>
</table>
</center>
</form>
<br />
<br />"""
# Add the "back to main menu" button
if finished == 0:
out += """ <a href="%(mainmenu)s" onclick="if (%(check_not_already_enabled)s){return confirm('%(surequit)s')}">
<img src="%(images)s/mainmenu.gif" border="0" alt="%(back)s" align="right" /></a>
<br /><br />""" % {
'surequit' : _("Are you sure you want to quit this submission?"),
'back' : _("Back to main menu"),
'images' : CFG_SITE_URL + '/img',
'mainmenu' : cgi.escape(mainmenu),
'check_not_already_enabled': CFG_WEBSUBMIT_CHECK_USER_LEAVES_SUBMISSION and 'false' or 'true',
}
else:
out += """ <a href="%(mainmenu)s">
<img src="%(images)s/mainmenu.gif" border="0" alt="%(back)s" align="right" /></a>
<br /><br />""" % {
'back' : _("Back to main menu"),
'images' : CFG_SITE_URL + '/img',
'mainmenu' : cgi.escape(mainmenu),
}
return out
def tmpl_function_output(self, ln, display_on, action, doctype, step, functions):
"""
Produces the output of the functions.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'display_on' *bool* - If debug information should be displayed
- 'doctype' *string* - The document type
- 'action' *string* - The action
- 'step' *int* - The current step in submission
- 'functions' *aray* - HTML code produced by functions executed and informations about the functions
- 'name' *string* - the name of the function
- 'score' *string* - the score of the function
- 'error' *bool* - if the function execution produced errors
- 'text' *string* - the HTML code produced by the function
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if display_on:
out += """<br /><br />%(function_list)s<P>
<table border="1" cellpadding="15">
<tr><th>%(function)s</th><th>%(score)s</th><th>%(running)s</th></tr>
""" % {
'function_list' : _("Here is the %(x_action)s function list for %(x_doctype)s documents at level %(x_step)s") % {
'x_action' : action,
'x_doctype' : doctype,
'x_step' : step,
},
'function' : _("Function"),
'score' : _("Score"),
'running' : _("Running function"),
}
for function in functions:
out += """<tr><td>%(name)s</td><td>%(score)s</td><td>%(result)s</td></tr>""" % {
'name' : function['name'],
'score' : function['score'],
'result' : function['error'] and (_("Function %s does not exist.") % function['name'] + "<br />") or function['text']
}
out += "</table>"
else:
for function in functions:
if not function['error']:
out += function['text']
return out
def tmpl_next_action(self, ln, actions):
"""
Produces the output of the functions.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'actions' *array* - The actions to display, in the structure
- 'page' *string* - the starting page
- 'action' *string* - the action (in terms of submission)
- 'doctype' *string* - the doctype
- 'nextdir' *string* - the path to the submission data
- 'access' *string* - the submission number
- 'indir' *string* - ??
- 'name' *string* - the name of the action
"""
# load the right message language
_ = gettext_set_language(ln)
out = "<br /><br />%(haveto)s<ul>" % {
'haveto' : _("You must now"),
}
i = 0
for action in actions:
if i > 0:
out += " <b>" + _("or") + "</b> "
i += 1
out += """<li><a href="" onclick="document.forms[0].action='/submit';document.forms[0].curpage.value='%(page)s';document.forms[0].startPg.value='%(page)s';document.forms[0].act.value='%(action)s';document.forms[0].doctype.value='%(doctype)s';document.forms[0].indir.value='%(nextdir)s';document.forms[0].access.value='%(access)s';document.forms[0].fromdir.value='%(indir)s';user_must_confirm_before_leaving_page = falsedocument.forms[0].submit();return false;"> %(name)s </a></li>""" % action
out += "</ul>"
return out
def tmpl_filelist(self, ln, filelist='', recid='', docname='', version=''):
"""
Displays the file list for a record.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'recid' *int* - The record id
- 'docname' *string* - The document name
- 'version' *int* - The version of the document
- 'filelist' *string* - The HTML string of the filelist (produced by the BibDoc classes)
"""
# load the right message language
_ = gettext_set_language(ln)
- title = _("record") + ' #' + '<a href="%s/record/%s">%s</a>' % (CFG_SITE_URL, recid, recid)
+ title = _("record") + ' #' + '<a href="%s/%s/%s">%s</a>' % (CFG_SITE_URL, CFG_SITE_RECORD, recid, recid)
if docname != "":
title += ' ' + _("document") + ' #' + str(docname)
if version != "":
title += ' ' + _("version") + ' #' + str(version)
out = """<div style="width:90%%;margin:auto;min-height:100px;margin-top:10px">
<!--start file list-->
%s
<!--end file list--></div>
""" % (filelist)
return out
def tmpl_bibrecdoc_filelist(self, ln, types, verbose_files=''):
"""
Displays the file list for a record.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'types' *array* - The different types to display, each record in the format:
- 'name' *string* - The name of the format
- 'content' *array of string* - The HTML code produced by tmpl_bibdoc_filelist, for the right files
- 'verbose_files' - A string representing in a verbose way the
file information.
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
for mytype in types:
out += "<small><b>%s</b> %s:</small>" % (mytype['name'], _("file(s)"))
out += "<ul>"
for content in mytype['content']:
out += content
out += "</ul>"
if verbose_files:
out += "<pre>%s</pre>" % verbose_files
return out
def tmpl_bibdoc_filelist(self, ln, versions=[], imageurl='', recid='', docname=''):
"""
Displays the file list for a record.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'versions' *array* - The different versions to display, each record in the format:
- 'version' *string* - The version
- 'content' *string* - The HTML code produced by tmpl_bibdocfile_filelist, for the right file
- 'previous' *bool* - If the file has previous versions
- 'imageurl' *string* - The URL to the file image
- 'recid' *int* - The record id
- 'docname' *string* - The name of the document
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<table border="0" cellspacing="1" class="searchbox">
<tr>
<td align="left" colspan="2" class="portalboxheader">
<img src='%(imageurl)s' border="0" />&nbsp;&nbsp;%(docname)s
</td>
</tr>""" % {
'imageurl' : imageurl,
'docname' : docname
}
for version in versions:
if version['previous']:
- versiontext = """<br />(%(see)s <a href="%(siteurl)s/record/%(recID)s/files/?docname=%(docname)s&amp;version=all%(ln_link)s">%(previous)s</a>)""" % {
+ versiontext = """<br />(%(see)s <a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(recID)s/files/?docname=%(docname)s&amp;version=all%(ln_link)s">%(previous)s</a>)""" % {
'see' : _("see"),
'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'docname' : urllib.quote(docname),
'recID': recid,
'previous': _("previous"),
'ln_link': (ln != CFG_SITE_LANG and '&amp;ln=' + ln) or '',
}
else:
versiontext = ""
out += """<tr>
<td class="portalboxheader">
<font size="-2">%(version)s %(ver)s%(text)s</font>
</td>
<td>
<table>
""" % {
'version' : _("version"),
'ver' : version['version'],
'text' : versiontext,
}
for content in version['content']:
out += content
out += "</table></td></tr>"
out += "</table>"
return out
def tmpl_bibdocfile_filelist(self, ln, recid, name, version, md, superformat, subformat, nice_size, description):
"""
Displays a file in the file list.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'recid' *int* - The id of the record
- 'name' *string* - The name of the file
- 'version' *string* - The version
- 'md' *datetime* - the modification date
- 'superformat' *string* - The display superformat
- 'subformat' *string* - The display subformat
- 'nice_size' *string* - The nice_size of the file
- 'description' *string* - The description that might have been associated
to the particular file
"""
# load the right message language
_ = gettext_set_language(ln)
- urlbase = '%s/record/%s/files/%s' % (
+ urlbase = '%s/%s/%s/files/%s' % (
CFG_SITE_URL,
+ CFG_SITE_RECORD,
recid,
'%s%s' % (name, superformat))
urlargd = {'version' : version}
if subformat:
urlargd['subformat'] = subformat
link_label = '%s%s' % (name, superformat)
if subformat:
link_label += ' (%s)' % subformat
link = create_html_link(urlbase, urlargd, cgi.escape(link_label))
return """<tr>
<td valign="top">
<small>%(link)s</small>
</td>
<td valign="top">
<font size="-2" color="green">[%(nice_size)s]</font>
<font size="-2"><em>%(md)s</em>
</td>
<td valign="top"><em>%(description)s</em></td>
</tr>""" % {
'link' : link,
'nice_size' : nice_size,
'md' : convert_datestruct_to_dategui(md.timetuple(), ln),
'description' : cgi.escape(description),
}
def tmpl_submit_summary (self, ln, values):
"""
Displays the summary for the submit procedure.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'values' *array* - The values of submit. Each of the records contain the following fields:
- 'name' *string* - The name of the field
- 'mandatory' *bool* - If the field is mandatory or not
- 'value' *string* - The inserted value
- 'page' *int* - The submit page on which the field is entered
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<body style="background-image: url(%(images)s/header_background.gif);"><table border="0">""" % \
{ 'images' : CFG_SITE_URL + '/img' }
for value in values:
if value['mandatory']:
color = "red"
else:
color = ""
out += """<tr>
<td align="right">
<small>
<a href='' onclick="window.opener.document.forms[0].curpage.value='%(page)s';window.opener.document.forms[0].action='/submit?ln=%(ln)s';window.opener.document.forms[0].submit();return false;">
<font color="%(color)s">%(name)s</font>
</a>
</small>
</td>
<td>
<i><small><font color="black">%(value)s</font></small></i>
</td>
</tr>""" % {
'color' : color,
'name' : value['name'],
'value' : value['value'],
'page' : value['page'],
'ln' : ln
}
out += "</table>"
return out
def tmpl_yoursubmissions(self, ln, order, doctypes, submissions):
"""
Displays the list of the user's submissions.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'order' *string* - The ordering parameter
- 'doctypes' *array* - All the available doctypes, in structures:
- 'id' *string* - The doctype id
- 'name' *string* - The display name of the doctype
- 'selected' *bool* - If the doctype should be selected
- 'submissions' *array* - The available submissions, in structures:
- 'docname' *string* - The document name
- 'actname' *string* - The action name
- 'status' *string* - The status of the document
- 'cdate' *string* - Creation date
- 'mdate' *string* - Modification date
- 'id' *string* - The id of the submission
- 'reference' *string* - The display name of the doctype
- 'pending' *bool* - If the submission is pending
- 'act' *string* - The action code
- 'doctype' *string* - The doctype code
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
out += """
<br />
<form action="">
<input type="hidden" value="%(order)s" name="order" />
<input type="hidden" name="deletedId" />
<input type="hidden" name="deletedDoctype" />
<input type="hidden" name="deletedAction" />
<input type="hidden" name="ln" value="%(ln)s"/>
<table class="searchbox" width="100%%" summary="" >
<tr>
<th class="portalboxheader">%(for)s&nbsp;
<select name="doctype" onchange="document.forms[0].submit();">
<option value="">%(alltype)s</option>
""" % {
'order' : order,
'for' : _("For"),
'alltype' : _("all types of document"),
'ln' : ln,
}
for doctype in doctypes:
out += """<option value="%(id)s" %(sel)s>%(name)s</option>""" % {
'id' : doctype['id'],
'name' : doctype['name'],
'sel' : doctype['selected'] and "selected=\"selected\"" or ""
}
out += """ </select>
</th>
</tr>
<tr>
<td class="portalboxbody">
<table>
<tr>
<td></td>
</tr>
"""
num = 0
docname = ""
for submission in submissions:
if submission['docname'] != docname:
docname = submission['docname']
out += """</table>
<br/>&nbsp;<br/><h3>%(docname)s</h3>
<table border="0" class="searchbox" align="left" width="100%%">
<tr>
<th class="headerselected">%(action)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="actiondown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="actionup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
<th class="headerselected">%(status)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="statusdown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="statusup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
<th class="headerselected">%(id)s</th>
<th class="headerselected">%(reference)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="refdown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="refup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
<th class="headerselected">%(first)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="cddown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="cdup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
<th class="headerselected">%(last)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="mddown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="mdup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
</tr>
""" % {
'docname' : submission['docname'],
'action' : _("Action"),
'status' : _("Status"),
'id' : _("Subm.No."),
'reference' : _("Reference"),
'images' : CFG_SITE_URL + '/img',
'first' : _("First access"),
'last' : _("Last access"),
}
if submission['pending']:
idtext = """<a href="submit/direct?access=%(id)s&sub=%(action)s%(doctype)s%(ln_link)s">%(id)s</a>
&nbsp;<a onclick='if (confirm("%(sure)s")){document.forms[0].deletedId.value="%(id)s";document.forms[0].deletedDoctype.value="%(doctype)s";document.forms[0].deletedAction.value="%(action)s";document.forms[0].submit();return true;}else{return false;}' href=''><img src="%(images)s/smallbin.gif" border="0" alt='%(delete)s' /></a>
""" % {
'images' : CFG_SITE_URL + '/img',
'id' : submission['id'],
'action' : submission['act'],
'doctype' : submission['doctype'],
'sure' : _("Are you sure you want to delete this submission?"),
'delete' : _("Delete submission %(x_id)s in %(x_docname)s") % {
'x_id' : str(submission['id']),
'x_docname' : str(submission['docname'])
},
'ln_link': (ln != CFG_SITE_LANG and '&amp;ln=' + ln) or ''
}
else:
idtext = submission['id']
if operator.mod(num,2) == 0:
color = "#e2e2e2"
else:
color = "#f0f0f0"
if submission['reference']:
reference = submission['reference']
if not submission['pending']:
# record was integrated, so propose link:
reference = create_html_link('%s/search' % CFG_SITE_URL, {
'ln' : ln,
'p' : submission['reference'],
'f' : 'reportnumber'
}, submission['reference'])
else:
reference = """<font color="red">%s</font>""" % _("Reference not yet given")
cdate = str(submission['cdate']).replace(" ","&nbsp;")
mdate= str(submission['mdate']).replace(" ","&nbsp;")
out += """
<tr bgcolor="%(color)s">
<td align="center" class="mycdscell">
%(actname)s
</td>
<td align="center" class="mycdscell">
%(status)s
</td>
<td class="mycdscell">
%(idtext)s
</td>
<td class="mycdscell">
&nbsp;%(reference)s
</td>
<td class="mycdscell">
%(cdate)s
</td>
<td class="mycdscell">
%(mdate)s
</td>
</tr>
""" % {
'color' : color,
'actname' : submission['actname'],
'status' : submission['status'],
'idtext' : idtext,
'reference' : reference,
'cdate' : cdate,
'mdate' : mdate,
}
num += 1
out += "</table></td></tr></table></form>"
return out
def tmpl_yourapprovals(self, ln, referees):
"""
Displays the doctypes and categories for which the user is referee
Parameters:
- 'ln' *string* - The language to display the interface in
- 'referees' *array* - All the doctypes for which the user is referee:
- 'doctype' *string* - The doctype
- 'docname' *string* - The display name of the doctype
- 'categories' *array* - The specific categories for which the user is referee:
- 'id' *string* - The category id
- 'name' *string* - The display name of the category
"""
# load the right message language
_ = gettext_set_language(ln)
out = """ <table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(refdocs)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'refdocs' : _("Refereed Documents"),
}
for doctype in referees:
out += """<ul><li><b>%(docname)s</b><ul>""" % doctype
if doctype ['categories'] is None:
out += '''<li><a href="publiline.py?doctype=%(doctype)s%(ln_link)s">%(generalref)s</a></li>''' % {
'docname' : doctype['docname'],
'doctype' : doctype['doctype'],
'generalref' : _("You are a general referee"),
'ln_link': '&amp;ln=' + ln}
else:
for category in doctype['categories']:
out += """<li><a href="publiline.py?doctype=%(doctype)s&amp;categ=%(categ)s%(ln_link)s">%(referee)s</a></li>""" % {
'referee' : _("You are a referee for category:") + ' ' + str(category['name']) + ' (' + str(category['id']) + ')',
'doctype' : doctype['doctype'],
'categ' : category['id'],
'ln_link': '&amp;ln=' + ln}
out += "</ul><br /></li></ul>"
out += "</td></tr></table>"
out += '''<p>To see the status of documents for which approval has been requested, click <a href=\"%(url)s/publiline.py?flow=cplx\">here</a></p>''' % {'url' : CFG_SITE_URL}
return out
def tmpl_publiline_selectdoctype(self, ln, docs):
"""
Displays the doctypes that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'docs' *array* - All the doctypes that the user can select:
- 'doctype' *string* - The doctype
- 'docname' *string* - The display name of the doctype
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(list)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(select)s:
</small>
<blockquote>""" % {
'list' : _("List of refereed types of documents"),
'select' : _("Select one of the following types of documents to check the documents status"),
}
for doc in docs:
params = {'ln' : ln}
params.update(doc)
out += '<li><a href="publiline.py?doctype=%(doctype)s&amp;ln=%(ln)s">%(docname)s</a></li><br />' % params
out += """</blockquote>
</td>
</tr>
</table>
<a href="publiline.py?flow=cplx&amp;ln=%s">%s</a>""" % (ln, _("Go to specific approval workflow"))
return out
def tmpl_publiline_selectcplxdoctype(self, ln, docs):
"""
Displays the doctypes that the user can select in a complex workflow
Parameters:
- 'ln' *string* - The language to display the interface in
- 'docs' *array* - All the doctypes that the user can select:
- 'doctype' *string* - The doctype
- 'docname' *string* - The display name of the doctype
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(list)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(select)s:
</small>
<blockquote>""" % {
'list' : _("List of refereed types of documents"),
'select' : _("Select one of the following types of documents to check the documents status"),
}
for doc in docs:
params = {'ln' : ln}
params.update(doc)
out += '<li><a href="publiline.py?flow=cplx&doctype=%(doctype)s&amp;ln=%(ln)s">%(docname)s</a></li><br />' % params
out += """</blockquote> </td> </tr> </table> </li><br/>"""
return out
def tmpl_publiline_selectcateg(self, ln, doctype, title, categories):
"""
Displays the categories from a doctype that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'title' *string* - The doctype name
- 'categories' *array* - All the categories that the user can select:
- 'id' *string* - The id of the category
- 'waiting' *int* - The number of documents waiting
- 'approved' *int* - The number of approved documents
- 'rejected' *int* - The number of rejected documents
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(title)s: %(list_categ)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(choose_categ)s
<blockquote>
<form action="publiline.py" method="get">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="" />
<input type="hidden" name="ln" value="%(ln)s" />
</form>
<table>
<tr>
<td align="left">""" % {
'title' : title,
'doctype' : doctype,
'list_categ' : _("List of refereed categories"),
'choose_categ' : _("Please choose a category"),
'ln' : ln,
}
for categ in categories:
num = categ['waiting'] + categ['approved'] + categ['rejected']
if categ['waiting'] != 0:
classtext = "class=\"blocknote\""
else:
classtext = ""
out += """<a href="" onclick="document.forms[0].categ.value='%(id)s';document.forms[0].submit();return false;"><span %(classtext)s>%(id)s</span></a> (%(num)s document(s)""" % {
'id' : categ['id'],
'classtext' : classtext,
'num' : num,
}
if categ['waiting'] != 0:
out += """| %(waiting)s <img alt="%(pending)s" src="%(images)s/waiting_or.gif" border="0" />""" % {
'waiting' : categ['waiting'],
'pending' : _("Pending"),
'images' : CFG_SITE_URL + '/img',
}
if categ['approved'] != 0:
out += """| %(approved)s<img alt="%(approved_text)s" src="%(images)s/smchk_gr.gif" border="0" />""" % {
'approved' : categ['approved'],
'approved_text' : _("Approved"),
'images' : CFG_SITE_URL + '/img',
}
if categ['rejected'] != 0:
out += """| %(rejected)s<img alt="%(rejected_text)s" src="%(images)s/cross_red.gif" border="0" />""" % {
'rejected' : categ['rejected'],
'rejected_text' : _("Rejected"),
'images' : CFG_SITE_URL + '/img',
}
out += ")<br />"
out += """
</td>
<td>
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(key)s:</th>
</tr>
<tr>
<td>
<img alt="%(pending)s" src="%(images)s/waiting_or.gif" border="0" /> %(waiting)s<br />
<img alt="%(approved)s" src="%(images)s/smchk_gr.gif" border="0" /> %(already_approved)s<br />
<img alt="%(rejected)s" src="%(images)s/cross_red.gif" border="0" /> %(rejected_text)s<br /><br />
<small class="blocknote">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</small> %(somepending)s<br />
</td>
</tr>
</table>
</td>
</tr>
</table>
</blockquote>
</td>
</tr>
</table>""" % {
'key' : _("Key"),
'pending' : _("Pending"),
'images' : CFG_SITE_URL + '/img',
'waiting' : _("Waiting for approval"),
'approved' : _("Approved"),
'already_approved' : _("Already approved"),
'rejected' : _("Rejected"),
'rejected_text' : _("Rejected"),
'somepending' : _("Some documents are pending."),
}
return out
def tmpl_publiline_selectcplxcateg(self, ln, doctype, title, types):
"""
Displays the categories from a doctype that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'title' *string* - The doctype name
- 'categories' *array* - All the categories that the user can select:
- 'id' *string* - The id of the category
- 'waiting' *int* - The number of documents waiting
- 'approved' *int* - The number of approved documents
- 'rejected' *int* - The number of rejected documents
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
#out = """
# <table class="searchbox" width="100%%" summary="">
# <tr>
# <th class="portalboxheader">%(title)s: %(list_type)s</th>
# </tr>
# </table><br />
# <table class="searchbox" width="100%%" summary="">
# <tr>""" % {
# 'title' : title,
# 'list_type' : _("List of specific approvals"),
# }
columns = []
columns.append ({'apptype' : 'RRP',
'list_categ' : _("List of refereing categories"),
'id_form' : 0,
})
#columns.append ({'apptype' : 'RPB',
# 'list_categ' : _("List of publication categories"),
# 'id_form' : 1,
# })
#columns.append ({'apptype' : 'RDA',
# 'list_categ' : _("List of direct approval categories"),
# 'id_form' : 2,
# })
for column in columns:
out += """
<td>
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(list_categ)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(choose_categ)s
<blockquote>
<form action="publiline.py" method="get">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="ln" value="%(ln)s" />
</form>
<table>
<tr>
<td align="left">""" % {
'doctype' : doctype,
'apptype' : column['apptype'],
'list_categ' : column['list_categ'],
'choose_categ' : _("Please choose a category"),
'ln' : ln,
}
for categ in types[column['apptype']]:
num = categ['waiting'] + categ['approved'] + categ['rejected'] + categ['cancelled']
if categ['waiting'] != 0:
classtext = "class=\"blocknote\""
else:
classtext = ""
out += """<table><tr><td width="200px">*&nbsp<a href="" onclick="document.forms[%(id_form)s].categ.value='%(id)s';document.forms[%(id_form)s].submit();return false;"><small %(classtext)s>%(desc)s</small></td><td width="150px"></a><small> Total document(s) : %(num)s """ % {
'id' : categ['id'],
'id_form' : column['id_form'],
'classtext' : classtext,
'num' : num,
'desc' : categ['desc'],
}
out += """<td width="100px">"""
#if categ['waiting'] != 0:
out += """ %(waiting)s &nbsp&nbsp<img alt="%(pending)s" src="%(images)s/waiting_or.gif" border="0" /></td>""" % {
'waiting' : categ['waiting'],
'pending' : _("Pending"),
'images' : CFG_SITE_URL + '/img',
}
out += """<td width="100px">"""
#if categ['approved'] != 0:
out += """ %(approved)s &nbsp&nbsp<img alt="%(approved_text)s" src="%(images)s/smchk_gr.gif" border="0" /></td>""" % {
'approved' : categ['approved'],
'approved_text' : _("Approved"),
'images' : CFG_SITE_URL + '/img',
}
out += """<td width="100px">"""
#if categ['rejected'] != 0:
out += """ %(rejected)s&nbsp&nbsp<img alt="%(rejected_text)s" src="%(images)s/cross_red.gif" border="0" /></td>""" % {
'rejected' : categ['rejected'],
'rejected_text' : _("Rejected"),
'images' : CFG_SITE_URL + '/img',
}
out += """<td width="100px">"""
#if categ['cancelled'] != 0:
out += """ %(cancelled)s&nbsp&nbsp<img alt="%(cancelled_text)s" src="%(images)s/smchk_rd.gif" border="0" /></td>""" % {
'cancelled' : categ['cancelled'],
'cancelled_text' : _("Cancelled"),
'images' : CFG_SITE_URL + '/img',
}
out += "</small></td></tr>"
out += """
</table>
</td>
</tr>
</table>
</blockquote>
</td>
</tr>
</table>
</td>"""
# Key
out += """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(key)s:</th>
</tr>
<tr>
<td>
<img alt="%(pending)s" src="%(images)s/waiting_or.gif" border="0" /> %(waiting)s<br />
<img alt="%(approved)s" src="%(images)s/smchk_gr.gif" border="0" /> %(already_approved)s<br />
<img alt="%(rejected)s" src="%(images)s/cross_red.gif" border="0" /> %(rejected_text)s<br />
<img alt="%(cancelled)s" src="%(images)s/smchk_rd.gif" border="0" /> %(cancelled_text)s<br /><br />
<small class="blocknote">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</small> %(somepending)s<br />
</td>
</tr>
</table>
</blockquote>
</td>
</tr>
</table>""" % {
'key' : _("Key"),
'pending' : _("Pending"),
'images' : CFG_SITE_URL + '/img',
'waiting' : _("Waiting for approval"),
'approved' : _("Approved"),
'already_approved' : _("Already approved"),
'rejected' : _("Rejected"),
'rejected_text' : _("Rejected"),
'cancelled' : _("Cancelled"),
'cancelled_text' : _("Cancelled"),
'somepending' : _("Some documents are pending."),
}
return out
def tmpl_publiline_selectdocument(self, ln, doctype, title, categ, docs):
"""
Displays the documents that the user can select in the specified category
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'title' *string* - The doctype name
- 'categ' *string* - the category
- 'docs' *array* - All the categories that the user can select:
- 'RN' *string* - The id of the document
- 'status' *string* - The status of the document
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(title)s - %(categ)s: %(list)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(choose_report)s
<blockquote>
<form action="publiline.py" method="get">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="" />
<input type="hidden" name="ln" value="%(ln)s">
</form>
<table class="searchbox">
<tr>
<th class="portalboxheader">%(report_no)s</th>
<th class="portalboxheader">%(pending)s</th>
<th class="portalboxheader">%(approved)s</th>
<th class="portalboxheader">%(rejected)s</th>
</tr>
""" % {
'doctype' : doctype,
'title' : title,
'categ' : categ,
'list' : _("List of refereed documents"),
'choose_report' : _("Click on a report number for more information."),
'report_no' : _("Report Number"),
'pending' : _("Pending"),
'approved' : _("Approved"),
'rejected' : _("Rejected"),
'ln': ln,
}
for doc in docs:
status = doc ['status']
if status == "waiting":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">
<img alt="check" src="%(images)s/waiting_or.gif" />
</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : CFG_SITE_URL + '/img',
}
elif status == "rejected":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/cross_red.gif" /></td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : CFG_SITE_URL + '/img',
}
elif status == "approved":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/smchk_gr.gif" /></td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : CFG_SITE_URL + '/img',
}
out += """ </table>
</blockquote>
</td>
</tr>
</table>"""
return out
def tmpl_publiline_selectcplxdocument(self, ln, doctype, title, categ, categname, docs, apptype):
"""
Displays the documents that the user can select in the specified category
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'title' *string* - The doctype name
- 'categ' *string* - the category
- 'docs' *array* - All the categories that the user can select:
- 'RN' *string* - The id of the document
- 'status' *string* - The status of the document
- 'apptype' *string* - the approval type
"""
# load the right message language
_ = gettext_set_language(ln)
listtype = ""
if apptype == "RRP":
listtype = _("List of refereed documents")
elif apptype == "RPB":
listtype = _("List of publication documents")
elif apptype == "RDA":
listtype = _("List of direct approval documents")
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(title)s - %(categname)s: %(list)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(choose_report)s
<blockquote>
<form action="publiline.py" method="get">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="ln" value="%(ln)s" />
</form>
<table class="searchbox">
<tr>
<th class="portalboxheader">%(report_no)s</th>
<th class="portalboxheader">%(pending)s</th>
<th class="portalboxheader">%(approved)s</th>
<th class="portalboxheader">%(rejected)s</th>
<th class="portalboxheader">%(cancelled)s</th>
</tr>
""" % {
'doctype' : doctype,
'title' : title,
'categname' : categname,
'categ' : categ,
'list' : listtype,
'choose_report' : _("Click on a report number for more information."),
'apptype' : apptype,
'report_no' : _("Report Number"),
'pending' : _("Pending"),
'approved' : _("Approved"),
'rejected' : _("Rejected"),
'cancelled' : _("Cancelled"),
'ln': ln,
}
for doc in docs:
status = doc ['status']
if status == "waiting":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center"><img alt="check" src="%(images)s/waiting_or.gif" /></td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : CFG_SITE_URL + '/img',
}
elif status == "rejected":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/cross_red.gif" /></td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : CFG_SITE_URL + '/img',
}
elif status == "approved":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/smchk_gr.gif" /></td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : CFG_SITE_URL + '/img',
}
elif status == "cancelled":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/smchk_rd.gif" /></td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : CFG_SITE_URL + '/img',
}
out += """ </table>
</blockquote>
</td>
</tr>
</table>"""
return out
def tmpl_publiline_displaydoc(self, ln, doctype, docname, categ, rn, status, dFirstReq, dLastReq, dAction, access, confirm_send, auth_code, auth_message, authors, title, sysno, newrn, note):
"""
Displays the categories from a doctype that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'docname' *string* - The doctype name
- 'categ' *string* - the category
- 'rn' *string* - The document RN (id number)
- 'status' *string* - The status of the document
- 'dFirstReq' *string* - The date of the first approval request
- 'dLastReq' *string* - The date of the last approval request
- 'dAction' *string* - The date of the last action (approval or rejection)
- 'confirm_send' *bool* - must display a confirmation message about sending approval email
- 'auth_code' *bool* - authorised to referee this document
- 'auth_message' *string* - ???
- 'authors' *string* - the authors of the submission
- 'title' *string* - the title of the submission
- 'sysno' *string* - the unique database id for the record
- 'newrn' *string* - the record number assigned to the submission
- 'note' *string* - Note about the approval request.
"""
# load the right message language
_ = gettext_set_language(ln)
if status == "waiting":
image = """<img src="%s/waiting_or.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "approved":
image = """<img src="%s/smchk_gr.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "rejected":
image = """<img src="%s/iconcross.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
else:
image = ""
out = """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(image)s %(rn)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'image' : image,
'rn' : rn,
}
if confirm_send:
out += """<i><strong class="headline">%(requestsent)s</strong></i><br /><br />""" % {
'requestsent' : _("Your request has been sent to the referee."),
}
out += """<form action="publiline.py">
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<small>""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'ln' : ln,
}
if title != "unknown":
out += """<strong class="headline">%(title_text)s</strong>%(title)s<br /><br />""" % {
'title_text' : _("Title:"),
'title' : title,
}
if authors != "":
out += """<strong class="headline">%(author_text)s</strong>%(authors)s<br /><br />""" % {
'author_text' : _("Author:"),
'authors' : authors,
}
if sysno != "":
out += """<strong class="headline">%(more)s</strong>
- <a href="%(siteurl)s/record/%(sysno)s?ln=%(ln)s">%(click)s</a>
+ <a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(sysno)s?ln=%(ln)s">%(click)s</a>
<br /><br />
""" % {
'more' : _("More information:"),
'click' : _("Click here"),
'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'sysno' : sysno,
'ln' : ln,
}
if note and auth_code == 0:
out += """<table><tr><td valign="top"><strong class="headline">%(note_text)s</strong></td><td><em>%(note)s</em></td></tr></table>""" % {
'note_text' : _("Approval note:"),
'note' : cgi.escape(note).replace('\n', '<br />'),
}
if status == "waiting":
out += _("This document is still %(x_fmt_open)swaiting for approval%(x_fmt_close)s.") % {'x_fmt_open': '<strong class="headline">',
'x_fmt_close': '</strong>'}
out += "<br /><br />"
out += _("It was first sent for approval on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
if dLastReq == "0000-00-00 00:00:00":
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
else:
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dLastReq) + '</strong><br />'
out += "<br />" + _("You can send an approval request email again by clicking the following button:") + " <br />" +\
"""<input class="adminbutton" type="submit" name="send" value="%(send)s" onclick="return confirm('%(warning)s')" />""" % {
'send' : _("Send Again"),
'warning' : _("WARNING! Upon confirmation, an email will be sent to the referee.")
}
if auth_code == 0:
out += "<br />" + _("As a referee for this document, you may click this button to approve or reject it") + ":<br />" +\
"""<input class="adminbutton" type="submit" name="approval" value="%(approve)s" onclick="window.location='approve.py?%(access)s&amp;ln=%(ln)s';return false;" />""" % {
'approve' : _("Approve/Reject"),
'access' : access,
'ln' : ln
}
if status == "approved":
out += _("This document has been %(x_fmt_open)sapproved%(x_fmt_close)s.") % {'x_fmt_open': '<strong class="headline">', 'x_fmt_close': '</strong>'}
out += '<br />' + _("Its approved reference is:") + ' <strong class="headline">' + str(newrn) + '</strong><br /><br />'
out += _("It was first sent for approval on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
if dLastReq == "0000-00-00 00:00:00":
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
else:
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dLastReq) + '</strong><br />' +\
_("It was approved on:") + ' <strong class="headline">' + str(dAction) + '</strong><br />'
if status == "rejected":
out += _("This document has been %(x_fmt_open)srejected%(x_fmt_close)s.") % {'x_fmt_open': '<strong class="headline">', 'x_fmt_close': '</strong>'}
out += "<br /><br />"
out += _("It was first sent for approval on:") + ' <strong class="headline">' + str(dFirstReq) +'</strong><br />'
if dLastReq == "0000-00-00 00:00:00":
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
else:
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dLastReq) +'</strong><br />'
out += _("It was rejected on:") + ' <strong class="headline">' + str(dAction) + '</strong><br />'
out += """ </small></form>
<br />
</td>
</tr>
</table>"""
return out
def tmpl_publiline_displaycplxdoc(self, ln, doctype, docname, categ, rn, apptype, status, dates, isPubCom, isEdBoard, isReferee, isProjectLeader, isAuthor, authors, title, sysno, newrn):
# load the right message language
_ = gettext_set_language(ln)
if status == "waiting":
image = """<img src="%s/waiting_or.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "approved":
image = """<img src="%s/smchk_gr.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "rejected":
image = """<img src="%s/iconcross.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "cancelled":
image = """<img src="%s/smchk_rd.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
else:
image = ""
out = """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(image)s %(rn)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'image' : image,
'rn' : rn,
}
out += """<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="" />
<input type="hidden" name="ln" value="%(ln)s" />
""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'ln': ln,
}
out += "<table><tr height='30px'><td width='120px'>"
if title != "unknown":
out += """<strong class="headline">%(title_text)s</strong></td><td>%(title)s</td></tr>""" % {
'title_text' : _("Title:"),
'title' : title,
}
out += "<tr height='30px'><td width='120px'>"
if authors != "":
out += """<strong class="headline">%(author_text)s</strong></td><td>%(authors)s</td></tr>""" % {
'author_text' : _("Author:"),
'authors' : authors,
}
out += "<tr height='30px'><td width='120px'>"
if sysno != "":
out += """<strong class="headline">%(more)s</strong>
- </td><td><a href="%(siteurl)s/record/%(sysno)s?ln=%(ln)s">%(click)s</a>
+ </td><td><a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(sysno)s?ln=%(ln)s">%(click)s</a>
</td></tr>
""" % {
'more' : _("More information:"),
'click' : _("Click here"),
'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'sysno' : sysno,
'ln' : ln,
}
out += "</table>"
out += "<br /><br />"
if apptype == "RRP":
out += "<table><tr><td width='400px'>"
out += _("It has first been asked for refereing process on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br /></td></tr>'
out += "<tr><td width='400px'>"
out += _("Last request e-mail was sent to the publication committee chair on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br /></td></tr>'
if dates['dRefereeSel'] != None:
out += "<tr><td width='400px'>"
out += _("A referee has been selected by the publication committee on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dRefereeSel']) + '</strong><br /></td></tr>'
else:
out += "<tr><td width='400px'>"
out += _("No referee has been selected yet.") + "</td><td>"
if (status != "cancelled") and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="RefereeSel", linkText=_("Select a referee"))
out += '<br /></td></tr>'
if dates['dRefereeRecom'] != None:
out += "<tr><td width='400px'>"
out += _("The referee has sent his final recommendations to the publication committee on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br /></td></tr>'
else:
out += "<tr><td width='400px'>"
out += _("No recommendation from the referee yet.") + "</td><td>"
if (status != "cancelled") and (dates['dRefereeSel'] != None) and (isReferee == 0):
out += displaycplxdoc_displayauthaction (action="RefereeRecom", linkText=_("Send a recommendation"))
out += '<br /></td></tr>'
if dates['dPubComRecom'] != None:
out += "<tr><td width='400px'>"
out += _("The publication committee has sent his final recommendations to the project leader on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dPubComRecom']) + '</strong><br /></td></tr>'
else:
out += "<tr><td width='400px'>"
out += _("No recommendation from the publication committee yet.") + "</td><td>"
if (status != "cancelled") and (dates['dRefereeRecom'] != None) and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="PubComRecom", linkText=_("Send a recommendation"))
out += '<br /></td></tr>'
if status == "cancelled":
out += "<tr><td width='400px'>"
out += _("It has been cancelled by the author on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br /></td></tr>'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += "<tr><td width='400px'>"
out += _("It has been approved by the project leader on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br /></td></tr>'
elif status == "rejected":
out += "<tr><td width='400px'>"
out += _("It has been rejected by the project leader on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br /></td></tr>'
else:
out += "<tr><td width='400px'>"
out += _("No final decision taken yet.") + "</td><td>"
if (dates['dPubComRecom'] != None) and (isProjectLeader == 0):
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
out += '<br /></table>'
elif apptype == "RPB":
out += _("It has first been asked for refereing process on the ") + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br />'
out += _("Last request e-mail was sent to the publication committee chair on the ") + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br />'
if dates['dEdBoardSel'] != None:
out += _("An editorial board has been selected by the publication committee on the ") + ' <strong class="headline">' + str(dates['dEdBoardSel']) + '</strong>'
if (status != "cancelled") and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="AddAuthorList", linkText=_("Add an author list"))
out += '<br />'
else:
out += _("No editorial board has been selected yet.")
if (status != "cancelled") and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="EdBoardSel", linkText=_("Select an editorial board"))
out += '<br />'
if dates['dRefereeSel'] != None:
out += _("A referee has been selected by the editorial board on the ") + ' <strong class="headline">' + str(dates['dRefereeSel']) + '</strong><br />'
else:
out += _("No referee has been selected yet.")
if (status != "cancelled") and (dates['dEdBoardSel'] != None) and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="RefereeSel", linkText=_("Select a referee"))
out += '<br />'
if dates['dRefereeRecom'] != None:
out += _("The referee has sent his final recommendations to the editorial board on the ") + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br />'
else:
out += _("No recommendation from the referee yet.")
if (status != "cancelled") and (dates['dRefereeSel'] != None) and (isReferee == 0):
out += displaycplxdoc_displayauthaction (action="RefereeRecom", linkText=_("Send a recommendation"))
out += '<br />'
if dates['dEdBoardRecom'] != None:
out += _("The editorial board has sent his final recommendations to the publication committee on the ") + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br />'
else:
out += _("No recommendation from the editorial board yet.")
if (status != "cancelled") and (dates['dRefereeRecom'] != None) and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="EdBoardRecom", linkText=_("Send a recommendation"))
out += '<br />'
if dates['dPubComRecom'] != None:
out += _("The publication committee has sent his final recommendations to the project leader on the ") + ' <strong class="headline">' + str(dates['dPubComRecom']) + '</strong><br />'
else:
out += _("No recommendation from the publication committee yet.")
if (status != "cancelled") and (dates['dEdBoardRecom'] != None) and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="PubComRecom", linkText=_("Send a recommendation"))
out += '<br />'
if status == "cancelled":
out += _("It has been cancelled by the author on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += _("It has been approved by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif status == "rejected":
out += _("It has been rejected by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
else:
out += _("No final decision taken yet.")
if (dates['dPubComRecom'] != None) and (isProjectLeader == 0):
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
out += '<br />'
elif apptype == "RDA":
out += _("It has first been asked for refereing process on the ") + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br />'
out += _("Last request e-mail was sent to the project leader on the ") + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br />'
if status == "cancelled":
out += _("It has been cancelled by the author on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += _("It has been approved by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif status == "rejected":
out += _("It has been rejected by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
else:
out += _("No final decision taken yet.")
if isProjectLeader == 0:
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
out += '<br />'
out += """ </form>
<br />
</td>
</tr>
</table>"""
return out
def tmpl_publiline_displaycplxdocitem(self,
doctype, categ, rn, apptype, action,
comments,
(user_can_view_comments, user_can_add_comment, user_can_delete_comment),
selected_category,
selected_topic, selected_group_id, comment_subject, comment_body, ln):
_ = gettext_set_language(ln)
if comments and user_can_view_comments:
comments_text = ''
comments_overview = '<ul>'
for comment in comments:
(cmt_uid, cmt_nickname, cmt_title, cmt_body, cmt_date, cmt_priority, cmtid) = comment
comments_overview += '<li><a href="#%s">%s - %s</a> (%s)</li>' % (cmtid, cmt_nickname, cmt_title, convert_datetext_to_dategui (cmt_date))
comments_text += """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr><td class="bsktitle"><a name="%s"></a>%s - %s (%s)</td><td><a href=%s/publiline.py?flow=cplx&doctype=%s&apptype=%s&categ=%s&RN=%s&reply=true&commentId=%s&ln=%s#add_comment>Reply</a></td><td><a href="#top">Top</a></td></tr>
</thead>
<tbody>
<tr><td colspan="2">%s</td></tr>
</tbody>
</table>""" % (cmtid, cmt_nickname, cmt_title, convert_datetext_to_dategui (cmt_date), CFG_SITE_URL, doctype, apptype, categ, rn, cmt_uid, ln, email_quoted_txt2html(cmt_body))
comments_overview += '</ul>'
else:
comments_text = ''
comments_overview = 'None.'
body = ''
if user_can_view_comments:
body += """<h4>%(comments_label)s</h4>"""
if user_can_view_comments:
body += """%(comments)s"""
if user_can_add_comment:
validation = """
<input type="hidden" name="validate" value="go" />
<input type="submit" class="formbutton" value="%(button_label)s" />""" % {'button_label': _("Add Comment")}
body += self.tmpl_publiline_displaywritecomment (doctype, categ, rn, apptype, action, _("Add Comment"), comment_subject, validation, comment_body, ln)
body %= {
'comments_label': _("Comments"),
'action': action,
'button_label': _("Write a comment"),
'comments': comments_text}
content = '<br />'
out = """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bsktitle">
<a name="top"></a>
<h4>%(comments_overview_label)s</h4>
%(comments_overview)s
</td>
<td class="bskcmtcol"></td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2" style="padding: 5px;">
%(body)s
</td>
</tr>
</tbody>
</table>""" % {
'comments_overview_label' : _('Comments overview'),
'comments_overview' : comments_overview,
'body' : body,}
return out
def tmpl_publiline_displaywritecomment(self, doctype, categ, rn, apptype, action, write_label, title, validation, reply_message, ln):
_ = gettext_set_language(ln)
return """
<div style="width:100%%%%">
<hr />
<h2>%(write_label)s</h2>
<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<p class="bsklabel">%(title_label)s:</p>
<a name="add_comment"></a>
<input type="text" name="msg_subject" size="80" value="%(title)s"/>
<p class="bsklabel">%(comment_label)s:</p>
<textarea name="msg_body" rows="20" cols="80">%(reply_message)s</textarea><br />
%(validation)s
</form>
</div>""" % {'write_label': write_label,
'title_label': _("Title"),
'title': title,
'comment_label': _("Comment"),
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'validation' : validation,
'reply_message' : reply_message,
'ln' : ln,
}
def tmpl_publiline_displaydocplxaction(self, ln, doctype, categ, rn, apptype, action, status, authors, title, sysno, subtitle1, email_user_pattern, stopon1, users, extrausers, stopon2, subtitle2, usersremove, stopon3, validate_btn):
# load the right message language
_ = gettext_set_language(ln)
if status == "waiting":
image = """<img src="%s/waiting_or.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "approved":
image = """<img src="%s/smchk_gr.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "rejected":
image = """<img src="%s/iconcross.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
else:
image = ""
out = """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(image)s %(rn)s</th>
</tr>
<tr>
<td class="portalboxbody">
""" % {
'image' : image,
'rn' : rn,
}
if title != "unknown":
out += """<strong class="headline">%(title_text)s</strong>%(title)s<br /><br />""" % {
'title_text' : _("Title:"),
'title' : title,
}
if authors != "":
out += """<strong class="headline">%(author_text)s</strong>%(authors)s<br /><br />""" % {
'author_text' : _("Author:"),
'authors' : authors,
}
if sysno != "":
out += """<strong class="headline">%(more)s</strong>
- <a href="%(siteurl)s/record/%(sysno)s?ln=%(ln)s">%(click)s</a>
+ <a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(sysno)s?ln=%(ln)s">%(click)s</a>
<br /><br />
""" % {
'more' : _("More information:"),
'click' : _("Click here"),
'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'sysno' : sysno,
'ln' : ln,
}
out += """ <br />
</td>
</tr>
</table>"""
if ((apptype == "RRP") or (apptype == "RPB")) and ((action == "EdBoardSel") or (action == "RefereeSel")):
out += """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(subtitle)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'subtitle' : subtitle1,
}
out += """<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="ln" value="%(ln)s" />""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'ln': ln,
}
out += ' <span class="adminlabel">1. %s </span>\n' % _("search for user")
out += ' <input class="admin_wvar" type="text" name="email_user_pattern" value="%s" />\n' % (email_user_pattern, )
out += ' <input class="adminbutton" type="submit" value="%s"/>\n' % (_("search for users"), )
if (stopon1 == "") and (email_user_pattern != ""):
out += ' <br /><span class="adminlabel">2. %s </span>\n' % _("select user")
out += ' <select name="id_user" class="admin_w200">\n'
out += ' <option value="0">*** %s ***</option>\n' % _("select user")
for elem in users:
elem_id = elem[0]
email = elem[1]
out += ' <option value="%s">%s</option>\n' % (elem_id, email)
for elem in extrausers:
elem_id = elem[0]
email = elem[1]
out += ' <option value="%s">%s %s</option>\n' % (elem_id, email, _("connected"))
out += ' </select>\n'
out += ' <input class="adminbutton" type="submit" value="%s" />\n' % (_("add this user"), )
out += stopon2
elif stopon1 != "":
out += stopon1
out += """
</form>
<br />
</td>
</tr>
</table>"""
if action == "EdBoardSel":
out += """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(subtitle)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'subtitle' : subtitle2,
}
out += """<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="ln" value="%(ln)s" />""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'ln': ln,
}
out += ' <span class="adminlabel">1. %s </span>\n' % _("select user")
out += ' <select name="id_user_remove" class="admin_w200">\n'
out += ' <option value="0">*** %s ***</option>\n' % _("select user")
for elem in usersremove:
elem_id = elem[0]
email = elem[1]
out += ' <option value="%s">%s</option>\n' % (elem_id, email)
out += ' </select>\n'
out += ' <input class="adminbutton" type="submit" value="%s" />\n' % (_("remove this user"), )
out += stopon3
out += """
</form>
<br />
</td>
</tr>
</table>"""
if validate_btn != "":
out += """<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="validate" value="go" />
<input type="hidden" name="ln" value="%(ln)s" />
<input class="adminbutton" type="submit" value="%(validate_btn)s" />
</form>""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'validate_btn' : validate_btn,
'ln': ln,
}
return out
def tmpl_publiline_displaycplxrecom(self, ln, doctype, categ, rn, apptype, action, status, authors, title, sysno, msg_to, msg_to_group, msg_subject):
# load the right message language
_ = gettext_set_language(ln)
if status == "waiting":
image = """<img src="%s/waiting_or.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "approved":
image = """<img src="%s/smchk_gr.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
elif status == "rejected":
image = """<img src="%s/iconcross.gif" alt="" align="right" />""" % (CFG_SITE_URL + '/img')
else:
image = ""
out = """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(image)s %(rn)s</th>
</tr>
<tr>
<td class="portalboxbody">
""" % {
'image' : image,
'rn' : rn,
}
if title != "unknown":
out += """<strong class="headline">%(title_text)s</strong>%(title)s<br /><br />""" % {
'title_text' : _("Title:"),
'title' : title,
}
if authors != "":
out += """<strong class="headline">%(author_text)s</strong>%(authors)s<br /><br />""" % {
'author_text' : _("Author:"),
'authors' : authors,
}
if sysno != "":
out += """<strong class="headline">%(more)s</strong>
- <a href="%(siteurl)s/record/%(sysno)s?ln=%(ln)s">%(click)s</a>
+ <a href="%(siteurl)s/%(CFG_SITE_RECORD)s/%(sysno)s?ln=%(ln)s">%(click)s</a>
<br /><br />
""" % {
'more' : _("More information:"),
'click' : _("Click here"),
'siteurl' : CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'sysno' : sysno,
'ln' : ln,
}
out += """ <br />
</td>
</tr>
</table>"""
# escape forbidden character
msg_to = escape_html(msg_to)
msg_to_group = escape_html(msg_to_group)
msg_subject = escape_html(msg_subject)
write_box = """
<form action="publiline.py" method="post">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<div style="float: left; vertical-align:text-top; margin-right: 10px;">
<table class="mailbox">
<thead class="mailboxheader">
<tr>
<td class="inboxheader" colspan="2">
<table class="messageheader">
<tr>
<td class="mailboxlabel">%(to_label)s</td>"""
if msg_to != "":
addr_box = """
<td class="mailboxlabel">%(users_label)s</td>
<td style="width:100%%%%;" class="mailboxlabel">%(to_users)s</td>""" % {'users_label': _("User"),
'to_users' : msg_to,
}
if msg_to_group != "":
addr_box += """
</tr>
<tr>
<td class="mailboxlabel">&nbsp;</td>
<td class="mailboxlabel">%(groups_label)s</td>
<td style="width:100%%%%;" class="mailboxlabel">%(to_groups)s</td>""" % {'groups_label': _("Group"),
'to_groups': msg_to_group,
}
elif msg_to_group != "":
addr_box = """
<td class="mailboxlabel">%(groups_label)s</td>
<td style="width:100%%%%;" class="mailboxlabel">%(to_groups)s</td>""" % {'groups_label': _("Group"),
'to_groups': msg_to_group,
}
else:
addr_box = """
<td class="mailboxlabel">&nbsp;</td>
<td class="mailboxlabel">&nbsp;</td>"""
write_box += addr_box
write_box += """
</tr>
<tr>
<td class="mailboxlabel">&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td class="mailboxlabel">%(subject_label)s</td>
<td colspan="2">
<input class="mailboxinput" type="text" name="msg_subject" value="%(subject)s" />
</td>
</tr>
</table>
</td>
</tr>
</thead>
<tfoot>
<tr>
<td style="height:0px" colspan="2"></td>
</tr>
</tfoot>
<tbody class="mailboxbody">
<tr>
<td class="mailboxlabel">%(message_label)s</td>
<td>
<textarea name="msg_body" rows="10" cols="50"></textarea>
</td>
</tr>
<tr class="mailboxfooter">
<td>
<select name="validate">
<option> %(select)s</option>
<option value="approve">%(approve)s</option>
<option value="reject">%(reject)s</option>
</select>
</td>
<td colspan="2" class="mailboxfoot">
<input type="submit" name="send_button" value="%(send_label)s" class="formbutton"/>
</td>
</tr>
</tbody>
</table>
</div>
</form>
"""
write_box = write_box % {'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'subject' : msg_subject,
'to_label': _("To:"),
'subject_label': _("Subject:"),
'message_label': _("Message:"),
'send_label': _("SEND"),
'select' : _("Select:"),
'approve' : _("approve"),
'reject' : _("reject"),
'ln': ln,
}
out += write_box
return out
def displaycplxdoc_displayauthaction(action, linkText):
return """ <strong class="headline">(<a href="" onclick="document.forms[0].action.value='%(action)s';document.forms[0].submit();return false;">%(linkText)s</a>)</strong>""" % {
"action" : action,
"linkText" : linkText
}
diff --git a/modules/websubmit/lib/websubmit_webinterface.py b/modules/websubmit/lib/websubmit_webinterface.py
index 2382c00b1..a938c9cfe 100644
--- a/modules/websubmit/lib/websubmit_webinterface.py
+++ b/modules/websubmit/lib/websubmit_webinterface.py
@@ -1,1194 +1,1195 @@
## This file is part of Invenio.
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__lastupdated__ = """$Date$"""
__revision__ = "$Id$"
import os
import time
import cgi
import sys
import shutil
from urllib import urlencode
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_TMPDIR, \
CFG_SITE_NAME_INTL, \
CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_WEBSUBMIT_STORAGEDIR, \
CFG_PREFIX, \
- CFG_CERN_SITE
+ CFG_CERN_SITE, \
+ CFG_SITE_RECORD
from invenio import webinterface_handler_config as apache
from invenio.dbquery import run_sql
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_admin import acc_is_role
from invenio.webpage import page, create_error_box, pageheaderonly, \
pagefooteronly
from invenio.webuser import getUid, page_not_authorized, collect_user_info, isGuestUser, isUserSuperAdmin
from invenio.websubmit_config import *
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import make_canonical_urlargd, redirect_to_url
from invenio.messages import gettext_set_language
from invenio.search_engine import \
guess_primary_collection_of_a_record, get_colID, record_exists, \
create_navtrail_links, check_user_can_view_record, record_empty, \
is_user_owner_of_record
from invenio.bibdocfile import BibRecDocs, normalize_format, file_strip_ext, \
stream_restricted_icon, BibDoc, InvenioWebSubmitFileError, stream_file, \
decompose_file, propose_next_docname, get_subformat_from_format
from invenio.errorlib import register_exception
from invenio.websubmit_icon_creator import create_icon, InvenioWebSubmitIconCreatorError
import invenio.template
websubmit_templates = invenio.template.load('websubmit')
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.session import get_session
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
websearch_templates = invenio.template.load('websearch')
try:
from invenio.fckeditor_invenio_connector import FCKeditorConnectorInvenio
fckeditor_available = True
except ImportError, e:
fckeditor_available = False
from invenio.websubmit_managedocfiles import \
create_file_upload_interface, \
get_upload_file_interface_javascript, \
get_upload_file_interface_css, \
move_uploaded_files_to_storage
class WebInterfaceFilesPages(WebInterfaceDirectory):
def __init__(self,recid):
self.recid = recid
def _lookup(self, component, path):
- # after /record/<recid>/files/ every part is used as the file
+ # after /<CFG_SITE_RECORD>/<recid>/files/ every part is used as the file
# name
filename = component
def getfile(req, form):
args = wash_urlargd(form, websubmit_templates.files_default_urlargd)
ln = args['ln']
_ = gettext_set_language(ln)
uid = getUid(req)
user_info = collect_user_info(req)
verbose = args['verbose']
if verbose >= 1 and not isUserSuperAdmin(user_info):
# Only SuperUser can see all the details!
verbose = 0
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE > 1:
- return page_not_authorized(req, "/record/%s" % self.recid,
+ return page_not_authorized(req, "/%s/%s" % (CFG_SITE_RECORD, self.recid),
navmenuid='submit')
if record_exists(self.recid) < 1:
msg = "<p>%s</p>" % _("Requested record does not seem to exist.")
return warningMsg(msg, req, CFG_SITE_NAME, ln)
if record_empty(self.recid):
msg = "<p>%s</p>" % _("Requested record does not seem to have been integrated.")
return warningMsg(msg, req, CFG_SITE_NAME, ln)
(auth_code, auth_message) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : ln, 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target, norobot=True)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_message)
readonly = CFG_ACCESS_CONTROL_LEVEL_SITE == 1
# From now on: either the user provided a specific file
# name (and a possible version), or we return a list of
# all the available files. In no case are the docids
# visible.
try:
bibarchive = BibRecDocs(self.recid)
except InvenioWebSubmitFileError, e:
register_exception(req=req, alert_admin=True)
msg = "<p>%s</p><p>%s</p>" % (
_("The system has encountered an error in retrieving the list of files for this document."),
_("The error has been logged and will be taken in consideration as soon as possible."))
return warningMsg(msg, req, CFG_SITE_NAME, ln)
if bibarchive.deleted_p():
return print_warning(req, _("Requested record does not seem to exist."))
docname = ''
format = ''
version = ''
warn = ''
if filename:
# We know the complete file name, guess which docid it
# refers to
## TODO: Change the extension system according to ext.py from setlink
## and have a uniform extension mechanism...
docname = file_strip_ext(filename)
format = filename[len(docname):]
if format and format[0] != '.':
format = '.' + format
if args['subformat']:
format += ';%s' % args['subformat']
else:
docname = args['docname']
if not format:
format = args['format']
if args['subformat']:
format += ';%s' % args['subformat']
if not version:
version = args['version']
# version could be either empty, or all or an integer
try:
int(version)
except ValueError:
if version != 'all':
version = ''
display_hidden = isUserSuperAdmin(user_info)
if version != 'all':
# search this filename in the complete list of files
for doc in bibarchive.list_bibdocs():
if docname == doc.get_docname():
try:
docfile = doc.get_file(format, version)
(auth_code, auth_message) = docfile.is_restricted(user_info)
if auth_code != 0 and not is_user_owner_of_record(user_info, self.recid):
if CFG_WEBSUBMIT_ICON_SUBFORMAT_RE.match(get_subformat_from_format(format)):
return stream_restricted_icon(req)
if user_info['email'] == 'guest':
cookie = mail_cookie_create_authorize_action('viewrestrdoc', {'status' : docfile.get_status()})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : ln, 'referer' : \
CFG_SITE_URL + user_info['uri']}, {})
redirect_to_url(req, target)
else:
req.status = apache.HTTP_UNAUTHORIZED
warn += print_warning(_("This file is restricted: ") + auth_message)
break
if not docfile.hidden_p():
if not readonly:
ip = str(req.remote_ip)
res = doc.register_download(ip, version, format, uid)
try:
return docfile.stream(req)
except InvenioWebSubmitFileError, msg:
register_exception(req=req, alert_admin=True)
req.status = apache.HTTP_INTERNAL_SERVER_ERROR
return warningMsg(_("An error has happened in trying to stream the request file."), req, CFG_SITE_NAME, ln)
else:
req.status = apache.HTTP_UNAUTHORIZED
warn = print_warning(_("The requested file is hidden and can not be accessed."))
except InvenioWebSubmitFileError, msg:
register_exception(req=req, alert_admin=True)
if docname and format and not warn:
req.status = apache.HTTP_NOT_FOUND
warn += print_warning(_("Requested file does not seem to exist."))
filelist = bibarchive.display("", version, ln=ln, verbose=verbose, display_hidden=display_hidden)
t = warn + websubmit_templates.tmpl_filelist(
ln=ln,
recid=self.recid,
docname=args['docname'],
version=version,
filelist=filelist)
cc = guess_primary_collection_of_a_record(self.recid)
unordered_tabs = get_detailed_page_tabs(get_colID(cc), self.recid, ln)
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x,y: cmp(x[1],y[1]))
link_ln = ''
if ln != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
+ '%s/%s/%s/%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, tab_id, link_ln), \
tab_id == 'files',
unordered_tabs[tab_id]['enabled']) \
for (tab_id, order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
top = webstyle_templates.detailed_record_container_top(self.recid,
tabs,
args['ln'])
bottom = webstyle_templates.detailed_record_container_bottom(self.recid,
tabs,
args['ln'])
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req, self.recid, args['ln'])
return pageheaderonly(title=title,
navtrail=create_navtrail_links(cc=cc, aas=0, ln=ln) + \
- ''' &gt; <a class="navtrail" href="%s/record/%s">%s</a>
+ ''' &gt; <a class="navtrail" href="%s/%s/%s">%s</a>
&gt; %s''' % \
- (CFG_SITE_URL, self.recid, title, _("Access to Fulltext")),
+ (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, title, _("Access to Fulltext")),
description="",
keywords="keywords",
uid=uid,
language=ln,
req=req,
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(ln) + \
top + t + bottom + \
websearch_templates.tmpl_search_pageend(ln) + \
pagefooteronly(lastupdated=__lastupdated__, language=ln, req=req)
return getfile, []
def __call__(self, req, form):
- """Called in case of URLs like /record/123/files without
+ """Called in case of URLs like /CFG_SITE_RECORD/123/files without
trailing slash.
"""
args = wash_urlargd(form, websubmit_templates.files_default_urlargd)
ln = args['ln']
link_ln = ''
if ln != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
- return redirect_to_url(req, '%s/record/%s/files/%s' % (CFG_SITE_URL, self.recid, link_ln))
+ return redirect_to_url(req, '%s/%s/%s/files/%s' % (CFG_SITE_URL, CFG_SITE_RECORD, self.recid, link_ln))
def websubmit_legacy_getfile(req, form):
""" Handle legacy /getfile.py URLs """
args = wash_urlargd(form, {
'recid': (int, 0),
'docid': (int, 0),
'version': (str, ''),
'name': (str, ''),
'format': (str, ''),
'ln' : (str, CFG_SITE_LANG)
})
_ = gettext_set_language(args['ln'])
def _getfile_py(req, recid=0, docid=0, version="", name="", format="", ln=CFG_SITE_LANG):
if not recid:
## Let's obtain the recid from the docid
if docid:
try:
bibdoc = BibDoc(docid=docid)
recid = bibdoc.get_recid()
except InvenioWebSubmitFileError, e:
return warningMsg(_("An error has happened in trying to retrieve the requested file."), req, CFG_SITE_NAME, ln)
else:
return warningMsg(_('Not enough information to retrieve the document'), req, CFG_SITE_NAME, ln)
else:
if not name and docid:
## Let's obtain the name from the docid
try:
bibdoc = BibDoc(docid)
name = bibdoc.get_docname()
except InvenioWebSubmitFileError, e:
return warningMsg(_("An error has happened in trying to retrieving the requested file."), req, CFG_SITE_NAME, ln)
format = normalize_format(format)
- redirect_to_url(req, '%s/record/%s/files/%s%s?ln=%s%s' % (CFG_SITE_URL, recid, name, format, ln, version and 'version=%s' % version or ''), apache.HTTP_MOVED_PERMANENTLY)
+ redirect_to_url(req, '%s/%s/%s/files/%s%s?ln=%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, recid, name, format, ln, version and 'version=%s' % version or ''), apache.HTTP_MOVED_PERMANENTLY)
return _getfile_py(req, **args)
# --------------------------------------------------
from invenio.websubmit_engine import home, action, interface, endaction
class WebInterfaceSubmitPages(WebInterfaceDirectory):
_exports = ['summary', 'sub', 'direct', '', 'attachfile', 'uploadfile', \
'getuploadedfile', 'managedocfiles', 'managedocfilesasync']
def managedocfiles(self, req, form):
"""
Display admin interface to manage files of a record
"""
argd = wash_urlargd(form, {
'ln': (str, ''),
'access': (str, ''),
'recid': (int, None),
'do': (int, 0),
'cancel': (str, None),
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
user_info = collect_user_info(req)
# Check authorization
(auth_code, auth_msg) = acc_authorize_action(req,
'runbibdocfile')
if auth_code and user_info['email'] == 'guest':
# Ask to login
target = '/youraccount/login' + \
make_canonical_urlargd({'ln' : argd['ln'],
'referer' : CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, referer="/submit/managedocfiles",
uid=uid, text=auth_msg,
ln=argd['ln'],
navmenuid="admin")
# Prepare navtrail
navtrail = '''<a class="navtrail" href="%(CFG_SITE_URL)s/help/admin">Admin Area</a> &gt; %(manage_files)s''' \
% {'CFG_SITE_URL': CFG_SITE_URL,
'manage_files': _("Manage Document Files")}
body = ''
if argd['do'] != 0 and not argd['cancel']:
# Apply modifications
working_dir = os.path.join(CFG_TMPDIR,
'websubmit_upload_interface_config_' + str(uid),
argd['access'])
move_uploaded_files_to_storage(working_dir=working_dir,
recid=argd['recid'],
icon_sizes=['180>','700>'],
create_icon_doctypes=['*'],
force_file_revision=False)
# Clean temporary directory
shutil.rmtree(working_dir)
# Confirm modifications
body += '<p style="color:#0f0">%s</p>' % \
(_('Your modifications to record #%i have been submitted') % argd['recid'])
elif argd['cancel']:
# Clean temporary directory
working_dir = os.path.join(CFG_TMPDIR,
'websubmit_upload_interface_config_' + str(uid),
argd['access'])
shutil.rmtree(working_dir)
body += '<p style="color:#c00">%s</p>' % \
(_('Your modifications to record #%i have been cancelled') % argd['recid'])
if not argd['recid'] or argd['do'] != 0:
body += '''
<form method="post" action="%(CFG_SITE_URL)s/submit/managedocfiles">
<label for="recid">%(edit_record)s:</label>
<input type="text" name="recid" id="recid" />
<input type="submit" value="%(edit)s" class="adminbutton" />
</form>
''' % {'edit': _('Edit'),
'edit_record': _('Edit record'),
'CFG_SITE_URL': CFG_SITE_URL}
access = time.strftime('%Y%m%d_%H%M%S')
if argd['recid'] and argd['do'] == 0:
# Displaying interface to manage files
# Prepare navtrail
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req, argd['recid'],
argd['ln'])
navtrail = '''<a class="navtrail" href="%(CFG_SITE_URL)s/help/admin">Admin Area</a> &gt;
<a class="navtrail" href="%(CFG_SITE_URL)s/submit/managedocfiles">%(manage_files)s</a> &gt;
%(record)s: %(title)s
''' \
% {'CFG_SITE_URL': CFG_SITE_URL,
'title': title,
'manage_files': _("Document File Manager"),
'record': _("Record #%i") % argd['recid']}
# FIXME: add parameters to `runbibdocfile' in order to
# configure the file editor based on role, or at least
# move configuration below to some config file.
body += create_file_upload_interface(\
recid=argd['recid'],
ln=argd['ln'],
doctypes_and_desc=[('main', 'Main document'),
('latex', 'LaTeX'),
('source', 'Source'),
('additional', 'Additional File'),
('audio', 'Audio file'),
('video', 'Video file'),
('script', 'Script'),
('data', 'Data'),
('figure', 'Figure'),
('schema', 'Schema'),
('graph', 'Graph'),
('image', 'Image'),
('drawing', 'Drawing'),
('slides', 'Slides')],
can_revise_doctypes=['*'],
can_comment_doctypes=['*'],
can_describe_doctypes=['*'],
can_delete_doctypes=['*'],
can_keep_doctypes=['*'],
can_rename_doctypes=['*'],
can_add_format_to_doctypes=['*'],
can_restrict_doctypes=['*'],
restrictions_and_desc=[('', 'Public'),
('restricted', 'Restricted')],
uid=uid,
sbm_access=access,
display_hidden_files=True)[1]
body += '''<br />
<form method="post" action="%(CFG_SITE_URL)s/submit/managedocfiles">
<input type="hidden" name="recid" value="%(recid)s" />
<input type="hidden" name="do" value="1" />
<input type="hidden" name="access" value="%(access)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<div style="font-size:small">
<input type="submit" name="cancel" value="%(cancel_changes)s" />
<input type="submit" onclick="user_must_confirm_before_leaving_page=false;return true;" class="adminbutton" name="submit" id="applyChanges" value="%(apply_changes)s" />
</div></form>''' % \
{'apply_changes': _("Apply changes"),
'cancel_changes': _("Cancel all changes"),
'recid': argd['recid'],
'access': access,
'ln': argd['ln'],
'CFG_SITE_URL': CFG_SITE_URL}
body += websubmit_templates.tmpl_page_do_not_leave_submission_js(argd['ln'], enabled=True)
return page(title = _("Document File Manager") + (argd['recid'] and (': ' + _("Record #%i") % argd['recid']) or ''),
navtrail=navtrail,
navtrail_append_title_p=0,
metaheaderadd = get_upload_file_interface_javascript(form_url_params='?access='+access) + \
get_upload_file_interface_css(),
body = body,
uid = uid,
language=argd['ln'],
req=req,
navmenuid='admin')
def managedocfilesasync(self, req, form):
"Upload file and returns upload interface"
argd = wash_urlargd(form, {
'ln': (str, ''),
'recid': (int, 1),
'doctype': (str, ''),
'access': (str, ''),
'indir': (str, ''),
})
user_info = collect_user_info(req)
include_headers = False
# User submitted either through WebSubmit, or admin interface.
if form.has_key('doctype') and form.has_key('indir') \
and form.has_key('access'):
# Submitted through WebSubmit. Check rights
include_headers = True
working_dir = os.path.join(CFG_WEBSUBMIT_STORAGEDIR,
argd['indir'], argd['doctype'],
argd['access'])
try:
assert(working_dir == os.path.abspath(working_dir))
except AssertionError:
return apache.HTTP_UNAUTHORIZED
try:
# Retrieve recid from working_dir, safer.
recid_fd = file(os.path.join(working_dir, 'SN'))
recid = int(recid_fd.read())
recid_fd.close()
except:
recid = ""
try:
act_fd = file(os.path.join(working_dir, 'act'))
action = act_fd.read()
act_fd.close()
except:
action = ""
# Is user authorized to perform this action?
(auth_code, auth_msg) = acc_authorize_action(user_info,
"submit",
doctype=argd['doctype'],
act=action)
if not acc_is_role("submit", doctype=argd['doctype'], act=action):
# There is NO authorization plugged. User should have access
auth_code = 0
else:
# User must be allowed to attach files
(auth_code, auth_msg) = acc_authorize_action(user_info,
'runbibdocfile')
recid = argd['recid']
if auth_code:
return apache.HTTP_UNAUTHORIZED
return create_file_upload_interface(recid=recid,
ln=argd['ln'],
print_outside_form_tag=False,
print_envelope=False,
form=form,
include_headers=include_headers,
sbm_indir=argd['indir'],
sbm_access=argd['access'],
sbm_doctype=argd['doctype'],
uid=user_info['uid'])[1]
def uploadfile(self, req, form):
"""
Similar to /submit, but only consider files. Nice for
asynchronous Javascript uploads. Should be used to upload a
single file.
Also try to create an icon, and return URL to file(s) + icon(s)
Authentication is performed based on session ID passed as
parameter instead of cookie-based authentication, due to the
use of this URL by the Flash plugin (to upload multiple files
at once), which does not route cookies.
FIXME: consider adding /deletefile and /modifyfile functions +
parsing of additional parameters to rename files, add
comments, restrictions, etc.
"""
if sys.hexversion < 0x2060000:
try:
import simplejson as json
simplejson_available = True
except ImportError:
# Okay, no Ajax app will be possible, but continue anyway,
# since this package is only recommended, not mandatory.
simplejson_available = False
else:
import json
simplejson_available = True
argd = wash_urlargd(form, {
'doctype': (str, ''),
'access': (str, ''),
'indir': (str, ''),
'session_id': (str, ''),
'rename': (str, ''),
})
curdir = None
if not form.has_key("indir") or \
not form.has_key("doctype") or \
not form.has_key("access"):
return apache.HTTP_BAD_REQUEST
else:
curdir = os.path.join(CFG_WEBSUBMIT_STORAGEDIR,
argd['indir'],
argd['doctype'],
argd['access'])
user_info = collect_user_info(req)
if form.has_key("session_id"):
# Are we uploading using Flash, which does not transmit
# cookie? The expect to receive session_id as a form
# parameter. First check that IP addresses do not
# mismatch. A ValueError will be raises if there is
# something wrong
session = get_session(req=req, sid=argd['session_id'])
try:
session = get_session(req=req, sid=argd['session_id'])
except ValueError, e:
return apache.HTTP_BAD_REQUEST
# Retrieve user information. We cannot rely on the session here.
res = run_sql("SELECT uid FROM session WHERE session_key=%s", (argd['session_id'],))
if len(res):
uid = res[0][0]
user_info = collect_user_info(uid)
try:
act_fd = file(os.path.join(curdir, 'act'))
action = act_fd.read()
act_fd.close()
except:
act = ""
# Is user authorized to perform this action?
(auth_code, auth_message) = acc_authorize_action(uid, "submit",
verbose=0,
doctype=argd['doctype'],
act=action)
if acc_is_role("submit", doctype=argd['doctype'], act=action) and auth_code != 0:
# User cannot submit
return apache.HTTP_UNAUTHORIZED
else:
# Process the upload and get the response
added_files = {}
for key, formfields in form.items():
filename = key.replace("[]", "")
file_to_open = os.path.join(curdir, filename)
if hasattr(formfields, "filename") and formfields.filename:
dir_to_open = os.path.abspath(os.path.join(curdir,
'files',
str(user_info['uid']),
key))
try:
assert(dir_to_open.startswith(CFG_WEBSUBMIT_STORAGEDIR))
except AssertionError:
register_exception(req=req, prefix='curdir="%s", key="%s"' % (curdir, key))
return apache.HTTP_FORBIDDEN
if not os.path.exists(dir_to_open):
try:
os.makedirs(dir_to_open)
except:
register_exception(req=req, alert_admin=True)
return apache.HTTP_FORBIDDEN
filename = formfields.filename
## Before saving the file to disc, wash the filename (in particular
## washing away UNIX and Windows (e.g. DFS) paths):
filename = os.path.basename(filename.split('\\')[-1])
filename = filename.strip()
if filename != "":
# Check that file does not already exist
n = 1
while os.path.exists(os.path.join(dir_to_open, filename)):
#dirname, basename, extension = decompose_file(new_destination_path)
basedir, name, extension = decompose_file(filename)
new_name = propose_next_docname(name)
filename = new_name + extension
# This may be dangerous if the file size is bigger than the available memory
fp = open(os.path.join(dir_to_open, filename), "w")
fp.write(formfields.file.read())
fp.close()
fp = open(os.path.join(curdir, "lastuploadedfile"), "w")
fp.write(filename)
fp.close()
fp = open(file_to_open, "w")
fp.write(filename)
fp.close()
try:
# Create icon
(icon_path, icon_name) = create_icon(
{ 'input-file' : os.path.join(dir_to_open, filename),
'icon-name' : filename, # extension stripped automatically
'icon-file-format' : 'gif',
'multipage-icon' : False,
'multipage-icon-delay' : 100,
'icon-scale' : "300>", # Resize only if width > 300
'verbosity' : 0,
})
icons_dir = os.path.join(os.path.join(curdir,
'icons',
str(user_info['uid']),
key))
if not os.path.exists(icons_dir):
# Create uid/icons dir if needed
os.makedirs(icons_dir)
os.rename(os.path.join(icon_path, icon_name),
os.path.join(icons_dir, icon_name))
added_files[key] = {'name': filename,
'iconName': icon_name}
except InvenioWebSubmitIconCreatorError, e:
# We could not create the icon
added_files[key] = {'name': filename}
continue
else:
return apache.HTTP_BAD_REQUEST
# Send our response
if simplejson_available:
return json.dumps(added_files)
def getuploadedfile(self, req, form):
"""
Stream uploaded files.
For the moment, restrict to files in ./curdir/files/uid or
./curdir/icons/uid directory, so that we are sure we stream
files only to the user who uploaded them.
"""
argd = wash_urlargd(form, {'indir': (str, None),
'doctype': (str, None),
'access': (str, None),
'icon': (int, 0),
'key': (str, None),
'filename': (str, None)})
if None in argd.values():
return apache.HTTP_BAD_REQUEST
uid = getUid(req)
if argd['icon']:
file_path = os.path.join(CFG_WEBSUBMIT_STORAGEDIR,
argd['indir'],
argd['doctype'],
argd['access'],
'icons',
str(uid),
argd['key'],
argd['filename']
)
else:
file_path = os.path.join(CFG_WEBSUBMIT_STORAGEDIR,
argd['indir'],
argd['doctype'],
argd['access'],
'files',
str(uid),
argd['key'],
argd['filename']
)
abs_file_path = os.path.abspath(file_path)
if abs_file_path.startswith(CFG_WEBSUBMIT_STORAGEDIR):
# Check if file exist. Note that icon might not yet have
# been created.
for i in range(5):
if os.path.exists(abs_file_path):
return stream_file(req, abs_file_path)
time.sleep(1)
# Send error 404 in all other cases
return apache.HTTP_NOT_FOUND
def attachfile(self, req, form):
"""
Process requests received from FCKeditor to upload files.
If the uploaded file is an image, create an icon version
"""
if not fckeditor_available:
return apache.HTTP_NOT_FOUND
if not form.has_key('type'):
form['type'] = 'File'
if not form.has_key('NewFile') or \
not form['type'] in \
['File', 'Image', 'Flash', 'Media']:
return apache.HTTP_NOT_FOUND
uid = getUid(req)
# URL where the file can be fetched after upload
user_files_path = '%(CFG_SITE_URL)s/submit/getattachedfile/%(uid)s' % \
{'uid': uid,
'CFG_SITE_URL': CFG_SITE_URL}
# Path to directory where uploaded files are saved
user_files_absolute_path = '%(CFG_PREFIX)s/var/tmp/attachfile/%(uid)s' % \
{'uid': uid,
'CFG_PREFIX': CFG_PREFIX}
try:
os.makedirs(user_files_absolute_path)
except:
pass
# Create a Connector instance to handle the request
conn = FCKeditorConnectorInvenio(form, recid=-1, uid=uid,
allowed_commands=['QuickUpload'],
allowed_types = ['File', 'Image', 'Flash', 'Media'],
user_files_path = user_files_path,
user_files_absolute_path = user_files_absolute_path)
user_info = collect_user_info(req)
(auth_code, auth_message) = acc_authorize_action(user_info, 'attachsubmissionfile')
if user_info['email'] == 'guest':
# User is guest: must login prior to upload
data = conn.sendUploadResults(1, '', '', 'Please login before uploading file.')
elif auth_code:
# User cannot submit
data = conn.sendUploadResults(1, '', '', 'Sorry, you are not allowed to submit files.')
else:
# Process the upload and get the response
data = conn.doResponse()
# At this point, the file has been uploaded. The FCKeditor
# submit the image in form['NewFile']. However, the image
# might have been renamed in between by the FCK connector on
# the server side, by appending (%04d) at the end of the base
# name. Retrieve that file
uploaded_file_path = os.path.join(user_files_absolute_path,
form['type'].lower(),
form['NewFile'].filename)
uploaded_file_path = retrieve_most_recent_attached_file(uploaded_file_path)
uploaded_file_name = os.path.basename(uploaded_file_path)
# Create an icon
if form.get('type','') == 'Image':
try:
(icon_path, icon_name) = create_icon(
{ 'input-file' : uploaded_file_path,
'icon-name' : os.path.splitext(uploaded_file_name)[0],
'icon-file-format' : os.path.splitext(uploaded_file_name)[1][1:] or 'gif',
'multipage-icon' : False,
'multipage-icon-delay' : 100,
'icon-scale' : "300>", # Resize only if width > 300
'verbosity' : 0,
})
# Move original file to /original dir, and replace it with icon file
original_user_files_absolute_path = os.path.join(user_files_absolute_path,
'image', 'original')
if not os.path.exists(original_user_files_absolute_path):
# Create /original dir if needed
os.mkdir(original_user_files_absolute_path)
os.rename(uploaded_file_path,
original_user_files_absolute_path + os.sep + uploaded_file_name)
os.rename(icon_path + os.sep + icon_name,
uploaded_file_path)
except InvenioWebSubmitIconCreatorError, e:
pass
# Transform the headers into something ok for mod_python
for header in conn.headers:
if not header is None:
if header[0] == 'Content-Type':
req.content_type = header[1]
else:
req.headers_out[header[0]] = header[1]
# Send our response
req.send_http_header()
req.write(data)
def _lookup(self, component, path):
""" This handler is invoked for the dynamic URLs (for getting
and putting attachments) Eg:
/submit/getattachedfile/41336978/image/myfigure.png
/submit/attachfile/41336978/image/myfigure.png
"""
if component == 'getattachedfile' and len(path) > 2:
uid = path[0] # uid of the submitter
file_type = path[1] # file, image, flash or media (as
# defined by FCKeditor)
if file_type in ['file', 'image', 'flash', 'media']:
file_name = '/'.join(path[2:]) # the filename
def answer_get(req, form):
"""Accessing files attached to submission."""
form['file'] = file_name
form['type'] = file_type
form['uid'] = uid
return self.getattachedfile(req, form)
return answer_get, []
# All other cases: file not found
return None, []
def getattachedfile(self, req, form):
"""
Returns a file uploaded to the submission 'drop box' by the
FCKeditor.
"""
argd = wash_urlargd(form, {'file': (str, None),
'type': (str, None),
'uid': (int, 0)})
# Can user view this record, i.e. can user access its
# attachments?
uid = getUid(req)
user_info = collect_user_info(req)
if not argd['file'] is None:
# Prepare path to file on disk. Normalize the path so that
# ../ and other dangerous components are removed.
path = os.path.abspath(CFG_PREFIX + '/var/tmp/attachfile/' + \
'/' + str(argd['uid']) + \
'/' + argd['type'] + '/' + argd['file'])
# Check that we are really accessing attachements
# directory, for the declared record.
if path.startswith(CFG_PREFIX + '/var/tmp/attachfile/') and os.path.exists(path):
return stream_file(req, path)
# Send error 404 in all other cases
return(apache.HTTP_NOT_FOUND)
def direct(self, req, form):
"""Directly redirected to an initialized submission."""
args = wash_urlargd(form, {'sub': (str, ''),
'access' : (str, '')})
sub = args['sub']
access = args['access']
ln = args['ln']
_ = gettext_set_language(ln)
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "direct",
navmenuid='submit')
myQuery = req.args
if not sub:
return warningMsg(_("Sorry, 'sub' parameter missing..."), req, ln=ln)
res = run_sql("SELECT docname,actname FROM sbmIMPLEMENT WHERE subname=%s", (sub,))
if not res:
return warningMsg(_("Sorry. Cannot analyse parameter"), req, ln=ln)
else:
# get document type
doctype = res[0][0]
# get action name
action = res[0][1]
# retrieve other parameter values
params = dict(form)
# find existing access number
if not access:
# create 'unique' access number
pid = os.getpid()
now = time.time()
access = "%i_%s" % (now,pid)
# retrieve 'dir' value
res = run_sql ("SELECT dir FROM sbmACTION WHERE sactname=%s", (action,))
dir = res[0][0]
mainmenu = req.headers_in.get('referer')
params['access'] = access
params['act'] = action
params['doctype'] = doctype
params['startPg'] = '1'
params['mainmenu'] = mainmenu
params['ln'] = ln
params['indir'] = dir
url = "%s/submit?%s" % (CFG_SITE_URL, urlencode(params))
redirect_to_url(req, url)
def sub(self, req, form):
"""DEPRECATED: /submit/sub is deprecated now, so raise email to the admin (but allow submission to continue anyway)"""
args = wash_urlargd(form, {'password': (str, '')})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../sub/",
navmenuid='submit')
try:
raise DeprecationWarning, 'submit/sub handler has been used. Please use submit/direct. e.g. "submit/sub?RN=123@SBIFOO" -> "submit/direct?RN=123&sub=SBIFOO"'
except DeprecationWarning:
register_exception(req=req, alert_admin=True)
ln = args['ln']
_ = gettext_set_language(ln)
#DEMOBOO_RN=DEMO-BOOK-2008-001&ln=en&password=1223993532.26572%40APPDEMOBOO
params = dict(form)
password = args['password']
if password:
del params['password']
if "@" in password:
params['access'], params['sub'] = password.split('@', 1)
else:
params['sub'] = password
else:
args = str(req.args).split('@')
if len(args) > 1:
params = {'sub' : args[-1]}
args = '@'.join(args[:-1])
params.update(cgi.parse_qs(args))
else:
return warningMsg(_("Sorry, invalid URL..."), req, ln=ln)
url = "%s/submit/direct?%s" % (CFG_SITE_URL, urlencode(params, doseq=True))
redirect_to_url(req, url)
def summary(self, req, form):
args = wash_urlargd(form, {
'doctype': (str, ''),
'act': (str, ''),
'access': (str, ''),
'indir': (str, '')})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../summary",
navmenuid='submit')
t=""
curdir = os.path.join(CFG_WEBSUBMIT_STORAGEDIR, args['indir'], args['doctype'], args['access'])
try:
assert(curdir == os.path.abspath(curdir))
except AssertionError:
register_exception(req=req, alert_admin=True, prefix='Possible cracking tentative: indir="%s", doctype="%s", access="%s"' % (args['indir'], args['doctype'], args['access']))
return warningMsg("Invalid parameters", req)
subname = "%s%s" % (args['act'], args['doctype'])
res = run_sql("select sdesc,fidesc,pagenb,level from sbmFIELD where subname=%s "
"order by pagenb,fieldnb", (subname,))
nbFields = 0
values = []
for arr in res:
if arr[0] != "":
val = {
'mandatory' : (arr[3] == 'M'),
'value' : '',
'page' : arr[2],
'name' : arr[0],
}
if os.path.exists(os.path.join(curdir, curdir,arr[1])):
fd = open(os.path.join(curdir, arr[1]),"r")
value = fd.read()
fd.close()
value = value.replace("\n"," ")
value = value.replace("Select:","")
else:
value = ""
val['value'] = value
values.append(val)
return websubmit_templates.tmpl_submit_summary(
ln = args['ln'],
values = values,
)
def index(self, req, form):
args = wash_urlargd(form, {
'c': (str, CFG_SITE_NAME),
'doctype': (str, ''),
'act': (str, ''),
'startPg': (str, "1"),
'access': (str, ''),
'mainmenu': (str, ''),
'fromdir': (str, ''),
'nextPg': (str, ''),
'nbPg': (str, ''),
'curpage': (str, '1'),
'step': (str, '0'),
'mode': (str, 'U'),
})
## Strip whitespace from beginning and end of doctype and action:
args["doctype"] = args["doctype"].strip()
args["act"] = args["act"].strip()
def _index(req, c, ln, doctype, act, startPg, access,
mainmenu, fromdir, nextPg, nbPg, curpage, step,
mode):
uid = getUid(req)
if isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : CFG_SITE_URL + req.unparsed_uri, 'ln' : args['ln']}, {})), norobot=True)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../submit",
navmenuid='submit')
if CFG_CERN_SITE:
## HACK BEGIN: this is a hack for CMS and ATLAS draft
from invenio.webuser import collect_user_info
user_info = collect_user_info(req)
if doctype == 'CMSPUB' and 'cds-admin [CERN]' not in user_info['group'] and not user_info['email'].lower() == 'cds.support@cern.ch':
if 'cms-publication-committee-chair [CERN]' not in user_info['group']:
return page_not_authorized(req, "../submit", text="In order to access this submission interface you need to be member of the CMS Publication Committee Chair.",
navmenuid='submit')
elif doctype == 'ATLPUB' and 'cds-admin [CERN]' not in user_info['group'] and not user_info['email'].lower() == 'cds.support@cern.ch':
if 'atlas-gen [CERN]' not in user_info['group']:
return page_not_authorized(req, "../submit", text="In order to access this submission interface you need to be member of ATLAS.",
navmenuid='submit')
## HACK END
if doctype=="":
return home(req,c,ln)
elif act=="":
return action(req,c,ln,doctype)
elif int(step)==0:
return interface(req, c, ln, doctype, act, startPg, access, mainmenu, fromdir, nextPg, nbPg, curpage)
else:
return endaction(req, c, ln, doctype, act, startPg, access,mainmenu, fromdir, nextPg, nbPg, curpage, step, mode)
return _index(req, **args)
# Answer to both /submit/ and /submit
__call__ = index
def errorMsg(title, req, c=None, ln=CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
if c is None:
c = CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)
return page(title = _("Error"),
body = create_error_box(req, title=str(title), verbose=0, ln=ln),
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='submit')
def warningMsg(title, req, c=None, ln=CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
if c is None:
c = CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)
return page(title = _("Warning"),
body = title,
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='submit')
def print_warning(msg, type='', prologue='<br />', epilogue='<br />'):
"""Prints warning message and flushes output."""
if msg:
return websubmit_templates.tmpl_print_warning(
msg = msg,
type = type,
prologue = prologue,
epilogue = epilogue,
)
else:
return ''
def retrieve_most_recent_attached_file(file_path):
"""
Retrieve the latest file that has been uploaded with the
FCKeditor. This is the only way to retrieve files that the
FCKeditor has renamed after the upload.
Eg: 'prefix/image.jpg' was uploaded but did already
exist. FCKeditor silently renamed it to 'prefix/image(1).jpg':
>>> retrieve_most_recent_attached_file('prefix/image.jpg')
'prefix/image(1).jpg'
"""
(base_path, filename) = os.path.split(file_path)
base_name = os.path.splitext(filename)[0]
file_ext = os.path.splitext(filename)[1][1:]
most_recent_filename = filename
i = 0
while True:
i += 1
possible_filename = "%s(%d).%s" % \
(base_name, i, file_ext)
if os.path.exists(base_path + os.sep + possible_filename):
most_recent_filename = possible_filename
else:
break
return os.path.join(base_path, most_recent_filename)
diff --git a/modules/websubmit/web/publiline.py b/modules/websubmit/web/publiline.py
index b9d3aad4d..bbbfc3f36 100644
--- a/modules/websubmit/web/publiline.py
+++ b/modules/websubmit/web/publiline.py
@@ -1,1900 +1,1905 @@
## This file is part of Invenio.
## Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
publiline_complex.py -- implementes ...
actors in this process are:
1. author -- subilmts ...
2. edi
3; ref
Il ne faut pas oublier de definir les roles...
"""
__revision__ = "$Id$"
## import interesting modules:
import os
import re
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_URL, \
CFG_PYLIBDIR, \
CFG_WEBSUBMIT_STORAGEDIR, \
CFG_SITE_SUPPORT_EMAIL, \
- CFG_SITE_SECURE_URL
+ CFG_SITE_SECURE_URL, \
+ CFG_SITE_RECORD
from invenio.dbquery import run_sql, Error, OperationalError
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_admin import acc_get_role_users, acc_get_role_id
from invenio.webpage import page, create_error_box
from invenio.webuser import getUid, get_email, page_not_authorized, collect_user_info
from invenio.messages import gettext_set_language, wash_language
#from invenio.websubmit_config import *
from invenio.search_engine import search_pattern, get_fieldvalues,check_user_can_view_record
from invenio.websubmit_functions.Retrieve_Data import Get_Field
from invenio.mailutils import send_email
from invenio.urlutils import wash_url_argument
from invenio.webgroup_dblayer import get_group_infos, insert_new_group, insert_new_member, delete_member
from invenio.webaccessadmin_lib import cleanstring_email
from invenio.access_control_config import MAXSELECTUSERS
from invenio.access_control_admin import acc_get_user_email
from invenio.access_control_engine import acc_get_authorized_emails
from invenio.webmessage import perform_request_send
import invenio.webbasket_dblayer as basketdb
from invenio.webbasket_config import CFG_WEBBASKET_SHARE_LEVELS, CFG_WEBBASKET_CATEGORIES, CFG_WEBBASKET_SHARE_LEVELS_ORDERED
from invenio.errorlib import register_exception
from invenio.bibrecord import create_records, record_get_field_value, record_get_field_values
execfile("%s/invenio/websubmit_functions/Retrieve_Data.py" % CFG_PYLIBDIR)
import invenio.template
websubmit_templates = invenio.template.load('websubmit')
CFG_WEBSUBMIT_PENDING_DIR = "%s/pending" % CFG_WEBSUBMIT_STORAGEDIR
CFG_WEBSUBMIT_DUMMY_MARC_XML_REC = "dummy_marcxml_rec"
CFG_WEBSUBMIT_MARC_XML_REC = "recmysql"
def perform_request_save_comment(*args, **kwargs):
"""
FIXME: this function is a dummy workaround for the obsoleted
function calls below. Should get deleted at the same time as
them.
"""
return
def index(req,c=CFG_SITE_NAME,ln=CFG_SITE_LANG,doctype="",categ="",RN="",send="",flow="",apptype="", action="", email_user_pattern="", id_user="", id_user_remove="", validate="", id_user_val="", msg_subject="", msg_body="", reply="", commentId=""):
ln = wash_language(ln)
categ = wash_url_argument(categ, 'str')
RN = wash_url_argument(RN, 'str')
send = wash_url_argument(send, 'str')
flow = wash_url_argument(flow, 'str')
apptype = wash_url_argument(apptype, 'str')
action = wash_url_argument(action, 'str')
email_user_pattern = wash_url_argument(email_user_pattern, 'str')
id_user = wash_url_argument(id_user, 'int')
id_user_remove = wash_url_argument(id_user_remove, 'int')
validate = wash_url_argument(validate, 'str')
id_user_val = wash_url_argument(id_user_val, 'int')
msg_subject = wash_url_argument(msg_subject, 'str')
msg_body = wash_url_argument(msg_body, 'str')
reply = wash_url_argument(reply, 'str')
commentId = wash_url_argument(commentId, 'str')
# load the right message language
_ = gettext_set_language(ln)
t=""
# get user ID:
try:
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../publiline.py/index",
navmenuid='yourapprovals')
uid_email = get_email(uid)
except Error, e:
return errorMsg(str(e),req, ln = ln)
if flow == "cplx":
if doctype == "":
t = selectCplxDoctype(ln)
elif (categ == "") or (apptype == ""):
t = selectCplxCateg(doctype, ln)
elif RN == "":
t = selectCplxDocument(doctype, categ, apptype, ln)
elif action == "":
t = __displayCplxDocument(req, doctype, categ, RN, apptype, reply, commentId, ln)
else:
t = __doCplxAction(req, doctype, categ, RN, apptype, action, email_user_pattern, id_user, id_user_remove, validate, id_user_val, msg_subject, msg_body, reply, commentId, ln)
return page(title=_("Document Approval Workflow"),
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'account' : _("Your Account"),
},
body=t,
description="",
keywords="",
uid=uid,
language=ln,
req=req,
navmenuid='yourapprovals')
else:
if doctype == "":
t = selectDoctype(ln)
elif categ == "":
t = selectCateg(doctype, ln)
elif RN == "":
t = selectDocument(doctype, categ, ln)
else:
t = __displayDocument(req, doctype, categ, RN, send, ln)
return page(title=_("Approval and Refereeing Workflow"),
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'account' : _("Your Account"),
},
body=t,
description="",
keywords="",
uid=uid,
language=ln,
req=req,
navmenuid='yourapprovals')
def selectDoctype(ln = CFG_SITE_LANG):
res = run_sql("select DISTINCT doctype from sbmAPPROVAL")
docs = []
for row in res:
res2 = run_sql("select ldocname from sbmDOCTYPE where sdocname=%s", (row[0],))
docs.append({
'doctype' : row[0],
'docname' : res2[0][0],
})
t = websubmit_templates.tmpl_publiline_selectdoctype(
ln = ln,
docs = docs,
)
return t
def selectCplxDoctype(ln = CFG_SITE_LANG):
res = run_sql("select DISTINCT doctype from sbmCPLXAPPROVAL")
docs = []
for row in res:
res2 = run_sql("select ldocname from sbmDOCTYPE where sdocname=%s", (row[0],))
docs.append({
'doctype' : row[0],
'docname' : res2[0][0],
})
t = websubmit_templates.tmpl_publiline_selectcplxdoctype(
ln = ln,
docs = docs,
)
return t
def selectCateg(doctype, ln = CFG_SITE_LANG):
t=""
res = run_sql("select ldocname from sbmDOCTYPE where sdocname=%s",(doctype,))
title = res[0][0]
sth = run_sql("select * from sbmCATEGORIES where doctype=%s order by lname",(doctype,))
if len(sth) == 0:
categ = "unknown"
return selectDocument(doctype,categ, ln = ln)
categories = []
for arr in sth:
waiting = 0
rejected = 0
approved = 0
sth2 = run_sql("select COUNT(*) from sbmAPPROVAL where doctype=%s and categ=%s and status='waiting'", (doctype,arr[1],))
waiting = sth2[0][0]
sth2 = run_sql("select COUNT(*) from sbmAPPROVAL where doctype=%s and categ=%s and status='approved'",(doctype,arr[1],))
approved = sth2[0][0]
sth2 = run_sql("select COUNT(*) from sbmAPPROVAL where doctype=%s and categ=%s and status='rejected'",(doctype,arr[1],))
rejected = sth2[0][0]
categories.append({
'waiting' : waiting,
'approved' : approved,
'rejected' : rejected,
'id' : arr[1],
})
t = websubmit_templates.tmpl_publiline_selectcateg(
ln = ln,
categories = categories,
doctype = doctype,
title = title,
)
return t
def selectCplxCateg(doctype, ln = CFG_SITE_LANG):
t=""
res = run_sql("SELECT ldocname FROM sbmDOCTYPE WHERE sdocname=%s",(doctype,))
title = res[0][0]
sth = run_sql("SELECT * FROM sbmCATEGORIES WHERE doctype=%s ORDER BY lname",(doctype,))
if len(sth) == 0:
categ = "unknown"
return selectCplxDocument(doctype,categ, "", ln = ln)
types = {}
for apptype in ('RRP', 'RPB', 'RDA'):
for arr in sth:
info = {'id' : arr[1],
'desc' : arr[2],}
for status in ('waiting', 'rejected', 'approved', 'cancelled'):
info[status] = __db_count_doc (doctype, arr[1], status, apptype)
types.setdefault (apptype, []).append(info)
t = websubmit_templates.tmpl_publiline_selectcplxcateg(
ln = ln,
types = types,
doctype = doctype,
title = title,
)
return t
def selectDocument(doctype,categ, ln = CFG_SITE_LANG):
t=""
res = run_sql("select ldocname from sbmDOCTYPE where sdocname=%s", (doctype,))
title = res[0][0]
if categ == "":
categ == "unknown"
docs = []
sth = run_sql("select rn,status from sbmAPPROVAL where doctype=%s and categ=%s order by status DESC,rn DESC",(doctype,categ))
for arr in sth:
docs.append({
'RN' : arr[0],
'status' : arr[1],
})
t = websubmit_templates.tmpl_publiline_selectdocument(
ln = ln,
doctype = doctype,
title = title,
categ = categ,
docs = docs,
)
return t
def selectCplxDocument(doctype,categ,apptype, ln = CFG_SITE_LANG):
t=""
res = run_sql("select ldocname from sbmDOCTYPE where sdocname=%s", (doctype,))
title = res[0][0]
sth = run_sql("select lname from sbmCATEGORIES where doctype=%s and sname=%s order by lname",(doctype,categ,))
if len(sth) != 0:
categname = sth[0][0]
else:
categname = "Unknown"
docs = []
sth = run_sql("select rn,status from sbmCPLXAPPROVAL where doctype=%s and categ=%s and type=%s order by status DESC,rn DESC",(doctype,categ,apptype))
for arr in sth:
docs.append({
'RN' : arr[0],
'status' : arr[1],
})
t = websubmit_templates.tmpl_publiline_selectcplxdocument(
ln = ln,
doctype = doctype,
title = title,
categ = categ,
categname = categname,
docs = docs,
apptype = apptype,
)
return t
def __displayDocument(req, doctype,categ,RN,send, ln = CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
t=""
res = run_sql("select ldocname from sbmDOCTYPE where sdocname=%s", (doctype,))
docname = res[0][0]
if categ == "":
categ = "unknown"
sth = run_sql("select rn,status,dFirstReq,dLastReq,dAction,access,note from sbmAPPROVAL where rn=%s",(RN,))
if len(sth) > 0:
arr = sth[0]
rn = arr[0]
status = arr[1]
dFirstReq = arr[2]
dLastReq = arr[3]
dAction = arr[4]
access = arr[5]
note = arr[6]
else:
return _("Approval has never been requested for this document.") + "<br />&nbsp;"
## Get the details of the pending item:
item_details = get_pending_item_details(doctype, RN)
## get_pending_item_details has returned either None or a dictionary
## with the following structure:
## { 'title' : '-', ## String - the item's title
## 'recid' : '', ## String - recid
## 'report-number' : '', ## String - the item's report number
## 'authors' : [], ## List - the item's authors
## }
if item_details is not None:
authors = ", ".join(item_details['authors'])
newrn = item_details['report-number']
title = item_details['title']
sysno = item_details['recid']
else:
# Was not found in the pending directory. Already approved?
try:
(authors, title, sysno) = getInfo(RN)
newrn = RN
if sysno is None:
return _("Unable to display document.")
except:
return _("Unable to display document.")
user_info = collect_user_info(req)
can_view_record_p, msg = check_user_can_view_record(user_info, sysno)
if can_view_record_p != 0:
return msg
confirm_send = 0
if send == _("Send Again"):
if authors == "unknown" or title == "unknown":
SendWarning(doctype, categ, RN, title, authors, access)
else:
# @todo - send in different languages
#SendEnglish(doctype,categ,RN,title,authors,access,sysno)
send_approval(doctype, categ, RN, title, authors, access, sysno)
run_sql("update sbmAPPROVAL set dLastReq=NOW() where rn=%s",(RN,))
confirm_send = 1
if status == "waiting":
if categ == "unknown":
## FIXME: This was necessary for document types without categories,
## such as DEMOBOO:
categ = "*"
(auth_code, auth_message) = acc_authorize_action(req, "referee",verbose=0,doctype=doctype, categ=categ)
else:
(auth_code, auth_message) = (None, None)
t = websubmit_templates.tmpl_publiline_displaydoc(
ln = ln,
docname = docname,
doctype = doctype,
categ = categ,
rn = rn,
status = status,
dFirstReq = dFirstReq,
dLastReq = dLastReq,
dAction = dAction,
access = access,
confirm_send = confirm_send,
auth_code = auth_code,
auth_message = auth_message,
authors = authors,
title = title,
sysno = sysno,
newrn = newrn,
note = note,
)
return t
def __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln = CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
t=""
uid = getUid(req)
res = run_sql("select ldocname from sbmDOCTYPE where sdocname=%s", (doctype,))
docname = res[0][0]
if categ == "":
categ = "unknown"
key = (RN, apptype)
infos = __db_get_infos (key)
if len(infos) > 0:
(status, id_group, id_bskBASKET, id_EdBoardGroup,
dFirstReq,dLastReq,dEdBoardSel, dRefereeSel, dRefereeRecom, dEdBoardRecom, dPubComRecom, dProjectLeaderAction) = infos[0]
dates = {'dFirstReq' : dFirstReq,
'dLastReq' : dLastReq,
'dEdBoardSel' : dEdBoardSel,
'dRefereeSel' : dRefereeSel,
'dRefereeRecom' : dRefereeRecom,
'dEdBoardRecom' : dEdBoardRecom,
'dPubComRecom' : dPubComRecom,
'dProjectLeaderAction' : dProjectLeaderAction,
}
else:
return _("Approval has never been requested for this document.") + "<br />&nbsp;"
## Removing call to deprecated "getInAlice" function and replacing it with
## a call to the newer "get_brief_doc_details_from_repository" function:
## try:
## (authors,title,sysno,newrn) = getInAlice(doctype,categ,RN)
## except TypeError:
## return _("Unable to display document.")
item_details = get_brief_doc_details_from_repository(RN)
## get_brief_doc_details_from_repository has returned either None
## or a dictionary with the following structure:
## { 'title' : '-', ## String - the item's title
## 'recid' : '', ## String - recid
## 'report-number' : '', ## String - the item's report number
## 'authors' : [], ## List - the item's authors
## }
if item_details is not None:
## Details of the item were found in the Invenio repository
authors = ", ".join(item_details['authors'])
newrn = item_details['report-number']
title = item_details['title']
sysno = item_details['recid']
else:
## Can't find any document details.
return _("Unable to display document.")
if status == "waiting":
isPubCom = __is_PubCom (req, doctype)
isEdBoard = __is_EdBoard (uid, id_EdBoardGroup)
isReferee = __is_Referee (uid, id_bskBASKET)
isProjectLeader = __is_ProjectLeader (req, doctype, categ)
isAuthor = __is_Author (uid, sysno)
else:
isPubCom = None
isEdBoard = None
isReferee = None
isProjectLeader = None
isAuthor = None
user_info = collect_user_info(req)
can_view_record_p, msg = check_user_can_view_record(user_info, sysno)
if can_view_record_p != 0:
return msg
t += websubmit_templates.tmpl_publiline_displaycplxdoc(
ln = ln,
docname = docname,
doctype = doctype,
categ = categ,
rn = RN,
apptype = apptype,
status = status,
dates = dates,
isPubCom = isPubCom,
isEdBoard = isEdBoard,
isReferee = isReferee,
isProjectLeader = isProjectLeader,
isAuthor = isAuthor,
authors = authors,
title = title,
sysno = sysno,
newrn = newrn,
)
if id_bskBASKET > 0:
rights = basketdb.get_max_user_rights_on_basket(uid, id_bskBASKET)
if not(__check_basket_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['READITM'])):
return t
# FIXME This error will be fixed with Sam's new version of publiline.
# pylint: disable=E1101
comments = basketdb.get_comments(id_bskBASKET, sysno)
# pylint: enable=E1101
if dProjectLeaderAction != None:
user_can_add_comment = 0
else:
user_can_add_comment = __check_basket_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['ADDCMT'])
comment_subject = ""
comment_body = ""
if reply == "true":
#Get the message subject and body from the comment
for comment in comments:
if str(commentId) == str(comment[0]):
comment_subject = comment[2]
comment_body = comment[3]
comment_subject = comment_subject.lstrip("Re: ")
comment_subject = "Re: " + comment_subject
comment_body = "> " + comment_body.replace("\n", "\n> ")
t += websubmit_templates.tmpl_publiline_displaycplxdocitem(
doctype, categ, RN, apptype, "AddComment",
comments,
(__check_basket_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['READCMT']),
user_can_add_comment,
__check_basket_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['DELCMT'])),
selected_category=CFG_WEBBASKET_CATEGORIES['GROUP'], selected_topic=0, selected_group_id=id_group,
comment_subject=comment_subject, comment_body=comment_body, ln=ln)
return t
def __check_basket_sufficient_rights(rights_user_has, rights_needed):
"""Private function, check if the rights are sufficient."""
try:
out = CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(rights_user_has) >= \
CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(rights_needed)
except ValueError:
out = 0
return out
def __is_PubCom (req,doctype):
(isPubCom, auth_message) = acc_authorize_action(req, "pubcomchair",verbose=0,doctype=doctype)
return isPubCom
def __is_EdBoard (uid, id_EdBoardGroup):
isEdBoard = None
if id_EdBoardGroup > 0:
edBoard = run_sql("""SELECT u.id
FROM user u LEFT JOIN user_usergroup ug ON u.id = ug.id_user
WHERE ug.id_usergroup = '%s' and user_status != 'A' AND user_status != 'P'""" % (id_EdBoardGroup, ))
for uid_scan in edBoard:
if uid == uid_scan[0]:
isEdBoard = 0
break
return isEdBoard
def __is_Referee (uid, id_bskBASKET):
isReferee = None
if id_bskBASKET > 0:
if basketdb.check_user_owns_baskets (uid, id_bskBASKET) == 1:
isReferee = 0
return isReferee
def __is_ProjectLeader (req, doctype, categ):
(isProjectLeader, auth_message) = acc_authorize_action(req, "projectleader",verbose=0,doctype=doctype,categ=categ)
return isProjectLeader
def __is_Author (uid, sysno):
email = Get_Field("8560_f",sysno)
email = re.sub("[\n\r ]+","",email)
uid_email = re.sub("[\n\r ]+","", acc_get_user_email(uid))
isAuthor = None
if (re.search(uid_email,email,re.IGNORECASE) != None) and (uid_email != ""):
isAuthor = 0
return isAuthor
def __db_count_doc (doctype, categ, status, apptype):
return run_sql("SELECT COUNT(*) FROM sbmCPLXAPPROVAL WHERE doctype=%s AND categ=%s AND status=%s AND type=%s",(doctype,categ,status,apptype,))[0][0]
def __db_get_infos (key):
return run_sql("SELECT status,id_group,id_bskBASKET,id_EdBoardGroup,dFirstReq,dLastReq,dEdBoardSel,dRefereeSel,dRefereeRecom,dEdBoardRecom,dPubComRecom,dProjectLeaderAction FROM sbmCPLXAPPROVAL WHERE rn=%s and type=%s", key)
def __db_set_EdBoardSel_time (key):
run_sql("UPDATE sbmCPLXAPPROVAL SET dEdBoardSel=NOW() WHERE rn=%s and type=%s", key)
def __db_check_EdBoardGroup ((RN,apptype), id_EdBoardGroup, uid, group_descr):
res = get_group_infos (id_EdBoardGroup)
if len(res) == 0:
id_EdBoardGroup = insert_new_group (uid, RN, group_descr % RN, "VM")
run_sql("UPDATE sbmCPLXAPPROVAL SET id_EdBoardGroup=%s WHERE rn=%s and type=%s", (id_EdBoardGroup,RN,apptype,))
return id_EdBoardGroup
def __db_set_basket ((RN,apptype), id_bsk):
run_sql("UPDATE sbmCPLXAPPROVAL SET id_bskBASKET=%s, dRefereeSel=NOW() WHERE rn=%s and type=%s", (id_bsk,RN,apptype,))
def __db_set_RefereeRecom_time (key):
run_sql("UPDATE sbmCPLXAPPROVAL SET dRefereeRecom=NOW() WHERE rn=%s and type=%s", key)
def __db_set_EdBoardRecom_time (key):
run_sql("UPDATE sbmCPLXAPPROVAL SET dEdBoardRecom=NOW() WHERE rn=%s and type=%s", key)
def __db_set_PubComRecom_time (key):
run_sql("UPDATE sbmCPLXAPPROVAL SET dPubComRecom=NOW() WHERE rn=%s and type=%s", key)
def __db_set_status ((RN,apptype), status):
run_sql("UPDATE sbmCPLXAPPROVAL SET status=%s, dProjectLeaderAction=NOW() WHERE rn=%s and type=%s", (status,RN,apptype,))
def __doCplxAction(req, doctype, categ, RN, apptype, action, email_user_pattern, id_user, id_user_remove, validate, id_user_val, msg_subject, msg_body, reply, commentId, ln=CFG_SITE_LANG):
"""
Perform complex action. Note: all argume,ts are supposed to be washed already.
Return HTML body for the paget.
In case of errors, deletes hard drive. ;-)
"""
# load the right message language
_ = gettext_set_language(ln)
TEXT_RSN_RefereeSel_BASKET_DESCR = "Requests for refereeing process"
TEXT_RSN_RefereeSel_MSG_REFEREE_SUBJECT = "Referee selection"
TEXT_RSN_RefereeSel_MSG_REFEREE_BODY = "You have been named as a referee for this document :"
TEXT_RSN_RefereeSel_MSG_GROUP_SUBJECT = "Please, review this publication"
TEXT_RSN_RefereeSel_MSG_GROUP_BODY = "Please, review the following publication"
TEXT_RSN_RefereeRecom_MSG_PUBCOM_SUBJECT = "Final recommendation from the referee"
TEXT_RSN_PubComRecom_MSG_PRJLEADER_SUBJECT = "Final recommendation from the publication board : "
TEXT_RSN_ProjectLeaderDecision_MSG_SUBJECT = "Final decision from the project leader"
TEXT_RPB_EdBoardSel_MSG_EDBOARD_SUBJECT = "You have been selected in a editorial board"
TEXT_RPB_EdBoardSel_MSG_EDBOARD_BODY = "You have been selected as a member of the editorial board of this document :"
TEXT_RPB_EdBoardSel_EDBOARD_GROUP_DESCR = "Editorial board for %s"
TEXT_RPB_RefereeSel_BASKET_DESCR = "Requests for publication"
TEXT_RPB_RefereeSel_MSG_REFEREE_SUBJECT = "Referee selection"
TEXT_RPB_RefereeSel_MSG_REFEREE_BODY = "You have been named as a referee for this document :"
TEXT_RPB_RefereeSel_MSG_GROUP_SUBJECT = "Please, review this publication"
TEXT_RPB_RefereeSel_MSG_GROUP_BODY = "Please, review the following publication"
TEXT_RPB_RefereeRecom_MSG_EDBOARD_SUBJECT = "Final recommendation from the referee"
TEXT_RPB_EdBoardRecom_MSG_PUBCOM_SUBJECT = "Final recommendation from the editorial board"
TEXT_RPB_PubComRecom_MSG_PRJLEADER_SUBJECT = "Final recommendation from the publication board"
TEXT_RPB_ProjectLeaderDecision_MSG_SUBJECT = "Final decision from the project leader"
t=""
uid = getUid(req)
if categ == "":
categ = "unknown"
key = (RN, apptype)
infos = __db_get_infos (key)
if len(infos) > 0:
(status, id_group, id_bskBASKET, id_EdBoardGroup, dummy, dummy,
dEdBoardSel, dRefereeSel, dRefereeRecom, dEdBoardRecom, dPubComRecom, dProjectLeaderAction) = infos[0]
else:
return _("Approval has never been requested for this document.") + "<br />&nbsp;"
## Removing call to deprecated "getInAlice" function and replacing it with
## a call to the newer "get_brief_doc_details_from_repository" function:
## try:
## (authors,title,sysno,newrn) = getInAlice(doctype,categ,RN)
## except TypeError:
## return _("Unable to display document.")
item_details = get_brief_doc_details_from_repository(RN)
## get_brief_doc_details_from_repository has returned either None
## or a dictionary with the following structure:
## { 'title' : '-', ## String - the item's title
## 'recid' : '', ## String - recid
## 'report-number' : '', ## String - the item's report number
## 'authors' : [], ## List - the item's authors
## }
if item_details is not None:
## Details of the item were found in the Invenio repository
authors = ", ".join(item_details['authors'])
newrn = item_details['report-number']
title = item_details['title']
sysno = item_details['recid']
else:
## Can't find any document details.
return _("Unable to display document.")
if (action == "EdBoardSel") and (apptype == "RPB"):
if __is_PubCom (req, doctype) != 0:
return _("Action unauthorized for this document.") + "<br />&nbsp;"
if status == "cancelled":
return _("Action unavailable for this document.") + "<br />&nbsp;"
if validate == "go":
if dEdBoardSel == None:
__db_set_EdBoardSel_time (key)
perform_request_send (uid, "", RN, TEXT_RPB_EdBoardSel_MSG_EDBOARD_SUBJECT, TEXT_RPB_EdBoardSel_MSG_EDBOARD_BODY)
return __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln)
id_EdBoardGroup = __db_check_EdBoardGroup (key, id_EdBoardGroup, uid, TEXT_RPB_EdBoardSel_EDBOARD_GROUP_DESCR)
subtitle1 = _('Adding users to the editorial board')
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
stopon1 = ""
stopon2 = ""
stopon3 = ""
users = []
extrausers = []
# pattern is entered
if email_user_pattern:
# users with matching email-address
try:
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s ORDER BY email """, (email_user_pattern, ))
except OperationalError:
users1 = ()
# users that are connected
try:
users2 = run_sql("""SELECT DISTINCT u.id, u.email
FROM user u LEFT JOIN user_usergroup ug ON u.id = ug.id_user
WHERE u.email<>'' AND ug.id_usergroup = %s AND u.email RLIKE %s
ORDER BY u.email """, (id_EdBoardGroup, email_user_pattern))
except OperationalError:
users2 = ()
# no users that match the pattern
if not (users1 or users2):
stopon1 = '<p>%s</p>' % _("no qualified users, try new search.")
elif len(users1) > MAXSELECTUSERS:
stopon1 = '<p><strong>%s %s</strong>, %s (%s %s)</p>' % (len(users1), _("hits"), _("too many qualified users, specify more narrow search."), _("limit"), MAXSELECTUSERS)
# show matching users
else:
users = []
extrausers = []
for (user_id, email) in users1:
if (user_id, email) not in users2: users.append([user_id,email,''])
for (user_id, email) in users2:
extrausers.append([-user_id, email,''])
try: id_user = int(id_user)
except ValueError: pass
# user selected already connected to role
email_out = acc_get_user_email(id_user)
if id_user < 0:
stopon2 = '<p>%s</p>' % _("users in brackets are already attached to the role, try another one...")
# a user is selected
elif email_out:
result = insert_new_member(id_user, id_EdBoardGroup, "M")
stopon2 = '<p>confirm: user <strong>%s</strong> added to the editorial board.</p>' % (email_out, )
subtitle2 = _('Removing users from the editorial board')
usersremove = run_sql("""SELECT DISTINCT u.id, u.email
FROM user u LEFT JOIN user_usergroup ug ON u.id = ug.id_user
WHERE u.email <> "" AND ug.id_usergroup = %s and user_status != 'A' AND user_status != 'P'
ORDER BY u.email """, (id_EdBoardGroup, ))
try: id_user_remove = int(id_user_remove)
except ValueError: pass
# user selected already connected to role
email_out = acc_get_user_email(id_user_remove)
# a user is selected
if email_out:
result = delete_member(id_EdBoardGroup, id_user_remove)
stopon3 = '<p>confirm: user <strong>%s</strong> removed from the editorial board.</p>' % (email_out, )
t = websubmit_templates.tmpl_publiline_displaydocplxaction (
ln = ln,
doctype = doctype,
categ = categ,
rn = RN,
apptype = apptype,
action = action,
status = status,
authors = authors,
title = title,
sysno = sysno,
subtitle1 = subtitle1,
email_user_pattern = email_user_pattern,
stopon1 = stopon1,
users = users,
extrausers = extrausers,
stopon2 = stopon2,
subtitle2 = subtitle2,
usersremove = usersremove,
stopon3 = stopon3,
validate_btn = _("Validate the editorial board selection"),
)
return t
elif (action == "RefereeSel") and ((apptype == "RRP") or (apptype == "RPB")):
if apptype == "RRP":
to_check = __is_PubCom (req, doctype)
TEXT_RefereeSel_BASKET_DESCR = TEXT_RSN_RefereeSel_BASKET_DESCR
TEXT_RefereeSel_MSG_REFEREE_SUBJECT = TEXT_RSN_RefereeSel_MSG_REFEREE_SUBJECT
TEXT_RefereeSel_MSG_REFEREE_BODY = TEXT_RSN_RefereeSel_MSG_REFEREE_BODY + " " + "\"" + item_details['title'] + "\""
TEXT_RefereeSel_MSG_GROUP_SUBJECT = TEXT_RSN_RefereeSel_MSG_GROUP_SUBJECT
TEXT_RefereeSel_MSG_GROUP_BODY = TEXT_RSN_RefereeSel_MSG_GROUP_BODY + " " + "\"" + item_details['title'] + "\""
elif apptype == "RPB":
to_check = __is_EdBoard (uid, id_EdBoardGroup)
TEXT_RefereeSel_BASKET_DESCR = TEXT_RSN_RefereeSel_BASKET_DESCR
TEXT_RefereeSel_MSG_REFEREE_SUBJECT = TEXT_RSN_RefereeSel_MSG_REFEREE_SUBJECT
TEXT_RefereeSel_MSG_REFEREE_BODY = TEXT_RSN_RefereeSel_MSG_REFEREE_BODY + " " + "\"" + item_details['title'] + "\""
TEXT_RefereeSel_MSG_GROUP_SUBJECT = TEXT_RSN_RefereeSel_MSG_GROUP_SUBJECT
TEXT_RefereeSel_MSG_GROUP_BODY = TEXT_RSN_RefereeSel_MSG_GROUP_BODY + " " + "\"" + item_details['title'] + "\""
else:
to_check = None
if to_check != 0:
return _("Action unauthorized for this document.") + "<br />&nbsp;"
if status == "cancelled":
return _("Action unavailable for this document.") + "<br />&nbsp;"
if validate == "go":
if dRefereeSel == None:
id_bsk = basketdb.create_basket (int(id_user_val), RN, TEXT_RefereeSel_BASKET_DESCR)
basketdb.share_basket_with_group (id_bsk, id_group, CFG_WEBBASKET_SHARE_LEVELS['ADDCMT'])
basketdb.add_to_basket (int(id_user_val), (sysno, ), (id_bsk, ))
__db_set_basket (key, id_bsk)
email_address = run_sql("""SELECT email FROM user WHERE id = %s """, (id_user_val, ))[0][0]
perform_request_send (uid, email_address, "", TEXT_RefereeSel_MSG_REFEREE_SUBJECT, TEXT_RefereeSel_MSG_REFEREE_BODY, 0, 0, 0, ln, 1)
sendMailToReferee(doctype,categ,RN,email_address,authors)
group_name = run_sql("""SELECT name FROM usergroup WHERE id = %s""", (id_group, ))[0][0]
perform_request_send (int(id_user_val), "", group_name, TEXT_RefereeSel_MSG_GROUP_SUBJECT, TEXT_RefereeSel_MSG_GROUP_BODY)
sendMailToGroup(doctype,categ,RN,id_group,authors)
return __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln)
subtitle1 = _('Referee selection')
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
stopon1 = ""
stopon2 = ""
users = []
extrausers = []
# pattern is entered
if email_user_pattern:
# users with matching email-address
try:
users1 = run_sql("""SELECT id, email FROM user WHERE email <> "" AND email RLIKE %s ORDER BY email """, (email_user_pattern, ))
except OperationalError:
users1 = ()
# no users that match the pattern
if not users1:
stopon1 = '<p>%s</p>' % _("no qualified users, try new search.")
elif len(users1) > MAXSELECTUSERS:
stopon1 = '<p><strong>%s %s</strong>, %s (%s %s)</p>' % (len(users1), _("hits"), _("too many qualified users, specify more narrow search."), _("limit"), MAXSELECTUSERS)
# show matching users
else:
users = []
for (user_id, email) in users1:
users.append([user_id,email,''])
try: id_user = int(id_user)
except ValueError: pass
# user selected already connected to role
email_out = acc_get_user_email(id_user)
# a user is selected
if email_out:
stopon2 = """<p>user <strong>%s</strong> will be the referee ?
<input type="hidden" name="id_user_val" value="%s" />
<input type="hidden" name="validate" value="go" />
<input class="adminbutton" type="submit" value="Validate the referee selection" />
</p>""" % (email_out, id_user)
t = websubmit_templates.tmpl_publiline_displaydocplxaction (
ln = ln,
doctype = doctype,
categ = categ,
rn = RN,
apptype = apptype,
action = action,
status = status,
authors = authors,
title = title,
sysno = sysno,
subtitle1 = subtitle1,
email_user_pattern = email_user_pattern,
stopon1 = stopon1,
users = users,
extrausers = [],
stopon2 = stopon2,
subtitle2 = "",
usersremove = [],
stopon3 = "",
validate_btn = "",
)
return t
elif (action == "AddAuthorList") and (apptype == "RPB"):
return ""
elif (action == "AddComment") and ((apptype == "RRP") or (apptype == "RPB")):
t = ""
if validate == "go":
(errors, infos) = perform_request_save_comment (uid, id_bskBASKET, sysno, msg_subject, msg_body, ln)
t += "%(infos)s<br /><br />" % {'infos' : infos[0]}
t += """
<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="submit" class="formbutton" value="%(button_label)s" />
</form>""" % {'doctype' : doctype,
'categ' : categ,
'rn' : RN,
'apptype' : apptype,
'button_label' : _("Come back to the document"),
}
return t
elif (action == "RefereeRecom") and ((apptype == "RRP") or (apptype == "RPB")):
if __is_Referee (uid, id_bskBASKET) != 0:
return _("Action unauthorized for this document.") + "<br />&nbsp;"
if status == "cancelled":
return _("Action unavailable for this document.") + "<br />&nbsp;"
if apptype == "RRP":
# Build publication committee chair's email address
user_addr = ""
# Try to retrieve the publication committee chair's email from the role database
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_%s" % (doctype,categ))):
user_addr += run_sql("""SELECT email FROM user WHERE id = %s """, (user[0], ))[0][0] + ","
# And if there are general publication committee chair's
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_*" % doctype)):
user_addr += run_sql("""SELECT email FROM user WHERE id = %s """, (user[0], ))[0][0] + ","
user_addr = re.sub(",$","",user_addr)
group_addr = ""
TEXT_RefereeRecom_MSG_SUBJECT = TEXT_RSN_RefereeRecom_MSG_PUBCOM_SUBJECT
elif apptype == "RPB":
user_addr = ""
group_addr = RN
TEXT_RefereeRecom_MSG_SUBJECT = TEXT_RPB_RefereeRecom_MSG_EDBOARD_SUBJECT
else:
user_addr = ""
group_addr = ""
TEXT_RefereeRecom_MSG_SUBJECT = ""
if validate == "approve" or validate == "reject":
if dRefereeRecom == None:
perform_request_send (uid, user_addr, group_addr, msg_subject, msg_body, 0, 0, 0, ln, 1)
if validate == "approve":
msg_body = "Approved : " + msg_body
else:
msg_body = "Rejected : " + msg_body
#Get the Project Leader's email address
# email = ""
# for user in acc_get_role_users(acc_get_role_id("projectleader_%s_%s" % (doctype,categ))):
# email += run_sql("""SELECT email FROM user WHERE id = %s """, (user[0], ))[0][0] + ","
# sendMailToProjectLeader(doctype, categ, RN, email, authors, "referee", msg_body)
sendMailtoCommitteeChair(doctype, categ, RN, user_addr, authors)
__db_set_RefereeRecom_time (key)
return __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln)
t = websubmit_templates.tmpl_publiline_displaycplxrecom (
ln = ln,
doctype = doctype,
categ = categ,
rn = RN,
apptype = apptype,
action = action,
status = status,
authors = authors,
title = title,
sysno = sysno,
msg_to = user_addr,
msg_to_group = group_addr,
msg_subject = TEXT_RefereeRecom_MSG_SUBJECT,
)
return t
elif (action == "EdBoardRecom") and (apptype == "RPB"):
if __is_EdBoard (uid, id_EdBoardGroup) != 0:
return _("Action unauthorized for this document.") + "<br />&nbsp;"
if status == "cancelled":
return _("Action unavailable for this document.") + "<br />&nbsp;"
# Build publication committee chair's email address
user_addr = ""
# Try to retrieve the publication committee chair's email from the role database
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_%s" % (doctype,categ))):
user_addr += run_sql("""SELECT nickname FROM user WHERE id = %s """, (user[0], ))[0][0] + ","
# And if there are general publication committee chair's
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_*" % doctype)):
user_addr += run_sql("""SELECT nickname FROM user WHERE id = %s """, (user[0], ))[0][0] + ","
user_addr = re.sub(",$","",user_addr)
if validate == "go":
if dEdBoardRecom == None:
perform_request_send (uid, user_addr, "", msg_subject, msg_body)
__db_set_EdBoardRecom_time (key)
return __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln)
t = websubmit_templates.tmpl_publiline_displaycplxrecom (
ln = ln,
doctype = doctype,
categ = categ,
rn = RN,
apptype = apptype,
action = action,
status = status,
authors = authors,
title = title,
sysno = sysno,
msg_to = user_addr,
msg_to_group = "",
msg_subject = TEXT_RPB_EdBoardRecom_MSG_PUBCOM_SUBJECT,
)
return t
elif (action == "PubComRecom") and ((apptype == "RRP") or (apptype == "RPB")):
if __is_PubCom (req, doctype) != 0:
return _("Action unauthorized for this document.") + "<br />&nbsp;"
if status == "cancelled":
return _("Action unavailable for this document.") + "<br />&nbsp;"
# Build project leader's email address
user_addr = ""
# Try to retrieve the project leader's email from the role database
for user in acc_get_role_users(acc_get_role_id("projectleader_%s_%s" % (doctype,categ))):
user_addr += run_sql("""SELECT email FROM user WHERE id = %s """, (user[0], ))[0][0] + ","
# And if there are general project leader's
for user in acc_get_role_users(acc_get_role_id("projectleader_%s_*" % doctype)):
user_addr += run_sql("""SELECT email FROM user WHERE id = %s """, (user[0], ))[0][0] + ","
user_addr = re.sub(",$","",user_addr)
if apptype == "RRP":
TEXT_PubComRecom_MSG_SUBJECT = TEXT_RSN_PubComRecom_MSG_PRJLEADER_SUBJECT
elif apptype == "RPB":
group_addr = RN
TEXT_PubComRecom_MSG_SUBJECT = TEXT_RPB_PubComRecom_MSG_PRJLEADER_SUBJECT
else:
TEXT_PubComRecom_MSG_SUBJECT = ""
if validate == "approve" or validate == "reject":
if validate == "approve":
msg_body = "Approved : " + msg_body
else:
msg_body = "Rejected : " + msg_body
if dPubComRecom == None:
perform_request_send (uid, user_addr, "", msg_subject, msg_body, 0, 0, 0, ln, 1)
sendMailToProjectLeader(doctype, categ, RN, user_addr, authors, "publication committee chair", msg_body)
__db_set_PubComRecom_time (key)
return __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln)
t = websubmit_templates.tmpl_publiline_displaycplxrecom (
ln = ln,
doctype = doctype,
categ = categ,
rn = RN,
apptype = apptype,
action = action,
status = status,
authors = authors,
title = title,
sysno = sysno,
msg_to = user_addr,
msg_to_group = "",
msg_subject = TEXT_PubComRecom_MSG_SUBJECT + " " + "\"" + item_details['title'] + "\"",
)
return t
elif (action == "ProjectLeaderDecision") and ((apptype == "RRP") or (apptype == "RPB")):
if __is_ProjectLeader (req, doctype, categ) != 0:
return _("Action unauthorized for this document.") + "<br />&nbsp;"
if status == "cancelled":
return _("Action unavailable for this document.") + "<br />&nbsp;"
t += """
<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="submit" class="formbutton" value="%(button_label)s" />
</form>""" % {'doctype' : doctype,
'categ' : categ,
'rn' : RN,
'apptype' : apptype,
'button_label' : _("Back to the document"),
}
if validate == "approve":
if dProjectLeaderAction == None:
(errors, infos) = perform_request_save_comment (uid, id_bskBASKET, sysno, msg_subject, msg_body, ln)
out = "%(infos)s<br /><br />" % {'infos' : infos[0]}
sendMailToSubmitter(doctype, categ, RN, "approved")
__db_set_status (key, 'approved')
return out + t
elif validate == "reject":
if dProjectLeaderAction == None:
(errors, infos) = perform_request_save_comment (uid, id_bskBASKET, sysno, msg_subject, msg_body, ln)
out = "%(infos)s<br /><br />" % {'infos' : infos[0]}
sendMailToSubmitter(doctype, categ, RN, "rejected")
__db_set_status (key, 'rejected')
return out + t
validation = """
<select name="validate">
<option> %(select)s</option>
<option value="approve">%(approve)s</option>
<option value="reject">%(reject)s</option>
</select>
<input type="submit" class="formbutton" value="%(button_label)s" />""" % {'select' : _('Select:'),
'approve' : _('Approve'),
'reject' : _('Reject'),
'button_label' : _('Take a decision'),
}
if apptype == "RRP":
TEXT_ProjectLeaderDecision_MSG_SUBJECT = TEXT_RSN_ProjectLeaderDecision_MSG_SUBJECT
elif apptype == "RPB":
TEXT_ProjectLeaderDecision_MSG_SUBJECT = TEXT_RPB_ProjectLeaderDecision_MSG_SUBJECT
else:
TEXT_ProjectLeaderDecision_MSG_SUBJECT = ""
t = websubmit_templates.tmpl_publiline_displaywritecomment(doctype, categ, RN, apptype, action, _("Take a decision"), TEXT_ProjectLeaderDecision_MSG_SUBJECT, validation, "", ln)
return t
elif (action == "ProjectLeaderDecision") and (apptype == "RDA"):
if __is_ProjectLeader (req, doctype, categ) != 0:
return _("Action unauthorized for this document.") + "<br />&nbsp;"
if status == "cancelled":
return _("Action unavailable for this document.") + "<br />&nbsp;"
if validate == "approve":
if dProjectLeaderAction == None:
__db_set_status (key, 'approved')
return __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln)
elif validate == "reject":
if dProjectLeaderAction == None:
__db_set_status (key, 'rejected')
return __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln)
t = """<p>
<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="validate" value="approve" />
<input class="adminbutton" type="submit" value="%(approve)s" />
</form>
<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="validate" value="reject" />
<input class="adminbutton" type="submit" value="%(reject)s" />
</form>
</p>""" % {
'rn' : RN,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'approve' : _('Approve'),
'reject' : _('Reject'),
}
return t
elif (action == "AuthorCancel") and ((apptype == "RRP") or (apptype == "RPB") or (apptype == "RDA")):
if __is_Author (uid, sysno) != 0:
return _("Action unauthorized for this document.") + "<br />&nbsp;"
if (status == "cancelled") or (dProjectLeaderAction != None):
return _("Action unavailable for this document.") + "<br />&nbsp;"
if validate == "go":
__db_set_status (key, 'cancelled')
return __displayCplxDocument(req, doctype,categ,RN,apptype, reply, commentId, ln)
t = """<p>
<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="validate" value="go" />
<input class="adminbutton" type="submit" value="%(cancel)s" />
</form>
</p>""" % {
'rn' : RN,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'cancel' : _('Cancel'),
}
return t
else:
return _("Wrong action for this document.") + "<br />&nbsp;"
return t
def get_pending_item_details(doctype, reportnumber):
"""Given a doctype and reference number, try to retrieve an item's details.
The first place to search for them should be the WebSubmit pending
directory. If nothing is retrieved from there, and attempt is made
to retrieve them from the Invenio repository itself.
@param doctype: (string) - the doctype of the item for which brief
details are to be retrieved.
@param reportnumber: (string) - the report number of the item
for which details are to be retrieved.
@return: (dictionary or None) - If details are found for the item,
they will be returned in a dictionary structured as follows:
{ 'title' : '-', ## String - the item's title
'recid' : '', ## String - recid taken from the SN file
'report-number' : '', ## String - the item's report number
'authors' : [], ## List - the item's authors
}
If no details were found a NoneType is returned.
"""
## First try to get the details of a document from the pending dir:
item_details = get_brief_doc_details_from_pending(doctype, \
reportnumber)
if item_details is None:
item_details = get_brief_doc_details_from_repository(reportnumber)
## Return the item details:
return item_details
def get_brief_doc_details_from_pending(doctype, reportnumber):
"""Try to get some brief details about the submission that is awaiting
the referee's decision.
Details sought are:
+ title
+ Authors
+ recid (why?)
+ report-number (why?)
This function searches for a MARC XML record in the pending submission's
working directory. It prefers the so-called 'dummy' record, but will
search for the final MARC XML record that would usually be passed to
bibupload (i.e. recmysql) if that is not present. If neither of these
records are present, no details will be found.
@param doctype: (string) - the WebSubmit document type of the item
to be refereed. It is used in order to locate the submission's
working directory in the WebSubmit pending directory.
@param reportnumber: (string) - the report number of the item for
which details are to be recovered. It is used in order to locate the
submission's working directory in the WebSubmit pending directory.
@return: (dictionary or None) - If details are found for the item,
they will be returned in a dictionary structured as follows:
{ 'title' : '-', ## String - the item's title
'recid' : '', ## String - recid taken from the SN file
'report-number' : '', ## String - the item's report number
'authors' : [], ## List - the item's authors
}
If no details were found (i.e. no MARC XML files in the submission's
working directory), a NoneType is returned.
"""
pending_doc_details = None
marcxml_rec_name = None
## Check for a MARC XML record in the pending dir.
## If it's there, we will use it to obtain certain bibliographic
## information such as title, author(s), etc, which we will then
## display to the referee.
## We favour the "dummy" record (created with the WebSubmit function
## "Make_Dummy_MARC_XML_Record"), because it was made for this
## purpose. If it's not there though, we'll take the normal
## (final) recmysql record that would generally be passed to bibupload.
if os.access("%s/%s/%s/%s" % (CFG_WEBSUBMIT_PENDING_DIR, \
doctype, \
reportnumber, \
CFG_WEBSUBMIT_DUMMY_MARC_XML_REC), \
os.F_OK|os.R_OK):
## Found the "dummy" marc xml record in the submission dir.
## Use it:
marcxml_rec_name = CFG_WEBSUBMIT_DUMMY_MARC_XML_REC
elif os.access("%s/%s/%s/%s" % (CFG_WEBSUBMIT_PENDING_DIR, \
doctype, \
reportnumber, \
CFG_WEBSUBMIT_MARC_XML_REC), \
os.F_OK|os.R_OK):
## Although we didn't find the "dummy" marc xml record in the
## submission dir, we did find the "real" one (that which would
## normally be passed to bibupload). Use it:
marcxml_rec_name = CFG_WEBSUBMIT_MARC_XML_REC
## If we have a MARC XML record in the pending submission's
## working directory, go ahead and use it:
if marcxml_rec_name is not None:
try:
fh_marcxml_record = open("%s/%s/%s/%s" \
% (CFG_WEBSUBMIT_PENDING_DIR, \
doctype, \
reportnumber, \
marcxml_rec_name), "r")
xmltext = fh_marcxml_record.read()
fh_marcxml_record.close()
except IOError:
## Unfortunately, it wasn't possible to read the details of the
## MARC XML record. Register the exception.
exception_prefix = "Error: Publiline was unable to read the " \
"MARC XML record [%s/%s/%s/%s] when trying to " \
"use it to recover details about a pending " \
"submission." % (CFG_WEBSUBMIT_PENDING_DIR, \
doctype, \
reportnumber, \
marcxml_rec_name)
register_exception(prefix=exception_prefix)
else:
## Attempt to use bibrecord to create an internal representation
## of the record, from which we can extract certain bibliographic
## information:
records = create_records(xmltext, 1, 1)
try:
record = records[0][0]
if record is None:
raise ValueError
except (IndexError, ValueError):
## Bibrecord couldn't successfully represent the record
## contained in the xmltext string. The record must have
## been empty or badly formed (or something).
pass
else:
## Dictionary to hold the interesting details of the
## pending item:
pending_doc_details = { 'title' : '-',
'recid' : '',
'report-number' : '',
'authors' : [],
}
## Get the recid:
## Note - the old "getInPending" function reads the "SN"
## file from the submission's working directory and since
## the "SN" file is currently "magic" and hardcoded
## throughout WebSubmit, I'm going to stick to this model.
## I could, however, have tried to get it from the MARC XML
## record as so:
## recid = record_get_field_value(rec=record, tag="001")
try:
fh_recid = open("%s/%s/%s/SN" \
% (CFG_WEBSUBMIT_PENDING_DIR, \
doctype, \
reportnumber), "r")
recid = fh_recid.read()
fh_recid.close()
except IOError:
## Probably, there was no "SN" file in the submission's
## working directory.
pending_doc_details['recid'] = ""
else:
pending_doc_details['recid'] = recid.strip()
## Item report number (from record):
## Note: I don't know what purpose this serves. It appears
## to be used in the email that is sent to the author, but
## it seems funny to me, since we already have the report
## number (which is indeed used to find the submission's
## working directory in pending). Perhaps it's used for
## cases when the reportnumber is changed after approval?
## To investigate when time allows:
finalrn = record_get_field_value(rec=record, \
tag="037", \
code="a")
if finalrn != "":
pending_doc_details['report-number'] = finalrn
## Item title:
title = record_get_field_value(rec=record, \
tag="245", \
code="a")
if title != "":
pending_doc_details['title'] = title
else:
## Alternative title:
alt_title = record_get_field_value(rec=record, \
tag="246", \
ind1="1", \
code="a")
if alt_title != "":
pending_doc_details['title'] = alt_title
## Item first author:
first_author = record_get_field_value(rec=record, \
tag="100", \
code="a")
if first_author != "":
pending_doc_details['authors'].append(first_author)
## Other Authors:
other_authors = record_get_field_values(rec=record, \
tag="700", \
code="a")
for author in other_authors:
pending_doc_details['authors'].append(author)
## Return the details discovered about the pending document:
return pending_doc_details
def get_brief_doc_details_from_repository(reportnumber):
"""Try to get some brief details about the submission that is awaiting
the referee's decision.
Details sought are:
+ title
+ Authors
+ recid (why?)
+ report-number (why?)
+ email
This function searches in the Invenio repository, based on
"reportnumber" for a record and then pulls the interesting fields
from it.
@param reportnumber: (string) - the report number of the item for
which details are to be recovered. It is used in the search.
@return: (dictionary or None) - If details are found for the item,
they will be returned in a dictionary structured as follows:
{ 'title' : '-', ## String - the item's title
'recid' : '', ## String - recid taken from the SN file
'report-number' : '', ## String - the item's report number
'authors' : [], ## List - the item's authors
}
If no details were found a NoneType is returned.
"""
## Details of the pending document, as found in the repository:
pending_doc_details = None
## Search for records matching this "report number"
found_record_ids = list(search_pattern(req=None, \
p=reportnumber, \
f="reportnumber", \
m="e"))
## How many records were found?
if len(found_record_ids) == 1:
## Found only 1 record. Get the fields of interest:
pending_doc_details = { 'title' : '-',
'recid' : '',
'report-number' : '',
'authors' : [],
'email' : '',
}
recid = found_record_ids[0]
## Authors:
first_author = get_fieldvalues(recid, "100__a")
for author in first_author:
pending_doc_details['authors'].append(author)
other_authors = get_fieldvalues(recid, "700__a")
for author in other_authors:
pending_doc_details['authors'].append(author)
## Title:
title = get_fieldvalues(recid, "245__a")
if len(title) > 0:
pending_doc_details['title'] = title[0]
else:
## There was no value for title - check for an alternative title:
alt_title = get_fieldvalues(recid, "2641_a")
if len(alt_title) > 0:
pending_doc_details['title'] = alt_title[0]
## Record ID:
pending_doc_details['recid'] = recid
## Report Number:
reptnum = get_fieldvalues(recid, "037__a")
if len(reptnum) > 0:
pending_doc_details['report-number'] = reptnum[0]
## Email:
email = get_fieldvalues(recid, "859__f")
if len(email) > 0:
pending_doc_details['email'] = email[0]
elif len(found_record_ids) > 1:
## Oops. This is unexpected - there shouldn't be me multiple matches
## for this item. The old "getInAlice" function would have simply
## taken the first record in the list. That's not very nice though.
## Some kind of warning or error should be raised here. FIXME.
pass
return pending_doc_details
# Retrieve info about document
def getInfo(RN):
"""
Retrieve basic info from record with given report number.
Returns (authors, title, sysno)
"""
authors = None
title = None
sysno = None
recids = search_pattern(p=RN, f='037__a')
if len(recids) == 1:
sysno = int(recids.tolist()[0])
authors = ','.join(get_fieldvalues(sysno, "100__a") + get_fieldvalues(sysno, "700__a"))
title = ','.join(get_fieldvalues(sysno, "245__a"))
return (authors, title, sysno)
#seek info in pending directory
def getInPending(doctype,categ,RN):
"""FIXME: DEPRECATED!"""
PENDIR="%s/pending" % CFG_WEBSUBMIT_STORAGEDIR
if os.path.exists("%s/%s/%s/AU" % (PENDIR,doctype,RN)):
fp = open("%s/%s/%s/AU" % (PENDIR,doctype,RN),"r")
authors=fp.read()
fp.close()
else:
authors = ""
if os.path.exists("%s/%s/%s/TI" % (PENDIR,doctype,RN)):
fp = open("%s/%s/%s/TI" % (PENDIR,doctype,RN),"r")
title=fp.read()
fp.close()
else:
title = ""
if os.path.exists("%s/%s/%s/SN" % (PENDIR,doctype,RN)):
fp = open("%s/%s/%s/SN" % (PENDIR,doctype,RN),"r")
sysno=fp.read()
fp.close()
else:
sysno = ""
if title == "" and os.path.exists("%s/%s/%s/TIF" % (PENDIR,doctype,RN)):
fp = open("%s/%s/%s/TIF" % (PENDIR,doctype,RN),"r")
title=fp.read()
fp.close()
if title == "":
return 0
else:
return (authors,title,sysno,"")
#seek info in Alice database
def getInAlice(doctype,categ,RN):
"""FIXME: DEPRECATED!"""
# initialize sysno variable
sysno = ""
searchresults = list(search_pattern(req=None, p=RN, f="reportnumber"))
if len(searchresults) == 0:
return 0
sysno = searchresults[0]
if sysno != "":
title = Get_Field('245__a',sysno)
emailvalue = Get_Field('8560_f',sysno)
authors = Get_Field('100__a',sysno)
authors += "\n%s" % Get_Field('700__a',sysno)
newrn = Get_Field('037__a',sysno)
return (authors,title,sysno,newrn)
else:
return 0
def SendEnglish(doctype,categ,RN,title,authors,access,sysno):
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
# retrieve useful information from webSubmit configuration
res = run_sql("select value from sbmPARAMETERS where name='categformatDAM' and doctype=%s", (doctype,))
categformat = res[0][0]
categformat = re.sub("<CATEG>","([^-]*)",categformat)
categs = re.match(categformat,RN)
if categs is not None:
categ = categs.group(1)
else:
categ = "unknown"
res = run_sql("select value from sbmPARAMETERS where name='addressesDAM' and doctype=%s",(doctype,))
if len(res) > 0:
otheraddresses = res[0][0]
otheraddresses = otheraddresses.replace("<CATEG>",categ)
else:
otheraddresses = ""
# Build referee's email address
refereeaddress = ""
# Try to retrieve the referee's email from the referee's database
for user in acc_get_role_users(acc_get_role_id("referee_%s_%s" % (doctype,categ))):
refereeaddress += user[1] + ","
# And if there are general referees
for user in acc_get_role_users(acc_get_role_id("referee_%s_*" % doctype)):
refereeaddress += user[1] + ","
refereeaddress = re.sub(",$","",refereeaddress)
# Creation of the mail for the referee
addresses = ""
if refereeaddress != "":
addresses = refereeaddress + ","
if otheraddresses != "":
addresses += otheraddresses
else:
addresses = re.sub(",$","",addresses)
if addresses=="":
SendWarning(doctype,categ,RN,title,authors,access)
return 0
if authors == "":
authors = "-"
res = run_sql("select value from sbmPARAMETERS where name='directory' and doctype=%s", (doctype,))
directory = res[0][0]
message = """
The document %s has been published as a Communication.
Your approval is requested for it to become an official Note.
Title: %s
Author(s): %s
To access the document(s), select the file(s) from the location:
- <%s/record/%s/files/>
+ <%s/%s/%s/files/>
To approve/reject the document, you should go to this URL:
<%s/approve.py?%s>
---------------------------------------------
Best regards.
- The submission team.""" % (RN,title,authors,CFG_SITE_URL,sysno,CFG_SITE_URL,access)
+ The submission team.""" % (RN,title,authors,CFG_SITE_URL,CFG_SITE_RECORD,sysno,CFG_SITE_URL,access)
# send the mail
send_email(FROMADDR,addresses,"Request for Approval of %s" % RN, message,footer="")
return ""
def send_approval(doctype, categ, rn, title, authors, access, sysno):
fromaddr = '%s Submission Engine <%s>' % (CFG_SITE_NAME, CFG_SITE_SUPPORT_EMAIL)
if not categ:
categ = "nocategory"
if not doctype:
doctype = "nodoctype"
addresses = acc_get_authorized_emails('referee', categ=categ, doctype=doctype)
if not addresses:
return SendWarning(doctype, categ, rn, title, authors, access)
if not authors:
authors = "-"
message = """
The document %s has been published as a Communication.
Your approval is requested for it to become an official Note.
Title: %s
Author(s): %s
To access the document(s), select the file(s) from the location:
<%s/record/%s/files/>
- As a referee for this document, you may approve or reject it
+ As a referee for this document, you may approve or reject it
from the submission interface:
<%s/submit?doctype=%s>
---------------------------------------------
Best regards.
The submission team.""" % (rn, title, authors, CFG_SITE_URL, sysno, CFG_SITE_URL, doctype)
# send the mail
return send_email(fromaddr, ', '.join(addresses), "Request for Approval of %s" % rn, message, footer="")
def SendWarning(doctype,categ,RN,title,authors,access):
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
message = "Failed sending approval email request for %s" % RN
# send the mail
send_email(FROMADDR,CFG_SITE_ADMIN_EMAIL,"Failed sending approval email request",message)
return ""
def errorMsg(title,req,c=CFG_SITE_NAME,ln=CFG_SITE_LANG):
return page(title="error",
body = create_error_box(req, title=title,verbose=0, ln=ln),
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='yourapprovals')
def warningMsg(title,req,c=CFG_SITE_NAME,ln=CFG_SITE_LANG):
return page(title="warning",
body = title,
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='yourapprovals')
def sendMailToReferee(doctype,categ,RN,email,authors):
item_details = get_brief_doc_details_from_repository(RN)
## get_brief_doc_details_from_repository has returned either None
## or a dictionary with the following structure:
## { 'title' : '-', ## String - the item's title
## 'recid' : '', ## String - recid
## 'report-number' : '', ## String - the item's report number
## 'authors' : [], ## List - the item's authors
## }
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
message = """
Scientific Note approval for document %s has been submitted to the CERN Document Server.
Your recommendation is requested on it.
Requested subcategory: %s
Title: %s
Author(s): %s
To access the document(s), select the file(s) from the location:
- <%s/record/%s>
+ <%s/%s/%s>
To make a reccommendation, you should go to this URL:
<%s>
You can also check the status of the document:
<%s>
---------------------------------------------
Best regards.
The submission team.""" % (str(RN),
str(categ),
str(item_details['title']),
authors,
CFG_SITE_URL,
+ CFG_SITE_URL,
str(item_details['recid']),
str(CFG_SITE_URL + "/publiline.py?flow=cplx&doctype="+doctype+"&ln=en&apptype=RRP&categ="+categ+"&RN="+RN+"&action=RefereeRecom"),
str(CFG_SITE_URL + "/publiline.py?flow=cplx&doctype="+doctype+"&ln=en&apptype=RRP&categ="+categ+"&RN="+RN))
# send the mail
send_email(FROMADDR, email,"Request for document %s recommendation" % (RN),message)
return ""
def sendMailToGroup(doctype,categ,RN,group_id,authors):
item_details = get_brief_doc_details_from_repository(RN)
## get_brief_doc_details_from_repository has returned either None
## or a dictionary with the following structure:
## { 'title' : '-', ## String - the item's title
## 'recid' : '', ## String - recid
## 'report-number' : '', ## String - the item's report number
## 'authors' : [], ## List - the item's authors
## }
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
message = """
Scientific Note approval for document %s has been submitted to the CERN Document Server.
Your comments are requested on this document.
Requested subcategory: %s
Title: %s
Author(s): %s
To access the document(s), select the file(s) from the location:
- <%s/record/%s>
+ <%s/%s/%s>
To leave a comment or check the status of the approval process, you should go to this URL:
<%s>
""" % (str(RN),
str(categ),
str(item_details['title']),
authors,
CFG_SITE_URL,
+ CFG_SITE_RECORD,
str(item_details['recid']),
str(CFG_SITE_URL + "/publiline.py?flow=cplx&doctype="+doctype+"&ln=en&apptype=RRP&categ="+categ+"&RN="+RN))
# send mails to all members of the ATLAS group
group_member_ids = run_sql("SELECT id_user FROM user_usergroup WHERE id_usergroup = '%s'" % (group_id))
for member_id in group_member_ids:
member_email = run_sql("SELECT email FROM user WHERE id = '%s'" % (member_id))
if not member_email[0][0] == "info@invenio-software.org":
send_email(FROMADDR, member_email[0][0],"Request for comment on document %s" % (RN),message)
return ""
def sendMailToProjectLeader(doctype, categ, RN, email, authors, actor, recommendation):
item_details = get_brief_doc_details_from_repository(RN)
## get_brief_doc_details_from_repository has returned either None
## or a dictionary with the following structure:
## { 'title' : '-', ## String - the item's title
## 'recid' : '', ## String - recid
## 'report-number' : '', ## String - the item's report number
## 'authors' : [], ## List - the item's authors
## }
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
message = """
Scientific Note approval for document %s has been submitted to the CERN Document Server.
Your approval is requested for this document. Once you have received recommendations from both the referee and the publication committee chair, you will be able to make your decision.
Requested subcategory: %s
Title: %s
Author(s): %s
To access the document(s), select the file(s) from the location:
- <%s/record/%s>
+ <%s/%s/%s>
The %s has made a recommendation for the document. He/she said the following:
%s
You can approve this document by visiting this page:
<%s>
You can also check the status of the document from:
<%s>
""" % (str(RN),
str(categ),
str(item_details['title']),
authors,
CFG_SITE_URL,
+ CFG_SITE_RECORD,
str(item_details['recid']),
actor,
recommendation,
str(CFG_SITE_URL + "/publiline.py?flow=cplx&doctype="+doctype+"&ln=en&apptype=RRP&categ="+categ+"&RN="+RN+"&action=ProjectLeaderDecision"),
str(CFG_SITE_URL + "/publiline.py?flow=cplx&doctype="+doctype+"&ln=en&apptype=RRP&categ="+categ+"&RN="+RN))
# send mails to all members of the ATLAS group
send_email(FROMADDR, email,"Request for approval/rejection of document %s" % (RN),message)
return ""
def sendMailToSubmitter(doctype, categ, RN, outcome):
item_details = get_brief_doc_details_from_repository(RN)
## get_brief_doc_details_from_repository has returned either None
## or a dictionary with the following structure:
## { 'title' : '-', ## String - the item's title
## 'recid' : '', ## String - recid
## 'report-number' : '', ## String - the item's report number
## 'authors' : [], ## List - the item's authors
## }
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
message = """
The approval process for your document : %s, has been completed. The details of this document are as follows:
Requested subcategory: %s
Title: %s
The project leader has made the following recommendation for the document:
%s
""" % (RN, categ, item_details['title'], outcome)
# send mails to all members of the ATLAS group
send_email(FROMADDR, item_details['email'],"Final outcome for approval of document : %s" % (RN),message)
return ""
def sendMailtoCommitteeChair(doctype, categ, RN, email, authors):
item_details = get_brief_doc_details_from_repository(RN)
## get_brief_doc_details_from_repository has returned either None
## or a dictionary with the following structure:
## { 'title' : '-', ## String - the item's title
## 'recid' : '', ## String - recid
## 'report-number' : '', ## String - the item's report number
## 'authors' : [], ## List - the item's authors
## }
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
message = """
The referree assigned to the document detailed below has made a reccommendation. You are now requested to make a reccommendation of your own.
Requested subcategory: %s
Title: %s
Author(s): %s
To access the document(s), select the file(s) from the location:
- <%s/record/%s>
+ <%s/%s/%s>
You can make a reccommendation by visiting this page:
<%s>
""" % (str(categ),
str(item_details['title']),
authors,
CFG_SITE_URL,
+ CFG_SITE_RECORD,
str(item_details['recid']),
str(CFG_SITE_URL + "/publiline.py?flow=cplx&doctype="+doctype+"&ln=en&apptype=RRP&categ="+categ+"&RN="+RN))
# send mails to all members of the ATLAS group
send_email(FROMADDR, email,"Request for reccommendation of document %s" % (RN),message)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d1e3d60db..6be2e7faa 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,267 +1,270 @@
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
#
# List of source files which contain translatable strings.
#
modules/webhelp/web/help-central.webdoc
modules/webhelp/web/admin/admin.webdoc
modules/websearch/doc/search-tips.webdoc
modules/websearch/doc/search-guide.webdoc
modules/websearch/doc/admin/websearch-admin-guide.webdoc
modules/websubmit/doc/submit-guide.webdoc
modules/websubmit/doc/admin/websubmit-admin-guide.webdoc
modules/bibedit/doc/admin/bibedit-admin-guide.webdoc
modules/bibupload/doc/admin/bibupload-admin-guide.webdoc
modules/bibformat/doc/admin/bibformat-admin-guide.webdoc
modules/bibharvest/doc/admin/bibharvest-admin-guide.webdoc
modules/webmessage/doc/admin/webmessage-admin-guide.webdoc
modules/webalert/doc/admin/webalert-admin-guide.webdoc
modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc
modules/bibmatch/doc/admin/bibmatch-admin-guide.webdoc
modules/bibconvert/doc/admin/bibconvert-admin-guide.webdoc
modules/bibsched/doc/admin/bibsched-admin-guide.webdoc
modules/bibrank/doc/admin/bibrank-admin-guide.webdoc
modules/webstat/doc/admin/webstat-admin-guide.webdoc
modules/bibindex/doc/admin/bibindex-admin-guide.webdoc
modules/webbasket/doc/admin/webbasket-admin-guide.webdoc
modules/webcomment/doc/admin/webcomment-admin-guide.webdoc
modules/websession/doc/admin/websession-admin-guide.webdoc
modules/webstyle/doc/admin/webstyle-admin-guide.webdoc
modules/webstyle/doc/hacking/webstyle-webdoc-syntax.webdoc
modules/elmsubmit/doc/admin/elmsubmit-admin-guide.webdoc
modules/bibformat/etc/format_templates/Default_HTML_files.bft
modules/bibformat/etc/format_templates/Default_HTML_actions.bft
modules/bibconvert/bin/bibconvert.in
modules/bibconvert/lib/bibconvert.py
modules/bibconvert/lib/bibconvert_tests.py
modules/bibedit/bin/refextract.in
modules/bibedit/bin/xmlmarclint.in
modules/bibedit/lib/bibedit_engine.py
modules/bibedit/lib/bibedit_templates.py
modules/bibedit/lib/bibrecord_config.py
modules/bibedit/lib/bibrecord.py
modules/bibedit/lib/bibrecord_tests.py
modules/bibedit/lib/refextract_config.py
modules/bibedit/lib/refextract.py
modules/bibformat/bin/bibreformat.in
modules/bibformat/lib/bibformat_engine_tests.py
modules/bibformat/lib/bibformat_templates.py
modules/bibformat/lib/bibformatadminlib.py
+modules/bibformat/lib/elements/bfe_aid_authors.py
modules/bibformat/lib/elements/bfe_authors.py
modules/bibformat/lib/elements/bfe_fulltext.py
modules/bibformat/lib/elements/bfe_fulltext_mini.py
modules/bibformat/lib/elements/bfe_edit_files.py
modules/bibformat/lib/elements/bfe_edit_record.py
modules/bibformat/web/admin/bibformatadmin.py
modules/bibharvest/bin/oaiharvest.in
modules/bibharvest/lib/bibharvest_templates.py
modules/bibindex/bin/bibindex.in
modules/bibindex/bin/bibstat.in
modules/bibindex/lib/bibindexadminlib.py
modules/bibindex/lib/bibindex_engine_config.py
modules/bibindex/lib/bibindex_engine.py
modules/bibindex/lib/bibindex_engine_stemmer.py
modules/bibindex/lib/bibindex_engine_stemmer_tests.py
modules/bibindex/lib/bibindex_engine_stopwords.py
modules/bibindex/lib/bibindex_engine_tests.py
modules/bibindex/web/admin/bibindexadmin.py
modules/bibrank/bin/bibrankgkb.in
modules/bibrank/bin/bibrank.in
modules/bibrank/lib/bibrankadminlib.py
modules/bibrank/lib/bibrank_citation_grapher.py
modules/bibrank/lib/bibrank_citation_indexer.py
modules/bibrank/lib/bibrank_citation_indexer_tests.py
modules/bibrank/lib/bibrank_citation_searcher.py
modules/bibrank/lib/bibrank_citation_searcher_tests.py
modules/bibrank/lib/bibrank_downloads_grapher.py
modules/bibrank/lib/bibrank_downloads_indexer.py
modules/bibrank/lib/bibrank_downloads_indexer_tests.py
modules/bibrank/lib/bibrank_downloads_similarity.py
modules/bibrank/lib/bibrank_grapher.py
modules/bibrank/lib/bibrank_record_sorter.py
modules/bibrank/lib/bibrank_record_sorter_tests.py
modules/bibrank/lib/bibrank_tag_based_indexer.py
modules/bibrank/lib/bibrank_tag_based_indexer_tests.py
modules/bibrank/lib/bibrank_word_indexer.py
modules/bibrank/web/admin/bibrankadmin.py
modules/bibsched/bin/bibsched.in
modules/bibsched/bin/bibtaskex.in
modules/bibupload/lib/batchuploader_templates.py
modules/bibupload/lib/batchuploader_webinterface.py
modules/elmsubmit/bin/elmsubmit.in
modules/elmsubmit/lib/elmsubmit_enriched2txt.py
modules/elmsubmit/lib/elmsubmit_EZArchive.py
modules/elmsubmit/lib/elmsubmit_EZEmail.py
modules/elmsubmit/lib/elmsubmit_field_validation.py
modules/elmsubmit/lib/elmsubmit_filename_generator.py
modules/elmsubmit/lib/elmsubmit_html2txt.py
modules/elmsubmit/lib/elmsubmit_misc.py
modules/elmsubmit/lib/elmsubmit.py
modules/elmsubmit/lib/elmsubmit_richtext2txt.py
modules/elmsubmit/lib/elmsubmit_submission_parser.py
modules/elmsubmit/lib/myhtmlentitydefs.py
modules/miscutil/bin/dbexec.in
modules/miscutil/lib/dateutils.py
modules/miscutil/lib/errorlib.py
modules/miscutil/lib/errorlib_tests.py
modules/miscutil/lib/errorlib_webinterface.py
modules/miscutil/lib/__init__.py
modules/miscutil/lib/inveniocfg.py
modules/miscutil/lib/miscutil_config.py
modules/miscutil/lib/mailutils.py
modules/miscutil/lib/messages.py
modules/miscutil/lib/textutils.py
modules/webaccess/bin/authaction.in
modules/webaccess/bin/webaccessadmin.in
modules/webaccess/lib/access_control_admin.py
modules/webaccess/lib/access_control_config.py
modules/webaccess/lib/access_control_engine.py
modules/webaccess/lib/external_authentication.py
modules/webaccess/lib/webaccessadmin_lib.py
modules/webaccess/web/admin/webaccessadmin.py
modules/webalert/bin/alertengine.in
modules/webalert/lib/alert_engine.py
modules/webalert/lib/htmlparser.py
modules/webalert/lib/webalert.py
modules/webalert/lib/webalert_templates.py
modules/webalert/lib/webalert_webinterface.py
modules/webbasket/lib/webbasket.py
modules/webbasket/lib/webbasket_config.py
modules/webbasket/lib/webbasket_templates.py
modules/webbasket/lib/webbasket_webinterface.py
modules/webcomment/lib/webcommentadminlib.py
modules/webcomment/lib/webcomment_config.py
modules/webcomment/lib/webcomment.py
modules/webcomment/lib/webcomment_templates.py
modules/webcomment/lib/webcomment_tests.py
modules/webcomment/lib/webcomment_webinterface.py
modules/webcomment/web/admin/webcommentadmin.py
modules/webmessage/bin/webmessageadmin.in
modules/webmessage/lib/webmessage_config.py
modules/webmessage/lib/webmessage_dblayer.py
modules/webmessage/lib/webmessage_mailutils.py
modules/webmessage/lib/webmessage.py
modules/webmessage/lib/webmessage_templates.py
modules/webmessage/lib/webmessage_webinterface.py
modules/websearch/bin/webcoll.in
modules/websearch/lib/search_engine_config.py
modules/websearch/lib/search_engine.py
modules/websearch/lib/search_engine_tests.py
modules/websearch/lib/websearchadminlib.py
modules/websearch/lib/websearch_templates.py
modules/websearch/lib/websearch_webinterface.py
modules/websearch/lib/websearch_external_collections.py
modules/websearch/lib/websearch_external_collections_templates.py
modules/websearch/lib/websearch_webcoll.py
modules/websearch/web/admin/websearchadmin.py
modules/websession/bin/inveniogc.in
modules/websession/lib/session.py
modules/websession/lib/webaccount.py
modules/websession/lib/websession_templates.py
modules/websession/lib/webuser.py
modules/websession/lib/websession_webinterface.py
modules/websession/lib/webgroup_dblayer.py
modules/websession/lib/webgroup.py
modules/websession/lib/websession_config.py
modules/webstat/bin/webstat.in
modules/webstyle/lib/template.py
modules/webstyle/lib/webpage.py
modules/webstyle/lib/webstyle_templates.py
modules/webstyle/lib/webdoc.py
modules/webstyle/lib/webdoc_tests.py
modules/webstyle/lib/webdoc_webinterface.py
modules/websubmit/lib/file.py
modules/websubmit/lib/websubmit_managedocfiles.py
modules/websubmit/lib/functions/Add_Files.py
modules/websubmit/lib/functions/CaseEDS.py
modules/websubmit/lib/functions/Create_Modify_Interface.py
modules/websubmit/lib/functions/Create_Recid.py
modules/websubmit/lib/functions/Finish_Submission.py
modules/websubmit/lib/functions/Format_Record.py
modules/websubmit/lib/functions/Get_Info.py
modules/websubmit/lib/functions/Get_Report_Number.py
modules/websubmit/lib/functions/Get_Sysno.py
modules/websubmit/lib/functions/Insert_Modify_Record.py
modules/websubmit/lib/functions/Insert_Record.py
modules/websubmit/lib/functions/Is_Original_Submitter.py
modules/websubmit/lib/functions/Is_Referee.py
modules/websubmit/lib/functions/Mail_Submitter.py
modules/websubmit/lib/functions/Make_Modify_Record.py
modules/websubmit/lib/functions/Make_Record.py
modules/websubmit/lib/functions/Move_Files_Archive.py
modules/websubmit/lib/functions/Move_From_Pending.py
modules/websubmit/lib/functions/Move_to_Done.py
modules/websubmit/lib/functions/Move_to_Pending.py
modules/websubmit/lib/functions/Print_Success_APP.py
modules/websubmit/lib/functions/Print_Success_DEL.py
modules/websubmit/lib/functions/Print_Success_MBI.py
modules/websubmit/lib/functions/Print_Success.py
modules/websubmit/lib/functions/Print_Success_SRV.py
modules/websubmit/lib/functions/Report_Number_Generation.py
modules/websubmit/lib/functions/Retrieve_Data.py
modules/websubmit/lib/functions/Send_APP_Mail.py
modules/websubmit/lib/functions/Send_Approval_Request.py
modules/websubmit/lib/functions/Send_Modify_Mail.py
modules/websubmit/lib/functions/Send_SRV_Mail.py
modules/websubmit/lib/functions/Shared_Functions.py
modules/websubmit/lib/functions/Test_Status.py
modules/websubmit/lib/functions/Update_Approval_DB.py
modules/websubmit/lib/websubmit_config.py
modules/websubmit/lib/websubmit_engine.py
modules/websubmit/lib/websubmit_templates.py
modules/websubmit/lib/websubmit_webinterface.py
modules/websubmit/lib/websubmitadmin_config.py
modules/websubmit/lib/websubmitadmin_engine.py
modules/websubmit/web/admin/referees.py
modules/websubmit/web/approve.py
modules/websubmit/web/publiline.py
modules/websubmit/web/yourapprovals.py
modules/websubmit/web/yoursubmissions.py
modules/websubmit/web/admin/websubmitadmin.py
modules/webjournal/lib/webjournal_utils.py
modules/webjournal/lib/webjournal_templates.py
modules/webjournal/lib/webjournal_config.py
modules/webjournal/lib/webjournaladminlib.py
modules/webjournal/web/admin/webjournaladmin.py
modules/bibcatalog/lib/bibcatalog_templates.py
modules/webjournal/lib/widgets/bfe_webjournal_widget_seminars.py
modules/webjournal/lib/widgets/bfe_webjournal_widget_whatsNew.py
modules/webjournal/lib/widgets/bfe_webjournal_widget_weather.py
modules/webjournal/lib/elements/bfe_webjournal_article_author.py
modules/webjournal/lib/elements/bfe_webjournal_imprint.py
modules/webjournal/lib/elements/bfe_webjournal_article_body.py
modules/webjournal/lib/elements/bfe_webjournal_rss.py
modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py
modules/webjournal/lib/elements/bfe_webjournal_archive.py
modules/bibedit/lib/bibedit_webinterface.py
modules/bibedit/lib/bibeditmulti_templates.py
modules/bibedit/lib/bibedit_utils.py
modules/bibedit/lib/bibeditmulti_webinterface.py
modules/bibexport/lib/bibexport_method_fieldexporter_templates.py
modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py
modules/bibexport/lib/bibexport_method_fieldexporter.py
modules/bibcirculation/lib/bibcirculation_utils.py
modules/bibcirculation/lib/bibcirculation_webinterface.py
modules/bibcirculation/lib/bibcirculation_templates.py
modules/websubmit/lib/functions/Create_Upload_Files_Interface.py
modules/bibcheck/web/admin/bibcheckadmin.py
modules/bibharvest/lib/oai_repository_admin.py
modules/bibharvest/lib/oai_harvest_admin.py
modules/bibknowledge/lib/bibknowledge_templates.py
modules/bibknowledge/lib/bibknowledgeadmin.py
modules/bibsword/lib/bibsword_webinterface.py
modules/bibupload/lib/batchuploader_engine.py
+modules/bibauthorid/lib/bibauthorid_webinterface.py
+modules/bibauthorid/lib/bibauthorid_templates.py
\ No newline at end of file

Event Timeline