diff --git a/NEWS b/NEWS index de0f9b029..084b3dac7 100644 --- a/NEWS +++ b/NEWS @@ -1,4196 +1,4347 @@ Invenio NEWS ============ Here is a short summary of the most notable changes in Invenio releases. For more information about the current release, please consult RELEASE-NOTES. For more information about changes, please consult ChangeLog. +Invenio v1.2.1 -- released 2015-05-21 +------------------------------------- + +Security fixes +~~~~~~~~~~~~~~ + ++ BibAuthorID: + + - Improves URL redirecting by properly quoting all URL parts, in + order to better protect against possible XSS attacks. + ++ WebStyle: + + - Adds back the `HttpOnly` cookie attribute in order to better + protect against potential XSS vulnerabilities. (#3064) + +Improved features +~~~~~~~~~~~~~~~~~ + ++ installation: + + - Apache virtual environments are now created with appropriate + `WSGIDaemonProcess` user value, taken from the configuration + variable `CFG_BIBSCHED_PROCESS_USER`, provided it is set. This + change makes it easier to run Invenio under non-Apache user + identity. + + - Apache virtual environments are now created with appropriate + `WSGIPythonHome` directive so that it would be easier to run + Invenio from within Python virtual environments. + +Bug fixes +~~~~~~~~~ + ++ BibDocFile: + + - Safer upgrade recipe for migrations from the old document storage + model (used in v1.1) to the new document storage model (used in + v1.2). + ++ WebSearch: + + - Removes special behaviour of the "subject" index that was hard- + coded based on the index name. Installations should rather + specify wanted behaviour by means of configurable tokeniser + instead. + + - Collection names containing slashes are now supported again. + However we recommend not to use slashes in collection names; if + slashes were wanted for aesthetic reasons, they can be added in + visible collection translations. (#2902) + ++ global: + + - Replaces `invenio-demo.cern.ch` by `demo.invenio-software.org` + which is the new canonical URL of the demo site. (#2867) + ++ installation: + + - Releases constraint on using an old version of `h5py` that was + anyway no longer available on PyPI. + ++ testutils: + + - Switches off SSL verification when running the test suite. Useful + for Python-2.7.9 where self-signed SSL certificates (that are + usually used on development installations) would cause apparent + test failures. (#2868) + +Invenio v1.1.6 -- released 2015-05-21 +------------------------------------- + +Security fixes +~~~~~~~~~~~~~~ + ++ WebStyle: + + - Adds back the `HttpOnly` cookie attribute in order to better + protect against potential XSS vulnerabilities. (#3064) + +Improved features +~~~~~~~~~~~~~~~~~ + ++ installation: + + - Apache virtual environments are now created with appropriate + `WSGIDaemonProcess` user value, taken from the configuration + variable `CFG_BIBSCHED_PROCESS_USER`, provided it is set. This + change makes it easier to run Invenio under non-Apache user + identity. + + - Apache virtual environments are now created with appropriate + `WSGIPythonHome` directive so that it would be easier to run + Invenio from within Python virtual environments. + +Bug fixes +~~~~~~~~~ + ++ global: + + - Replaces `invenio-demo.cern.ch` by `demo.invenio-software.org` + which is the new canonical URL of the demo site. (#2867) + ++ testutils: + + - Switches off SSL verification when running the test suite. Useful + for Python-2.7.9 where self-signed SSL certificates (that are + usually used on development installations) would cause apparent + test failures. (#2868) + +Invenio v1.0.9 -- released 2015-05-21 +------------------------------------- + +Security fixes +~~~~~~~~~~~~~~ + ++ WebStyle: + + - Adds back the `HttpOnly` cookie attribute in order to better + protect against potential XSS vulnerabilities. (#3064) + +Improved features +~~~~~~~~~~~~~~~~~ + ++ installation: + + - Apache virtual environments are now created with appropriate + `WSGIDaemonProcess` user value, taken from the configuration + variable `CFG_BIBSCHED_PROCESS_USER`, provided it is set. This + change makes it easier to run Invenio under non-Apache user + identity. + + - Apache virtual environments are now created with appropriate + `WSGIPythonHome` directive so that it would be easier to run + Invenio from within Python virtual environments. + +Bug fixes +~~~~~~~~~ + ++ global: + + - Replaces `invenio-demo.cern.ch` by `demo.invenio-software.org` + which is the new canonical URL of the demo site. (#2867) + ++ testutils: + + - Switches off SSL verification when running the test suite. Useful + for Python-2.7.9 where self-signed SSL certificates (that are + usually used on development installations) would cause apparent + test failures. (#2868) + Invenio v2.0.3 -- released 2015-05-15 ------------------------------------- Security fixes ~~~~~~~~~~~~~~ + script: - Switches from insecure standard random number generator to secure OS-driven entropy source (/dev/urandom on linux) for secret key generation. New features ~~~~~~~~~~~~ + formatter: - Adds html_class and link_label attributes to bfe_edit_record. (#3020) + script: - Adds `SERVER_BIND_ADDRESS` and `SERVER_BIND_PORT` to overwrite bind address and port independently from the public URL. This gives control over the used network interface as well as the ability to bind Invenio to a protected port and use a reverse proxy for access. Priority of the config is (1) runserver command arguments, (2) `SERVER_BIND_ADDRESS` and `SERVER_BIND_PORT` configuration, (3) data from `CFG_SITE_URL`, (4) defaults (`127.0.0.1:80`). Improved features ~~~~~~~~~~~~~~~~~ + docker: - Slims down docker image by building on top of less bloated base image and only install what is really required. Also purges unneeded packages, flushes caches and clean temporary files. All these parts should not be in a production image and are also not required by developers. You can still install components when extending the Invenio base image. + docs: - Adds missing 'libffi' library and howto start redis server. Causing an exception when running `pip install --process- dependency-links -e .[development]`: 'ffi.h' file not found and 'sudo: service: command not found' when starting redis server (OS X Yosemite, 10.10). - Adds a step describing how to install MySQL on CentOS 7 because it does not have 'mysql-server' package by default. Bug fixes ~~~~~~~~~ + email: - Fixes 'send_email' to expect an 'EmailMessage' object from the 'forge_email' method rather than a string-like object. (#3076) - Fixes reference to CFG_SITE_ADMIN_EMAIL (not a global). + legacy: - Makes lazy loading of `stopwords_kb` variable to avoid file parsing during script loading. (#1462) + logging: - Fixes Sentry proxy definition pointing to a wrong application attribute. + matcher: - Fixes Unicode conversion required to use the levenshtein_distance function. (#3047) Invenio v2.0.2 -- released 2015-04-17 ------------------------------------- Security fixes ~~~~~~~~~~~~~~ + celery: - Forces Celery to only accept msgpack content when using standard configuration. This disallows pickle messages which can be used for remote code execution. (#3003) + global: - Disables all attempts to serve directory listings for directories found under static root. Incompatible changes ~~~~~~~~~~~~~~~~~~~~ + celery: - If you use any Celery serializer other than msgpack, you must update configuration variable CELERY_ACCEPT_CONTENT to include that serializer. + pidstore: - Refactors DataCite provider to use the new external DataCite API client. - Removes DataCite API client from Invenio. New features ~~~~~~~~~~~~ + docs: - Adds "Code of Conduct" to the "Community" documentation. - Adds new fast track deprecation policy. - Documents commit message labels used by developers (such as NEW, SECURITY, FIX, etc.) used in automatic generation of structured release notes. (#2856) + global: - Adds a `inveniomanage config locate` command to request the location of the instance config file. - Adds new configurable variable `INVENIO_APP_CONFIG_ENVS` that can be set both from `invenio.cfg` and OS environment. Application factory iterates over comma separated list of configuration variable names and updates application config with equivalent OS environment value. (#2858) + template: - Adds 'u' filter that converts str to unicode in Jinja2 templates since support for str has been deprecated. Example: `{{ mystr|u }}`. (#2862) Improved features ~~~~~~~~~~~~~~~~~ + docs: - Adds example of how to deprecate a feature and includes deprecation policy in documentation. + global: - Moves datacite API wrapper to external package. - Escapes all unicode characters in Jinja2 templates. + installation: - Apache virtual environments are now created with appropriate `WSGIDaemonProcess` user value, taken from the configuration variable `CFG_BIBSCHED_PROCESS_USER`, provided it is set. This change makes it easier to run Invenio under non-Apache user identity. - Apache virtual environments are now created with appropriate `WSGIPythonHome` directive so that it would be easier to run Invenio from within Python virtual environments. + jsonalchemy: - Introduces support for accepting MARC fields having any indicator. (#1722 #2075) Bug fixes ~~~~~~~~~ + admin: - Adds `admin.js` bundle that loads `select2.js` library on `/admin` pages. (#2690 #2781) + assets: - Implements `__deepcopy__` method for `webassets.filter.option` in order to fix unexpected behavior of the `option` class contructor. (#2777 #2864 #2921) + documents: - Flask-Login import in field definition. (#2905) - Safer upgrade recipe for migrations from the old document storage model (used in v1.1) to the new document storage model (used in v1.2). + global: - Drops support for serving directories in Apache site configuration to avoid problems with loading '/admin' url without trailing slash that attempts to serve the static directory of the same name. (#2470 #2943) + installation: - Adds Babel as setup requirements for installing compile_catalog command. + jsonalchemy: - Fixes the definition of time_and_place_of_event_note, series_statement and source_of_description fields. + oairepository: - Switches keys in CFG_OAI_METADATA_FORMATS configuration mapping. (#2962) - Amends bfe_oai_marcxml element since get_preformatted_record does not return a tuple anymore. + search: - Fixes portalbox text overflow and and syntax error in CSS. (#3023) - Collection names containing slashes are now supported again. However we recommend not to use slashes in collection names; if slashes were wanted for aesthetic reasons, they can be added in visible collection translations. (#2902) + sorter: - Comparison function of record tags uses space concatened string from list of all tags values. (#2750) Notes ~~~~~ + assets: - Adds deprecation warning when LESS_RUN_IN_DEBUG is used. (#2923) + global: - Deprecates use of invenio.utils.datacite:DataCite (to be removed in Invenio 2.2). - External authentication methods are being deprecated. Please use `invenio.modules.oauthclient` or Flask-SSO instead. (#1083) - Recreate Apache site configurations using new template. Run following command: `inveniomanage apache create-config`. - Deprecates custom remote debuggers. Please use native Werkzeug debugger or other (*)pdb equivalents. (#2945) - Adds deprecation warning for `invenio.ext.jinja2hacks` and all detected non-ascii strings usage in templates mainly coming from legacy (1.x) modules. (#2862) + installation: - Limits version of SQLAlchemy<=1.0 and SQLAlchemy-Utils<=0.30. + oairepository: - Changes current behavior of OAI-PMH server for logged in users to take into account all records a user can view and not only public records. Invenio v2.0.1 -- released 2015-03-20 ------------------------------------- New features: ~~~~~~~~~~~~~ + global: - Deprecation policy comes with new deprecation warnings wrappers. (#2875) Bug fixes: ~~~~~~~~~~ + assets: - Avoids bundle changes to persist between requests in DEBUG mode, which is not desired. (#2777) + docs: - Adds missing `invenio.base` package to the `config.py` file for a custom overlay in the docs. + global: - Replaces `invenio-demo.cern.ch` by `demo.invenio-software.org` which is the new canonical URL of the demo site. (#2867) + installation: - Reorders 'compile_catalog' and 'install' commands to fix installation process from PyPI. - Adds apache2 xsendfile package to installation script. (#2857) + messages: - Defines a path for jquery.ui required by jQuery-Timepicker-Addon and sets an exact version for the plugin instead of latest. (#2910) + records: - Changes creation_date field definition in tests. (#2214) + search: - Generates correct url for `/collection` redirect. Invenio v2.0.0 -- released 2015-03-04 ------------------------------------- *) access: mailcookie port using SQLAlchemy; Flask-Admin interface addition; new has_(super)_admin_role methods (#2509); fix PEP8 and PEP257 for models; infinite recursion hotfix (#2509); fix holdingpenusers role definition; Holding Pen role; removal of site specific configuration; site specific configuration of demo roles; file renaming; jinja base templates renaming; fix edge cases of user info usage; module import fix; jsonalchemy acl extension; using unittest2 in Python 2.6; string translation fix; fix admin blueprint folders; improve login performance; regression tests fix; fix firerole uid test; addition of redirections to legacy app (#1425); Flask logger removal; MySQL 5.5.3+ autocommit fix *) accounts: login template allow set title; user full name addition to model (#2647); upgrade fix; enhancement in UserUsergroup; require.js refactoring; template fixes; lost password view protection; bundles 2.0; secure url for login form's POST action; settings initial release; gettext import fix; fix html template escaping; fix user password change; template blocks addition; legacy webuser import fix; LostPassword form import addition; disabled autoescaping for SSO link; WTForms import fix; blueprint name renaming *) admin: administration menu fix (#1822); admin menu visibility fix; blueprint customization removal; registry discovery *) adminutils: fix for global admin instance; initial release *) alerts: PEP8/257 improvements in models; CSS cleanup (#1644); fix translatable strings; regression tests fix *) annotations: fix for broken bundles (#2327); jinja base templates renaming; sphinx friendly documentation; api improvements; JSON-LD publishing; record document annotations; file attachments skeleton; initial commit *) apikeys: fix for early import outside app context; add option to disable signing; SQLAlchemy model; fix for import and print statements; initial port to Flask; initial Flask port *) archiver: initial port to new code structure (#1579 #2258) *) arxiv: fix database search with prefix; fix 'status' key lookup; response code addition; OAI2 API usage and status code addition (#1866); docs entry addition; initial Flask extension commit *) assets: bower command --output-file option; cleancss url rebasing; requirejs exclude option (#2411); bundles cleanup per request (#2290); jquery-ui bundle removal; resolution of jquery to ~1.11; auto_build option; smarter bower command; registry proxy usage fix; bundles without names; bundles with weight; burial of js/css jinja extension; absolute paths in debug mode; wrapper logger; bower updates; bower command; bundles 2.0; filters behavior fix; requirejs and uglifyjs; Flask-Assets update to 0.10.dev; error logging if binary are missing; fix bundle builder; less flavor of bootstrap; fix some missing url_for("static"); working combined assets *) authorids: removal of legacy code; models addition (#1790); fix for templates *) authorlist: initial release (#1891) *) authors: fix missing stub message template; base record; initial release; SQLAlchemy model *) babel: no compiled translation error improvement; logger removal; setuptools integration; translation loading from PACKAGES (#828); initial release *) base: ext fix language usage; PEP8/257 fixes; table drop order fix; page template block addition; fix jquery and select2 loading in admin (#2690); fix url of RELEASE-NOTES; move of remote autocomplete field; jquery- multifile source update; bundle less filename correction; fix dangerous demosite populate (#2294); requirejs improvements; navigation menu buttons cleanup; build.js improvement; dropdown menu improvement; dropdown menu and mobile UI (#1994); fix footer links (#2248); admin drop-down menu fix (#2246); fix for demosite populate extra info; fix database create error message; new `demosite populate` force-recids option; removal of typeahead.js from bundles; CFG_WEBDIR fix; undefined config variables fix; gentler web page title warning (#2215 #2198); dropdown menu and mobile UI fix (#1994); padding removal from top of Flask-Admin page (#2201); fix missing default config value; missing MathJax config variable; fix for recreation of broken links; global index run during demo site population; database create/drop for storage engines; better signaling support for cli; CFG_RUNDIR addition; separation of styles to independent files; account settings drop- down menu; global tooltip activation; user 'Login' and 'Register' button addition (#1943); bundles documentation; jquery-form loaded via require.js; database script documentation formatting; separation of bootstrap bundle; move the ckeditor plugins; jquery- ui renaming; default module in PACKAGES; bundles structure changes; bundles block; fix package name and source in bundles; scripts position for legacy; jquery ui extras; require.js config in global conf; dropdown menu fix; baseUrl for require.js; demosite cleanup; requirejs bundle ordering fix; fix for wsgi PATH_INFO handling (#1823); PEP8 and PEP257 clean-up in factory; render field enhancement; absolute icon font path; footer modularity improvement; eval is evil; wsgi middlewares reorganization; fix static files serving from DocumentRoot; footer modified; deprecation of `STATIC_MAP`; Blueprint for static files in base; documentation fixes; dead code removal; fix admin template; helpers fix six string and text type; new signal `before_handle_user_exception`; wrapper doctest addition; config PEP8 improvements; PDFTK path discovery; bibupload allowed paths fix; fix misc index stemming language in demosite; Apache 2.4 compatibility fix; font awesome addition; Apache server alias fix; signal webcoll_after_reclist_cache_update; fix config UTF-8 problems; sticky footer fix; Apache configuration template fix; hot fixes of i18n issues in legacy; simplification of redundant role=navigation; correction of malformed tags; static bindModals focus element specification; static modal binding element filter addition; deletion of redundant/obsolete meta and rev.; setuptools inveniomange command; render_filter_form kwargs parsing fix; improvements of database exception handling; fix for long language list; sticky footer fix; template blocks addition; add pre-template-render signal; add inveniomanage database diff command; messages to flashed_messages macro rename; add footer and header base templates; flashed (alerts) messages macro; css and js Jinja blocks in base template; package order aware template loading; application factory cleanup; errorlib and logger consolidation; fix config autodiscovery order; initial port from pluginutils; blueprint static folder check addition *) batchuploader: import fix (#1779); template syntax fix *) bibcatalog: move to new code structure; system email unit tests fix *) bibcirculation: using jquery-ui; double imports removal; regression tests fix; after demosite populate receiver; fix CrcBORROWER.ccid in model; fix for missing app ctx in handler *) bibconvert: BFX engine removal from cli (#2124); lxml support for local document(); Exceptions management fixes; regression tests fix; manager port initial release *) bibdocfile: pdfjs previewer fix; undefined variable fix; fix for undefined docname in get_text; logging fix; javascript fixes (#1900); model and API expunge fix; wrong field name fix; hotfix plugins loading; port of plugins discovery; fix for --hide --with-version; fix typo; regression tests fix; add download progress callback; SQLAlchemy model fix; Bibdocmoreinfo model addition; SQLAlchemy model *) bibexport: app context fix *) bibingest: move module to legacy folder; new module to handle document ingestion *) bibmatch: regression tests fix *) bibupload: modification date fix; get_record dog-piling prevention; support for strings in utils; legacy import fix; fix sender msgpackable value; record signals addition; fix for inserting duplicate subfields; PEP8 fixes; regression tests fix *) bibuploadutils: initial release *) bower: typeahead version 0.10.1; upgrade ckeditor to version 4 *) bulletin: translation fix *) cache: use CFG_DATABASE_NAME as CACHE_PREFIX if not specified *) celery: default changed from Msgpack to cpickle; queue utilities addition; email address for errors; deprecated celeryd replacement; test case helper; signal handling fix; before first request processing fix; task registry addition; make Redis default broker; msgpack serialization usage; double app creation fix; eager task execution fix; fix email reporting; change configration behaviour; fix issue with undefined database; addition of Flask support; initial release (#1458) *) checker: model addition (#1889); move to new code structure; initial move to new code struture *) classifier: classifier tasks; registry definition fix; fix classifer registry name; error handling and PEP8; PEP8 and PEP257 fix; case insensitive taxonomy; dict output fix; processing and output decoupling; API string support; new API; regression tests fix *) cloudconnector: fix of cloud applications (#1920); jinja base templates renaming; onedrive replaces skydrive; OAuthClient usage for Dropbox; cloudconnector initial port; initial release *) collect: addition of sorting filter; addition of filter for Blueprints (#2353); bugfix to not symlink yourself done right; bugfix to not symlink yourself; symbolic link storage *) comments: assets 2.0; jinja base templates renaming; annotations integration; login required for vote and report; fix tranlatable strings and client host; collapse.js refactoring; tests import fix; reviews.html template; reviews_base.html template; template blocks addition; remove unused property; bind modal on record tab change; fix for JavaScript in record tab; Bootstrap3 fixes; stops toggle event propagation; order by creation date; regression tests fix; prepare attachement location fix; improved guest commenting/reviewing (#1539); code style improvements; guest commenting/reviewing not allowed (#1539); CmtSUBSCRIPTION model improvement; collapsable comment threads; multiple form submission fixes; page title and menu renaming *) communities: portal box template fix; delete modal dialog fix; deprecated WTForms validator removal (#2620); enabling search by id; featured community UI problems fixup; featured community addition; search fixes; ckeditor toolbar changes; hbpro format database fix; bibupload notimechange option removal; upload priority removal; assets 2.0; fix community model tests; jinja base templates renaming; bfe_primary_report_number replaced; documentation fix; pagination fix; ranking fix; curation button fixes; broken url fix; removal of hardcoded parameters; slicing removal from filter; admin views; default sort order config; ranker upgrade recipe; query improvements and PEP8 fixes; ranker periodic task; button fix; initial release *) config: pdfopt workaround; add site configuration loading; fix set/update of list and dict types *) connector: InvenioConnector URL validation; regression tests fix *) crossref: docs entry addition; tests addition; database search fix; initial release of Flask extension (#1906) *) dataciteutils: fix text encoding issue; fix for creator and date getter; metadata parser initial commit *) datastructures: MutableMapping register SmartDict; SmartDict.update() addition; SmartDict addition; lazy dictionaries addition *) dateutils: move of dateutil version detection; fix for wrong datetime import (#1435); new pretty_date() function *) dbdump: disable workers parameter; flaskshell import addition in dbdump.in *) dbquery: fix regression test cases; regression tests fix; regression tests fix; handle also CFG_DATABASE_TYPE; app logger addition *) demosite: PendingDeprecationWarning on populate (#2394); update demosite package for create/populate; fix default value of package argument; fix for packages default value; add packages repetable parameter; removal *) deposit: autocomplete deprecation fix; dynamic list macro addition; eonasdan-bootstrap-datetimepicker fix (#2689); workflow delete fix; validate on paste event; uploader allow filters; Bootstrap multiselect fix; separation of typeahead initialization (#2442); snapshot object fix; object creation fix; edit robustness; pid processor normalization enhancement; errorlist typo fix; jasmine tests adaptation to requirejs; checkbox support addition; InvalidDepositionType handling; js uploader component; field_display kwargs support; form button click fix; jquery datepicker leftovers; doi syntax validator improvements; datetime picker library modification; decorating inner function in task closure; fix dynamic field list addition (#1784 #2372); form.js field modified fix; exposure of handle_field_msg; plupload improvements; fix PLUpload in IE9 (#2299); usage of requirejs for typeahead; plupload error div selector addition; plupload filter option addition; s/deposition/deposit/ (#1915); fix errors in an inline form (#2141); fix for sort function for authors; assets build fix; run_base.html adapt to new JS; form.js refactor; initial plupload separation; plupload template change; autocomplete data attrs configuration; minor edit action bar fix; ColumnInput description addition (#1949); refactoring bug fixes; base template refactoring fix; refactoring fix; fix plupload config usage; fix init plupload arguments; independent jquery-ui modules; record merge fix; Flask-OAuthlib upgrade fix; base version of form.html template; translation fix; jquery-ui required for sortable; form page customization; wrapping DynamicFieldList into a class; fieldlist plugin separation; saner deposit/form.js; fix for translated labels; upload priority decrease; method name fix; minor text updates; string fix in templates (#1915); field messages fix; addition of holding pen; assets 2.0; for loop rewrite to $.each; ckeditor sanity check; PEP8 and PEP257 improvements; addition of dynamic KB autocomplete func; assets import clean-up (#1817); jinja base templates renaming; WTForms version to be <2.0; fix for flag checking; dead code removal; edit button now shown fix; workflows reinitialization fix; workflow integration changes; workflow integration update; fields deprecation; simple record tests; SIP upload check improvement; record merge customization; deposition type refactoring; simple record deposition; SHERPA/RoMEO removal; Flask-RESTful update; double action bar fix; fix fields argument on post processors; post processor api test; template fixes; widget templates and js fixes; adaptation to new typeahead; adaptation to new typeahead; fix file size length; jsonalchemy refactoring fix; default deposition fix; progress-bar and icon fix; fix for deposit types url converter; missing super() call in a template; fix pu-branch migration issues; plupload filetable fix; static file fixes; migrate workflows and fix test; improvements for new code structure; class-based design refactoring; refactoring of data processing and ui; poetry deposition addition; field grouping; record id field record loading; form status & ui actions on fields; dropbox WTField widget; uncook json functionality addition; photo deposition completion; file cooking configuration addition; new record id field; blueprint checks & customize template; webdeposit_utils testing based fixes; added regression tests; subtitle, file, comment fields cooking; checking existence of CKEditor in js; optional ckeditor & date format; collection and title addition; Python-2.6 compatibility fix; fix bibupload task submission call; configuration file and MARCXML export; user filesystem directory addition; autocompletion and validation utils; CKEditor & page form status checking; plupload widget enhancements; base field and datepicker fixes; DOI and generic field addition; field autocompletion enhancements; BibWorkflow integration & enhancements; `db.func.max` call fix; file renaming and cleaning; autocomplete replacement by typeahead; fix for article demo deposition; fix for plupload JS and CSS location; dynamic loading of deposition types; usage of invenio_pretty_date(); select deposition page addition; more depositions and various fixes; columns type change to db.JSON; workflow cleanup; fix links and type check addition; change of database column name; dynamic breadcrumbs additions; javascript check for required fields; sequential form rendering; new workflow class and functions; subfields support and submit widget addition; autocompletion and draft enhancement; model addition and plupload chunking; field widgets addition; initial release *) docextract: port of convert_journals cli; regression tests fix; invalid form values handling fix; model file move *) docs: jasmine ext inclusion; fix spelling in getting started with overlay (#2595); sphinx target not found for ExternalTool fix; jsonalchemy grammar and rewording; configuration theme cleanup; fix links in overlay.rst; missing mkdir command addition; license inclusion; jsonalchemy field definition documentation; missing subversion dependency; addition of bundles to base.rst; how to create translations section addition; overlay deployment using fabric; how to create an invenio overlay; almost gruntless world; uploader initial docs addition; installation on Centos; typos and fixes; fix installation; fixes to docs; typo fix in INSTALL.rst; admin guides port from webdoc; nit-picky documentation; css theme overrides; fix of sphinx warnings.; fix typo in INSTALL.rst; documentation for collect during INSTALL; INSTALL guide update; documentation refactoring; cleanup of git-workflow; installation on Ubuntu; Ubuntu 13.10 setup; how to develop modules addition; new documentation structure; git workflow additions and corrections; commit message format section correction; fix WebSupport builder; jinja base templates renaming; initial release with manage command *) documents: Flask-OAuthlib upgrade fix; files field rename (#1898); test improvements; checker of source and uri addition; fix engine configuration; test coverage improvements; acl extension usage; fix for model creation; update field and model definitions; set_content and resful API; initial commit *) editor: HstRECORD affected_field no default value; partial legacy port; PEP8/257 improvements in models; configuration fixes (#1965); fix BibEDITCACHE model (#1790); BibEDITCACHE model addition; fix model move; move from record_editor; regression tests fix (#1584); Bibrec model methods addition; SQLAlchemy model fix; invenio_2012_11_15_bibdocfile_model fix *) elasticsearch: fix for signal receivers arguments (#2594); initial commit *) email: celery error email fix; fix for undisclosed recipients test *) encoder: fix encoding of websubmit.js *) errorlib: regression tests fix *) exporter: move from export; SQLAlchemy model update *) fixtures: hotfix dataset loading; port to extension with signals usage *) flask: debug_toolbar error reporting fix; Flask-Login version 0.2.7 usage; Flask-Cache version upgrade to 0.11.1; Flask-Cache import fix; Flask-Cache dynamic jinja cache; Flask-SSLify fix url standard ports rewrite; Flask-SSLify fix url non-standard port rewrite; Flask-SSLify addition of extra criteria callback; Flask-SSLify original file addition; WTForms config option CFG_SITE_SECRET_KEY usage; WTForms Flask extension inclusion; integration of legacy unit tests; configurable DB engine testing; jquery-ui includes fix; compatibility with new request object fix; shell utils for CLI scripts; initial comit with SQLAlchemy and Bootstrap *) formatter: recid int cast fix; support for dates < 1900 (#2673); removal of old admin interface (#2668); filtered hidden fields in recjson; mimetype fix; addition of format.mime_type column; display record with no record id (#2278); display records with no recid (#2272); fix mediaelement video view (#1999); include 'cc' in RSS 's (#2013); format record extra context fix; master merge fix; fix Bibfmt model import (#1781); kind column in bibfmt; hotfix format.code column size; 'recjson' format addition (#1908); xm hidden tags fix; format record extra context fix; better logging in xslt engine (#2049); test engine xslt format addition (#2048); fix RSS generation; DOI inclusion in BibTeX export; format record with no record ID; bfe_authors pep8 fix (#1962); bfe_authors fix; fix for unit tests after merge; fix configuration and i18n messages; file migration fix; bfe_authority_institut{ion->e} rename fix; fix secure link to record editor (#1821); int or long type cast of recid; type check of recid in BibFormatObject; bfe_primary_report_number replaced; unicode decoding error fix; improved error reporting; TypeError fix in record template; error pass-through; BibTex Jinja2 format template; text MARC output format addition; format template path fix; test overlay package; output format TEST1.bfo move; fix order of output formats; output format/elements loading fix; template loading order fix; encoding error fix; fix /rss encoding issue; fix missing output format; fix for elements encoding issues; licenses for templates files; templates modularisation; fix unicode decoding error in rss; fix for xml record formatting; fix for Babel string formatting; print statement removal; fix usage of registry by output formats; fix broken bfe_comments; fix for XSS vulnerability in `ln`; get fulltext snippets docstring fix; port back-to-search links; template ctx function prefix changed; fix for bfe_fultext function; support for fulltext snippet display (#1588); regression tests fix (#1585 #1508); fix of page context test; converted detailed record templates; template context function module fix; fix preview record using tpl; regression tests fix; removal of bfe_* function calls (#911); second version of HB templates (#911); initial port of HB format templates (#911); add filtering of indicators in MARCXML (#1497); bibfield record addition to tpl ctx; bfe elements loader inside engine; fix app contenxt issue in bibreformat; load bfe_elements in Jinja env; bft2tpl match template option addition; skipping BFOs with only XSL stylesheets (#1470); manager initial release; format records templates *) global: git ignore `.noseids` and `compile`; removal of legacy scripts; WTForms 2 compatibility fixes; importing modules from packages fix; defaultdict fix (#2030); translations fixes (#1911); merge fixes; legacy directory pre- creation (#1789); merge fixes; autotools and config clean up; translation move and po clean up; `watchdog` package addition (#2778); removal of depreated WTForms extenstion; removal of depreated WTForms extenstion (#2620); Invenio 1.9999.5.dev; invenio.utils.connector deprecation; silent version from git; removal of ZENODO mentions (#2371); enhance unit test for LazyDict; Invenio 1.9999.4.dev; Invenio 1.9999.3.dev; iter_suites overlay usage; refactoring fix; 4suite removal; Invenio 1.9999.2.dev; datatables* into bundles; MathJax into bundles; jquery.tablesorter into bundles; jquery- multifile into bundles; bootstrap-tagsinput into bundles; bootstrap-switch removal; jquery.ajaxPager out of bower; jquery.bookmark into bundles; jquery-migrate into bundles; prism into bundles; (jquery-)flot into bundles; uploadify into bundles; swfobject into bundles; jquery.treeview into bundles; json2 and jquery.hotkeys into bundles; jquery-ui paths; dynamic version fix (#2001); Invenio 1.9999.1.dev; dynamic PEP440 version number; missing testsuites; old bundle names; white spaces; assets 2.0; fix for testing 401 after redirection (#1883); jellyfish to replace editdist; fix legacy static files includes (#1777); kwalitee fixes in invenio.testsuite; `has_key` to `in` operator fix; html entities import fixes; six string_types usage; urlparse import fix; import fixes; os mask fixes; print function usage; exception syntax fix for Python 3; six iteritems usage; file header post code fix; fix nose skip decorator usage; fix for imports and translatable strings; grunt fixes for jquery-ui; grunt improvemnts and bootstrap upgrade; fixes for javascript and translations; fix for translatable strings; version file addition; base templates creation; translation fixes; fixes for JavaScript loading; fix handling of debug and simplify toolbar; Flask-Collect and URL map integration; syntax fixes; Boostrap 3 style for search results page; fixes for imports and trailing spaces; migration to Twitter Bootstrap 3; porting modules and extra requirements; add Grunt and Bower; various fixes; various fixes and improvements; modules move to new code structure; move to new code structure; move to new code structure; move to new code structure; move to new code structure; new code structure; file renaming; document CFG_DEVEL_TOOLS for Apache; fix remote debugger to work with Flask; new configuration variable CFG_DEVEL_TOOLS (#1325); fixes for encoding and tests; shell support for Flask *) groups: jasmine tests adaptation to requirejs; user selection by autocomplete (#1788); port missing functionality (#1788); account settings fixes; jinja base templates renaming; blueprint name renaming *) grunt: dev typeahead installation; jquery.form from bower; jquery.hotkeys specify version (#1778); fix for prism CSS path; jquery-migrate via bower; ColVis filename update; jquery plugins additions; fix Prism configuration; typeahead.js fix; fix for jquery.min.map cleanup *) hashutils: usage update in modules; initial release *) i18n: PO file update for the release of v2.0.0; Babel usage; JS helper; fixes for string messages *) importutils: ignore exceptions option addition; `lazy_import` function addition; initial release *) indexer: SQL query fix (#2750); add admin interface; auto- generation of models; PEP8/257 improvements in models; fix tokenizer loading; changes in data model; fix for regression tests; model *19* addition; move new files to legacy and fix imports *) installation: fix MANIFEST.in and wrong filename; package.json addition; updated requirements; redis server name; updated Ubuntu packages; Pillow minimum version; httpretty<=0.8.0 version limit; python-twitter>=2.0 (#2015); WTForms, dateutil and redis update; Flask-Admin>=1.0.9 (#1797); disable SSLv3 in Apache config (#2515); WTForms, Flask-WTF>=0.10.2; workflow>=1.2.0 (#1797); improvement of OS X installation; addition of OS X installation guide (#2392); SQLAlchemy, SQLAlchemy-Utils upgrade (#1776); setuptools>=2.2; fix for typos in install doc; relax requirement on reportlab; postgresql driver dependency; testing of development requirements (#2044); dependency links renovation (#1797); Flask- OAuthlib 0.6 upgrade; relax version number constraints; Flask- Admin>1.0.8,<1.1; Flask-Admin>=1.0.8,<1.1 (#1797); lxml instead of pyRXP; lxml update to 3.3; setuptools-bower removal; automatic catalog compilation; jellyfish update to 0.3.1; jellyfish to 0.3; setuptools-bower to development; setuptools-bower 0.2.0; fix for setuptools-bower source; Flask-Assets 0.10; bootstrap 3.2.0; Flask-SSO version upgrade; requirements update; Flask-Collect from PyPI; Flask-Registry version update; cerberus package upgrade; mercurial addition; pip1.6 ready setup.py; update wtforms-alchemy to 0.12.6; fix six version (#1800); requirement addition for six library; Flask-Assets 0.9 and Jinja2 2.7.2; virtualenv based path for static; Pillow instead of PIL; Flask-Admin requirements version fix; PyLD to 0.5.0; MANIFEST template fix; quick installation guide; Flask-Collect to use 0.2.3-dev; Python 2.6 on Travis CI; Flask-DebugToolbar Python 3 friendly; Pillow img requirement; fix for inversed user/database name; Bower font- awesome; setuptools version; typeahead Grunt fix; pytz; upgrade of fixture version 1.5; MAINFEST template fix; Python 2.6 compatibility fix; Apache configuration updates; setuptools alias commands; bootstrap-switch inclusion; MANIFEST.in file recursive- include fix; version modification to 1.9999; fix apache configuration; version compare >= by default; parse version from dependency links; egg info adddition for dependency links; Grunt for js and css libraries; import and sql fixes; fix missing configuration loading; initial Procfile; location of plupload; Jinja2 version 2.7.1; Flask-Gravatar version 0.4.0; SQLAlchemy version 0.8.2; duplicate mechanize removal (#1520); Flask-Script version 0.6.2; empty Travis configuration; Hogan prerequisite documentation; Tokeninput download from GitHub; release control fix; hogan.js template engine addition; mysql default date value fix; jinja2utils and requirements upgrade (#1476); `apache create- config` renaming; test presence of flask_admin; secret key creation fix; replace libxslt with lxml; demosite fixtures addition; fix for BibWorkflow table dropping (#1283); use concrete SHA1 for workflow; fix for removed invenio.conf values; database populate command addition; renaming of demo site fixtures; inveniocfg create/drop db depretated (#1283); fix database commands create & drop (#1283); initial apache manager release; updated missing requirements; fix Apple touch icons in Apache conf; switch to ASCII-only secret key; improvements to secret key creation; empty CFG_SITE_SECRET_KEY checker; info about creation of secret key; fix for WebDeposit tables in tabdrop; info about install-plupload-plugin; document Bootstrap and Tokeninput; typo fix in instructions; search cache enabled by default; fix for Werkzeug version check; Werkzeug version check in configure; pip general requirement files; JQuery Tokeinput; merge problem with Makefiles fix; new pip requirements files *) intbitset: usage of separate package *) inveniocfg: stop logging capture fix; fix for `--reset-recjson- cache`; --create-secret-key compatibility fix; clarification of warning phrases; fix of typo and disabling action chain; fix --drop-tables command (#1283); --create-secret-key new line addition (#1406); --create-secret-key addition; SQLAlchemy upgrader model *) inveniomanage: unit test fix; cache, bibrecord and runserver cmds (#1549); demosite create/populate/drop (#1534); command signal addition; config manager initial release; `apache version` command addition; version command addition; upgrade manager improvements (#1332); initial release (#1332) *) jasmine: tests helpers; fix for ASSETS_DEBUG=False; registry fix; adaptation to requirejs; fixture loading; proper dir walking; initial release *) jinja2utils: add date formatting template filter; functions and filter to context; new filters addition; named bundles generation; application template filters; LangExtension initial commit *) jsonalchemy: @hidden decorator addition (#2197); function for safe conversion to int; print statements removal; fix problem with reserved names (#2593); validation fixes; fix SmartJson dumps documentation; cache engine search fix; dumps with specified keywords; support for storage create/drop; documentation and PEP8 fixes; documentation release; dirty fix for default values; unit tests for module import fix; hotfix for optional fields; fix for `__additional_info__` access; preserving original tags inside JSON (#1722); move to `isinstace(foo, Mapping)`; default values for subfields; cache engine addition; fixes for versionable extension; deprecation warning fix; create_record error catching; Versionable test addition; fix usage of `storage_engine`; `StorageEngine` metaclass addition; failing test fix; memory engine search method addition; enhance extension parser behavior; extension model fix; model resolver fix; bug fixes; validator test fix; `uuid` and `objectid` validator fix; UUID validation fix; import and PEP8 fixes; fix `six.iteritems` typo; update readers and SmartJson; add `jsonext` as common namespace; update `parser.py` for pyparsing 2; in memory engine addition; versionable extension; JSON-LD tweaks; refactoring fixes; enhance default value search; JSON-LD addition; exception messages improvements; storage engine configuration fix; bug fixes and tests improvements; bug fixes; allow `extend` on parser extension; initial commit; initial release *) knowledge: slugify and flag to access rest api (#2686); fix update form in admin interface; implement new admin gui; endpoint move (#2686); REST API addition (#2570); mapping limit support; fix get_kbr_values returned value; fix get_kbs_info query result; fix backward incompatible change in API (#2555); API migration to SQLAlchemy; PEP8 and PEP257 improvements (#2184); searchtype parameter addition; internationalisation fix; translation string fix; regression tests fix; lxml port get_kbt_items_for_bibedit *) legacy: uft8 error fix websearch admin interface; fix import overriding local variable (#2665); webuser usage cleanup; get_most_popular_field_values fix; fix import in bibstat cli (#2293); bibrank unicode errors fix; fix websearch unformatted vars stacktrace; new webinterfaces registry (#2239); fix webbasket template translation string (#2362); fix for run_sql import in bibrecord (#2295); bibrecord scripts move; indexer recjson value fix (#2285); webhelp docs move (#2244); fix field xml output generation (#2233); authorlist imports fix (#2210 #2223); authorlist move to new code structure (#2210 #2007 #2223); docextract imports fix (#2210 #2223); docextract move to new code structure (#2210 #2223); dbquery pep8/257 fixes; dbdump refactoring fix (#2088); support for postresql engine in dbquery (#2020); legacy admin interfaces addition; bibindex admin interface fix (#2190); websearch circular import removal; oaiharvest admin import fix (#2194 #2188); fix form file attribute (#1900); fix broken import to create_record; fix javascript on /record/edit (#2143 #2178); xmlmarclint import fix; webinterface_handler_local removal; fix missing imports; fix for static file handler; fix for imports and module renaming (#1790); fix import problems; merge fix for bibclassify; webdoc legacy test fix; import fix; dbdump fix; translation string fix; tasklets configuration and loading; hotfix POST request handling; hotfix in https url site replace; removal of legacy OpenAIRE code; fix issue with undefined variables; fixes mod published support; migrate OAIHarvest CLI; webinterface import fix; initial port; Bootstraped table of content (#1374); hotfix schTASK user length; Option to return all task options *) linkbacks: fix tab visibility if excluded (#1707); fix external url creation (#1707); fix external url creation (#1707); jinja base templates renaming; fix regression test cases (#1589); fix missing model in makefile; initial Flask port *) logging: formatter fix; documentation update; sentry sanitizer for access tokens (#2130); celery logging to sentry fix; warnings logging; error reporting refactoring; fix issue with db.func.now; fix config lookup *) login: fix last_login column update (#2669); fix PEP8/257 errors; handle 401 error; fix redirection to secure page (#2052); redirect to secure url before login; fix uid comparison with `None` value; change of unauthorized message for guest *) mailutils: fix for double mail sending issue (#1598); fix unicode error in templates (#1598); config email backend preference; Flask-Email initial port (#1531) *) merger: syntax fix *) messages: initial upgrade; require.js messages; assets 2.0; div in messages menu fix; jinja base templates renaming; fix for translatable strings; icon library change; fix message menu display; fix unit test imports; fix for failing regression test; fix regression tests; fix reply on message; fix menu and broken links (#1487); fix javascript block; fix link on /yourmessages; blueprint name renaming; empty set usage after IN operator fix; user settings quickfix; restricted collection hiding; initial porting to Flask *) mimetypeutils: initial release *) mixer: blend improvement; fix requirements; dump database fixes; new extension that uses Mixer library *) multimedia: Image API documentation update; IIIF Image API addition; initial release of Image API *) oaiharvester: static files move; move tests to new code strutures; configurable namespace addition; post process check record; record extraction improvement; OAI post process update; authorlist extraction task; record splitting improvement; refextract task fix; sample approval based workflow; decorating inner function in tasks; small task update; workflows integration; initial upgrade; add save to model; update model with defaults; PEP8 and errors category; reliability improvement and docs; model update and fix for cli; session_manager usage; logging creation fix; workflows in admin; fix admin pages (#2188); move to workflows; Integrate new workflows; fix for app context; move from oai_harvest *) oairepository: schema/namespace fix (#2676); date overflow fix; fix date handling; include restricted records; automatically compute model field; regression tests fix *) oauth2server: upgrade recipe fix; redis configuration fix; fix support of SQLAlchemy-Utils (#2629); url decoding fix; upgrade recipe fix; form field order; access and refresh tokens encrytion (#2127); confidential and public clients (#2113); addition of translatable strings; fix token expiration and refresh (#2112); redirect uri validation fix (#2175); missing access token in test case (#2166); Flask-OAuthlib<0.7 version limit (#2158); resource authorization tests; authorization flow bug fixes; scopes registry (#1773); jinja base templates renaming; settings test; fix for default redirect uri; initial release *) oauthclient: fix missing config in ORCID test; orcid login fix + tests; revert setting extra_data; upgrade recipe fix; fix forgoten replacement; code style improvements; cross-site request forgery fix; PEP8/257 fixes; orcid full name fetch; local account discovery improvement (#2532); permanent login support; access token encryption (#2127); authorize url fix (#2487); missing attribute addition (#2483); save orcid in extra data; nullable extra_data column; documentation update; github/orcid sign-in/up support; error handling fix; signup support; helper test case; error handling and tests; unauthorized disconnect fix; get token fix; initial release *) orcid: fix search url *) pages: info log removal; initial tests; global url_map modification fix; jinja base templates renaming; model improvements; 404 exception handling; new route registration; initial release *) paginationutils: initial release *) pdfchecker: model addtion (#1790) *) persistentid: fix ISSN validation issue; add function to create url *) pidstore: initial upgrade; template filters addition; new pid provider for record identifiers; provider status sync and celery tasks; model relationship; admin interface; name conflict fix; import fix; refactoring initial release *) pidutils: add pid normalize feature; initial release *) plotextractor: regression tests fix; XML direct output option *) pluginutils: optional disabling register_exception *) previewer: zip previewer enhancements (#2748); markdown rendering; zip preview and styling fixes; initial pdf.js integration; Mozilla pdf.js viewer component; fix d3js ui block on huge table loading; addition of support for Documents; d3js csv previewer; fix folders identifiers in zip archive; initial release of ZIP file plugin (#2321); fix base template for bundles support; PDFtk previewer; template fixes; refactoring; post-move fixes *) previews: move to previewer; initial release *) principal: action class and registry addition; raise 401 on authorization failure *) ranker: rank method function fix; fix missing column in RnkCITATIONDICT; RnkCITATIONDATAData fixture removal (#1905); models addition; PEP8 and PEP257 improvements; RnkCITATIONDICT model update (#1905); usage of configuration registry for tags; partial regression tests fix; fix regression test; relocation of CollectionRnkMETHOD model; fix RnkCITATIONDATAERR model base class; SQLAlchemy model for rnkCITATIONDATAERR; SQLAlchemy models addition; Flask shell support fix; fix legacy import; fix config loading *) records: Python 2.6 compatibility fix; fix back to search links; auto-generation of models; PEP8/257 improvements in models; display tabs (#1646); better PID list; record_json table; fix bibrec.additional_info upgrade script (#2132); get_unique_record_json 'status' key move; return cleaned record json; fix for document default name generator; refactoring fix; fix for MarcXML indentation on creation; move new recordext function to records; assets 2.0; fix `get_blob` to ease transition.; fix typo in the API; atlantis.cfg merge problem fix; no JSON version cached check fix; fix for `test_error_catching` (#1814); move legacy methods to the Record object; API for database querying with DOI; jinja base templates renaming; acl hook added to record documents.; API test case addition; PEP8 errors fix; bibupload timestamp fix (#1431); aggregation field definitions fix; update to new JSONAlchemy; fix usage of calculated fields; fix for loading iso datetime; enhance the API to create empty records; `reset_cache` added to `api.get_record`; fix for export handler; base variant of base.html; add configurable breadcrumb title; fix mini reviews display; fix for api Record.create(...); tab switching events addition; move to legacy.bibfield; laziest reader loading; manager port initial release; fix unit tests imports *) redirector: registry addition and refactoring; API migration to SQLAlchemy *) refextract: fix for command line app ctx *) registry: keygetter value fix; fix package exclude for sub registry; missing function addition; dict-style auto discover registry; imports from `flask_registry`; move to separate package Flask-Registry; initial release *) requirements: pymongo addition; qrcode removal; better separation; dictdiffer egg fix; broken pypi links fix; version bumps *) restful: addition of validate method; pagination fixes (#2102 #1724 #2087); API keys fix; decorators test cases; API testcase fixes; fix for testing accesstoken; `require_header` value checker addition; apikey and oauth2 authentication support; API unit test base class; fix extension initialization; fix registry loading; initial release *) scheduler: tasklet registry addition; post-process data exchange; fix usage of CFG_RUNDIR config variable; fix monitor; fixes for bibtasklet cli; max length of `SchTASK.progress` fix *) script: refactoring of manager loading; registry usage for managers; Python 3 compatibility fixes *) scripts: demosite populate options *) search: migration of JournalHintService; facet upgrade recipe improvement; removal of depreated WTForms extenstion (#2620); UserQuery relationship addition; PEP8/257 improvements in models; fix for search typeahead configuration; requirejs facets fix; facets unicode error fix; unnecessary `decode('utf-8')` removal; fix /collection/ url routing; fix query string in add-to-search (#2251 #2252); fix filtered output format (#2292); quick fix for queries with leading space; quickfix pagination troubles with facets (#2306 #2308); tuning of hierarchical facet; fix for return key handling in search form (#2253 #2282); facets relation definition move; flask-admin module to configure facets; configuration of facets per collection; loading of Bloodhound using requirejs; fix for stucked focus on the search field; fix for improper suggestions merging; fix of undefined query_range in typeahead; requirejs for search typeahead; typeahead js code style improvements; user-preffered output format (#1587); fix advanced add to search form (#1811); fix jrec handling (#1756); ids removal from format fixtures; affix width fix; require.js refactoring; cleancss and requirejs filters; typeahead.css into base bundle; jshint fixes; init.js; factor out javascript from macro; mustache templates via hgn; inline script as a separate file; update of fixtures and models; assets 2.0; layout fix; fix admin interface of collection tree (#1860); fix null reclist parsing; incorrect test removal after merge; PEP8 and PEP257 fixes; templates hierarchy; fix browse pagination links (#1824); jinja base templates renaming; search form as files; collection template loading; fix copyright year; collection template loading; facet registry; fix decoding Unicode is not supported.; fix initial request missing stylsheets.; fix for dissapearing search field text; typeahead 0.10 adaptation; browse button fix; code clean-up and documentation; browse.html inheritance change; typeahead 0.10 search bar adaptation; templates inheritance schema change; label `for` attribute addition; fix css file path; default `of` for search with `cc`; fix default of for collections; template macros import fix; fix for collection preservation on search; change union_update to union; missing space between attributes of input; fix translatable string; fix for the alignment of the search navbar; searchbar separation; fix translatable strings in templates; template blocks addition; clearer collection name in search pages; templates javascript fix; fix restricted collection search; webcoll post-process data; webcoll fix; fix for citesummary link template; fix for not visible variable "new_args"; Snippet display after clicking on facets; fix for non-ASCII fulltext terms; import fixes in regression test suite; back-to-search links improvements; jinja template for back-to-search links; fix for cache timestamp file handling; search admin regression tests fix; summarizer regression tests fix; disable webcoll part two; fix demo site fixtures; regression tests fix; facet discovery improvement; fix for facet builder return type; regression tests fix; template `url_for` fixes; websearch user settings form fix; record usage tab fix; CollectionExample demosite fixture fix; collection view improvement; browse functionality initial port; blueprints refactoring and cleanup; video collection fixture fix; fix facet unicode value problem; support for 'x*' search output format (#1508); fix model __init__ functions; fix Externalcollection engine property; collection template addition; url `of` argument quickfix (#1473); faceted results order fix (#1352); temporary move js script on top; fix import to use full module path; record tabs improvements; fix javascript block in /record pages; force integer type of recid; discussions compatibility fix (#1422); fix division by zero in Pagination; fix for translatable strings; fix access to restricted records (#1340); add download graph to record blueprint; fix encoding and caching; fix title encoding problem; fix default sort order; SQLAlchemy model fix; loadable facets; configurable hotkeys in user settings; hotkey navigation for search results; fix for cache prefix import; facet debugging improvement; conditional results cache fix; search results cache stats removal; search results cache relocation; search cache timeout addition; seach query cacher; improved caching; checkbox label class fix; Collectionname __init__ removal; search query string trimming; export functionality initial commit; fix for empty collection on frontpage; facet and format option improvements; Bootstrap 2.2.1 fix; new dropdown menu with search examples; format options, ui improvements; search example dropdown menu; disabled focus in search field; search query at first line in typeahead; tab caching problem fix; encoding/decoding of facet URL fragment; hierarchical facets support; hierarchical facets; Python 2.6 dict problem fix; search within and examples; facets and user settings widgets; intersect_results_with_collrecs port; query logging fix; improved search interface.; collection facets and modal window; search in collection by its name; import CFG_WEBSEARCH_WILDCARD_LIMIT fix; admin interface improvement; Portalbox drag and drop organizing; Code quality improval; Collection name translations editing; Collection managment with relation type; Drag collections as subtree into leafs; Drag and drop Collection managment; missing colon addition in search box; pybabel fixes *) sequencegenerator: migration of texkey generator; integer size fix; SQLAlchemy model *) session: hotfix for schema and locale check; removal of unnecessary Set-Cookie (#2291); docs, PEP8 and PEP257 improvements; fix commit after automatic table creation (#2265); fix duplicate session commit (#2264); simple cache fix; backend data loading fix; fixes login when no cache backend exists; fix for translatable strings; fix link for reset password; fix legacy webuser import; fix for validation of changed email (#1601); invalid accounts login fix; change password initial port; lost password blueprint addition; email form validation addition; fix for login referer redirection (#1598); fix for settings data saving; fix user settings edit url; regression tests fix; Flask-Login session fix; settings widget closing fix; customizable settings widgets; login redirection fix; partial regression tests fix; user agent in current user fix; reporting errors in *_user_settings.py (#1570); fix empty password registrations.; user registration initial Flask port; external authentication port to Flask (#1338); fix guest user uid in current_user; authentication with email address (#1338); fix typo in setUid (#1424); cache decorator removal on logout (#1339); request info preferable in user info; current user uri value fix; fix missing default precached value; fix default user settings; logger removal; Flask HTTPS redirection fix; fix for HTTPS redirection; current app logger removal; user info cache split; split user info and session; webuser flask bug fixing WIP; update of settings in session fix; user info cleanup; user settings page addition; new login form style; user logout fix; getter of session from request fix; get_session() calls removal; HTTPS quick fix; logout, reload user and redirect fix *) sherpa_romeo: error handling improvement; caching and API enhancement *) signalutils: new record creation and modification signals; initial release *) sorter: multiple tag sorting fixes; Admin Guide improvements; SQLAlchemy models *) sqlalchemy: default mysql parameters for db.Table (#2491); fix mysql index creation; fix mysql primary key creation; custom EncryptedType removal (#2343); fix PostgreSQL test connection; fix default integer constructor (#1776); addition of Encrypted type; postgresql types support; name addition for Enum types; addition of Encrypted column type (#2204); add session_manager; addition of Encrypted column type (#2173); revert to library default enum; JSON MySQL storage type fix; change JSON type to native one; default charset utf8mb4 for mysql; UUID type addition; autodiscover modules on demand; fix for MySQL gone exception handling (#1518); fix MarshalBinary impl type; use_unicode=False by default; MarshalBinary and import fixes; support for version 0.8.0 (#1409); model synchronization with tabcreate (#1226); create index statement; model updates; autocommit event listener; autocommit listener; initialization quick fix REMOVE LATER; initial commit; change field to mutable type; fix missing database host port *) sso: fix group/groups key inconsistency; print statements removal; fix external groups concatenation; user group names loading; initial release *) tags: initial upgrade; restful test fix; Flask-OAuthlib upgrade fix; REST API addition; fix for editor in search results (#1792); incompatible dict usage fix; initial release *) template: fix for unicode url handling; Flask 1.0 compatibility fix (#2216); deprecated blueprint_is_module function; @template_args decorator addition (#2009); documentation and formatting; add page_base.html; tests for template order loading *) testsuite: fix InvenioConnector test; testsuite iteration fix; registry addition for testsuites (#2211); new demo record with CSV data files (#1927 #2208); python 2.6 fix; fix for build and run regression tests; fix for secure base url in test client; fix for client login https scheme usage; fix for testing page with 401 error; regression tests fix; logount when not logged in fix; login and logout in InvenioTestCase; fix pyparsing import troubles; fix global imports (#1491); fix for passing engine to app factory (#1491); fix for importing CFG_DATABASE values; support for Flask shell *) travis: minimal requirements testing (#2044); bower configuration files; less log; deactivate requirejs and al. after build; config simplification; removal of Python 2.6; less verbose output; collection of the static files; travis_retry statement for grunt; npm, bower and grunt during setup; extra requirements to tests; CFG_TMPDIR set to /tmp; enabling apache version module; enabling excluded packages for test; pip --upgrade removal; initial configuration release; initial release of configuration *) upgrader: bibsched precheck removal; has_table function; documentation fix; fix for docstring style; sphinx friendly documentation; package detection fix; fix auto-generation of upgrades; add auto-generation of upgrades; change to module-aware engine; initial port using autodiscovery; partial fix for SQLAlchemy init *) uploader: refactoring of workflow definition; initial manage command implementation (#1772); support for relative document paths (#1191); files to link addition (#1772); typos fixes; removal of empty workflows; field definitions enhancement; document model addition; documents module connector addition; workflows pre and post tasks hooks; saving master format to bibfmt table; initial release of insert mode *) urlutils: fix for wrong URL arguments encoding *) utils: hepdataharvest cli port; `which` from distuitls; orcid validation enhancement; datacite ssl protocol fix; datastructures docs and PEP257 improvements; function remove_underscore_keys removal; removal of duplicate `SmartDict` definition (#2031); no CRSF protection in testing; arXiv identifier normalization (#1958 #1961); arXiv persistent identifier fix; fix date tests; import json from arxiv api; etree to dict translation reorganization; slugify text function addition; addition of call checks for vcs commands; Git & SVN Harvester; persistentid 100% test coverage; LazyDict delitem support; test `date.convert_datestruct_to_dategui`; test `convert_datetext_to_datestruct`; fix for `dateutils.datetime.combine` method; formatting + PEP8 + PEP257; datacite tester fix; `TextField` replacement by `StringField`; fix for create tag from utf8; PEP8 fixes; json import fix; HTML ID washer; which function addition; unicode fix; fix for utf8 issue in create_html_link; xmlDict tag attribute fix; xmlDict initial release *) webbasket: fix configuration variables in template; adjustments for Twitter Bootstrap usage *) webhooks: minor documentation update; Flask-OAuthlib upgrade fix; signature validation; initial release *) webjournal: regression tests fix *) webstyle: fix translatable string; debug-toolbar display condition fix; fix blueprint loading refactoring issue; blueprint loading using importutils; regression tests fix; debug toolbar only for super admin; handling file POST or PUT fix; legacy form files fix; fix external url creation; authorized decorator fix; error code 401 on authorization failure; CFG_WEBSITE_TEMPLATE_SKIN support; legacy request form multivalue fix; fix remote debugger import; harmonize blueprint method signature; fix of youraccount index menu link; fix legacy publisher form dictionary (#1474); menu rendering improvement; fix Jinja2 context; Flask request class customization; fix bug in pageheader template; database creation in app factory; fix for remote host getter; autodiscovery of models on app creation; non strict handling of last slash in url; configurable placeholder for js assets (#1398); fix for block javascript usage; refactoring of legacy template rendering; support for absolute url in url_for; fix legacy form values unicode issue; fix content type change detection; fix response headers (#1351); fix response status code (#1328); fix redirects from mp_legacy_publisher (#1335); catch HTTPS redirects in debug toolbar (#1325); check empty variable CFG_SITE_SECRET_KEY; new config option CFG_SITE_SECRET_KEY; document Flask request processing; fix POST requests to WSGI legacy app; option to disable loading of blueprints; fix content type for legacy publisher; werkzeug debugger for devel sites; use utf8 for jinja2 str to unicode; fix stdout redirect for mod_wsgi and shell; fix POST request for mp_legacy_publisher; fix missing files in bundles; new invenio_pretty_date() jinja2 filter; legacy publisher support for Flask; SimulatedModPythonRequest port; favicon addition; Goto model addition; fix encoding problem in admin interfaces; fix for multiple typos and code cleaning; addition devel site level for debuging; debug toolbar extension; fix for Bootstrap script link; new 401 app error handler; register template context processor; unused date message removal; cache (Redis) server down exception fix; new bootstrap select library; Invenio logo in navigation bar; error message fix; lowlevel mimetype fix; invenio_format_date jinja filter fix; Flask redirection handling fix; usage of Flask app in wsgi handler; cleanup function registration; unified Flask app with legacy fallback; Bootstrap JS file location change; new Flask-Gravatar icon support; 1st commit of Flask-Invenio bridge *) websubmit: fix for fileupload interface; partial regression tests fix; regression tests fix; removal of foreign key in SbmFIELDDESC *) workflows: import order fix; harvesting description fix; no error if nothing harvested; Holding Pen sorting fix; exception handling improvement; conversion to SmartJSON using models; Holding Pen improvement; new name for ObjectVersion; object state names match docs; aborting and skipping; Holding Pen previous/next robustness; always save current object; Holding Pen details fix; task_counter value check; documentation enrichment; actions JS loading; Holding Pen file serving fix; snapshot generation fix; template naming fix; template fixes; template renaming; default definition improvements; runtime based start_async_workflow; deprecated admin area removal; AMD compatible and flightJS; cache prefix for Holding Pen; fix bootstrap-tagsinput bundling (#2423); harvesting description template fix; upgrade compatibility fix; log output in Holding Pen; admin dropdown menu fix (#2384); indentation from 4 to 2 spaces; error signal catching; styles fixes; attached files improvement; blocks in styles templates; harvesting description arXiv link; fix for indentation in templates; translation support; detailed workflow task list; new stage for error; new stage for halted; Holding Pen authorization; formatted data from model; signals addition; PEP8 fixes; update of MARC views; object navigation; classifier task result addition; field name change; sample tasks refactoring; cleaning of engine calls; new session_manager usage; task result upgrade; task result templates; dismissable approval alert; wrong default position (#2177 #2186); passed data check; initial upgrade; cascading deletion; add_task_results hotfix; Holding Pen cache update; documentation addition; removal of vendor files; engine API additions; remove view test; Holding Pen search cache; blueprint code update; record loading optimization; actions as templates; Holding Pen tags and pagination; Holding Pen table speed upgrade; detailed display update; Holding Pen change; log model relationship addition; installation of *dataTables* from npm; assets 2.0; fix for registry keygetter; hotfix unittest; missing RQ worker code; Holding Pen UI/UX update; better widget and filtering; INSPIRE task removal; test cleanup; merging correction; Holding Pen object history; Holding Pen stabilization; Holding Pen display changes; str() addition to calls; Holding Pen improvement; overhaul update; global improvement; more unit tests; abstraction layer for workers; registry fix for keygetter method; fix harvesting workflow with bibsched; INSPIRE specific removal; PEP257 improvements; user session in Holding Pen; widget to action name change; definition fetching improvements; initial user UI integration; fix for Celery worker tasks loading; bibholding pen improvement; holding pen fixes; registry refactoring; object workflow API addition; test registry cleanup; continue execution fix; unit tests stabilization; auto discover registry; registry recreation in test setUp; add more docs; revamp testsuite; update API and errors; update generic_harvesting_workflow; add more unit tests; sphinx documentation and more; revamp session handling; Python 2.6 compatibility; change default XSLT; move persistent_ids to extra_data; PEP8 fixes; Python 3 compatibility; clean up templates; update HTML attribute; add missing license; further update JavaScript; fix get_current_task API; refactor JavaScript files; update static files on admin pages; Holding Pen update; update workflow tasks; new API in engine and model; refactor holding pen widgets; add OAIHarvest integration; update calls to OaiHarvest model; engine branch halt testcase; js fixes and interface improvements; improvements and fixes; fix celery workflows worker; update holding pen details page; fix broken import; holding pen changes and additions; move to new structure and fixes; added mini approval widget; fix redis host configuration issue; fix typo in worker import path; model relationship addition; PEP8 compatibility fixes; workflows loading/naming; updates of Holding Pen; API updates; `Workflow` API prototype; API changes; add Holding Pen interface; workflow model changes; specific worker loading; workflow query API initial release; general cleanup; unit tests instead of flask tests; warnings addition in admin pages; additional fixes; PEP8 corrections; task example and celery intergration; exception deactivation during loading; fix for restarting workflow; initial release Invenio v1.2.0 -- released 2015-03-03 ------------------------------------- *) BatchUploader: apache error codes; insert or replace mode; authorize via CIDR; add holdingpen directory; several improvements; bibtask logs via email (#1255); multiple improvements (#603); fix for permission checking (#1747 #1748) *) BibAuthorID: user prefs and session fix; inactivation of test_save_matrix() (#1678); merge and manage fixes; caches badly stored in user settings; fix 'create new person' ticketing issue; leftover print statement; disables debug output; Claiming page is now reloaded after commit.; hepnames match; add_cname_to_hepname_record(); hotfix in name comparison; remove changes tempfile.rootdir; graceful external system query; adds webuser user merge utility; fix arXiv redirect link; improvements and bug fixes; improvements and bug fixes; DOIs from ORCID check; WaP daemon and BAI interface fixes; fix in templates handlers; hotfixes for authorpages and webauthorprofile daemon; Help pages and messages; a new hope; use defaultdict from containerutils *) BibAuthority: new names for authority collections; source file mode fix; separate Authorities collection (#1605); initial release (#1602); fix for unit test suite *) BibCatalog: ticket_id type is now string (#2096); better error reporting; requestor on ticket submit; ticket_submit() docstring update (#2094); improve RT search error handling; return empty list if no search params; RT discovery; email content cleanup; bug fix; pylint fixes; refactoring; adds bibcatalog bin to ignored files; add daemon task (#1528); default email backend (#872); new email ticketing backend (#872) *) BibCheck: $$9 bibcheck to DOIs (#1955); improvements in DOI checks (#1955); allow filtering by subfield contents (#2474); last_run correct update; properly cumulates records; compatiblity with dateutil 2.2; improve url plugin and tasklet; improve url plugin; adds --config option; improve exception handling crossref queries; add retry download to crossrefutils; improve doi plugin; avoid checking dummy records; add option to consider deleted records; new BibCheck module *) BibCirculation: library creation and other fixes (#2550 #2551 #2552 #2562 #2373); fix for CERN returnees; fix for typo; missing web tests; minor spelling error fix; fix for mandatory library type (#1519); email ID changes and test fixes (#1479); admin guide cleanup; patron-driven acquisition and more (#1280); personid CERN attribute; ILL improvement; CERN LDAP improvements (#1186); set colour of some buttons; fix for ILL title and request type; fix for library ID variable name; various updates; fixed notes link; code cleaning; better ill/purchase search; auto-fill for purchase request; remove reference to apache_user; optimize CERN LDAP query; temporary barcode for new copies; extended ILL to manage acquisition; make statuses customizable; lots of small fixes; 'cancelled' status for ILL request; sorting last issued loans; edit ill request details; loan and renew process enhancement; added budget_code to crcILLREQUEST; edit library type; arrival date and library merge; extended item statuses; improved book_title_from_MARC; fixed multi-barcode loan; fixed user interface loan renewal; pylint and kwalitee cleaning; small fixes on printing & intrface; daemon for overdue letters; email alerts for new requests; delete a copy of a book; added CERN id in borrower profile (#207); use new URL handler for admin pages; avoid multiple loan creation (#305) *) BibClassify: ontology cache check improvement (#2672); always use invenio code; raises an exception if rdflib is missing; unit tests temp dir fix; remove ability to run as standalone (#1459) *) BibConvert: lxml support for local document() (#2497) *) BibDocFile: pickle support fix (#2549); decompose_file_url() and subformat (#2556 #2557); bibdocfile.BibDoc memory fix (#2082 #2136); change name failure raises exception (#2071); more robust decompose_bibdocfile_url() (#1957); escape file URLs in /files tab (#2067); fix type of bibrec-bibdoc connection (#1759); get_icon() for smallest size icon (#1350 #1743); undefined variable fix; register downloads with recid (#1831 #1832); new web tests; bibdocmoreinfo query typo fix (#1706); textification with OCR fix (#1676); get_file() exact_docformat support; display counts in tabs; fix "delete" CLI option; no access to filesystem; preferred extension (#1619); load plugins at global level; migration script fix; fixes wrong variable name; error reporting changes; CERN AFS awareness (#1388); retry mkstemp in case of failure; CERN AFS awareness (#1388); fix for bibdoc unattached to record (#1551); improve BibDoc display in Files tab; raise exception in _build_file_list(); additional mimetypes support; fix version in register_download() (#1532); fix for BibDocFile instantiation (#1317); implements format renaming (#1318); allow doctype renaming (#980); revert md5 property patch (#1249); new document data model; fix for display of hidden icons; change_name missing parameter fix (#1818) *) BibEdit: only notifications on error; kwalitee improvements; add email notification on submit; user name in BibSched column; wrongly displayed HP changeset bug; autocompletion of fields from KBs (#1258 #73); author names into history revisions; duplicate code removal; new RT ticket through UI dialog; int object is not iterable fix; InvalidCache exception on clone; modal submission preview window; check for record in BibSched queue fix; debugging all user actions; holding pen fix; prevent deletion of managed DOIs fields (#1445); fix revert when no 005 in history; Holding Pen fix; add AJAX profiling option; adds affiliation guessing; bibHOLDINGPEN from TEXT to longblob; bibupload xml file path conflicts; support for simple ticketing; small merging fix; BibEdit web test improvements.; fix textmarc2xmlmarc unit test; record from history instead of bibfmt; BibCatalog and other improvements; fixes errors in case of deleted records; several bugfixes; moves files cache to the database; open DOI source in new window; fixes pdf detection; several improvements; HoldingPen multiple improvements; update CFG_BIBEDIT_EXTEND_RECORD_WITH_COLLECTION_TEMPLATE; fixes date parsing problem; hide authors when they exceed max number; multiple fixes and improvements (#1190); send latest timestamp when reverting; update admin help page and shortcuts; remove extra holding pen call; show/hide specific parts of the record; merge record with template; custom errors for AJAX requests; better holdingpen integration (#87); tab switch between fields; minor fixes; fix perform_doi_search function; refactoring and fixes; use perform_request_search on search; add version to bibedit css name; sort HoldingPen changes alphabetically; add direct link to dx.doi.org; save changes periodically; avoid sync request to see if record has pdf; change cache folder; amend textmarc to xmlmarc unit test (#1269); import CrossRef data; improvements and fixes (#761 #1032); css changes; allow opening deleted records (#573); delete cache if record not modified; add extraction of references from URL; several fixes; introduce textmarc editor; cnum generation on conference records; multiple improvements (#696); revert to master version (#792 #63 #118 #125); fix return binding on Jeditable cells; fix jEditable callback when pressing return; fix input default value; disable preview button when reverting record; fix jEditable callback when pressing return; hide delete record button by default; add field in specific position (#583); fix apply all HP changes (#125); clean JavaScript code (#63); extract css into a separate file (#118); upgrade to jQuery 1.5 *) BibEditMulti: only notifications on error; add email notification on submit; adds support for hidden fields (#707); allow non- delayed processing and priority change; several improvements; display all MARC fields (#1489); fix for multilanguage interface (#1331); multiple improvements and fixes (#1146 #1147 #1148 #1130 #1149 #1156 #1158) *) BibEncode: support for FFmpeg >= v0.9; updated for latest BibDocFile APIs; fix uuid Python 2.4 compatibility (#1478) *) BibExport: update Google Scholar exporter; hidden files and recrawling *) BibField: new CFG_BIBFORMAT_HIDDEN_RECJSON_FIELDS (#2197 #2396); better create_record error catching (#2510); fix copyright field names (#1933); backported improvements from pu (#1687); no caching of calculated fields; change recid field type to integer (#1633); improvements backported from next; fix number_of_copies field (#1625); new upgrade recipe to remove json cache; new field filtering for `get_record`; elimination of None values in recjson; `schema` in `split_blob`; `is_empty` update; fix for '__eq__' to better compare recs; fix for `is_type_isbn`; continuable vs fatal errors; bibdoc integration; new decorator @only_if_master_format; better handling of calculated fields; fix for misbehavior when parsing rules; new producer section added to config; clean up of atlantis.cfg demo file (#1557); volume subfield addition; creation date addition and keyword fix; new fft field in `atlantis.cfg`; Python-2.4 compatibility fix (#1533); legacy_export_as_marc escaping fix (#1509); new calculated fields; virtual field aliases amended (#1530); new @persistent_identifier "decorator" (#1500); bug fixes when using decorators (#1502); fix for lxml compatibility; Python-2.4 compliance; initial release (#1300) *) BibFormat: new BFO for authority records (#1699 #1749); links to public resources of authors (#1700 #1749); better display of authority records (#1749 #1699); ORCID display for authors; removal of obsolete BFX engine (#2563 #2124); recjson update using bibreformat (#1708 #2220); PEP8 fixes in bibreformat (#2220); add sponsor information to copyright (#1975); larger column `format.code` (#2072); advertise /doi URL in DC output; improve Dublin Core output (#320 #1213); configurable /record; new bfe_oai_identifier element; new bfe_date parsing/formatting options; plot file identification improvements (#1514); fix for eval_format_element return type; file rights fix; new bfe_arxiv_link; affiliation improvements; left over print in format_record; reworks exceptions handling; only save default site lang on the fly; look for missing caches by default; fix snippet generation; needs_2nd_pass in bibreformat; duplicate code in bibreformat.py; improves and updates bfe_plots; fixes tests; fix external function for libxslt; improve Google Scholar support (#1513); Displays the DOI in the EndNote; dublin core export now includes DOI; to fixup to removes old php format; small fixes; several fixes; fix in date comparison; second formatting pass (#1464); lazy missing formats updates; empty record check; progress display improvement; initial example of Twitter Card support; fix for snippet generation; author links for mobile app; initial release of mobile app formats; new Solr fulltext snippet facility (#1301); QR-code format element (#1441); add DataCite XSL stylesheet; remove 0248_a field from title; fixes last run date for HDREF (#1236) *) BibIndex: ambiguous SQL query fix for MariaDB-5.5 (#2759); tag.recjsonvalue NOT NULL (#1947 #2259); fix new-old record incremental indexing (#2693); clean up after authority regression test (#2448); author ID performance improvements (#1952); upgrade recipe for `tag.recjson_value`; recjson fields in admin interface; indexing non-MARC standards; abstraction layer for terms retrieval; WordTable API changes; move helper functions to utils file; changes in WordTable argument list; PEP8 compliance fixes; fix for virtual index filtering; new DOI index (#1655); virtual index queue dupe optimisation; new 'all-virtual' CLI option; minor engine refactoring; documentation for virtual indexes; new pattern for tokenizer inheritance (#1704); new abstract class for indexes; separate class for virtual index (#1661); common words in virtual index (#1653); fixes admin regression tests; BibIndexDefaultTokenizer upgrade; bad word check optimized; consistency check optimizations (#1436); ingestion health and "unneeded" indexes (#1632); index type in admin interface; virtual global index (#1574); indexing only affected indexes (#1573); clean warning messages in test suite (#1615); filetype and itemcount tokenizer fix (#1609); new index 'filetype' (#473); new index 'itemcount'; tokenisation of authority records; fix syntax error in bibstat; support for CJK languages (#285); pluginutils for tokenizers (#852); centralisation of tokenizers (#852); new regression tests; centralisation of LaTeX/HTML treatment (#852); centralisation of stopword treatment (#852); centralisation of synonym treatment (#852); fix for external fulltext indexing; rework of error handling (#1075); move of text extraction to BibDocFile; new exacttitle index (#1397); new filename index (#1717) *) BibKnowledge: searchtype parameter in KB export (#2570 #2581); fix get_kbt_items_for_bibedit (#1879 #1895); lxml port get_kbt_items_for_bibedit *) BibMatch: allow tests to login over plain http; Fix validator problem; use other author comparison function; more print statements; improves get_longest_words; improve fuzzy queries; validation fixes *) BibMerge: adds CFG_SITE_RECORD as script data (#2580 #2237); `onclickSubmitButton` missing comma fix (#2230); prevent loss of DOI when merging records (#1446); delete cache of master record before submission; change order of updates; add subfield sorting and interface fixes; several fixes; add 981__a field to master record; delete duplicate record first (#1645) *) BibRank: fix path for download history graph (#2554 #2374); fix of similar-to-recid result order (#1745 #2236); missing selfcites for collaborations; record ID citations catchup; citation blobs in Redis (#1689); adds a new option to disable bibsort (#1617); minor refactoring in word ranking; handle missing files when removing graphs; more leanient date handling in citation graphs; more leanient date handling in citation graphs; remove outdated import in citations tab; exception when gnuplot is not available; unicode recid in citation indexer; Added ISBN, recid and HBL identifiers; fix bad variable name; fixes for sorting; missing drop table rnkSELFCITEDICT; new way to generate graphs (#1244); consider only one year in citation graph; fixes for cited by sort; function to get citations of a single record; fixes sorting; optimized cited by sorting; filter citations on collections (#1504); logging of citation changes (#1426); store selfcites in a table (#1417); citesummary optimizations (#1481 #1217); handle records with mulitple journals (#1394); optimized cited by sorting; no citerank error when no citations (#1624); better Solr regression tests; faster Solr indexer; new multi-tag Solr indexer; index latest records first in Solr; increase rnkDOWNLOADS.file_format size; self cites upgrade recipe improvement (#1482); detect external word similarity ranker; storing citation indexer warnings in DB (#1210); optimisations in citation algorithm (#1073); selfcites fixes; fix for citation indexer checks; citation indexer sanity checks and alerts (#1091); reference linking improvements (#950); citation indexer date check change (#946); fix for missing Python files in Makefile; two algorithms for self-citations (#945); change import to defining module; better exception handling in Solr indexer (#1199); better default mode in Solr indexer (#1192); more invalid Solr character replacements; new Solr and Xapian ranking bridge (#1084 #1168) *) BibRecord: namespaces ignored for lxml (#2604); search & compare subfields; new API records_identical(); new API identical_records(); record_get_field_values with filtering (#1550); filter field instances (#1550) *) BibReformat: chunking of updated records query *) BibSched: email-logs-on-error parameter (#2205); check schSTATUS when detecting status; pep8 for bibtask.py; pep8 for bibched.py; subdirs for bibsched logs; fixes a bug with --profile cli option; fix priority for the same sequence id; increase max log file size to 5Mb; display mode for non-periodical tasks; adds more task changing commands; get_modified_records_since() (#1538); monitor auto mode selection bug; invalid sql in monitor history tab; setting to continue on errors; many improvements (#1177 #991); error when switching to manual; refactoring and improvements (#1274 #1275 #1449); enhanced write_message(); motd update check; problem parsing task CLI options (#1330); interface responsiveness improvements (#1303); priority in automatic mode; CLI-started tasks host field; kwalitee fixes; shell output leakage upon task kill (#1343); single error reporting (#1342); scheduling algorithm improvements (#1281); fixes task chain-sleeping (#1304); fixes monotasks for multi-node (#1304); fixes for multi-node setup (#925); new --email-logs-to bibtask CLI (#1252); subprocess instead of deprecated popen2; new web UI for BibSched live view *) BibSort: improved washers (#2283 #1754); add check before deleting; fix typos and CLI arguments *) BibUpload: creation_date based on incoming 005 (#2693 #1604 #2684); faster recjson deletion after updates (#1708); no reload() in regression tests (#1702); --append only new fields (#1440); removed print statement; do not always process MoreInfo; CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG fix; new CFG_BIBUPLOAD_MATCH_DELETED_RECORDS (#1438); affected fields and insert mode; 8564_s support; less useless computation; less verbose; messages cleaning; ingore 856 tag order in conflict (#1606); smarter conflict report; smarter conflict detection; no tickets in pretend mode; ticket creation fix; improves utf-8 checks; task error messages (#1449); utf-8 encoding; encoding checks; regression tests cleanup; matching existing records (#1438); pretend holding pen fix (#1618); fill affected_fields in hstRECORD (#1572); fix for inserts with 005 (#1595); conflicting revision ticket queue; smart record uploader fixes; BibCatalog connection; sensible history and other goodies (#498 #1250 #871); bibrec timestamp bug (#1431); smart record uploader (#816 #864 #897); check DOI uniqueness (#1160) *) DocExtract: new CMS PAS report numbers; additional report numbers; extract page-end from references; removes stdout ouput from tests; rework of regression tests; fixes regression tests; improves bibrecord; increases compiled regexp cache size; preload docextract author regexp; using -i instead of -r; preload kbs on wsgi load; re-enable caching of kbs; outdated import in webtool; reduce verbose in tests; fixes DESY-THIS rn recognition; 5 digits arxiv numbers detection; do not create old tickets; lower bibupload priority; webinterface text box fix; optional unidecode dependency; help messages & compatibility warnings (#1220); several improvements; move mislabelled regression tests (#1309); journal rawref search fix (#1306); nose-friendly refextract tests; fix reference extractor unit tests; refextract unit tests file name fix; preparing for merging into master; multiple fixes (#966 #958); new docextract and refextract modules (#944 #1014) *) HepData: updates to formats; fixes unit tests; clean hepdata.js inclusion; new HepData module; adds hepdataharvest bin to ignored files *) HepNames: update form migrated to INSPIRE *) I18N: PO file update for the release of v1.2.0; more complete POTFILES.in; fix wrong msgids in Persian translation; updates to the Persian translation; POTFILES.in update; initial Persian (Farsi) translation; infrastructure for Persian (Farsi); several fixes in Spanish translation; Catalan and Spanish updates to Search Guide; Catalan and Spanish updates to Search Tips *) InvenioConnector: allow logins over plain http; fix for CDS authentication *) OAIHarvest: fix identifier parsing (#2408); conversion argument name upgrade (#1753); error reporting fix (#1804 #1812); respect hidden fields; do not launch BibIndex when done; bibindex priority to 4; only update lastrun on successful harvest; small daemon enhancement; fixes missing import; priority of single harvest tasks; improves arXiv identifier harvesting; several improvements (#547); sample OAI-ArXiv conversion update (#678); CERN-specific "arXiv" doctype; consider source_id for selective post-processing; configurable selective post-processing (#1477) *) OAIRepository: lower priority to updating uploads (#2525); fix for hidden OAI tags (#2642); more lenient time limit for tests; do not report cache not found errors; allows running slow machine; oai_get_recid() for merged/deleted records (#1429); marcxml created in shared directory; forcing clients to re-harvest (#1218) *) PdfChecker: clean up after regression tests (#2448); log full list of updated recids; skip records without unique ids in 037 tags; new module for arxiv pdf checker *) RefExtract: avoid double encoding (#2602); refactored book handling; improved book search; addresses warning in tests; removes leftover print; only accpet digits as numeration; allows more lines between title and numeration; changes condition for ticket creation; improves docextract, refextract *) SolrUtils: fix of similar-to-recid result order (#1745 #2236); better exception handling in indexers; faster snippet factility; support entire full-text indexing; cleaner schema.xml; support high count of logical clauses *) WebAccess: automatically fetch SSO nicknames (#2583); CERN- specific authorization message; fixes user details page links; CFG_ACCESS_CONTROL_LEVEL_SITE=1 support (#1501); remove Facebook testing credentials; update check for "external" account at CERN; ORCID support (#1124); OpenID and OAuth authentication (#1124); new CERN auth method support *) WebAlert: update tests for newly introduced records *) WebApiKey: unit Vs. regression tests *) WebAuthorList: fix import from recid; fix import from record id; ignore empty affiliations when exporting; add new author list manager tool *) WebAuthorProfile: reenable profile pages; disable if not available; compatiblity with atlantis; fixes unit tests; recompute link as a post action; new regression test suite *) WebBasket: 'move item' improvements; new 'move item' functionality (#1547); Create Basket link in the main display (#1333); correct referer when adding to basket (#1194); fix copying external items *) WebComment: fix for get_first_comments_or_remarks (#2522 #2523); more prominent subscription link (#2434); deleted record message; "Your Comments" page; link to "Your Comments" after posting; "Your Comments" page (#974) *) WebJournal: Indico seminars widget improvement (#1980 #1981); fix image dimension retrieval; sample Twitter Card markup; new image template; better exception handling when caching; structured cache (#1544); CERN-specific fix; fix for what's new widget test case *) WebLinkback: safer notification email; clean regression test suite (#1285); fix for importing CFG_DATABASE values; pending linkback notification emails (#1247); minor improvement; better documentation; module optional (#1245); better global /linkbacks page; better /linkbacks tab display; better URL title display; fix for DB name in regression tests; fix for user_info passing; unit test module rename; auto-increment regression test fix (#1136); initial release (#627 #857 #1136); fix truncated FSF address in docstring *) WebMessage: English corrections in output messages (#1849) *) WebSearch: optional refersto/citedby record limit (#2711); removal of hard-coded Holdings tab (#2592 #2664); new test case for pattern-limit queries (#1750 #1751); search results pattern limit fix (#1750 #1751); proper re-raise in RSS handling (#2084 #2598); fix for the number of printed records (#2512); inverted collection scores (#2058); stemming and '*' (#2468); smarter journal hint (#2352); new Journal Hint Service (#2352); kwalitee fix (#2352); richer `/record` and `/search` API docs (#2303); fix for record numbering in pagination (#1762 #1763); new Add-to-Search Interface (#622 #271 #1738); CERN-specific video latest additions (#2068); CERN-specific lecture latest additions (#2068); improve detection of record owners (#2068); better retrieval of record tabs (#2068); fix IndexError in is_hosted_collection (#1764); CERN-specific hack for latest additions (#1976); CERN-specific collection sorting (#2017); fix for 'rhs is of unknown type' (#1819); resolve (internal) DOIs (#1322); anyfield in CFG_WEBSEARCH_SYNONYM_KBRS (#1493); faster collection children cache (#1739); initial support for recjson output; fix detailed record page tab tracebacks; update collection page markup; fix search URL in timeout message; CERN-specific collection sorting; better sort order in citation tabs (#1307); timestamp detection fix for empty sites; sorting fixes (#1674); reverse order and scores; rg parameter with of='id'; reworks async downloader; wgsi.errors in fake request; handle case in /collections/ urls; fixes regression tests; stdout.flush conflicting with mod_wsgi; changes citation tab count; search API changes for record sorting (#1657); fix sorting options (#801); spires date parsing errors fixes; no 'back to search' on empty session; display deleted records in citation log; make query parser use Invenio datetime; fix mixed parameter for re.sub(); bibfmt on innodb; outdated import in citations tab; takes into account new record in tests; fix HepData templates; change CFG_SITE_URL to CFG_BASE_URL; fixes search bug with --empty hitset; add cataloguer: search unit; SPIRES date format 11/93; change CFG_SITE_URL to CFG_BASE_URL; testing fixing subject lookup; add record edit link in brief; pep8 fixes; INSPIRE vol to use volume field; INSPIRE texkey in 035__%; handles selfcites searches; spires syntax and quotes; INSPIRE fulltext warning update; find doi in search engine (#1051); CV output formats (#314); correct record sums from hosted colls (#1651); search services (#1278); custom i18n collection boxes (#1286); item count regression test activation; fix for hidden-field admin access test; support for intbitset output format (#1460); fix missing cc info in req object; CERN-specific hack update; display number of hits in mobile output; Greek translation of Search Guide; fix Python 2.4 syntax error; most popular field values optimisation (#1096); fix Search Guide reformatting; fix Search Guide mismatch tags; Search Guide reformat and pretty-print; fix browsing deleted/restricted records (#1292); webcoll performance improvements; permitted restricted colls for guests; empty unit test suite for summarizer; removal of excess summariser tests; revamping of citesummary pages (#134); summarizer unit test update; fix searching with limits; enforcement of record view restrictions; translation-friendly overview box; restricted record search improvements; fix for regression test link targets; fix for double display of the footer; better restricted collection search (#1161); "p=el*;rm=citation" test inactivation (#1174); sorting test amendments; mixed ranking/sorting test amendments; wildcard limit parameter is 0 in p_r_s; bugfix for empty set sorting; refactored perform_request_search() (#542); add regression tests for "em" parameter; add "em" parameter; include 'cc' in RSS 's (#2013 #2014); fix for "--language" option (#1399 #2219) *) WebSession: no differentiation between guests (#2786 #2813); CSRF token in profiling settings (#1855); disable ORCID login (#1667); user preference to enable profiling; new Redis session storage backend (#1688); fixes session_cleanup; session_param_get() default value (#1294) *) WebStat: fix for custom query summary graph (#2553 #2375); default query in the Custom query summary (#2388); list link fix for system health UI page (#1713); ingestion health monitor fix (#1631); use Invenio instead of CDS in pages; new ingestion monitor (#936); no wildcard limit for custom summary data; add bibcirculation config variables *) WebStyle: richer documentation on record page tabs (#216); ping handler returning 200 status code (#2700); POST handling fix (#1951); req object with no headers; fix gotoadmin CLI parmeters parsing (#1427); move charset higher in the document; move of lang and dir attributes to html; fixes for /goto CERN-HR plugin example; silence client disconnected errors; blocking read in handle_file_post; add missing icons to Makefile; WebInterfaceDisabledPages(); quote canonical and alternate URLs (#1515); /info pages using webdoc infrastructure; more accurate "Restricted" flag display (#867); inactivate two regression tests (#1293); goto engine typo fix; canonical and alternate URLs (#1251 #368); new /goto URL handler (#1178); memory leak fix in session handling (#571) *) WebSubmit: Set_Embargo optional and functional (#2699); link to successfully created record (#1641); more robust JavaScript check (#1741); print white space instead of None (#1741); support for elements' custom_level (#1741); `test_revise_picture_admin` test fix (#2142); `deferRelatedFormatsCreation` param fix (#2142); Link_Records error message fix (#1734); fix access restrictions in /uploadfile (#1703 #2066); allow record owners to upload files; allow image conversion of .tiff (#1909); grant access to the superadmin as owner (#2065); doilookup function in webinterface (#2025); guests support improved in /direct URL (#1240); rotate created icons according to EXIF (#1516); web tests for DEMOART and DEMOPIC; DEMOART uses bibdocfile_managedocfile; skip pdf optimization if pdfopt is missing; fixes for openoffice handling; INSPIRE specific amendments; add traceback info on error; new Run_PlotExtractor function (#1506); source file mode fix; login offer to guests on action page; fix for icon creation for bibdocfiles; jquery-ui update for photo submission *) containerutils: new Python-2.4 defaultdict *) crossrefutils: new Fundref-based APIs *) dataciteutils: refactor DataCite API wrapper (#1457); DataCite DOI support and test cases *) dateutils: adds __add__ to our custom datetime; strptime for Python-2.4 compatibility; fix for strftime() function; consolidate localtime_to_utc; day ranges; fix for unit test suite; new get_time_estimator function *) dbdump: partial dumps; ignore with regexp (#579); dump on detached slave (#1282); fix compress mode; add option to ignore tables; add slave support; improve error handling *) dbquery: fix for importing CFG_DATABASE values; more reinstall- friendly dbquery *) demo site: fix double 245 MARC field *) docker: more complete configuration *) errorlib: Sentry logging improvements (#2535 #2546); tags context fix for sentry (#2623); fix Sentry context syntax issue (#1960 #2147); context support in sentry (#1960 #2147); support for Sentry logging (#1726); makes SMS messages shorter; time independent tests; hostname in error notifications (#1546); wrap warnings to invenio.err (#1616) *) filedownloadutils: add verbose to download_url(); utility for file retrieval (#1076) *) general: new CFG_SCOAP3_SITE flag; optional remote debugger; test fixes; Propagating exceptions in debug mode; unit-tests fixes *) git: ignore KDevelop4 project files *) global: PEP-8 style in block comments (#1904); test suite original modification date fix (#2737); removal of INSERT DELAYED SQL statements (#2268 #2269); removal of leftover files; InvenioTestCase in test suite (part 2); InvenioTestCase in test suite; cdsweb.cern.ch becomes cds.cern.ch *) htmlutils: render MathML by MathJax; improve js string escaping *) importutils: fix None values error; Makefile clean up; Python 2.4 support and test case; initial release *) installation: new release_1_2_0 upgrade recipe; 2015_03_03_tag_value upgrade recipe; 2013_09_16_aidPERSONIDDATA fix; 2014_08_12_format upgrade recipe fix; all upgrade recipes in tabcreate (#1753); richer uninstall-jquery-plugins (#2418); python-twitter requirement update (#2015); lxml recommended; location of demo_table_jui.css; location of jquery.omniwindow.js; location of jquery.blockUI.js; location of sly.min.js; location of parsley.js; Redis and Nydus pre-requisites; jinja2 prerequisite (#1677); move h5py to extra requirements; h5py requirement clean- up; update to MathJax-2.3; add h5py dependency; use custom faster jeditable; ColVis.js on invenio-software.org; table creation fix; Python-2.6 and pip requirements; fix for BibAuthority upgrade recipe; fix for rnkDOWNLOADS upgrade recipe; help for BibAuthorID email settings; support for Apache-2.4 (#1552); help for `--load- bibfield-conf` step; oaiREPOSITORY_last_updated upgrade; fix for table drops and upgrades; selfcites upgrade recipe add-on; more gentle idxINDEX.indexer recipe; maint-1.1-to-master upgrade recipe (#1198); fix for 2012_10_29 upgrade recipe; fix for duplicate /css alias *) intbitset: initialization from iterator (#1698); no crash when intbitset is on rhs (#1287); atomic installation; union() and isdisjoint() support; type checking for operators *) inveniocfg: adds option to failt tests on first error; restore wrapping showarning after running unit tests; do not capture warning in unit tests; workaround bibfmt corruption; fixes BibSched check in upgrader; new derived config CFG_BASE_URL *) inveniogc: guest users gc optimization (#428 #1950); clean up gc tasks (#1950); delete refextract logs after 7 days (from 28); BibEdit related improvements.; add new session deletion mode; delete BibEdit temporary files *) kwalitee: even stricter PEP-8 compliance *) mailutils: better email header type detection (#2713); support invalid senders (#2256 #2385); fix for send_email() error on DEV site (#1744); extend send_email with BCC option; send_email() with attachments (#1253) *) mathpreview: js-based math preview panel (#1221) *) oaiharvest: fixes harvest() web interface (#2524) *) plotextractor: recid parsing fix (#2566); sanity in plotextractor tests; do not add FFT if there is no location; remove dummy caption generation; fixes arg parsing and more; more shell argument escaping; process files of a record; fix CLI parameters parsing *) redisutils: initial release *) sequtils: increases size of seqSTORE.seq_value; no texkey if no year; increases size of seqSTORE.seq_value; fix texkey generation; add start_date parameter to CnumSeq; wait for BibUpload to finish; new seq generator for texkeys *) shellutils: Mac OS compatibility (#1184) *) solrutils: clean unit and regression test suite (#1284); add search and ranking tests; fix for ranking result display; better collection filter generator; removal of unused code; better invalid character handling (#1197); add documentation *) testutils: wait for element to be displayed/hidden; default to assertEqual in py26; add new relative url function; regression_tests fix; new JavaScript unit test framework *) textmarc2xmlmarc: remove content regexp check (#1267) *) textutils: wash_for_utf8() simplification (#1755); translate_to_ascii() unknown chars fix (#1754); show_diff() API clean-up (#1465); fix old import statement; sharp-s to ss; unidecode verision; add ALA-LC transliteration (#1092); create function to show diff view *) urlutils: new function get_relative_url(); use hashlib instead of md5 if possible *) xmlmarc2textmarc: order only by tags Invenio v1.1.5 -- released 2015-03-02 ------------------------------------- *) BibCirculation: get_book_cover quick fix (#2578 #2653); fix for wrong non-borrower message (#2597) *) OAIHarvest: remove_duplicates and regexp fixes (#2300 #2608) *) WebBasket: better formatting of deletion message (#2449) *) docker: initial release (#2736) *) docs: initial release of CONTRIBUTING guide (#2163) *) installation: MathJax distribution location update (#2732); explicit jQuery plugin versions (#11 #2655); disable SSLv2/SSLv3 in Apache config (#2515) Invenio v1.0.8 -- released 2015-03-02 ------------------------------------- *) docker: initial release (#2736) *) docs: initial release of CONTRIBUTING guide (#2163) *) installation: MathJax distribution location update (#2732); disable SSLv2/SSLv3 in Apache config (#2515) Invenio v1.1.4 -- released 2014-08-31 ------------------------------------- *) BibDocFile: FFT comment/description documentation (#635); duplicate docname fix (#1930); convert files and icons asynchronously (#1428) *) BibEncode: fix video-encoded files synchro to DB (#1647) *) BibRank: (Overflow|ZeroDivision)Error usability (#105 #2146) *) BibSched: authorization typo fix in BibTasklet (#1746); more customizable icon creation tasklet; icons creation tasklet *) BibSort: `last_updated` column name typo fix (#1408 #1742) *) OAIRepository: OAI-PMH handler URL documented (#1027 #2152) *) WebComment: attachments in multi-node setup *) WebJournal: update demo "Article Header" style *) WebSearch: disable meta tags for deleted records (#1680) *) WebSession: CSRF token in API key settings form (#1855); CSRF tokens in account settings forms (#1855); Python-2.4 combatibility issue fix *) WebSubmit: file stamper option to copy metadata (#1569); new Create_Modify_Interface parameters; value escaping for modifications (#1578); better value escaping (#1578); more customizable Link_Records function; no double-submit (#1020) *) installation: GnuPG key server location update; location of jquery.treeview *) jQuery: fix for DataTables dependency URL location (#2078) *) sequtils: more robust cnum generation (#2119) *) I18N: fix gender problem in a French translation (#2089) Invenio v1.0.7 -- released 2014-08-31 ------------------------------------- *) BibDocFile: FFT comment/description documentation (#635); duplicate docname fix (#1930) *) BibRank: (Overflow|ZeroDivision)Error usability (#105 #2146) *) WebSession: CSRF tokens in account settings forms (#1855) *) installation: GnuPG key server location update; location of jquery.treeview *) I18N: fix gender problem in a French translation (#2089) Invenio v1.1.3 -- released 2014-02-25 ------------------------------------- *) BatchUploader: rights to ::1 for robot upload; avoid tempfile.tempdir redefinition (#1594) *) BibCatalog: no newlines in subject for RT plugin *) BibDocFile: RHEL6 magic bindings support (#1466) *) BibFormat: fix for BibTeX regression tests; better BibTeX title and collaboration *) BibRank: temporary file storage in CFG_TMPDIR (#1594) *) BibSword: author MARC tag definition fix *) BibUpload: FFT replace warning in guide *) I18N: PO file update for the release of v1.1.3; PO file update for the release of v1.0.6; PO file update for the release of v0.99.9; collection demo names for new translations *) OAIHarvest: for for bad exception handling *) OAIRepository: optional support for --notimechange *) Travis CI: initial release of configuration *) WebSearch: nonexisting record API test case fix (#1692); correct record sums from hosted colls (#1651); space between records in MARC HTML; fix for BibTeX regression tests; field-filtered MARCXML API output (#1591); more complete API regression test suite; get_fieldvalues_alephseq_like() utils; asciification of `oe` grapheme (#1582); bug fix for SPIRES date math search *) WebSession: fix mail cookie expiration (#1596) *) WebSubmit: fix for typo in Shared_Functions; optional pdftk regression tests *) dbquery: closes redundant connection *) git: addition of compile to gitignore; new entry in gitignore *) global: language value always in link URLs *) installation: pip requirement version updates; pip requirements; no user prompt for warnings; empty Travis configuration; location of jquery-1.7.1.min.js; location of flot; information about unidecode; fix autotools rsync instructions *) intbitset: no crash when intbitset is on rhs (#1287) *) inveniocfg: fix for mod_headers *) kwalitee: list comprehensions instead of lambdas; compatibility with pylint 1.0.0 Invenio v1.0.6 -- released 2014-01-31 ------------------------------------- *) BatchUploader: avoid tempfile.tempdir redefinition (#1594) *) BibRank: temporary file storage in CFG_TMPDIR (#1594) *) BibUpload: FFT replace warning in guide *) dbquery: closes redundant connection *) global: language value always in link URLs *) installation: fix autotools rsync instructions; pip requirements; pip requirement version updates *) intbitset: no crash when intbitset is on rhs (#1287) *) WebSearch: asciification of `oe` grapheme (#1582); correct record sums from hosted colls (#1651); nonexisting record API test case fix (#1692); space between records in MARC HTML *) WebSession: fix mail cookie expiration (#1596) *) WebSubmit: fix for typo in Shared_Functions CDS Invenio v0.99.9 -- released 2014-01-31 ------------------------------------------ *) temporary file storage in CFG_TMPDIR (BibRank) Invenio v1.1.2 -- released 2013-08-19 ------------------------------------- *) BibAuthorID: fix in name comparisons (#1313 #1314); improvements and fixes; improvements, fixes and optimizations; UI and backend improvements *) BibCatalog: removal of print statement (#1337) *) BibClassify: escape keywords in tag cloud and MARCXML *) BibDocFile: better JS washing in web UI; display file upload progress (#1020 #1021); display "Restricted" label correctly (#1299); fix check-md5 with bibdocfsinfo cache (#1249); fix check-md5 with bibdocfsinfo cache (#1249); fix error in calling register_download (#1311); handling of exceptions in Md5Folder (#1060); revert md5 property patch (#1249); support new magic library (#1207) *) BibEncode: minor fix in process_batch_job() *) BibFormat: additional fulltext file display in HB (#1219); checks for bibformat bin; fix CLI call to old PHP-based formatter; fixes unit tests (#1320); fix for fulltext file format; fix snippets for phrase queries (#1201); format_element initialisation fix; passing of user_info for Excel format; replacement of CDS Invenio by Invenio; setUp/tearDown in unit tests (#1319); skip hidden icons in OpenGraph image tag *) BibIndex: better wording for stemming in admin UI; replacement of CDS Invenio by Invenio; synonym indexing speed up (#1484); use human friendly index name (#1329) *) BibKnowledge: /kb/export 500 error fix; optional memoisation of KBR lookups (#1484) *) BibMerge: delete cache file on submit *) BibSched: bibupload max_priority check; bugfix for high-priority monotasks; increases size of monitor columns; parse_runtime_limit() fix (#1432); parse_runtime_limit() tests fix (#1432) *) BibUpload: FMT regression test case fix (#1152); indicators in strong tags (#939) *) CKEditor: updated to version 3.6.6 *) dateutils: strftime improvement (#1065); strptime for Python-2.4 compatibility *) errorlib: hiding bibcatalog info in exception body *) global: test suite nosification *) htmlutils: fix single quote escaping; improve js string escaping; MathJax 2.1 (#1050) *) I18N: updates to Catalan and Spanish translations *) installation: fix collectiondetailedrecordpagetabs (#1496); fix for jQuery hotkeys add-on URL (#1507); fix for MathJax OS X install issue (#1455); support for Apache-2.4 (#1552) *) inveniocfg: tests runner file closure fix (#1327) *) InvenioConnector: fix for CDS authentication; mechanize dependency *) inveniogc: consider journal cache subdirs *) memoiseutils: initial release *) OAIHarvest: fix path for temporary authorlists; holding-pen UI bugfixes (#1401) *) OAIRepository: CFG_OAI_REPOSITORY_MARCXML_SIZE; no bibupload -n *) RefExtract: replacement of CDS Invenio by Invenio *) WebAccess: fix variable parsing in robot auth (#1456); IP-based rules and offline user fix (#1233); replacement of CDS Invenio by InveniO *) WebApiKey: renames unit tests to regression tests (#1324) *) WebAuthorProfile: fix XSS vulnerability *) WebComment: escape review "title"; escape review "title" *) WebSearch: 410 HTTP code for deleted records; advanced search notification if no hits; better cleaning of word patterns; fix infinite synonym lookup cases (#804); handles "find feb 12" (#948); nicer browsing of fuzzy indexes (#1348); respect default `rg` in Advanced Search; SPIRES date math search fixes (#431 #948); SPIRES invalid date search fix (#1467); tweaks SPIRES two-digit search; unit test disabling for CFG_CERN_SITE; unit test update (#1326) *) WebSession: fix for list of admin activities (#1444); login_method changes; unit vs regression test suite cleanup *) WebStat: use CFG_JOURNAL_TAG instead of 773/909C4 (#546) *) WebSubmit: new websubmitadmin CLI (#1334); replacement of CDS Invenio v1.0.5 -- released 2013-08-19 ------------------------------------- *) BibClassify: escape keywords in tag cloud and MARCXML *) BibDocFile: support new magic library *) BibFormat: additional fulltext file display in HB; fix CLI call to old PHP-based formatter; format_element initialisation fix *) BibIndex: better wording for stemming in admin UI *) BibKnowledge: /kb/export 500 error fix *) BibUpload: FMT regression test case fix; indicators in strong tags *) errorlib: hiding bibcatalog info in exception body *) global: test suite nosification *) installation: fix collectiondetailedrecordpagetabs; support for Apache-2.4 *) WebAccess: IP-based rules and offline user fix; replacement of CDS Invenio by InveniO *) WebComment: escape review "title" *) WebSearch: respect default `rg` in Advanced Search *) WebSession: fix for list of admin activities; login_method changes *) WebSubmit: new websubmitadmin CLI CDS Invenio v0.99.8 -- released 2013-08-19 ------------------------------------------ *) escape keywords in tag cloud and MARCXML (BibClassify) *) fix CLI call to old PHP-based formatter; fix format_element initialisation (BibFormat) *) better wording for stemming in admin UI (BibIndex) *) IP-based rules and offline user fix (WebAccess) *) escape review "title" (WebComment) *) fix collectiondetailedrecordpagetabs (installation) Invenio v1.1.1 -- released 2012-12-21 ------------------------------------- *) BatchUploader: error reporting improvements *) BibAuthorID: arXiv login upgrade; fix for small bug in claim interface *) BibConvert: fix bug with SPLITW function; target/source CLI flag description fix *) BibDocFile: better error report for unknown format; explicit redirection to secure URL; fix for file upload in submissions *) BibEdit: 'bibedit' CSS class addition to page body *) BibFormat: clean Default_HTML_meta template; fix for js_quicktags location; ISBN tag update for meta format; "ln" parameter in bfe_record_url output; meta header output fix; relator code filter in bfe_authors; fix for reformatting by record IDs *) errorlib: register_exception improvements *) global: login link using absolute URL redirection *) installation: aidUSERINPUTLOG consistency upgrade; bigger hstRECORD.marcxml size; fix for wrong name in tabcreate; inclusion of JS quicktags in tarball; mark upgrade recipes as applied; rephrase 1.1 upgrade recipe warning; safer upgrader bibsched status parse; strip spaces in CFG list values *) jQuery: tablesorter location standardisation *) mailutils: authentication and TLS support *) OAIRepository: Edit OAI Set page bug fix; fix for OAI set editing; print_record() fixes *) plotextractor: washing of captions and context *) pluginutils: fix for failing bibformat test case *) solrutils: addition of files into release tarball *) WebAccess: admin interface usability improvement; guest unit tests for firerole *) WebAlert: new regression tests for alerts *) WebComment: cleaner handling of non-reply comments *) WebJournal: better language handling in widgets; CERN-specific translation; explicit RSS icon dimensions; fix for CFG_TMPSHAREDDIR; fix for retrieval of deleted articles; search select form by name *) WebSearch: fix for webcoll grid layout markup; get_all_field_values() typo; next-hit/previous-hit numbering fix; respect output format content-type; washing of 'as' argument *) WebSession: fix for login-with-referer issue; fix for merge_usera_into_userb() *) WebStyle: dumb page loading fix Google Analytics documentation update; memory leak fix in session handling; new /ping handler; removal of excess language box call; req.is_https() fix; *) WebSubmit: display login link on /submit page; fix for Send_APP_Mail function; fix the approval URL for publiline *) WebUser: fix for referer URL protocol Invenio v1.0.4 -- released 2012-12-21 ------------------------------------- *) installation: inclusion of JS quicktags in tarball *) bibdocfile: better error report for unknown format *) WebAccess: admin interface usability improvement Invenio v1.0.3 -- released 2012-12-19 ------------------------------------- *) BatchUploader: error reporting improvements *) BibConvert: fix bug with SPLITW function; target/source CLI flag description fix *) BibEdit: 'bibedit' CSS class addition to page body *) BibFormat: fix for js_quicktags location *) jQuery: tablesorter location standardisation *) WebComment: cleaner handling of non-reply comments *) WebJournal: explicit RSS icon dimensions; fix for CFG_TMPSHAREDDIR; fix for retrieval of deleted articles *) WebSearch: external search pattern_list escape fix; respect output format content-type; washing of 'as' argument *) WebStyle: dumb page loading fix; Google Analytics documentation update; memory leak fix in session handling; new /ping handler; removal of excess language box call; req.is_https() fix *) WebSubmit: fix for Send_APP_Mail function *) WebUser: fix for referer URL protocol CDS Invenio v0.99.7 -- released 2012-12-18 ------------------------------------------ *) Google Analytics documentation update (WebStyle) *) target/source CLI flag description fix (BibConvert) Invenio v1.1.0 -- released 2012-10-21 ------------------------------------- *) BatchUploader: RESTful interface, runtime checks, TextMARC input, job priority selection *) BibAuthorID: new automatic author disambiguation and paper claiming facility *) BibCatalog: storage of ticket requestor, default RT user *) BibCirculation: security fixes *) BibClassify: UI improvements and refactoring *) BibConvert: new BibTeX-to-MARCXML conversion, new oaidmf2marcxml conversion, fixes for WORDS *) BibDocFile: new filesystem cache for faster statistics, caseless authorisation, disable HTTP range requests, improve file format policies, and more *) BibEdit: new options related to preview and printing, reference curation, autocompletion, record and field template manager, editing fields and subfields, per-collection authorisations, use of knowledge bases, and more *) BibEditMulti: new actions with conditions on fields, partial matching for subfields, faster preview generation, and more *) BibEncode: new audio and video media file processing tool, new Video demo collection *) BibFormat: new full-text snippet display facility, new configuration for I18N caching, updates to EndNote, Excel, Dublin Core and other formats, updates to formatting elements such as DOI, author, updates to podcast output, updates to XSLT processing, and more *) OAIHarvest: new configurable workflow with reference extraction, new author list extraction post process, upload priority, OpenAIRE compliance, better handling of timeouts, and more *) BibIndex: new full-text indexing via Solr, new support for author ID indexing, better author tokeniser *) BibKnowledge: dynamic knowledge bases for record editor, support for JSON format *) BibMatch: new matching of restricted collections *) BibMerge: subfield order in slave record, confirmation pop up, record selection bug fix *) BibRank: new index term count ranking method, new support for flot graphs, updates to citation graphs *) BibRecord: new possibility to use lxml parser, sanity checks *) BibSched: new motd-like facility for queue monitor, new continuable error status for tasks, new tasklet framework, new multi-node support, new monotask support, new support for task sequences, improvements to scheduling algorithm *) BibSort: new in-memory fast sorting tool using configurable buckets *) BibUpload: new automatic generation of MARC tag 005, new `--callback-url' CLI parameter, fixes for appending existing files, fixes for multiple 001 tags, and more *) WebAccess: new external person ID support, performance improvements, robot manager UI improvements, fixes for firerole handling, *) WebAlert: new alert description facility, fixes for restricted collections *) WebApiKey: new user-signed Web API key facility *) WebAuthorProfile: new author pages with dynamic box layout *) WebBasket: add to basket interface improvements, better XML export, fixes for external records and other improvements *) WebComment: new collapsible comment support, new permalink to comments, loss prevention of unsubmitted comments, tidying up HTML markup of comments, and more *) WebJournal: new Open Graph markup, more customisable newsletter, redirect to latest release of specific category, refresh chosen collections on release, remove unnecessary encoding/decoding, update weather widget for new APIs, and more *) WebSearch: new index-time and search-time synonym support, new Open Graph markup, new Google Scholar friendly metadata in page header, new limit option for wildcard queries, new support for access to merged records, new next/previous/back link support, new `authorcount' indexing and searching, new relative date search facility, clean OpenSearch support, improved speed, improvements to SPIRES query syntax support, improvements to self-cite math, primary collection guessing, other numerous fixes *) WebSession: new useful guest sessions, reintroduces configurable IP checking, enforcement of nickname refresh, several other fixes *) WebStat: new login statistics, new custom query summary, error analyser, custom event improvements *) WebStyle: new display restriction flag for restricted records, new initial right-to-left language support, authenticated user and HTTPS support, IP check for proxy configurations, layout updates and fixes for MSIE, and more *) WebSubmit: new initial support for converting to PDF/X, new embargo support, better LibreOffice compatibility, better async file upload, enhancements for Link_Records, support for hiding HIDDEN files in document manager, configurable initial value for counter, make use of BibSched task sequences, and more *) installation: updates to jQuery, CKEditor, unoconv, and other prerequisites *) dbdump: new compression support, reworked error handling *) dbquery: new possibility to query DB slave nodes, new dict-like output, fix for MySQL 5.5.3 and higher versions *) errorlib: stack analysis improvements, outline style improvements for invenio.err *) htmlutils: improvements to HTML markup removal, HTML tidying *) I18N: new Arabic and Lithuanian translations, updates to Catalan, Czech, French, German, Greek, Italian, Russian, Slovak, Spanish translations *) intbitset: new performance improvements, new get item support, new pickle support, several memory leak fixes *) inveniocfg: new automated Invenio Upgrader tool *) InvenioConnector: new search with retries, improved search parameters, improved local site check, use of Invenio user agent *) jsonutils: new JSON utility library *) mailutils: possibility to specify Reply-To header, fixes to multipart *) plotextractor: better TeX detection, better PDF harvesting from arXiv, configurable sleep timer *) pluginutils: new create_enhanced_plugin_builder API, external plugin loading *) RefExtract: new daemon operation mode, new DOI recognition, better author recognition, new author knowledge base *) remote debugger: new remote debuggng support *) sequtils: new sequence generator tool *) solrutils: new support for full-text query dispatching to Solr *) testutils: new Selenium web test framework *) textutils: updates to string-to-ascii functions, LaTeX symbols to Unicode *) urlutils: fix for redirect_to_url *) xmlmarclint: fix for error report formatting *) ... and other numerous smaller fixes and improvements Invenio v1.0.2 -- released 2012-10-19 ------------------------------------- *) BibConvert: fix for static files in admin guide *) BibEdit: regression test case fix *) BibFormat: fix call to bfe_primary_report_number; revert fix for format validation report *) BibHarvest: OAI harvesting via HTTP proxy *) BibRank: begin_date initialisation in del_recids(); INSERT DELAYED INTO rnkPAGEVIEWS; user-friendlier message for similar docs *) BibUpload: clarify correct/replace mode help *) WebJournal: catch ValueError when reading cache; use CFG_TMPSHAREDDIR in admin UI *) WebSearch: allow webcoll to query hidden tags; external collection search fix; external search XSS vulnerability fix; fix for parentheses inside quotes; get_collection_reclist() fix; more uses of `rg` configurable default; 'verbose' mode available to admins only; XSS and verbose improvements *) WebSession: fix possibly undefined variables; prevent nickname modification *) WebStyle: workaround IE bug with cache and HTTPS *) WebSubmit: configurable Document File Manager; fix JS check for mandatory fields; unoconv calling fix *) bibdocfile: guess_format_from_url() improvement; guess_format_from_url() improvements; INSERT DELAYED INTO rnkDOWNLOADS *) global: removal of psyco *) I18N: Spanish and Catalan updates to Search Tips; updates to German translation *) installation: fix for jQuery UI custom; fix md5sum example arguments; new index on session.session_expiry *) intbitset: fix memory leak *) inveniogc: tmp directory removal improvements *) urlutils: MS Office redirection workaround CDS Invenio v0.99.6 -- released 2012-10-18 ------------------------------------------ *) improved XSS safety in external collection searching (WebSearch) *) verbose level in the search results pages is now available only to admins, preventing potential restricted record ID disclosure even though record content would remain restricted (WebSearch) Invenio v1.0.1 -- released 2012-06-28 ------------------------------------- *) BibFormat: fix format validation report; fix opensearch prefix exclusion in RSS; fix retrieval of collection identifier *) BibIndex: new unit tests for the Greek stemmer *) BibSched: improve low level submission arg parsing; set ERROR status when wrong params; task can stop immediately when sleeping *) BibSword: remove dangling documentation *) BibUpload: fix setting restriction in -a/-ir modes *) WebAlert: simplify HTML markup *) WebComment: only logged users to use report abuse *) WebJournal: hide deleted records *) WebSearch: adapt test cases for citation summary; fix collection order on the search page; look at access control when webcolling; sorting in citesummary breakdown links *) WebSession: simplify HTML markup *) WebSubmit: capitalise doctypes in Doc File Manager; check authorizations in endaction; check for problems when archiving; ensure unique tmp file name for upload; fix email formatting; fix Move_to_Done function; remove 8564_ field from demo templates; skip file upload if necessary; update CERN-specific config *) bibdocfile: BibRecDocs recID argument type check *) data cacher: deletes cache before refilling it *) dbquery: fix dbexec CLI WRT max allowed packet *) I18N: updates to Greek translation *) installation: fix circular install-jquery-plugins; fix demo user initialisation; fix jQuery tablesorter download URL; fix jQuery uploadify download URL; more info about max_allowed_packet; remove unneeded rxp binary package Invenio v1.0.0 -- released 2012-02-29 ------------------------------------- *) BatchUploader: fix retrieval of recs from extoaiid *) BibCirculation: fix regexp for dictionary checking; security check before eval *) BibConvert: fix UP and DOWN for UTF-8 strings *) bibdocfile: add missing normalize_format() calls; check_bibdoc_authorization caseless; fix append WRT description/restriction; fix cli_set_batch function; fix documentation WRT --with-version; fix handling of embargo firerole rule; fix parsing of complex subformats *) BibEdit: fix crash in Ajax request; fix undefined dictionary key *) BibFormat: better escape BFE in admin test UI; do not exit if no XSLT processor found; fix regression test; fix URL to ejournal resolver; fix XSLT formatting of MARCXML snippets; removes 'No fulltext' message; special handling of INSPIRE-PUBLIC type; use default namespace in XSL *) BibHarvest: check for empty resumptionToken; fix MARCXML creation in OAI updater; optional JSON dependency *) BibIndex: fix author:Campbell-Wilson word query; fix double-stemming upon indexing; fix Porter stemmer in multithread; Greek stemmer improvements *) BibKnowledge: make XML/XSLT libs optional *) BibRank: CERN hack to inactivate similarity lists; fix citation indexer time stamp updating; fix citation indexing of deleted records; fix citedby/refersto for infinite sets; fix empty citation data cacher; fix incremental citation indexer leaks; make numpy optional; minimum x-axis in citation history graphs; run citation indexer after word indexer *) BibRecord: fix for record_get_field_instances() *) BibSched: fix guess_apache_process_user_from_ps; use larger timouts for launching tasks *) BibUpload: FFT regression tests not to use CDS *) htmlutils: fix FCKeditor upload URLs *) installation: add note about optional hashlib; change table TYPE to ENGINE in SQL; fix 'install-mathjax-plugin'; fix issue with FCKeditor; fix 'make install-jquery-plugins'; fix output message cosmetics; new 'make install-ckeditor-plugin'; re-enable WSGI pre-loading *) intbitset: fix never ending loop in __repr__; fix several memory leaks *) inveniocfg: fix resetting ranking method names *) inveniogc: new CLI options check/optimise tables *) kwalitee: grep-like output and exit status changes; use `--check-some` as default CLI option *) mailutils: remove unnecessary 'multipart/related' *) plotextractor: fix INSPIRE unit test *) textmarc2xmlmarc: fix handling of BOM *) urlutils: new Indico request generator helper *) WebAccess: fix Access policy page; fix FireRole handling integer uid; fix retrieving emails from firerole *) WebAlert: fix the display of records in alerts *) WebBasket: fix missing return statement; fix number of items in public baskets *) WebComment: CERN-specific hack for ATLAS comments; fix discussion display in bfe_comments; fix washing of email to admin; improve sanity checks *) WebHelp: HOWTO MARC document update *) WebJournal: fix seminar widget encoding issue; fix seminar widget for new Indico APIs; update weather widget for new APIs *) WebSearch: add refersto:/a b c/ example to guide; CERN-specific hack for journal sorting; CERN-specific hack for latest additions; fix case-insensitive collection search; fix CDSIndico external search; fix collection translation in admin UI; fix get_fieldvalues() when recid is str; fix get_index_id_from_field(); fix structured regexp query parsing; fix symbol name typo in loop checking; parenthesised collection definitions; remove accent-search warning in guide; remove Report for INSPIRE author pages; replace CDS Indico by Indico; updates some output phrases *) WebSession: fix crash when no admin user exists *) WebStyle: better service failure message; fix implementation of req.get_hostname; fluid width of the menu; pre-load citation dictionaries for web *) WebSubmit: avoid printing empty doctype section; check_user_can_view_record in publiline; fix filename bug in document manager; fix handling of uploaded files; fix record_search_pattern in DEMOJRN *) xmlmarclint: 'no valid record detected' error *) I18N: updates to Catalan, Czech, French, German, Greek, Italian, Slovak, and Spanish translations *) Note: for a complete list of new features in Invenio v1.0 release series over Invenio v0.99 release series, please see: CDS Invenio v0.99.5 -- released 2012-02-21 ------------------------------------------ *) improved sanity checks when reporting, voting, or replying to a comment, or when accessing comment attachments, preventing URL mangling attempts (WebComment) CDS Invenio v0.99.4 -- released 2011-12-19 ------------------------------------------ *) fixed double stemming during indexing (BibIndex) *) fixed collection translation in admin UI (WebSearch) *) fixed UP and DOWN functions for UTF-8 strings (BibConvert) Invenio v1.0.0-rc0 -- released 2010-12-21 ----------------------------------------- *) CDS Invenio becomes Invenio as of this release *) new facility of hosted collections; support for external records in search collections, user alerts and baskets (WebSearch, WebAlert, WebBasket) *) support for nested parentheses in search query syntax (WebSearch) *) new refersto/citedby search operators for second-order searches in citation map (BibRank, WebSearch) *) numerous improvements to SPIRES query syntax parser (WebSearch) *) enhancement to search results summaries, e.g. co-author lists on author pages, e.g. h-index (WebSearch) *) new support for unAPI, Zotero, OpenSearch, AWS (WebSearch) *) new phrase and word-pair indexes (BibIndex) *) new fuzzy author name matching mode (BibIndex) *) new time-dependent citation ranking family of methods (BibRank) *) full-text search now shows context snippets (BibFormat) *) improvements to the basket UI, basket export facility (WebBasket) *) new support for FCKeditor in submissions and user comments, possibility to attach files (WebComment, WebSubmit) *) commenting facility enhanced with rounds and threads (WebComment) *) new facility to moderate user comments (WebComment) *) enhanced CLI tool for document file management bringing new options such as hidden file flag (WebSubmit) *) numerous improvements to the submission system, e.g. asynchronous JavaScript upload support, derived document formats, icon creation, support for automatic conversion of OpenOffice documents, PDF/A, OCR (WebSubmit) *) new full-text file metadata reader/writer tool (WebSubmit) *) new experimental SWORD protocol client application (BibSword) *) complete rewrite of the record editor using Ajax technology for faster user operation, with new features such as field templates, cloning, copy/paste, undo/redo, auto-completion, etc (BibEdit) *) new multi-record editor to alter many records in one go (BibEdit) *) new Ajax-based record differ and merger (BibMerge) *) new fuzzy record matching mode, with possibility to match records against remote Invenio installations (BibMatch) *) new circulation and holdings module (BibCirculation) *) new facility for matching provenance information when uploading records (BibUpload) *) new possibility of uploading incoming changes into holding pen (BibUpload) *) new batch uploader facility to support uploading of metadata files and of full-text files either in CLI or over web (BibUpload) *) new record exporting module supporting e.g. Sitemap and Google Scholar export methods (BibExport) *) improvements to the keyword classifier, e.g. author and core keywords (BibClassify) *) new facility for external robot-like login method (WebAccess) *) numerous improvements to the journal creation facility, new journal `Atlantis Times' demo journal (WebJournal) *) refactored and improved OAI exporter and harvester (BibHarvest) *) new taxonomy-based and dynamic-query knowledge base types (BibKnowledge) *) possibility to switch on/off user features such as alerts and baskets based on RBAC rules (WebAccess and other modules) *) various improvements to task scheduler, for example better communication with tasks, possibility to run certain bibsched tasks within given time limit, etc (BibSched) *) new database dumper for backup purposes (MiscUtil) *) new plotextractor library for extracting plots from compuscripts, new figure caption index and the Plots tab (MiscUtil, BibIndex, Webearch) *) enhanced reference extrator, e.g. support for DOI, for author name recognition (MiscUtil) *) new register emergency feature e.g. to alert admins by SMS in case the task queue stops (MiscUtil) *) infrastructure move from mod_python to mod_wsgi, support for mod_xsendfile (WebStyle and many modules) *) infrastructure move from jsMath to MathJax (MiscUtil) *) some notable backward-incompatible changes: removed authentication methods related to Apache user and group files, changed BibFormat element's API (BibFormat, many modules) *) new translations (Afrikaans, Galician, Georgian, Romanian, Kinyarwanda) plus many translation updates *) other numerous improvements and bug fixes done in about 1600 commits over Invenio v0.99 series CDS Invenio v0.99.3 -- released 2010-12-13 ------------------------------------------ *) fixed issues in the harvesting daemon when harvesting from more than one OAI repository (BibHarvest) *) fixed failure in formatting engine when dealing with not-yet-existing records (BibFormat) *) fixed traversal of final URL parts in the URL dispatcher (WebStyle) *) improved bibdocfile URL recognition upon upload of MARC files (BibUpload) *) fixed bug in admin interface for adding authorizations (WebAccess) *) keyword extractor is now compatible with rdflib releases older than 2.3.2 (BibClassify) *) output of `bibsched status' now shows the queue mode status as AUTOMATIC or MANUAL to help queue monitoring (BibSched) CDS Invenio v0.99.2 -- released 2010-10-20 ------------------------------------------ *) stricter checking of access to restricted records: in order to view a restricted record, users are now required to have authorizations to access all restricted collections the given record may belong to (WebSearch) *) strict checking of user query history when setting up email notification alert, preventing URL mangling attempts (WebAlert) *) fixed possible Unix signal conflicts for tasks performing I/O operations or running external processes, relevant notably to full-text indexing of remote files (BibSched) *) fixed full-text indexing and improved handling of files of `unexpected' extensions (BibIndex, WebSubmit) *) streaming of files of `unknown' MIME type now defaults to application/octet-stream (WebSubmit) *) fixed addition of new MARC fields in the record editor (BibEdit) *) fixed issues in full-text file attachment via MARC (BibUpload) *) fixed authaction CLI client (WebAccess) *) ... plus other minor fixes and improvements CDS Invenio v0.99.1 -- released 2008-07-10 ------------------------------------------ *) search engine syntax now supports parentheses (WebSearch) *) search engine syntax now supports SPIRES query language (WebSearch) *) strict respect for per-collection sort options on the search results pages (WebSearch) *) improved parsing of search query with respect to non-existing field terms (WebSearch) *) fixed "any collection" switch on the search results page (WebSearch) *) added possibility for progressive display of detailed record page tabs (WebSearch) *) added support for multi-page RSS output (WebSearch) *) new search engine summarizer module with the cite summary output format (WebSearch, BibRank) *) "cited by" links are now generated only when needed (WebSearch) *) new experimental comprehensive author page (WebSearch) *) stemming for many indexes is now enabled by default (BibIndex) *) new intelligent journal index (BibIndex) *) new logging of missing citations (BibRank) *) citation indexer and searcher improvements and caching (BibRank) *) new low-level task submission facility (BibSched) *) new options in bibsched task monitor: view task options, log and error files; prune task to a history table; extended status reporting; failed tasks now need acknowledgement in order to restart the queue (BibSched) *) safer handling of task sleeping and waking up (BibSched) *) new experimental support for task priorities and concurrent task execution (BibSched) *) improved user-configured browser language matching (MiscUtil) *) new default behaviour not differentiating between guest users; this removes a need to keep sessions/uids for guests and robots (WebSession) *) optimized sessions and collecting external user information (WebSession) *) improved logging conflicts for external vs internal users (WebAccess) *) improved Single Sign-On session preservation (WebAccess) *) new 'become user' debugging facility for admins (WebAccess) *) new bibdocfile CLI tool to manipulate full-text files archive (WebSubmit) *) optimized redirection of old URLs (WebSubmit) *) new icon creation tool in the submission input chain (WebSubmit) *) improved full-text file migration tool (WebSubmit) *) improved stamping of full-text files (WebSubmit) *) new approval-related end-submission functions (WebSubmit) *) comments and descriptions of full-text files are now kept also in bibdoc tables, not only in MARC; they are synchronized during bibupload (WebSubmit, BibUpload) *) fixed navigation in public baskets (WebBasket) *) added detailed record page link to basket records (WebBasket) *) new removal of HTML markup in alert notification emails (WebAlert) *) improved OAI harvester logging and handling (BibHarvest) *) improved error checking (BibConvert) *) improvements to the record editing tool: subfield order change, repetitive subfields; improved record locking features; configurable per-collection curators (BibEdit) *) fully refactored WebJournal module (WebJournal) *) new RefWorks output format, thanks to Theodoros Theodoropoulos (BibFormat) *) fixed keyword detection tool's output; deactivated taxonomy compilation (BibClassify) *) new /stats URL for administrators (WebStat) *) better filtering of unused translations (WebStyle) *) updated French, Italian, Norwegian and Swedish translations; updated Japanese translation (thanks to Makiko Matsumoto and Takao Ishigaki); updated Greek translation (thanks to Theodoros Theodoropoulos); new Hungarian translation (thanks to Eva Papp) *) ... plus many other minor bug fixes and improvements CDS Invenio v0.99.0 -- released 2008-03-27 ------------------------------------------ *) new Invenio configuration language, new inveniocfg configuration tool permitting more runtime changes and enabling separate local customizations (MiscUtil) *) phased out WML dependency everywhere (all modules) *) new common RSS cache implementation (WebSearch) *) improved access control to the detailed record pages (WebSearch) *) when searching non-existing collections, do not revert to searching in public Home anymore (WebSearch) *) strict calculation of number of hits per multiple collections (WebSearch) *) propagate properly language environment in browse pages, thanks to Ferran Jorba (WebSearch) *) search results sorting made accentless, thanks to Ferran Jorba (WebSearch) *) new OpenURL interface (WebSearch) *) added new search engine API argument to limit searches to record creation/modification dates and times instead of hitherto creation dates only (WebSearch) *) do not allow HTTP POST method for searches to prevent hidden mining (WebSearch) *) added alert and RSS teaser for search engine queries (WebSearch) *) new optimized index structure for fast integer bit vector operations, leading to significant indexing time improvements (MiscUtil, BibIndex, WebSearch) *) new tab-based organisation of detailed record pages, with new URL schema (/record/1/usage) and related CSS changes (BibFormat, MiscUtil, WebComment, WebSearch, WebStyle, WebSubmit) *) phased out old PHP based code; migration to Python-based output formats recommended (BibFormat, WebSubmit) *) new configurability to show/hide specific output formats for specific collections (BibFormat, WebSearch) *) new configurability to have specific stemming settings for specific indexes (BibIndex, WebSearch) *) optional removal of LaTeX markup for indexer (BibIndex, WebSearch) *) performance optimization for webcoll and optional arguments to refresh only parts of collection cache (WebSearch) *) optional verbosity argument propagation to the output formatter (BibFormat, WebSearch) *) new convenient reindex option to the indexer (BibIndex) *) fixed problem with indexing of some lengthy UTF-8 accented names, thanks to Theodoros Theodoropoulos for reporting the problem (BibIndex) *) fixed full-text indexing of HTML pages (BibIndex) *) new Stemmer module dependency, fixes issues on 64-bit systems (BibIndex) *) fixed download history graph display (BibRank) *) improved citation ranking and history graphs, introduced self-citation distinction, added new demo records (BibRank) *) fixed range redefinition and output message printing problems in the ranking indexer, thanks to Mike Marino (BibRank) *) new XSLT output formatter support; phased out old BFX formats (BibFormat) *) I18N output messages are now translated in the output formatter templates (BibFormat) *) formats fixed to allow multiple author affiliations (BibFormat) *) improved speed of the record output reformatter in case of large sets (BibFormat) *) support for displaying LaTeX formulas via JavaScript (BibFormat) *) new and improved output formatter elements (BibFormat) *) new escaping modes for format elements (BibFormat) *) output format template editor cache and element dependency checker improvements (BibFormat) *) output formatter speed improvements in PHP-compatible mode (BibFormat) *) new demo submission configuration and approval workflow examples (WebSubmit) *) new submission full-text file stamper utility (WebSubmit) *) new submission icon-creation utility (WebSubmit) *) separated submission engine and database layer (WebSubmit) *) submission functions can now access user information (WebSubmit) *) implemented support for restricted icons (WebSubmit, WebAccess) *) new full-text file URL and cleaner storage facility; requires file names to be unique within a given record (WebSearch, WebSubmit) *) experimental release of the complex approval and refereeing workflow (WebSubmit) *) new end-submission functions to move files to storage space (WebSubmit) *) added support for MD5 checking of full-text files (WebSubmit) *) improved behaviour of the submission system with respect to the browser "back" button (WebSubmit) *) removed support for submission "cookies" (WebSubmit) *) flexible report number generation during submission (WebSubmit) *) added support for optional filtering step in the OAI harvesting chain (BibHarvest) *) new text-oriented converter functions IFDEFP, JOINMULTILINES (BibConvert) *) selective harvesting improvements, sets, non-standard responses, safer resumption token handling (BibHarvest) *) OAI archive configuration improvements: collections retrieval, multiple set definitions, new clean mode, timezones, and more (BibHarvest) *) OAI gateway improvements: XSLT used to produce configurable output (BibHarvest) *) added support for "strong tags" that can resist metadata replace mode (BibUpload) *) added external OAI ID tag support to the uploader (BibUpload) *) added support for full-text file transfer during uploading (BibUpload) *) preserving full history of all MARCXML versions of a record (BibEdit, BibUpload) *) XMLMARC to TextMarc improvements: empty indicators and more (BibEdit) *) numerous reference extraction tool improvements: year handling, LaTeX handling, URLs, journal titles, output methods, and more (BibEdit) *) new classification daemon (BibClassify) *) classification taxonomy caching resulting in speed optimization (BibClassify) *) new possibility to define more than one keyword taxonomy per collection (BibClassify) *) fixed non-standalone keyword detection, thanks to Annette Holtkamp (BibClassify) *) new embedded page generation profiler (WebStyle) *) new /help pages layout and webdoc formatting tool (WebStyle) *) new custom style template verification tool (WebStyle) *) added support for the XML page() output format, suitable for AJAX interfaces (WebStyle) *) introduction of navigation menus (WebStyle) *) general move from HTML to XHTML markup (all modules) *) fixed alert deletion tool vulnerability (WebAlert) *) do not advertise baskets/alerts much for guest users; show only the login link (WebSession) *) password reset interface improvements (WebSession) *) new permanent "remember login" mechanism (WebSession, WebAccess) *) local user passwords are now encrypted (WebSession, WebAccess) *) new LDAP external authentication plugin (WebAccess) *) new password reset mechanism using new secure mail cookies and temporary role membership facilities (WebAccess, WebSession) *) added support for Single Sign-On Shibboleth based authentication method (WebAccess) *) new firewall-like based role definition language, new demo examples (WebAccess) *) external authentication and groups improvements: nicknames, account switching, and more (WebSession, WebAccess) *) task log viewer integrated in the task monitor (BibSched) *) new journal creation module (WebJournal) *) new generic statistic gathering and display facility (WebStat) *) deployed new common email sending facility (MiscUtil, WebAlert, WebComment, WebSession, WebSubmit) *) dropped support for MySQL-4.0, permitting to use clean and strict UTF-8 storage methods; upgrade of MySQLdb to at least 1.2.1_p2 required (MiscUtil) *) uncatched exceptions are now being sent by email to the administrator (MiscUtil, WebStyle) *) new general garbage collector with a possibility to run via the task scheduler and a possibility to clean unreferenced bibliographic values (MiscUtil) *) new generic SQL and data cacher (MiscUtil) *) new HTML page validator plugin (MiscUtil) *) new web test suite running in a real browser (MiscUtil) *) improved code kwalitee checker (MiscUtil) *) translation updates: Spanish and Catalan (thanks to Ferran Jorba), Japanese (Toru Tsuboyama), German (Benedikt Koeppel), Polish (Zbigniew Szklarz and Zbigniew Leonowicz), Greek (Theodoros Theodoropoulos), Russian (Yana Osborne), Swedish, Italian, French *) new translations: Chinese traditional and Chinese simplified (thanks to Kam-ming Ku) *) ... plus many other minor bug fixes and improvements CDS Invenio v0.92.1 -- released 2007-02-20 ------------------------------------------ *) new support for external authentication systems (WebSession, WebAccess) *) new support for external user groups (WebSession) *) new experimental version of the reference extraction program (BibEdit) *) new optional Greek stopwords list, thanks to Theodoropoulos Theodoros (BibIndex) *) new Get_Recid submission function (WebSubmit) *) new config variable governing the display of the download history graph (BibRank) *) started deployment of user preferences (WebSession, WebSearch) *) split presentation style for "Narrow search", "Focus on" and "Search also" search interface boxes (WebSearch, WebStyle) *) updated CERN Indico and KEK external collection searching facility (WebSearch) *) fixed search interface portalbox and collection definition escaping behaviour (WebSearch Admin) *) fixed problems with external system number and OAI ID matching (BibUpload) *) fixed problem with case matching behaviour (BibUpload) *) fixed problems with basket record display and basket topic change (WebBasket) *) fixed output format template attribution behaviour (BibFormat) *) improved language context propagation in output formats (BibFormat) *) improved output format treatment of HTML-aware fields (BibFormat) *) improved BibFormat migration kit (BibFormat) *) improved speed and eliminated set duplication of the OAI repository gateway (BibHarvest) *) fixed resumption token handling (BibHarvest) *) improved record editing interface (BibEdit) *) fixed problem with empty fields treatment (BibConvert) *) updated Report_Number_Generation submission function to be able to easily generate report numbers from any submission information (WebSubmit) *) fixed problem with submission field value escaping (WebSubmit) *) fixed problem with submission collection ordering (WebSubmit) *) fixed BibSched task signal handling inconsistency (BibSched) *) fixed TEXT versus BLOB database problems for some tables/columns *) minor updates to the HOWTO Migrate guide and several admin guides (WebHelp, BibIndex, BibFormat) *) minor bugfixes to several modules; see ChangeLog for details and credits CDS Invenio v0.92.0 -- released 2006-12-22 ------------------------------------------ *) previously experimental output formatter in Python improved and made default (BibFormat) *) previously experimental new submission admin interface in Python improved and made default (WebSubmit) *) new XML-oriented output formatting mode (BibFormat) *) new export-oriented output formats: EndNote, NLM (BibFormat) *) RSS 2.0 latest additions feed service (WebSearch, BibFormat) *) new XML-oriented metadata converter mode (BibConvert) *) new metadata uploader in Python (BibUpload) *) new integrated parallel external collection searching (WebSearch) *) improved document classifier: composite keywords, wildcards, cloud output (BibClassify) *) improved UTF-8 fulltext indexing (BibIndex) *) improved external login authentication subsystem (WebAccess) *) added possibility to order submission categories (WebSubmit) *) improved handling of cached search interface page formats, preferential sort pattern functionality, international collection names (WebSearch) *) improved behaviour of OAI harvester: sets, deleted records, harvested metadata transformation (BibHarvest) *) improved MARCXML schema compatibility concerning indicators; updates to the HTML MARC output format (BibEdit, BibUpload, BibFormat, and other modules) *) multiple minor bugs fixed thanks to the wider deployment of the regression test suite (all modules) *) new translation (Croatian) and several translation updates (Catalan, Bulgarian, French, Greek, Spanish); thanks to Ferran Jorba, Beatriu Piera, Alen Vodopijevec, Jasna Marković, Theodoros Theodoropoulos, and Nikolay Dyankov (see also THANKS file) *) removed dependency on PHP; not needed anymore *) full compatibility with MySQL 4.1 and 5.0; upgrade from MySQL 4.0 now recommended *) full compatibility with FreeBSD and Mac OS X CDS Invenio v0.90.1 -- released 2006-07-23 ------------------------------------------ *) output messages improved and enhanced to become more easily translatable in various languages (all modules) *) new translation (Bulgarian) and several updated translations (Greek, French, Russian, Slovak) *) respect langugage choice in various web application links (WebAlert, WebBasket, WebComment, WebSession, WebSubmit) *) fixed problem with commenting rights in a group-shared basket that is also a public basket with lesser rights (WebBasket) *) guest users are now forbidden to share baskets (WebBasket) *) fixed guest user garbage collection, adapted to the new baskets schema (WebSession) *) added possibility to reject group membership requests; sending informational messages when users are approved/refused by group administrators (WebSession) *) experimental release of the new BibFormat in Python (BibFormat) *) started massive deployment of the regression test suite, checking availability of all web interface pages (BibEdit, BibFormat, BibHarvest, BibIndex, BibRank, MiscUtil, WebAccess, WebBasket, WebComment, WebMessage, WebSearch, WebSession, WebSubmit) *) updated developer documentation (I18N output messages policy, test suite policy, coding style) CDS Invenio v0.90.0 -- released 2006-06-30 ------------------------------------------ *) formerly known as CDSware; the application name change clarifies the relationship with respect to the CDS Sofware Consortium producing two flagship applications (CDS Indico and Invenio) *) version number increased to v0.90 in the anticipation of the forthcoming v1.0 release after all the major codebase changes are now over *) new possibility to define user groups (WebGroup) *) new personal basket organization in topics (WebBasket) *) new basket sharing among user groups (WebBasket) *) new open peer reviewing and commenting on documents (WebComment) *) new user and group web messaging system (WebMessage) *) new ontology-based document classification system (BibClassify) *) new WebSubmit Admin (WebSubmit) *) new record editing web interface (BibEdit) *) new record matching tool (BibMatch) *) new OAI repository administration tool (BibHarvest) *) new OAI periodical harvesting tool (BibHarvest) *) new web layout templating system (WebStyle) *) new clean URL schema (e.g. /collection/Theses, /record/1234) (WebStyle) *) new BibTeX output format support (BibFormat) *) new possibility of secure HTTPS authentication while keeping the rest of the site non-HTTPS (WebSession) *) new centralized error library (MiscUtil) *) new gettext-based international translations, with two new beta translations (Japanese, Polish) *) new regression testing suite framework (MiscUtil) *) new all prerequisites are now apt-gettable for Debian "Sarge" GNU/Linux *) new full support for Mac OS X *) ... plus many fixes and changes worth one year of development CDSware v0.7.1 -- released 2005-05-04 ------------------------------------- *) important bugfix for bibconvert's ``source data in a directory'' mode, as invoked by the web submission system (BibConvert) *) minor bugfix in the search engine, thanks to Frederic Gobry (WebSearch) *) minor bugfix in the WebSearch Admin interface (WebSearch) *) automatic linking to Google Print in the ``Haven't found what you were looking for...'' page box (WebSearch) *) BibFormat Admin Guide cleaned, thanks to Ferran Jorba *) new Catalan translation, thanks to Ferran Jorba *) updated Greek and Portuguese translations, thanks to Theodoros Theodoropoulos and Flávio C. Coelho *) updated Spanish translation CDSware v0.7.0 -- released 2005-04-06 ------------------------------------- *) experimental release of the refextract program for automatic reference extraction from PDF fulltext files (BibEdit) *) experimental release of the citation and download ranking tools (BibRank) *) new module for gathering usage statistics out of Apache log files (WebStat) *) new similar-records-navigation tool exploring end-user viewing habits: "people who viewed this page also viewed" (WebSearch, BibRank) *) OAI gateway validated against OAI Repository Explorer (BibHarvest) *) fixed "records modified since" option for the indexer (BibIndex) *) collection cache update is done only when the cache is not up to date (WebSearch) [closing #WebSearch-016] *) cleanup of user login mechanism (WebSession, WebAccess) *) fixed uploading of already-existing records in the insertion mode (BibUpload) *) fixed submission in UTF-8 languages (WebSubmit) *) updated HOWTO Run Your Existing CDSware Installation (WebHelp) *) test suite improvements (WebSearch, BibHarvest, BibRank, BibConvert) *) German translation updated and new German stopwords list added, thanks to Guido Pelzer *) new Greek and Ukrainian translations, thanks to Theodoros Theodoropoulos and Vasyl Ostrovskyi *) all language codes now comply to RFC 1766 and ISO 639 *) numerous other small fixes and improvements, with many contributions by the EPFL team headed by Frederic Gobry (BibConvert, BibUpload, WebSearch, WebSubmit, WebSession) CDSware v0.5.0 -- released 2004-12-17 ------------------------------------- *) new rank engine, featuring word similarity rank method and the journal impact factor rank demo (BibRank) *) search engine includes ranking option (WebSearch) *) record similarity search based on word frequency (WebSearch, BibRank) *) stopwords possibility when ranking and indexing (BibRank, BibIndex) *) stemming possibility when ranking and indexing (BibRank, BibIndex) *) search engine boolean query processing stages improved (WebSearch) *) search engine accent matching in phrase searches (WebSearch) *) regular expression searching mode introduced into the Simple Search interface too (WebSearch) *) Search Tips split into a brief Search Tips page and detailed Search Guide page (WebSearch) *) improvements to the ``Try your search on'' hints (WebSearch) *) author search hints introduced (WebSearch) *) search interface respects title prologue/epilogue portalboxes (WebSearch) *) improvements to admin interfaces (WebSearch, BibIndex, BibRank, WebAccess) *) basket item ordering problem fixed (WebBasket) *) access error messages introduced (WebAccess and its clients) *) new account management to enable/disable guest users and automatic vs to-be-approved account registration (WebAccess) *) possibility for temporary read-only access to, and closure of, the site; useful for backups (WebAccess and its clients) *) possibility for external authentication login methods (WebAccess) *) new XML MARC handling library (BibEdit) *) when uploading, bad XML records are marked as errors (BibUpload) *) improvements to the submission engine and its admin interface, thanks to Tiberiu Dondera (WebSubmit) *) preparations for electronic mail submission feature, not yet functional (ElmSubmit) *) added example on MARC usage at CERN (WebHelp) *) legacy compatibility with MySQL 3.23.x assured (BibUpload) *) legacy compatibility with Python 2.2 assured (WebSubmit) *) test suite additions and corrections (BibRank, BibIndex, WebSearch, BibEdit) *) French translation fixes, thanks to Eric Grand *) minor Czech and Slovak translation cleanup CDSware v0.3.3 (DEVELOPMENT) -- released 2004-07-16 --------------------------------------------------- *) new international phrases, collection and field names; thanks to Guido, Flavio, Tullio *) collection international names are now respected by the search engine and interfaces (WebSearch) *) field international names are now respected by the search engine and interfaces (WebSearch) *) when no hits found in a given collection, do not display all public hits straight away but only link to them (WebSearch) *) records marked as DELETED aren't shown anymore in XML MARC and other formats (WebSearch) *) detailed record page now features record creation and modification times (WebSearch) *) improved XML MARC parsing and cumulative record count in case of uploading of several files in one go (BibUpload) *) personal `your admin activities' page introduced (WebSession) *) added option to fulltext-index local files only (BibIndex) *) initial release of the BibIndex Admin interface (BibIndex) *) checking of mandatory selection box definitions (WebSubmit) *) WebSearch Admin interface cleanup (WebSearch) *) introducing common test suite infrastructure (WebSearch, BibIndex, MiscUtil, WebHelp) *) fixed accent and link problems for photo demo records (MiscUtil) *) conference title exported via OAI XML DC (BibHarvest) *) enabled building out of source directory; thanks to Frederic CDSware v0.3.2 (DEVELOPMENT) -- released 2004-05-12 --------------------------------------------------- *) admin area improved: all the modules have now Admin Guides; some guides were updated, some are still to be updated (WebHelp, BibConvert, BibFormat, BibIndex, BibSched, WebAlert, WebSession, WebSubmit, BibEdit, BibHarvest, BibRank, BibUpload, WebAccess, WebBasket, WebSearch, WebStyle) *) initial release of the WebSearch Admin interface (WebSearch) *) initial release of the BibRank Admin interface (BibRank) *) search cache expiry after insertion of new records (WebSearch) *) search engine now does on-the-fly formatting via BibFormat CLI call to handle restricted site situations (WebSearch) *) webcoll default verbosity decreased for efficiency (WebSearch) *) added BibConvert configuration example for converting XML Dublin Core to XML MARC (BibConvert) *) BibConvert knowledge base mode extended by various case-sensitive matching possibilities (BibConvert) *) fixed various problems with fulltext file names and the submission from MS Windows platform (WebSubmit) *) fixed problem with bibupload append mode not updating XML MARC properly (BibUpload) *) fixed small problems with the submission interface such as multiple fields selection (WebSubmit) *) session revoking and session expiry strengthened (WebSession) *) page design and style sheet updated to better fit large variety of browsers (WebStyle) *) added output format argument for basket display (WebBasket) *) new Swedish translation and updated German, Russian, and Spanish translations; thanks to Urban, Guido, Lyuba, and Magaly *) faster creation of I18N static HTML and PHP files during make CDSware v0.3.1 (DEVELOPMENT) -- released 2004-03-12 --------------------------------------------------- *) security fix preventing exposure of local configuration variables by malicious URL crafting (WebSearch, WebSubmit, WebAlert, WebBasket, WebSession, BibHarvest, MiscUtil) *) initial release of the ranking engine (BibRank) *) new guide on HOWTO Run Your CDSware Installation (WebHelp) *) fixed submit configurations with respect to fulltext links and metadata tags (WebSubmit, MiscUtil) *) Your Account personal corner now shows the list and the status of submissions and approvals (WebSession) *) uniform help and version number option for CLI executables (WebSearch, BibSched, BibIndex, BibRank, BibHarvest, BibConvert, WebAccess, BibFormat, WebSession, WebAlert) *) uniform technique for on-the-fly formatting of search results via `hb_' and `hd_' output format parameters (WebSearch) *) check for presence of pcntl and mysql PHP libraries (BibUpload) CDSware v0.3.0 (DEVELOPMENT) -- released 2004-03-05 --------------------------------------------------- *) new development branch release (important SQL table changes) *) introducing a new submission engine and the end-user web interface (WebSubmit) *) bibupload is now a BibSched task with new options (BibUpload) *) BibWords renamed into BibIndex in the view of future phrase indexing changes (BibIndex) *) more secure DB server connectivity (BibSched) *) record matching functionality (BibConvert) *) character encoding conversion tables (BibConvert) *) Qualified Dublin Core conversion example (BibConvert) *) OAI deleted records policy can now be specified (BibHarvest) *) multi-language collection portalboxes (WebSearch) *) HTML pages now respect language selections (WebSearch, WebHelp) *) minor layout changes (WebStyle) *) updated Russian and other translations *) ChangeLog is now generated from CVS log messages *) plus the usual set of bugfixes (see ChangeLog) CDSware v0.1.2 (DEVELOPMENT) -- released 2003-12-21 --------------------------------------------------- *) development branch release *) fix BibReformat task launching problem (BibFormat) *) fix BibTeX -> XML MARC conversion example (BibConvert) *) updated Spanish translation CDSware v0.1.1 (DEVELOPMENT) -- released 2003-12-19 --------------------------------------------------- *) development branch release *) access control engine now used by BibWords, BibFormat (admin and bibreformat), WebSearch (webcoll), and BibTaskEx *) access control engine admin guide started (WebAccess) *) search engine support for sorting by more than one field (WebSearch) *) more internationalization of the search engine messages (WebSearch) *) new language: Norwegian (bokmÃ¥l) *) simple example for converting BibTeX into XML MARC (BibConvert) *) new optional --with-python configuration option *) Python module detection during configure *) bugfixes: os.tempnam() warning, login page referer, and others CDSware v0.1.0 (DEVELOPMENT) -- released 2003-12-04 --------------------------------------------------- *) development branch release *) search engine redesign to yield five times more search performance for larger sites (WebSearch, BibWords) *) fulltext indexation of PDF, PostScript, MS Word, MS PowerPoint and MS Excel files (WebSearch) *) integrated combined metadata/fulltext/citation search (WebSearch) *) multi-stage search guidance in cases of no exact match (WebSearch) *) OAI-PMH harvestor (BibHarvest) *) bibliographic task scheduler (BibSched) *) automatic daemon mode of the indexer, the formatter and the collection cache generator (BibWords, BibFormat, WebSearch) *) user management and session handling rewrite (WebSession) *) user personalization, document baskets and notification alert system (WebBasket, WebAlert) *) role-based access control engine (WebAccess) *) internationalization of the interface started (currently with Czech, German, English, Spanish, French, Italian, Portuguese, Russian, and Slovak support) *) web page design update (WebStyle) *) introduction of programmer-oriented technical documentation corner (WebHelp) *) source tree reorganization, mod_python technology adopted for most of the modules CDSware v0.0.9 (STABLE) -- released 2002-08-01 ---------------------------------------------- *) first "public" alpha release of CDSware *) recently standardized Library of Congress' MARC XML schema adopted in all CDSware modules as the new default internal XML file format (BibConvert, BibFormat, BibUpload, WebSubmit, WebSearch) *) support for OAI-PMH v2.0 in addition to OAI-PMH v1.1 (WebSearch) *) search interface now honors multiple output formats per collection (BibFormat, WebSearch) *) search interface now honors search fields, search options, and sort options from the database config tables (WebSearch, WebSearch Admin) *) search interface now honors words indexes from the database config tables (BibWords, WebSearch) *) easy reformatting of already uploaded bibliographic records via web admin. tool (BibFormat Admin/Reformat Records) *) new submission form field type ("response") allowing greater flexibility (WebSubmit) [thanks to Frank Sudholt] *) demo site "Atlantis Institute of Science" updated to demonstrate: Pictures collection of photographs; specific per-collection formats; references inside Articles and Preprints; "cited by" search link; published version linking; subject category searching; search within, search options, sort options in the web collection pages. - end of file - diff --git a/invenio/base/translations/fa/messages.po b/invenio/base/translations/fa/messages.po index 28cf5ecc7..d8e2cf70b 100644 --- a/invenio/base/translations/fa/messages.po +++ b/invenio/base/translations/fa/messages.po @@ -1,15075 +1,15075 @@ # # This file is part of Invenio. # # Copyright (C) 2013, 2014 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. msgid "" msgstr "" -"Project-Id-Version: Invenio 1.2.0\n" +"Project-Id-Version: Invenio 1.2.1\n" "Report-Msgid-Bugs-To: info@invenio-software.org\n" -"POT-Creation-Date: 2015-03-03 18:01+0100\n" +"POT-Creation-Date: 2015-05-21 17:22+0200\n" "PO-Revision-Date: 2014-04-12 06:30+0330\n" "Last-Translator: Mehdi Zahedi \n" "Language-Team: FA \n" "Language: fa_IR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 1.6.4\n" "Plural-Forms: nplurals=1; plural=0;\n" #: modules/websearch/doc/search-guide.webdoc:456 #: modules/websearch/doc/search-guide.webdoc:492 #: modules/websearch/doc/search-guide.webdoc:596 #: modules/websearch/doc/search-guide.webdoc:632 #: modules/websearch/doc/search-guide.webdoc:735 #: modules/websearch/doc/search-guide.webdoc:771 #: modules/websearch/doc/search-guide.webdoc:879 #: modules/websearch/doc/search-guide.webdoc:914 #: modules/websearch/doc/search-guide.webdoc:1023 #: modules/websearch/doc/search-guide.webdoc:1059 #: modules/websearch/lib/search_engine.py:1405 #: modules/websearch/lib/websearch_templates.py:1347 msgid "AND NOT" msgstr "و نه" #: modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc:22 #: modules/bibconvert/doc/admin/bibconvert-admin-guide.webdoc:21 #: modules/bibedit/doc/admin/bibedit-admin-guide.webdoc:21 #: modules/bibformat/doc/admin/bibformat-admin-guide.webdoc:21 #: modules/bibindex/doc/admin/bibindex-admin-guide.webdoc:21 #: modules/bibmatch/doc/admin/bibmatch-admin-guide.webdoc:21 #: modules/bibrank/doc/admin/bibrank-admin-guide.webdoc:21 #: modules/bibsched/doc/admin/bibsched-admin-guide.webdoc:21 #: modules/bibupload/doc/admin/bibupload-admin-guide.webdoc:21 #: modules/elmsubmit/doc/admin/elmsubmit-admin-guide.webdoc:21 #: modules/oaiharvest/doc/admin/oaiharvest-admin-guide.webdoc:21 #: modules/webalert/doc/admin/webalert-admin-guide.webdoc:21 #: modules/webbasket/doc/admin/webbasket-admin-guide.webdoc:21 #: modules/webcomment/doc/admin/webcomment-admin-guide.webdoc:21 #: modules/webhelp/web/admin/admin.webdoc:18 #: modules/webmessage/doc/admin/webmessage-admin-guide.webdoc:21 #: modules/websearch/doc/admin/websearch-admin-guide.webdoc:21 #: modules/websession/doc/admin/websession-admin-guide.webdoc:21 #: modules/webstat/doc/admin/webstat-admin-guide.webdoc:21 #: modules/webstyle/doc/admin/webstyle-admin-guide.webdoc:21 #: modules/websubmit/doc/admin/websubmit-admin-guide.webdoc:21 #: modules/bibformat/lib/bibformatadminlib.py:62 #: modules/bibformat/web/admin/bibformatadmin.py:75 #: modules/bibknowledge/lib/bibknowledgeadmin.py:69 #: modules/webcomment/lib/webcommentadminlib.py:45 #: modules/weblinkback/lib/weblinkbackadminlib.py:55 #: modules/webstyle/lib/webdoc_webinterface.py:153 msgid "Admin Area" msgstr "ناحیه مدیر" #: modules/websearch/doc/search-guide.webdoc:525 #: modules/websearch/doc/search-guide.webdoc:665 #: modules/websearch/doc/search-guide.webdoc:804 #: modules/websearch/doc/search-guide.webdoc:948 #: modules/websearch/doc/search-guide.webdoc:1093 #: modules/websearch/lib/search_engine.py:5901 #: modules/websearch/lib/websearch_templates.py:844 #: modules/websearch/lib/websearch_templates.py:922 #: modules/websearch/lib/websearch_templates.py:1045 #: modules/websearch/lib/websearch_templates.py:2160 #: modules/websearch/lib/websearch_templates.py:2264 #: modules/websearch/lib/websearch_templates.py:2321 #: modules/websearch/lib/websearch_templates.py:2378 #: modules/websearch/lib/websearch_templates.py:2417 #: modules/websearch/lib/websearch_templates.py:2440 #: modules/websearch/lib/websearch_templates.py:2471 msgid "Browse" msgstr "مرور" #: modules/webhelp/web/help-central.webdoc:50 #: modules/websearch/doc/search-tips.webdoc:20 #: modules/websearch/lib/websearch_templates.py:845 #: modules/websearch/lib/websearch_templates.py:923 #: modules/websearch/lib/websearch_templates.py:1046 #: modules/websearch/lib/websearch_templates.py:1238 #: modules/websearch/lib/websearch_templates.py:2268 #: modules/websearch/lib/websearch_templates.py:2325 #: modules/websearch/lib/websearch_templates.py:2382 msgid "Search Tips" msgstr "نکات جستجو" #: modules/websearch/doc/search-guide.webdoc:438 #: modules/websearch/doc/search-guide.webdoc:474 #: modules/websearch/doc/search-guide.webdoc:509 #: modules/websearch/doc/search-guide.webdoc:578 #: modules/websearch/doc/search-guide.webdoc:614 #: modules/websearch/doc/search-guide.webdoc:649 #: modules/websearch/doc/search-guide.webdoc:717 #: modules/websearch/doc/search-guide.webdoc:753 #: modules/websearch/doc/search-guide.webdoc:788 #: modules/websearch/doc/search-guide.webdoc:861 #: modules/websearch/doc/search-guide.webdoc:896 #: modules/websearch/doc/search-guide.webdoc:932 #: modules/websearch/doc/search-guide.webdoc:1005 #: modules/websearch/doc/search-guide.webdoc:1041 #: modules/websearch/doc/search-guide.webdoc:1077 #: modules/miscutil/lib/inveniocfg.py:655 msgid "abstract" msgstr "چکیده" #: modules/websearch/doc/search-guide.webdoc:443 #: modules/websearch/doc/search-guide.webdoc:479 #: modules/websearch/doc/search-guide.webdoc:514 #: modules/websearch/doc/search-guide.webdoc:583 #: modules/websearch/doc/search-guide.webdoc:619 #: modules/websearch/doc/search-guide.webdoc:654 #: modules/websearch/doc/search-guide.webdoc:722 #: modules/websearch/doc/search-guide.webdoc:758 #: modules/websearch/doc/search-guide.webdoc:793 #: modules/websearch/doc/search-guide.webdoc:866 #: modules/websearch/doc/search-guide.webdoc:901 #: modules/websearch/doc/search-guide.webdoc:937 #: modules/websearch/doc/search-guide.webdoc:1010 #: modules/websearch/doc/search-guide.webdoc:1046 #: modules/websearch/doc/search-guide.webdoc:1082 #: modules/miscutil/lib/inveniocfg.py:660 msgid "fulltext" msgstr "تمام متن" #: modules/websearch/doc/search-guide.webdoc:432 #: modules/websearch/doc/search-guide.webdoc:467 #: modules/websearch/doc/search-guide.webdoc:503 #: modules/websearch/doc/search-guide.webdoc:572 #: modules/websearch/doc/search-guide.webdoc:607 #: modules/websearch/doc/search-guide.webdoc:643 #: modules/websearch/doc/search-guide.webdoc:711 #: modules/websearch/doc/search-guide.webdoc:746 #: modules/websearch/doc/search-guide.webdoc:782 #: modules/websearch/doc/search-guide.webdoc:854 #: modules/websearch/doc/search-guide.webdoc:890 #: modules/websearch/doc/search-guide.webdoc:925 #: modules/websearch/doc/search-guide.webdoc:998 #: modules/websearch/doc/search-guide.webdoc:1034 #: modules/websearch/doc/search-guide.webdoc:1070 #: modules/websearch/lib/search_engine.py:1427 #: modules/websearch/lib/websearch_templates.py:1303 msgid "Regular expression:" msgstr "" #: modules/websearch/doc/search-guide.webdoc:428 #: modules/websearch/doc/search-guide.webdoc:463 #: modules/websearch/doc/search-guide.webdoc:499 #: modules/websearch/doc/search-guide.webdoc:568 #: modules/websearch/doc/search-guide.webdoc:603 #: modules/websearch/doc/search-guide.webdoc:639 #: modules/websearch/doc/search-guide.webdoc:707 #: modules/websearch/doc/search-guide.webdoc:742 #: modules/websearch/doc/search-guide.webdoc:778 #: modules/websearch/doc/search-guide.webdoc:850 #: modules/websearch/doc/search-guide.webdoc:886 #: modules/websearch/doc/search-guide.webdoc:921 #: modules/websearch/doc/search-guide.webdoc:994 #: modules/websearch/doc/search-guide.webdoc:1030 #: modules/websearch/doc/search-guide.webdoc:1066 #: modules/websearch/lib/search_engine.py:1423 #: modules/websearch/lib/websearch_templates.py:1295 msgid "All of the words:" msgstr "همه کلمات" #: modules/websearch/doc/search-guide.webdoc:446 #: modules/websearch/doc/search-guide.webdoc:482 #: modules/websearch/doc/search-guide.webdoc:517 #: modules/websearch/doc/search-guide.webdoc:586 #: modules/websearch/doc/search-guide.webdoc:622 #: modules/websearch/doc/search-guide.webdoc:657 #: modules/websearch/doc/search-guide.webdoc:725 #: modules/websearch/doc/search-guide.webdoc:761 #: modules/websearch/doc/search-guide.webdoc:796 #: modules/websearch/doc/search-guide.webdoc:869 #: modules/websearch/doc/search-guide.webdoc:904 #: modules/websearch/doc/search-guide.webdoc:940 #: modules/websearch/doc/search-guide.webdoc:1013 #: modules/websearch/doc/search-guide.webdoc:1049 #: modules/websearch/doc/search-guide.webdoc:1085 #: modules/miscutil/lib/inveniocfg.py:657 msgid "report number" msgstr "شماره گزارش" #: modules/websearch/doc/search-guide.webdoc:449 #: modules/websearch/doc/search-guide.webdoc:485 #: modules/websearch/doc/search-guide.webdoc:520 #: modules/websearch/doc/search-guide.webdoc:589 #: modules/websearch/doc/search-guide.webdoc:625 #: modules/websearch/doc/search-guide.webdoc:660 #: modules/websearch/doc/search-guide.webdoc:728 #: modules/websearch/doc/search-guide.webdoc:764 #: modules/websearch/doc/search-guide.webdoc:799 #: modules/websearch/doc/search-guide.webdoc:872 #: modules/websearch/doc/search-guide.webdoc:907 #: modules/websearch/doc/search-guide.webdoc:943 #: modules/websearch/doc/search-guide.webdoc:1016 #: modules/websearch/doc/search-guide.webdoc:1052 #: modules/websearch/doc/search-guide.webdoc:1088 #: modules/websearch/doc/search-tips.webdoc:490 #: modules/websearch/doc/search-tips.webdoc:497 #: modules/websearch/doc/search-tips.webdoc:498 #: modules/websearch/doc/search-tips.webdoc:499 #: modules/websearch/doc/search-tips.webdoc:500 #: modules/websearch/doc/search-tips.webdoc:501 #: modules/websearch/doc/search-tips.webdoc:519 #: modules/websearch/doc/search-tips.webdoc:520 #: modules/websearch/doc/search-tips.webdoc:521 #: modules/websearch/doc/search-tips.webdoc:522 #: modules/websearch/doc/search-tips.webdoc:523 #: modules/bibcirculation/lib/bibcirculation_templates.py:6151 #: modules/bibcirculation/lib/bibcirculation_templates.py:6760 #: modules/miscutil/lib/inveniocfg.py:663 msgid "year" msgstr "سال" #: modules/websearch/doc/search-guide.webdoc:447 #: modules/websearch/doc/search-guide.webdoc:483 #: modules/websearch/doc/search-guide.webdoc:518 #: modules/websearch/doc/search-guide.webdoc:587 #: modules/websearch/doc/search-guide.webdoc:623 #: modules/websearch/doc/search-guide.webdoc:658 #: modules/websearch/doc/search-guide.webdoc:726 #: modules/websearch/doc/search-guide.webdoc:762 #: modules/websearch/doc/search-guide.webdoc:797 #: modules/websearch/doc/search-guide.webdoc:870 #: modules/websearch/doc/search-guide.webdoc:905 #: modules/websearch/doc/search-guide.webdoc:941 #: modules/websearch/doc/search-guide.webdoc:1014 #: modules/websearch/doc/search-guide.webdoc:1050 #: modules/websearch/doc/search-guide.webdoc:1086 #: modules/miscutil/lib/inveniocfg.py:658 msgid "subject" msgstr "موضوع" #: modules/websearch/doc/search-tips.webdoc:344 #: modules/bibedit/lib/bibeditmulti_templates.py:630 #: modules/oaiharvest/lib/oai_harvest_admin.py:371 #: modules/websubmit/lib/websubmit_templates.py:1093 msgid "or" msgstr "یا" #: modules/websearch/doc/search-guide.webdoc:431 #: modules/websearch/doc/search-guide.webdoc:466 #: modules/websearch/doc/search-guide.webdoc:502 #: modules/websearch/doc/search-guide.webdoc:571 #: modules/websearch/doc/search-guide.webdoc:606 #: modules/websearch/doc/search-guide.webdoc:642 #: modules/websearch/doc/search-guide.webdoc:710 #: modules/websearch/doc/search-guide.webdoc:745 #: modules/websearch/doc/search-guide.webdoc:781 #: modules/websearch/doc/search-guide.webdoc:853 #: modules/websearch/doc/search-guide.webdoc:889 #: modules/websearch/doc/search-guide.webdoc:924 #: modules/websearch/doc/search-guide.webdoc:997 #: modules/websearch/doc/search-guide.webdoc:1033 #: modules/websearch/doc/search-guide.webdoc:1069 #: modules/websearch/lib/search_engine.py:1426 #: modules/websearch/lib/websearch_templates.py:1301 msgid "Partial phrase:" msgstr "عبارت نسبی" #: modules/websearch/doc/search-guide.webdoc:445 #: modules/websearch/doc/search-guide.webdoc:481 #: modules/websearch/doc/search-guide.webdoc:516 #: modules/websearch/doc/search-guide.webdoc:585 #: modules/websearch/doc/search-guide.webdoc:621 #: modules/websearch/doc/search-guide.webdoc:656 #: modules/websearch/doc/search-guide.webdoc:724 #: modules/websearch/doc/search-guide.webdoc:760 #: modules/websearch/doc/search-guide.webdoc:795 #: modules/websearch/doc/search-guide.webdoc:868 #: modules/websearch/doc/search-guide.webdoc:903 #: modules/websearch/doc/search-guide.webdoc:939 #: modules/websearch/doc/search-guide.webdoc:1012 #: modules/websearch/doc/search-guide.webdoc:1048 #: modules/websearch/doc/search-guide.webdoc:1084 #: modules/miscutil/lib/inveniocfg.py:659 msgid "reference" msgstr "ارجاع" #: modules/websearch/doc/search-guide.webdoc:278 #: modules/websearch/doc/search-guide.webdoc:306 #: modules/websearch/doc/search-guide.webdoc:335 #: modules/websearch/doc/search-guide.webdoc:364 #: modules/websearch/doc/search-guide.webdoc:393 #: modules/websearch/doc/search-guide.webdoc:439 #: modules/websearch/doc/search-guide.webdoc:475 #: modules/websearch/doc/search-guide.webdoc:510 #: modules/websearch/doc/search-guide.webdoc:579 #: modules/websearch/doc/search-guide.webdoc:615 #: modules/websearch/doc/search-guide.webdoc:650 #: modules/websearch/doc/search-guide.webdoc:718 #: modules/websearch/doc/search-guide.webdoc:754 #: modules/websearch/doc/search-guide.webdoc:789 #: modules/websearch/doc/search-guide.webdoc:862 #: modules/websearch/doc/search-guide.webdoc:897 #: modules/websearch/doc/search-guide.webdoc:933 #: modules/websearch/doc/search-guide.webdoc:1006 #: modules/websearch/doc/search-guide.webdoc:1042 #: modules/websearch/doc/search-guide.webdoc:1078 #: modules/websearch/doc/search-guide.webdoc:1145 #: modules/websearch/doc/search-guide.webdoc:1178 #: modules/websearch/doc/search-guide.webdoc:1211 #: modules/websearch/doc/search-guide.webdoc:1248 #: modules/websearch/doc/search-guide.webdoc:1286 #: modules/websearch/doc/search-guide.webdoc:1338 #: modules/websearch/doc/search-guide.webdoc:1362 #: modules/websearch/doc/search-guide.webdoc:1387 #: modules/websearch/doc/search-guide.webdoc:1405 #: modules/websearch/doc/search-guide.webdoc:1455 #: modules/websearch/doc/search-guide.webdoc:1480 #: modules/websearch/doc/search-guide.webdoc:1507 #: modules/websearch/doc/search-guide.webdoc:1525 #: modules/websearch/doc/search-guide.webdoc:1576 #: modules/websearch/doc/search-guide.webdoc:1601 #: modules/websearch/doc/search-guide.webdoc:1625 #: modules/websearch/doc/search-guide.webdoc:1643 #: modules/websearch/doc/search-guide.webdoc:1691 #: modules/websearch/doc/search-guide.webdoc:1717 #: modules/websearch/doc/search-guide.webdoc:1742 #: modules/websearch/doc/search-guide.webdoc:1760 #: modules/websearch/doc/search-guide.webdoc:1809 #: modules/websearch/doc/search-guide.webdoc:1835 #: modules/websearch/doc/search-guide.webdoc:1859 #: modules/websearch/doc/search-guide.webdoc:1877 #: modules/websearch/doc/search-guide.webdoc:2332 #: modules/websearch/doc/search-guide.webdoc:2348 #: modules/websearch/doc/search-guide.webdoc:2368 #: modules/websearch/doc/search-guide.webdoc:2390 #: modules/websearch/doc/search-guide.webdoc:2406 #: modules/websearch/doc/search-guide.webdoc:2427 #: modules/websearch/doc/search-guide.webdoc:2449 #: modules/websearch/doc/search-guide.webdoc:2465 #: modules/websearch/doc/search-guide.webdoc:2486 #: modules/websearch/doc/search-guide.webdoc:2508 #: modules/websearch/doc/search-guide.webdoc:2525 #: modules/websearch/doc/search-guide.webdoc:2546 #: modules/websearch/doc/search-guide.webdoc:2569 #: modules/websearch/doc/search-guide.webdoc:2586 #: modules/websearch/doc/search-guide.webdoc:2607 #: modules/websearch/doc/search-guide.webdoc:2665 #: modules/websearch/doc/search-guide.webdoc:2754 #: modules/websearch/doc/search-guide.webdoc:2767 #: modules/websearch/doc/search-guide.webdoc:2783 #: modules/websearch/doc/search-guide.webdoc:2799 #: modules/websearch/doc/search-guide.webdoc:2814 #: modules/websearch/doc/search-guide.webdoc:2834 #: modules/websearch/doc/search-guide.webdoc:2847 #: modules/websearch/doc/search-guide.webdoc:2863 #: modules/websearch/doc/search-guide.webdoc:2879 #: modules/websearch/doc/search-guide.webdoc:2894 #: modules/websearch/doc/search-guide.webdoc:2914 #: modules/websearch/doc/search-guide.webdoc:2927 #: modules/websearch/doc/search-guide.webdoc:2943 #: modules/websearch/doc/search-guide.webdoc:2959 #: modules/websearch/doc/search-guide.webdoc:2974 #: modules/websearch/doc/search-guide.webdoc:2994 #: modules/websearch/doc/search-guide.webdoc:3007 #: modules/websearch/doc/search-guide.webdoc:3023 #: modules/websearch/doc/search-guide.webdoc:3039 #: modules/websearch/doc/search-guide.webdoc:3054 #: modules/websearch/doc/search-guide.webdoc:3074 #: modules/websearch/doc/search-guide.webdoc:3087 #: modules/websearch/doc/search-guide.webdoc:3103 #: modules/websearch/doc/search-guide.webdoc:3119 #: modules/websearch/doc/search-guide.webdoc:3134 #: modules/websearch/doc/search-guide.webdoc:3169 #: modules/websearch/doc/search-guide.webdoc:3186 #: modules/websearch/doc/search-guide.webdoc:3207 #: modules/websearch/doc/search-guide.webdoc:3224 #: modules/websearch/doc/search-guide.webdoc:3244 #: modules/websearch/doc/search-guide.webdoc:3261 #: modules/websearch/doc/search-guide.webdoc:3282 #: modules/websearch/doc/search-guide.webdoc:3299 #: modules/websearch/doc/search-guide.webdoc:3319 #: modules/websearch/doc/search-guide.webdoc:3337 #: modules/websearch/doc/search-guide.webdoc:3373 #: modules/websearch/doc/search-guide.webdoc:3390 #: modules/websearch/doc/search-guide.webdoc:3406 #: modules/websearch/doc/search-guide.webdoc:3423 #: modules/websearch/doc/search-guide.webdoc:3453 #: modules/websearch/doc/search-guide.webdoc:3470 #: modules/websearch/doc/search-guide.webdoc:3486 #: modules/websearch/doc/search-guide.webdoc:3503 #: modules/websearch/doc/search-guide.webdoc:3536 #: modules/websearch/doc/search-guide.webdoc:3554 #: modules/websearch/doc/search-guide.webdoc:3571 #: modules/websearch/doc/search-guide.webdoc:3589 #: modules/websearch/doc/search-guide.webdoc:3623 #: modules/websearch/doc/search-guide.webdoc:3641 #: modules/websearch/doc/search-guide.webdoc:3657 #: modules/websearch/doc/search-guide.webdoc:3674 #: modules/websearch/doc/search-guide.webdoc:3708 #: modules/websearch/doc/search-guide.webdoc:3726 #: modules/websearch/doc/search-guide.webdoc:3742 #: modules/websearch/doc/search-guide.webdoc:3759 #: modules/websearch/doc/search-guide.webdoc:3807 #: modules/websearch/doc/search-guide.webdoc:3824 #: modules/websearch/doc/search-guide.webdoc:3840 #: modules/websearch/doc/search-guide.webdoc:3869 #: modules/websearch/doc/search-guide.webdoc:3886 #: modules/websearch/doc/search-guide.webdoc:3902 #: modules/websearch/doc/search-guide.webdoc:3931 #: modules/websearch/doc/search-guide.webdoc:3948 #: modules/websearch/doc/search-guide.webdoc:3964 #: modules/websearch/doc/search-guide.webdoc:3996 #: modules/websearch/doc/search-guide.webdoc:4013 #: modules/websearch/doc/search-guide.webdoc:4029 #: modules/websearch/doc/search-guide.webdoc:4058 #: modules/websearch/doc/search-guide.webdoc:4075 #: modules/websearch/doc/search-guide.webdoc:4091 #: modules/websearch/doc/search-guide.webdoc:4131 #: modules/websearch/doc/search-guide.webdoc:4158 #: modules/websearch/doc/search-guide.webdoc:4185 #: modules/websearch/doc/search-guide.webdoc:4213 #: modules/websearch/doc/search-guide.webdoc:4241 #: modules/websearch/doc/search-guide.webdoc:4268 #: modules/websearch/doc/search-guide.webdoc:4287 #: modules/websearch/doc/search-guide.webdoc:4307 #: modules/websearch/doc/search-guide.webdoc:4326 #: modules/websearch/doc/search-guide.webdoc:4345 #: modules/websearch/doc/search-guide.webdoc:4368 #: modules/websearch/doc/search-guide.webdoc:4389 #: modules/websearch/doc/search-guide.webdoc:4411 #: modules/websearch/doc/search-guide.webdoc:4432 #: modules/websearch/doc/search-guide.webdoc:4453 #: modules/websearch/doc/search-guide.webdoc:4476 #: modules/websearch/doc/search-guide.webdoc:4493 #: modules/websearch/doc/search-guide.webdoc:4515 #: modules/websearch/doc/search-guide.webdoc:4532 #: modules/websearch/doc/search-guide.webdoc:4555 #: modules/websearch/doc/search-guide.webdoc:4572 #: modules/websearch/doc/search-guide.webdoc:4595 #: modules/websearch/doc/search-guide.webdoc:4612 #: modules/websearch/doc/search-guide.webdoc:4634 #: modules/websearch/doc/search-guide.webdoc:4651 #: modules/websearch/doc/search-guide.webdoc:4727 #: modules/websearch/doc/search-guide.webdoc:4743 #: modules/websearch/doc/search-guide.webdoc:4762 #: modules/websearch/doc/search-guide.webdoc:4778 #: modules/websearch/doc/search-guide.webdoc:4797 #: modules/websearch/doc/search-guide.webdoc:4814 #: modules/websearch/doc/search-guide.webdoc:4834 #: modules/websearch/doc/search-guide.webdoc:4851 #: modules/websearch/doc/search-guide.webdoc:4871 #: modules/websearch/doc/search-guide.webdoc:4888 #: modules/websearch/doc/search-guide.webdoc:4952 #: modules/websearch/doc/search-guide.webdoc:4993 #: modules/websearch/doc/search-guide.webdoc:5033 #: modules/websearch/doc/search-guide.webdoc:5085 #: modules/websearch/doc/search-guide.webdoc:5139 #: modules/websearch/doc/search-guide.webdoc:5194 #: modules/websearch/doc/search-guide.webdoc:5230 #: modules/websearch/doc/search-guide.webdoc:5250 #: modules/websearch/doc/search-guide.webdoc:5269 #: modules/websearch/doc/search-guide.webdoc:5292 #: modules/websearch/doc/search-guide.webdoc:5313 #: modules/websearch/doc/search-guide.webdoc:5333 #: modules/websearch/doc/search-guide.webdoc:5356 #: modules/websearch/doc/search-guide.webdoc:5377 #: modules/websearch/doc/search-guide.webdoc:5397 #: modules/websearch/doc/search-guide.webdoc:5421 #: modules/websearch/doc/search-guide.webdoc:5443 #: modules/websearch/doc/search-guide.webdoc:5463 #: modules/websearch/doc/search-guide.webdoc:5486 #: modules/websearch/doc/search-guide.webdoc:5507 #: modules/websearch/doc/search-guide.webdoc:5527 #: modules/websearch/doc/search-guide.webdoc:5684 #: modules/websearch/doc/search-guide.webdoc:5713 #: modules/websearch/doc/search-guide.webdoc:5740 #: modules/websearch/doc/search-guide.webdoc:5764 #: modules/websearch/doc/search-guide.webdoc:5792 #: modules/websearch/doc/search-guide.webdoc:5825 #: modules/websearch/doc/search-guide.webdoc:5867 #: modules/websearch/doc/search-guide.webdoc:5898 #: modules/websearch/doc/search-guide.webdoc:5926 #: modules/websearch/doc/search-guide.webdoc:5951 #: modules/websearch/doc/search-guide.webdoc:5979 #: modules/websearch/doc/search-guide.webdoc:6014 #: modules/websearch/doc/search-guide.webdoc:6057 #: modules/websearch/doc/search-guide.webdoc:6088 #: modules/websearch/doc/search-guide.webdoc:6116 #: modules/websearch/doc/search-guide.webdoc:6143 #: modules/websearch/doc/search-guide.webdoc:6172 #: modules/websearch/doc/search-guide.webdoc:6207 #: modules/websearch/doc/search-guide.webdoc:6253 #: modules/websearch/doc/search-guide.webdoc:6285 #: modules/websearch/doc/search-guide.webdoc:6314 #: modules/websearch/doc/search-guide.webdoc:6339 #: modules/websearch/doc/search-guide.webdoc:6369 #: modules/websearch/doc/search-guide.webdoc:6405 #: modules/websearch/doc/search-guide.webdoc:6452 #: modules/websearch/doc/search-guide.webdoc:6483 #: modules/websearch/doc/search-guide.webdoc:6512 #: modules/websearch/doc/search-guide.webdoc:6538 #: modules/websearch/doc/search-guide.webdoc:6567 #: modules/websearch/doc/search-guide.webdoc:6602 #: modules/websearch/doc/search-guide.webdoc:6976 #: modules/websearch/doc/search-guide.webdoc:6995 #: modules/websearch/doc/search-guide.webdoc:7017 #: modules/websearch/doc/search-guide.webdoc:7037 #: modules/websearch/doc/search-guide.webdoc:7058 #: modules/websearch/doc/search-guide.webdoc:7078 #: modules/websearch/doc/search-guide.webdoc:7100 #: modules/websearch/doc/search-guide.webdoc:7121 #: modules/websearch/doc/search-guide.webdoc:7143 #: modules/websearch/doc/search-guide.webdoc:7163 #: modules/websearch/doc/search-guide.webdoc:7200 #: modules/websearch/doc/search-guide.webdoc:7216 #: modules/websearch/doc/search-guide.webdoc:7233 #: modules/websearch/doc/search-guide.webdoc:7252 #: modules/websearch/doc/search-guide.webdoc:7275 #: modules/websearch/doc/search-guide.webdoc:7292 #: modules/websearch/doc/search-guide.webdoc:7310 #: modules/websearch/doc/search-guide.webdoc:7330 #: modules/websearch/doc/search-guide.webdoc:7351 #: modules/websearch/doc/search-guide.webdoc:7367 #: modules/websearch/doc/search-guide.webdoc:7385 #: modules/websearch/doc/search-guide.webdoc:7405 #: modules/websearch/doc/search-guide.webdoc:7427 #: modules/websearch/doc/search-guide.webdoc:7444 #: modules/websearch/doc/search-guide.webdoc:7462 #: modules/websearch/doc/search-guide.webdoc:7483 #: modules/websearch/doc/search-guide.webdoc:7505 #: modules/websearch/doc/search-guide.webdoc:7522 #: modules/websearch/doc/search-guide.webdoc:7539 #: modules/websearch/doc/search-guide.webdoc:7561 #: modules/websearch/doc/search-tips.webdoc:39 #: modules/websearch/doc/search-tips.webdoc:75 #: modules/websearch/doc/search-tips.webdoc:121 #: modules/websearch/doc/search-tips.webdoc:178 #: modules/websearch/doc/search-tips.webdoc:205 #: modules/websearch/doc/search-tips.webdoc:233 #: modules/websearch/doc/search-tips.webdoc:292 #: modules/websearch/doc/search-tips.webdoc:340 #: modules/websearch/doc/search-tips.webdoc:351 #: modules/websearch/doc/search-tips.webdoc:375 #: modules/websearch/doc/search-tips.webdoc:399 #: modules/websearch/doc/search-tips.webdoc:442 #: modules/websearch/doc/search-tips.webdoc:489 #: modules/websearch/doc/search-tips.webdoc:512 #: modules/websearch/doc/search-tips.webdoc:534 #: modules/websearch/doc/search-tips.webdoc:575 #: modules/websearch/doc/search-tips.webdoc:596 #: modules/websearch/doc/search-tips.webdoc:621 #: modules/websearch/doc/search-tips.webdoc:665 #: modules/websearch/doc/search-tips.webdoc:694 #: modules/websearch/doc/search-tips.webdoc:702 #: modules/websearch/doc/search-tips.webdoc:705 #: modules/websearch/doc/search-tips.webdoc:707 #: modules/websearch/doc/search-tips.webdoc:709 #: modules/websearch/doc/search-tips.webdoc:711 #: modules/websearch/doc/search-tips.webdoc:722 #: modules/websearch/doc/search-tips.webdoc:745 #: modules/webstyle/doc/hacking/webstyle-webdoc-syntax.webdoc:131 #: modules/bibcirculation/lib/bibcirculation_templates.py:2928 #: modules/bibcirculation/lib/bibcirculation_templates.py:2936 #: modules/bibcirculation/lib/bibcirculation_templates.py:2944 #: modules/bibcirculation/lib/bibcirculation_templates.py:2952 #: modules/bibcirculation/lib/bibcirculation_templates.py:6152 #: modules/bibcirculation/lib/bibcirculation_templates.py:6761 #: modules/bibcirculation/lib/bibcirculation_templates.py:10252 #: modules/miscutil/lib/inveniocfg.py:654 msgid "author" msgstr "نویسنده" #: modules/webhelp/web/help-central.webdoc:98 #: modules/websearch/doc/search-guide.webdoc:20 msgid "Search Guide" msgstr "راهنمای جستجو" #: modules/bibformat/etc/format_templates/Default_HTML_actions.bft:6 msgid "Export as" msgstr "خروجی به صورت " #: modules/websearch/doc/hacking/search-services.webdoc:20 #, fuzzy msgid "Search Services" msgstr "نکات جستجو" #: modules/websearch/doc/search-guide.webdoc:429 #: modules/websearch/doc/search-guide.webdoc:464 #: modules/websearch/doc/search-guide.webdoc:500 #: modules/websearch/doc/search-guide.webdoc:569 #: modules/websearch/doc/search-guide.webdoc:604 #: modules/websearch/doc/search-guide.webdoc:640 #: modules/websearch/doc/search-guide.webdoc:708 #: modules/websearch/doc/search-guide.webdoc:743 #: modules/websearch/doc/search-guide.webdoc:779 #: modules/websearch/doc/search-guide.webdoc:851 #: modules/websearch/doc/search-guide.webdoc:887 #: modules/websearch/doc/search-guide.webdoc:922 #: modules/websearch/doc/search-guide.webdoc:995 #: modules/websearch/doc/search-guide.webdoc:1031 #: modules/websearch/doc/search-guide.webdoc:1067 #: modules/websearch/lib/search_engine.py:1424 #: modules/websearch/lib/websearch_templates.py:1297 msgid "Any of the words:" msgstr "هر کدام از کلمات" #: modules/websearch/doc/search-guide.webdoc:441 #: modules/websearch/doc/search-guide.webdoc:477 #: modules/websearch/doc/search-guide.webdoc:512 #: modules/websearch/doc/search-guide.webdoc:581 #: modules/websearch/doc/search-guide.webdoc:617 #: modules/websearch/doc/search-guide.webdoc:652 #: modules/websearch/doc/search-guide.webdoc:720 #: modules/websearch/doc/search-guide.webdoc:756 #: modules/websearch/doc/search-guide.webdoc:791 #: modules/websearch/doc/search-guide.webdoc:864 #: modules/websearch/doc/search-guide.webdoc:899 #: modules/websearch/doc/search-guide.webdoc:935 #: modules/websearch/doc/search-guide.webdoc:1008 #: modules/websearch/doc/search-guide.webdoc:1044 #: modules/websearch/doc/search-guide.webdoc:1080 #: modules/miscutil/lib/inveniocfg.py:662 msgid "division" msgstr "بخش ها" #: modules/websearch/doc/search-guide.webdoc:280 #: modules/websearch/doc/search-guide.webdoc:308 #: modules/websearch/doc/search-guide.webdoc:337 #: modules/websearch/doc/search-guide.webdoc:366 #: modules/websearch/doc/search-guide.webdoc:395 #: modules/websearch/doc/search-guide.webdoc:524 #: modules/websearch/doc/search-guide.webdoc:664 #: modules/websearch/doc/search-guide.webdoc:803 #: modules/websearch/doc/search-guide.webdoc:947 #: modules/websearch/doc/search-guide.webdoc:1092 #: modules/websearch/doc/search-guide.webdoc:1147 #: modules/websearch/doc/search-guide.webdoc:1180 #: modules/websearch/doc/search-guide.webdoc:1213 #: modules/websearch/doc/search-guide.webdoc:1250 #: modules/websearch/doc/search-guide.webdoc:1288 #: modules/websearch/doc/search-guide.webdoc:1340 #: modules/websearch/doc/search-guide.webdoc:1364 #: modules/websearch/doc/search-guide.webdoc:1389 #: modules/websearch/doc/search-guide.webdoc:1407 #: modules/websearch/doc/search-guide.webdoc:1457 #: modules/websearch/doc/search-guide.webdoc:1482 #: modules/websearch/doc/search-guide.webdoc:1509 #: modules/websearch/doc/search-guide.webdoc:1527 #: modules/websearch/doc/search-guide.webdoc:1578 #: modules/websearch/doc/search-guide.webdoc:1603 #: modules/websearch/doc/search-guide.webdoc:1627 #: modules/websearch/doc/search-guide.webdoc:1645 #: modules/websearch/doc/search-guide.webdoc:1693 #: modules/websearch/doc/search-guide.webdoc:1719 #: modules/websearch/doc/search-guide.webdoc:1744 #: modules/websearch/doc/search-guide.webdoc:1762 #: modules/websearch/doc/search-guide.webdoc:1811 #: modules/websearch/doc/search-guide.webdoc:1837 #: modules/websearch/doc/search-guide.webdoc:1861 #: modules/websearch/doc/search-guide.webdoc:1879 #: modules/websearch/doc/search-guide.webdoc:2334 #: modules/websearch/doc/search-guide.webdoc:2350 #: modules/websearch/doc/search-guide.webdoc:2370 #: modules/websearch/doc/search-guide.webdoc:2392 #: modules/websearch/doc/search-guide.webdoc:2408 #: modules/websearch/doc/search-guide.webdoc:2429 #: modules/websearch/doc/search-guide.webdoc:2451 #: modules/websearch/doc/search-guide.webdoc:2467 #: modules/websearch/doc/search-guide.webdoc:2488 #: modules/websearch/doc/search-guide.webdoc:2510 #: modules/websearch/doc/search-guide.webdoc:2527 #: modules/websearch/doc/search-guide.webdoc:2548 #: modules/websearch/doc/search-guide.webdoc:2571 #: modules/websearch/doc/search-guide.webdoc:2588 #: modules/websearch/doc/search-guide.webdoc:2609 #: modules/websearch/doc/search-guide.webdoc:2667 #: modules/websearch/doc/search-guide.webdoc:2756 #: modules/websearch/doc/search-guide.webdoc:2769 #: modules/websearch/doc/search-guide.webdoc:2785 #: modules/websearch/doc/search-guide.webdoc:2801 #: modules/websearch/doc/search-guide.webdoc:2816 #: modules/websearch/doc/search-guide.webdoc:2836 #: modules/websearch/doc/search-guide.webdoc:2849 #: modules/websearch/doc/search-guide.webdoc:2865 #: modules/websearch/doc/search-guide.webdoc:2881 #: modules/websearch/doc/search-guide.webdoc:2896 #: modules/websearch/doc/search-guide.webdoc:2916 #: modules/websearch/doc/search-guide.webdoc:2929 #: modules/websearch/doc/search-guide.webdoc:2945 #: modules/websearch/doc/search-guide.webdoc:2961 #: modules/websearch/doc/search-guide.webdoc:2976 #: modules/websearch/doc/search-guide.webdoc:2996 #: modules/websearch/doc/search-guide.webdoc:3009 #: modules/websearch/doc/search-guide.webdoc:3025 #: modules/websearch/doc/search-guide.webdoc:3041 #: modules/websearch/doc/search-guide.webdoc:3056 #: modules/websearch/doc/search-guide.webdoc:3076 #: modules/websearch/doc/search-guide.webdoc:3089 #: modules/websearch/doc/search-guide.webdoc:3105 #: modules/websearch/doc/search-guide.webdoc:3121 #: modules/websearch/doc/search-guide.webdoc:3136 #: modules/websearch/doc/search-guide.webdoc:3171 #: modules/websearch/doc/search-guide.webdoc:3188 #: modules/websearch/doc/search-guide.webdoc:3209 #: modules/websearch/doc/search-guide.webdoc:3226 #: modules/websearch/doc/search-guide.webdoc:3246 #: modules/websearch/doc/search-guide.webdoc:3263 #: modules/websearch/doc/search-guide.webdoc:3283 #: modules/websearch/doc/search-guide.webdoc:3301 #: modules/websearch/doc/search-guide.webdoc:3321 #: modules/websearch/doc/search-guide.webdoc:3339 #: modules/websearch/doc/search-guide.webdoc:3375 #: modules/websearch/doc/search-guide.webdoc:3392 #: modules/websearch/doc/search-guide.webdoc:3408 #: modules/websearch/doc/search-guide.webdoc:3425 #: modules/websearch/doc/search-guide.webdoc:3455 #: modules/websearch/doc/search-guide.webdoc:3472 #: modules/websearch/doc/search-guide.webdoc:3488 #: modules/websearch/doc/search-guide.webdoc:3505 #: modules/websearch/doc/search-guide.webdoc:3538 #: modules/websearch/doc/search-guide.webdoc:3556 #: modules/websearch/doc/search-guide.webdoc:3573 #: modules/websearch/doc/search-guide.webdoc:3591 #: modules/websearch/doc/search-guide.webdoc:3625 #: modules/websearch/doc/search-guide.webdoc:3643 #: modules/websearch/doc/search-guide.webdoc:3659 #: modules/websearch/doc/search-guide.webdoc:3676 #: modules/websearch/doc/search-guide.webdoc:3710 #: modules/websearch/doc/search-guide.webdoc:3728 #: modules/websearch/doc/search-guide.webdoc:3744 #: modules/websearch/doc/search-guide.webdoc:3761 #: modules/websearch/doc/search-guide.webdoc:3809 #: modules/websearch/doc/search-guide.webdoc:3826 #: modules/websearch/doc/search-guide.webdoc:3842 #: modules/websearch/doc/search-guide.webdoc:3871 #: modules/websearch/doc/search-guide.webdoc:3888 #: modules/websearch/doc/search-guide.webdoc:3904 #: modules/websearch/doc/search-guide.webdoc:3933 #: modules/websearch/doc/search-guide.webdoc:3950 #: modules/websearch/doc/search-guide.webdoc:3966 #: modules/websearch/doc/search-guide.webdoc:3998 #: modules/websearch/doc/search-guide.webdoc:4015 #: modules/websearch/doc/search-guide.webdoc:4031 #: modules/websearch/doc/search-guide.webdoc:4060 #: modules/websearch/doc/search-guide.webdoc:4077 #: modules/websearch/doc/search-guide.webdoc:4093 #: modules/websearch/doc/search-guide.webdoc:4133 #: modules/websearch/doc/search-guide.webdoc:4160 #: modules/websearch/doc/search-guide.webdoc:4187 #: modules/websearch/doc/search-guide.webdoc:4215 #: modules/websearch/doc/search-guide.webdoc:4243 #: modules/websearch/doc/search-guide.webdoc:4270 #: modules/websearch/doc/search-guide.webdoc:4289 #: modules/websearch/doc/search-guide.webdoc:4309 #: modules/websearch/doc/search-guide.webdoc:4328 #: modules/websearch/doc/search-guide.webdoc:4347 #: modules/websearch/doc/search-guide.webdoc:4370 #: modules/websearch/doc/search-guide.webdoc:4391 #: modules/websearch/doc/search-guide.webdoc:4413 #: modules/websearch/doc/search-guide.webdoc:4434 #: modules/websearch/doc/search-guide.webdoc:4454 #: modules/websearch/doc/search-guide.webdoc:4478 #: modules/websearch/doc/search-guide.webdoc:4495 #: modules/websearch/doc/search-guide.webdoc:4517 #: modules/websearch/doc/search-guide.webdoc:4534 #: modules/websearch/doc/search-guide.webdoc:4557 #: modules/websearch/doc/search-guide.webdoc:4574 #: modules/websearch/doc/search-guide.webdoc:4597 #: modules/websearch/doc/search-guide.webdoc:4614 #: modules/websearch/doc/search-guide.webdoc:4636 #: modules/websearch/doc/search-guide.webdoc:4653 #: modules/websearch/doc/search-guide.webdoc:4729 #: modules/websearch/doc/search-guide.webdoc:4745 #: modules/websearch/doc/search-guide.webdoc:4764 #: modules/websearch/doc/search-guide.webdoc:4780 #: modules/websearch/doc/search-guide.webdoc:4799 #: modules/websearch/doc/search-guide.webdoc:4816 #: modules/websearch/doc/search-guide.webdoc:4836 #: modules/websearch/doc/search-guide.webdoc:4853 #: modules/websearch/doc/search-guide.webdoc:4873 #: modules/websearch/doc/search-guide.webdoc:4890 #: modules/websearch/doc/search-guide.webdoc:4954 #: modules/websearch/doc/search-guide.webdoc:4995 #: modules/websearch/doc/search-guide.webdoc:5035 #: modules/websearch/doc/search-guide.webdoc:5087 #: modules/websearch/doc/search-guide.webdoc:5141 #: modules/websearch/doc/search-guide.webdoc:5196 #: modules/websearch/doc/search-guide.webdoc:5232 #: modules/websearch/doc/search-guide.webdoc:5252 #: modules/websearch/doc/search-guide.webdoc:5271 #: modules/websearch/doc/search-guide.webdoc:5294 #: modules/websearch/doc/search-guide.webdoc:5315 #: modules/websearch/doc/search-guide.webdoc:5335 #: modules/websearch/doc/search-guide.webdoc:5358 #: modules/websearch/doc/search-guide.webdoc:5379 #: modules/websearch/doc/search-guide.webdoc:5399 #: modules/websearch/doc/search-guide.webdoc:5423 #: modules/websearch/doc/search-guide.webdoc:5445 #: modules/websearch/doc/search-guide.webdoc:5464 #: modules/websearch/doc/search-guide.webdoc:5488 #: modules/websearch/doc/search-guide.webdoc:5509 #: modules/websearch/doc/search-guide.webdoc:5529 #: modules/websearch/doc/search-guide.webdoc:5686 #: modules/websearch/doc/search-guide.webdoc:5715 #: modules/websearch/doc/search-guide.webdoc:5742 #: modules/websearch/doc/search-guide.webdoc:5766 #: modules/websearch/doc/search-guide.webdoc:5794 #: modules/websearch/doc/search-guide.webdoc:5827 #: modules/websearch/doc/search-guide.webdoc:5869 #: modules/websearch/doc/search-guide.webdoc:5900 #: modules/websearch/doc/search-guide.webdoc:5928 #: modules/websearch/doc/search-guide.webdoc:5953 #: modules/websearch/doc/search-guide.webdoc:5981 #: modules/websearch/doc/search-guide.webdoc:6016 #: modules/websearch/doc/search-guide.webdoc:6059 #: modules/websearch/doc/search-guide.webdoc:6090 #: modules/websearch/doc/search-guide.webdoc:6118 #: modules/websearch/doc/search-guide.webdoc:6145 #: modules/websearch/doc/search-guide.webdoc:6174 #: modules/websearch/doc/search-guide.webdoc:6209 #: modules/websearch/doc/search-guide.webdoc:6255 #: modules/websearch/doc/search-guide.webdoc:6287 #: modules/websearch/doc/search-guide.webdoc:6316 #: modules/websearch/doc/search-guide.webdoc:6341 #: modules/websearch/doc/search-guide.webdoc:6371 #: modules/websearch/doc/search-guide.webdoc:6407 #: modules/websearch/doc/search-guide.webdoc:6454 #: modules/websearch/doc/search-guide.webdoc:6485 #: modules/websearch/doc/search-guide.webdoc:6514 #: modules/websearch/doc/search-guide.webdoc:6540 #: modules/websearch/doc/search-guide.webdoc:6569 #: modules/websearch/doc/search-guide.webdoc:6604 #: modules/websearch/doc/search-guide.webdoc:6978 #: modules/websearch/doc/search-guide.webdoc:6997 #: modules/websearch/doc/search-guide.webdoc:7019 #: modules/websearch/doc/search-guide.webdoc:7039 #: modules/websearch/doc/search-guide.webdoc:7059 #: modules/websearch/doc/search-guide.webdoc:7080 #: modules/websearch/doc/search-guide.webdoc:7102 #: modules/websearch/doc/search-guide.webdoc:7123 #: modules/websearch/doc/search-guide.webdoc:7145 #: modules/websearch/doc/search-guide.webdoc:7165 #: modules/websearch/doc/search-guide.webdoc:7202 #: modules/websearch/doc/search-guide.webdoc:7218 #: modules/websearch/doc/search-guide.webdoc:7235 #: modules/websearch/doc/search-guide.webdoc:7254 #: modules/websearch/doc/search-guide.webdoc:7277 #: modules/websearch/doc/search-guide.webdoc:7294 #: modules/websearch/doc/search-guide.webdoc:7312 #: modules/websearch/doc/search-guide.webdoc:7332 #: modules/websearch/doc/search-guide.webdoc:7353 #: modules/websearch/doc/search-guide.webdoc:7369 #: modules/websearch/doc/search-guide.webdoc:7387 #: modules/websearch/doc/search-guide.webdoc:7407 #: modules/websearch/doc/search-guide.webdoc:7429 #: modules/websearch/doc/search-guide.webdoc:7446 #: modules/websearch/doc/search-guide.webdoc:7464 #: modules/websearch/doc/search-guide.webdoc:7485 #: modules/websearch/doc/search-guide.webdoc:7507 #: modules/websearch/doc/search-guide.webdoc:7524 #: modules/websearch/doc/search-guide.webdoc:7541 #: modules/websearch/doc/search-guide.webdoc:7563 #: modules/websearch/doc/search-tips.webdoc:41 #: modules/websearch/doc/search-tips.webdoc:77 #: modules/websearch/doc/search-tips.webdoc:123 #: modules/websearch/doc/search-tips.webdoc:180 #: modules/websearch/doc/search-tips.webdoc:207 #: modules/websearch/doc/search-tips.webdoc:235 #: modules/websearch/doc/search-tips.webdoc:294 #: modules/websearch/doc/search-tips.webdoc:342 #: modules/websearch/doc/search-tips.webdoc:353 #: modules/websearch/doc/search-tips.webdoc:377 #: modules/websearch/doc/search-tips.webdoc:401 #: modules/websearch/doc/search-tips.webdoc:444 #: modules/websearch/doc/search-tips.webdoc:492 #: modules/websearch/doc/search-tips.webdoc:514 #: modules/websearch/doc/search-tips.webdoc:536 #: modules/websearch/doc/search-tips.webdoc:577 #: modules/websearch/doc/search-tips.webdoc:598 #: modules/websearch/doc/search-tips.webdoc:623 #: modules/websearch/doc/search-tips.webdoc:667 #: modules/websearch/doc/search-tips.webdoc:696 #: modules/websearch/doc/search-tips.webdoc:724 #: modules/websearch/doc/search-tips.webdoc:747 #: modules/webstyle/doc/hacking/webstyle-webdoc-syntax.webdoc:133 #: modules/bibcirculation/lib/bibcirculation_templates.py:155 #: modules/bibcirculation/lib/bibcirculation_templates.py:199 #: modules/bibcirculation/lib/bibcirculation_templates.py:1876 #: modules/bibcirculation/lib/bibcirculation_templates.py:2038 #: modules/bibcirculation/lib/bibcirculation_templates.py:2231 #: modules/bibcirculation/lib/bibcirculation_templates.py:2978 #: modules/bibcirculation/lib/bibcirculation_templates.py:3476 #: modules/bibcirculation/lib/bibcirculation_templates.py:3951 #: modules/bibcirculation/lib/bibcirculation_templates.py:5281 #: modules/bibcirculation/lib/bibcirculation_templates.py:6182 #: modules/bibcirculation/lib/bibcirculation_templates.py:6793 #: modules/bibcirculation/lib/bibcirculation_templates.py:9915 #: modules/bibcirculation/lib/bibcirculation_templates.py:10282 #: modules/bibcirculation/lib/bibcirculation_templates.py:10802 #: modules/bibcirculation/lib/bibcirculation_templates.py:11346 #: modules/bibcirculation/lib/bibcirculation_templates.py:11538 #: modules/bibcirculation/lib/bibcirculation_templates.py:14076 #: modules/bibcirculation/lib/bibcirculation_templates.py:14624 #: modules/bibcirculation/lib/bibcirculation_templates.py:14958 #: modules/bibcirculation/lib/bibcirculation_templates.py:15505 #: modules/bibcirculation/lib/bibcirculation_templates.py:15828 #: modules/bibedit/lib/bibeditmulti_templates.py:354 #: modules/bibknowledge/lib/bibknowledge_templates.py:82 #: modules/bibknowledge/lib/bibknowledge_templates.py:423 #: modules/webbasket/lib/webbasket_templates.py:592 #: modules/websearch/lib/websearch_templates.py:843 #: modules/websearch/lib/websearch_templates.py:921 #: modules/websearch/lib/websearch_templates.py:1044 #: modules/websearch/lib/websearch_templates.py:1235 #: modules/websearch/lib/websearch_templates.py:2157 #: modules/websearch/lib/websearch_templates.py:2263 #: modules/websearch/lib/websearch_templates.py:2320 #: modules/websearch/lib/websearch_templates.py:2377 #: modules/webstyle/lib/webdoc_unit_tests.py:86 #: modules/webstyle/lib/webstyle_templates.py:492 #: modules/webstyle/lib/webstyle_templates.py:578 msgid "Search" msgstr "جستجو" #: modules/webhelp/web/help-central.webdoc:133 msgid "Citation Metrics" msgstr "سنجه های استنادی" #: modules/websearch/doc/search-guide.webdoc:442 #: modules/websearch/doc/search-guide.webdoc:478 #: modules/websearch/doc/search-guide.webdoc:513 #: modules/websearch/doc/search-guide.webdoc:582 #: modules/websearch/doc/search-guide.webdoc:618 #: modules/websearch/doc/search-guide.webdoc:653 #: modules/websearch/doc/search-guide.webdoc:721 #: modules/websearch/doc/search-guide.webdoc:757 #: modules/websearch/doc/search-guide.webdoc:792 #: modules/websearch/doc/search-guide.webdoc:865 #: modules/websearch/doc/search-guide.webdoc:900 #: modules/websearch/doc/search-guide.webdoc:936 #: modules/websearch/doc/search-guide.webdoc:1009 #: modules/websearch/doc/search-guide.webdoc:1045 #: modules/websearch/doc/search-guide.webdoc:1081 #: modules/miscutil/lib/inveniocfg.py:665 msgid "experiment" msgstr "آزمایش" #: modules/websearch/doc/search-guide.webdoc:276 #: modules/websearch/doc/search-guide.webdoc:304 #: modules/websearch/doc/search-guide.webdoc:333 #: modules/websearch/doc/search-guide.webdoc:362 #: modules/websearch/doc/search-guide.webdoc:391 #: modules/websearch/doc/search-guide.webdoc:437 #: modules/websearch/doc/search-guide.webdoc:473 #: modules/websearch/doc/search-guide.webdoc:508 #: modules/websearch/doc/search-guide.webdoc:577 #: modules/websearch/doc/search-guide.webdoc:613 #: modules/websearch/doc/search-guide.webdoc:648 #: modules/websearch/doc/search-guide.webdoc:716 #: modules/websearch/doc/search-guide.webdoc:752 #: modules/websearch/doc/search-guide.webdoc:787 #: modules/websearch/doc/search-guide.webdoc:860 #: modules/websearch/doc/search-guide.webdoc:895 #: modules/websearch/doc/search-guide.webdoc:931 #: modules/websearch/doc/search-guide.webdoc:1004 #: modules/websearch/doc/search-guide.webdoc:1040 #: modules/websearch/doc/search-guide.webdoc:1076 #: modules/websearch/doc/search-guide.webdoc:1143 #: modules/websearch/doc/search-guide.webdoc:1176 #: modules/websearch/doc/search-guide.webdoc:1209 #: modules/websearch/doc/search-guide.webdoc:1246 #: modules/websearch/doc/search-guide.webdoc:1284 #: modules/websearch/doc/search-guide.webdoc:1336 #: modules/websearch/doc/search-guide.webdoc:1360 #: modules/websearch/doc/search-guide.webdoc:1385 #: modules/websearch/doc/search-guide.webdoc:1403 #: modules/websearch/doc/search-guide.webdoc:1453 #: modules/websearch/doc/search-guide.webdoc:1478 #: modules/websearch/doc/search-guide.webdoc:1505 #: modules/websearch/doc/search-guide.webdoc:1523 #: modules/websearch/doc/search-guide.webdoc:1574 #: modules/websearch/doc/search-guide.webdoc:1599 #: modules/websearch/doc/search-guide.webdoc:1623 #: modules/websearch/doc/search-guide.webdoc:1641 #: modules/websearch/doc/search-guide.webdoc:1689 #: modules/websearch/doc/search-guide.webdoc:1715 #: modules/websearch/doc/search-guide.webdoc:1740 #: modules/websearch/doc/search-guide.webdoc:1758 #: modules/websearch/doc/search-guide.webdoc:1807 #: modules/websearch/doc/search-guide.webdoc:1833 #: modules/websearch/doc/search-guide.webdoc:1857 #: modules/websearch/doc/search-guide.webdoc:1875 #: modules/websearch/doc/search-guide.webdoc:2330 #: modules/websearch/doc/search-guide.webdoc:2346 #: modules/websearch/doc/search-guide.webdoc:2366 #: modules/websearch/doc/search-guide.webdoc:2388 #: modules/websearch/doc/search-guide.webdoc:2404 #: modules/websearch/doc/search-guide.webdoc:2425 #: modules/websearch/doc/search-guide.webdoc:2447 #: modules/websearch/doc/search-guide.webdoc:2463 #: modules/websearch/doc/search-guide.webdoc:2484 #: modules/websearch/doc/search-guide.webdoc:2506 #: modules/websearch/doc/search-guide.webdoc:2523 #: modules/websearch/doc/search-guide.webdoc:2544 #: modules/websearch/doc/search-guide.webdoc:2567 #: modules/websearch/doc/search-guide.webdoc:2584 #: modules/websearch/doc/search-guide.webdoc:2605 #: modules/websearch/doc/search-guide.webdoc:2663 #: modules/websearch/doc/search-guide.webdoc:2752 #: modules/websearch/doc/search-guide.webdoc:2765 #: modules/websearch/doc/search-guide.webdoc:2781 #: modules/websearch/doc/search-guide.webdoc:2797 #: modules/websearch/doc/search-guide.webdoc:2812 #: modules/websearch/doc/search-guide.webdoc:2832 #: modules/websearch/doc/search-guide.webdoc:2845 #: modules/websearch/doc/search-guide.webdoc:2861 #: modules/websearch/doc/search-guide.webdoc:2877 #: modules/websearch/doc/search-guide.webdoc:2892 #: modules/websearch/doc/search-guide.webdoc:2912 #: modules/websearch/doc/search-guide.webdoc:2925 #: modules/websearch/doc/search-guide.webdoc:2941 #: modules/websearch/doc/search-guide.webdoc:2957 #: modules/websearch/doc/search-guide.webdoc:2972 #: modules/websearch/doc/search-guide.webdoc:2992 #: modules/websearch/doc/search-guide.webdoc:3005 #: modules/websearch/doc/search-guide.webdoc:3021 #: modules/websearch/doc/search-guide.webdoc:3037 #: modules/websearch/doc/search-guide.webdoc:3052 #: modules/websearch/doc/search-guide.webdoc:3072 #: modules/websearch/doc/search-guide.webdoc:3085 #: modules/websearch/doc/search-guide.webdoc:3101 #: modules/websearch/doc/search-guide.webdoc:3117 #: modules/websearch/doc/search-guide.webdoc:3132 #: modules/websearch/doc/search-guide.webdoc:3167 #: modules/websearch/doc/search-guide.webdoc:3184 #: modules/websearch/doc/search-guide.webdoc:3205 #: modules/websearch/doc/search-guide.webdoc:3222 #: modules/websearch/doc/search-guide.webdoc:3242 #: modules/websearch/doc/search-guide.webdoc:3259 #: modules/websearch/doc/search-guide.webdoc:3280 #: modules/websearch/doc/search-guide.webdoc:3297 #: modules/websearch/doc/search-guide.webdoc:3317 #: modules/websearch/doc/search-guide.webdoc:3335 #: modules/websearch/doc/search-guide.webdoc:3371 #: modules/websearch/doc/search-guide.webdoc:3388 #: modules/websearch/doc/search-guide.webdoc:3404 #: modules/websearch/doc/search-guide.webdoc:3421 #: modules/websearch/doc/search-guide.webdoc:3451 #: modules/websearch/doc/search-guide.webdoc:3468 #: modules/websearch/doc/search-guide.webdoc:3484 #: modules/websearch/doc/search-guide.webdoc:3501 #: modules/websearch/doc/search-guide.webdoc:3534 #: modules/websearch/doc/search-guide.webdoc:3552 #: modules/websearch/doc/search-guide.webdoc:3569 #: modules/websearch/doc/search-guide.webdoc:3587 #: modules/websearch/doc/search-guide.webdoc:3621 #: modules/websearch/doc/search-guide.webdoc:3639 #: modules/websearch/doc/search-guide.webdoc:3655 #: modules/websearch/doc/search-guide.webdoc:3672 #: modules/websearch/doc/search-guide.webdoc:3706 #: modules/websearch/doc/search-guide.webdoc:3724 #: modules/websearch/doc/search-guide.webdoc:3740 #: modules/websearch/doc/search-guide.webdoc:3757 #: modules/websearch/doc/search-guide.webdoc:3805 #: modules/websearch/doc/search-guide.webdoc:3822 #: modules/websearch/doc/search-guide.webdoc:3838 #: modules/websearch/doc/search-guide.webdoc:3867 #: modules/websearch/doc/search-guide.webdoc:3884 #: modules/websearch/doc/search-guide.webdoc:3900 #: modules/websearch/doc/search-guide.webdoc:3929 #: modules/websearch/doc/search-guide.webdoc:3946 #: modules/websearch/doc/search-guide.webdoc:3962 #: modules/websearch/doc/search-guide.webdoc:3994 #: modules/websearch/doc/search-guide.webdoc:4011 #: modules/websearch/doc/search-guide.webdoc:4027 #: modules/websearch/doc/search-guide.webdoc:4056 #: modules/websearch/doc/search-guide.webdoc:4073 #: modules/websearch/doc/search-guide.webdoc:4089 #: modules/websearch/doc/search-guide.webdoc:4129 #: modules/websearch/doc/search-guide.webdoc:4156 #: modules/websearch/doc/search-guide.webdoc:4183 #: modules/websearch/doc/search-guide.webdoc:4211 #: modules/websearch/doc/search-guide.webdoc:4239 #: modules/websearch/doc/search-guide.webdoc:4266 #: modules/websearch/doc/search-guide.webdoc:4285 #: modules/websearch/doc/search-guide.webdoc:4305 #: modules/websearch/doc/search-guide.webdoc:4324 #: modules/websearch/doc/search-guide.webdoc:4343 #: modules/websearch/doc/search-guide.webdoc:4366 #: modules/websearch/doc/search-guide.webdoc:4387 #: modules/websearch/doc/search-guide.webdoc:4409 #: modules/websearch/doc/search-guide.webdoc:4430 #: modules/websearch/doc/search-guide.webdoc:4451 #: modules/websearch/doc/search-guide.webdoc:4474 #: modules/websearch/doc/search-guide.webdoc:4491 #: modules/websearch/doc/search-guide.webdoc:4513 #: modules/websearch/doc/search-guide.webdoc:4530 #: modules/websearch/doc/search-guide.webdoc:4553 #: modules/websearch/doc/search-guide.webdoc:4570 #: modules/websearch/doc/search-guide.webdoc:4593 #: modules/websearch/doc/search-guide.webdoc:4610 #: modules/websearch/doc/search-guide.webdoc:4632 #: modules/websearch/doc/search-guide.webdoc:4649 #: modules/websearch/doc/search-guide.webdoc:4725 #: modules/websearch/doc/search-guide.webdoc:4741 #: modules/websearch/doc/search-guide.webdoc:4760 #: modules/websearch/doc/search-guide.webdoc:4776 #: modules/websearch/doc/search-guide.webdoc:4795 #: modules/websearch/doc/search-guide.webdoc:4812 #: modules/websearch/doc/search-guide.webdoc:4832 #: modules/websearch/doc/search-guide.webdoc:4849 #: modules/websearch/doc/search-guide.webdoc:4869 #: modules/websearch/doc/search-guide.webdoc:4886 #: modules/websearch/doc/search-guide.webdoc:4950 #: modules/websearch/doc/search-guide.webdoc:4991 #: modules/websearch/doc/search-guide.webdoc:5031 #: modules/websearch/doc/search-guide.webdoc:5083 #: modules/websearch/doc/search-guide.webdoc:5137 #: modules/websearch/doc/search-guide.webdoc:5192 #: modules/websearch/doc/search-guide.webdoc:5228 #: modules/websearch/doc/search-guide.webdoc:5248 #: modules/websearch/doc/search-guide.webdoc:5267 #: modules/websearch/doc/search-guide.webdoc:5290 #: modules/websearch/doc/search-guide.webdoc:5311 #: modules/websearch/doc/search-guide.webdoc:5331 #: modules/websearch/doc/search-guide.webdoc:5354 #: modules/websearch/doc/search-guide.webdoc:5375 #: modules/websearch/doc/search-guide.webdoc:5395 #: modules/websearch/doc/search-guide.webdoc:5419 #: modules/websearch/doc/search-guide.webdoc:5441 #: modules/websearch/doc/search-guide.webdoc:5461 #: modules/websearch/doc/search-guide.webdoc:5484 #: modules/websearch/doc/search-guide.webdoc:5505 #: modules/websearch/doc/search-guide.webdoc:5525 #: modules/websearch/doc/search-guide.webdoc:5682 #: modules/websearch/doc/search-guide.webdoc:5711 #: modules/websearch/doc/search-guide.webdoc:5738 #: modules/websearch/doc/search-guide.webdoc:5762 #: modules/websearch/doc/search-guide.webdoc:5790 #: modules/websearch/doc/search-guide.webdoc:5823 #: modules/websearch/doc/search-guide.webdoc:5865 #: modules/websearch/doc/search-guide.webdoc:5896 #: modules/websearch/doc/search-guide.webdoc:5924 #: modules/websearch/doc/search-guide.webdoc:5949 #: modules/websearch/doc/search-guide.webdoc:5977 #: modules/websearch/doc/search-guide.webdoc:6012 #: modules/websearch/doc/search-guide.webdoc:6055 #: modules/websearch/doc/search-guide.webdoc:6086 #: modules/websearch/doc/search-guide.webdoc:6114 #: modules/websearch/doc/search-guide.webdoc:6141 #: modules/websearch/doc/search-guide.webdoc:6170 #: modules/websearch/doc/search-guide.webdoc:6205 #: modules/websearch/doc/search-guide.webdoc:6251 #: modules/websearch/doc/search-guide.webdoc:6283 #: modules/websearch/doc/search-guide.webdoc:6312 #: modules/websearch/doc/search-guide.webdoc:6337 #: modules/websearch/doc/search-guide.webdoc:6367 #: modules/websearch/doc/search-guide.webdoc:6403 #: modules/websearch/doc/search-guide.webdoc:6450 #: modules/websearch/doc/search-guide.webdoc:6481 #: modules/websearch/doc/search-guide.webdoc:6510 #: modules/websearch/doc/search-guide.webdoc:6536 #: modules/websearch/doc/search-guide.webdoc:6565 #: modules/websearch/doc/search-guide.webdoc:6600 #: modules/websearch/doc/search-guide.webdoc:6974 #: modules/websearch/doc/search-guide.webdoc:6993 #: modules/websearch/doc/search-guide.webdoc:7015 #: modules/websearch/doc/search-guide.webdoc:7035 #: modules/websearch/doc/search-guide.webdoc:7056 #: modules/websearch/doc/search-guide.webdoc:7076 #: modules/websearch/doc/search-guide.webdoc:7098 #: modules/websearch/doc/search-guide.webdoc:7119 #: modules/websearch/doc/search-guide.webdoc:7141 #: modules/websearch/doc/search-guide.webdoc:7161 #: modules/websearch/doc/search-guide.webdoc:7198 #: modules/websearch/doc/search-guide.webdoc:7214 #: modules/websearch/doc/search-guide.webdoc:7231 #: modules/websearch/doc/search-guide.webdoc:7250 #: modules/websearch/doc/search-guide.webdoc:7273 #: modules/websearch/doc/search-guide.webdoc:7290 #: modules/websearch/doc/search-guide.webdoc:7308 #: modules/websearch/doc/search-guide.webdoc:7328 #: modules/websearch/doc/search-guide.webdoc:7349 #: modules/websearch/doc/search-guide.webdoc:7365 #: modules/websearch/doc/search-guide.webdoc:7383 #: modules/websearch/doc/search-guide.webdoc:7403 #: modules/websearch/doc/search-guide.webdoc:7425 #: modules/websearch/doc/search-guide.webdoc:7442 #: modules/websearch/doc/search-guide.webdoc:7460 #: modules/websearch/doc/search-guide.webdoc:7481 #: modules/websearch/doc/search-guide.webdoc:7503 #: modules/websearch/doc/search-guide.webdoc:7520 #: modules/websearch/doc/search-guide.webdoc:7537 #: modules/websearch/doc/search-guide.webdoc:7559 #: modules/websearch/doc/search-tips.webdoc:37 #: modules/websearch/doc/search-tips.webdoc:73 #: modules/websearch/doc/search-tips.webdoc:119 #: modules/websearch/doc/search-tips.webdoc:176 #: modules/websearch/doc/search-tips.webdoc:203 #: modules/websearch/doc/search-tips.webdoc:231 #: modules/websearch/doc/search-tips.webdoc:290 #: modules/websearch/doc/search-tips.webdoc:338 #: modules/websearch/doc/search-tips.webdoc:349 #: modules/websearch/doc/search-tips.webdoc:373 #: modules/websearch/doc/search-tips.webdoc:397 #: modules/websearch/doc/search-tips.webdoc:440 #: modules/websearch/doc/search-tips.webdoc:487 #: modules/websearch/doc/search-tips.webdoc:510 #: modules/websearch/doc/search-tips.webdoc:532 #: modules/websearch/doc/search-tips.webdoc:573 #: modules/websearch/doc/search-tips.webdoc:594 #: modules/websearch/doc/search-tips.webdoc:619 #: modules/websearch/doc/search-tips.webdoc:663 #: modules/websearch/doc/search-tips.webdoc:674 #: modules/websearch/doc/search-tips.webdoc:676 #: modules/websearch/doc/search-tips.webdoc:679 #: modules/websearch/doc/search-tips.webdoc:681 #: modules/websearch/doc/search-tips.webdoc:683 #: modules/websearch/doc/search-tips.webdoc:692 #: modules/websearch/doc/search-tips.webdoc:720 #: modules/websearch/doc/search-tips.webdoc:743 #: modules/webstyle/doc/hacking/webstyle-webdoc-syntax.webdoc:129 #: modules/miscutil/lib/inveniocfg.py:652 msgid "any field" msgstr "هر فیلد" #: modules/webhelp/web/help-central.webdoc:20 #: modules/webhelp/web/help-central.webdoc:25 #: modules/webhelp/web/help-central.webdoc:26 #: modules/webhelp/web/help-central.webdoc:27 #: modules/webhelp/web/help-central.webdoc:28 #: modules/webhelp/web/help-central.webdoc:29 #: modules/webhelp/web/help-central.webdoc:30 #: modules/webhelp/web/help-central.webdoc:31 #: modules/webhelp/web/help-central.webdoc:32 #: modules/webhelp/web/help-central.webdoc:33 #: modules/webhelp/web/help-central.webdoc:34 #: modules/webhelp/web/help-central.webdoc:35 #: modules/webhelp/web/help-central.webdoc:36 #: modules/webhelp/web/help-central.webdoc:37 #: modules/webhelp/web/help-central.webdoc:38 #: modules/webhelp/web/help-central.webdoc:39 #: modules/webhelp/web/help-central.webdoc:40 #: modules/webhelp/web/help-central.webdoc:41 #: modules/webhelp/web/help-central.webdoc:42 #: modules/webhelp/web/help-central.webdoc:43 #: modules/webhelp/web/help-central.webdoc:44 #: modules/webhelp/web/help-central.webdoc:45 #: modules/websearch/doc/search-guide.webdoc:21 #: modules/websearch/doc/search-tips.webdoc:21 #: modules/websubmit/doc/submit-guide.webdoc:21 #: modules/webstyle/lib/webdoc_unit_tests.py:105 #: modules/webstyle/lib/webdoc_webinterface.py:155 msgid "Help Central" msgstr "" #: modules/websearch/doc/search-guide.webdoc:440 #: modules/websearch/doc/search-guide.webdoc:476 #: modules/websearch/doc/search-guide.webdoc:511 #: modules/websearch/doc/search-guide.webdoc:580 #: modules/websearch/doc/search-guide.webdoc:616 #: modules/websearch/doc/search-guide.webdoc:651 #: modules/websearch/doc/search-guide.webdoc:719 #: modules/websearch/doc/search-guide.webdoc:755 #: modules/websearch/doc/search-guide.webdoc:790 #: modules/websearch/doc/search-guide.webdoc:863 #: modules/websearch/doc/search-guide.webdoc:898 #: modules/websearch/doc/search-guide.webdoc:934 #: modules/websearch/doc/search-guide.webdoc:1007 #: modules/websearch/doc/search-guide.webdoc:1043 #: modules/websearch/doc/search-guide.webdoc:1079 #: modules/miscutil/lib/inveniocfg.py:661 msgid "collection" msgstr "مجموعه" #: modules/websearch/doc/admin/websearch-admin-guide.webdoc:20 msgid "WebSearch Admin Guide" msgstr "" #: modules/websearch/doc/search-guide.webdoc:430 #: modules/websearch/doc/search-guide.webdoc:465 #: modules/websearch/doc/search-guide.webdoc:501 #: modules/websearch/doc/search-guide.webdoc:570 #: modules/websearch/doc/search-guide.webdoc:605 #: modules/websearch/doc/search-guide.webdoc:641 #: modules/websearch/doc/search-guide.webdoc:709 #: modules/websearch/doc/search-guide.webdoc:744 #: modules/websearch/doc/search-guide.webdoc:780 #: modules/websearch/doc/search-guide.webdoc:852 #: modules/websearch/doc/search-guide.webdoc:888 #: modules/websearch/doc/search-guide.webdoc:923 #: modules/websearch/doc/search-guide.webdoc:996 #: modules/websearch/doc/search-guide.webdoc:1032 #: modules/websearch/doc/search-guide.webdoc:1068 #: modules/websearch/lib/search_engine.py:1425 #: modules/websearch/lib/websearch_templates.py:1299 msgid "Exact phrase:" msgstr "عبارت دقیق" #: modules/webhelp/web/help-central.webdoc:108 #: modules/websubmit/doc/submit-guide.webdoc:20 msgid "Submit Guide" msgstr "راهنمای واگذاری" #: modules/websearch/doc/search-guide.webdoc:454 #: modules/websearch/doc/search-guide.webdoc:490 #: modules/websearch/doc/search-guide.webdoc:594 #: modules/websearch/doc/search-guide.webdoc:630 #: modules/websearch/doc/search-guide.webdoc:733 #: modules/websearch/doc/search-guide.webdoc:769 #: modules/websearch/doc/search-guide.webdoc:877 #: modules/websearch/doc/search-guide.webdoc:912 #: modules/websearch/doc/search-guide.webdoc:1021 #: modules/websearch/doc/search-guide.webdoc:1057 #: modules/websearch/lib/search_engine.py:1403 #: modules/websearch/lib/websearch_templates.py:1345 msgid "AND" msgstr "و" #: modules/websearch/doc/search-guide.webdoc:444 #: modules/websearch/doc/search-guide.webdoc:480 #: modules/websearch/doc/search-guide.webdoc:515 #: modules/websearch/doc/search-guide.webdoc:584 #: modules/websearch/doc/search-guide.webdoc:620 #: modules/websearch/doc/search-guide.webdoc:655 #: modules/websearch/doc/search-guide.webdoc:723 #: modules/websearch/doc/search-guide.webdoc:759 #: modules/websearch/doc/search-guide.webdoc:794 #: modules/websearch/doc/search-guide.webdoc:867 #: modules/websearch/doc/search-guide.webdoc:902 #: modules/websearch/doc/search-guide.webdoc:938 #: modules/websearch/doc/search-guide.webdoc:1011 #: modules/websearch/doc/search-guide.webdoc:1047 #: modules/websearch/doc/search-guide.webdoc:1083 #: modules/miscutil/lib/inveniocfg.py:656 msgid "keyword" msgstr "کلیدواژه" #: modules/websearch/doc/search-guide.webdoc:277 #: modules/websearch/doc/search-guide.webdoc:305 #: modules/websearch/doc/search-guide.webdoc:334 #: modules/websearch/doc/search-guide.webdoc:363 #: modules/websearch/doc/search-guide.webdoc:392 #: modules/websearch/doc/search-guide.webdoc:448 #: modules/websearch/doc/search-guide.webdoc:484 #: modules/websearch/doc/search-guide.webdoc:519 #: modules/websearch/doc/search-guide.webdoc:588 #: modules/websearch/doc/search-guide.webdoc:624 #: modules/websearch/doc/search-guide.webdoc:659 #: modules/websearch/doc/search-guide.webdoc:727 #: modules/websearch/doc/search-guide.webdoc:763 #: modules/websearch/doc/search-guide.webdoc:798 #: modules/websearch/doc/search-guide.webdoc:871 #: modules/websearch/doc/search-guide.webdoc:906 #: modules/websearch/doc/search-guide.webdoc:942 #: modules/websearch/doc/search-guide.webdoc:1015 #: modules/websearch/doc/search-guide.webdoc:1051 #: modules/websearch/doc/search-guide.webdoc:1087 #: modules/websearch/doc/search-guide.webdoc:1144 #: modules/websearch/doc/search-guide.webdoc:1177 #: modules/websearch/doc/search-guide.webdoc:1210 #: modules/websearch/doc/search-guide.webdoc:1247 #: modules/websearch/doc/search-guide.webdoc:1285 #: modules/websearch/doc/search-guide.webdoc:1337 #: modules/websearch/doc/search-guide.webdoc:1361 #: modules/websearch/doc/search-guide.webdoc:1386 #: modules/websearch/doc/search-guide.webdoc:1404 #: modules/websearch/doc/search-guide.webdoc:1454 #: modules/websearch/doc/search-guide.webdoc:1479 #: modules/websearch/doc/search-guide.webdoc:1506 #: modules/websearch/doc/search-guide.webdoc:1524 #: modules/websearch/doc/search-guide.webdoc:1575 #: modules/websearch/doc/search-guide.webdoc:1600 #: modules/websearch/doc/search-guide.webdoc:1624 #: modules/websearch/doc/search-guide.webdoc:1642 #: modules/websearch/doc/search-guide.webdoc:1690 #: modules/websearch/doc/search-guide.webdoc:1716 #: modules/websearch/doc/search-guide.webdoc:1741 #: modules/websearch/doc/search-guide.webdoc:1759 #: modules/websearch/doc/search-guide.webdoc:1808 #: modules/websearch/doc/search-guide.webdoc:1834 #: modules/websearch/doc/search-guide.webdoc:1858 #: modules/websearch/doc/search-guide.webdoc:1876 #: modules/websearch/doc/search-guide.webdoc:2331 #: modules/websearch/doc/search-guide.webdoc:2347 #: modules/websearch/doc/search-guide.webdoc:2367 #: modules/websearch/doc/search-guide.webdoc:2389 #: modules/websearch/doc/search-guide.webdoc:2405 #: modules/websearch/doc/search-guide.webdoc:2426 #: modules/websearch/doc/search-guide.webdoc:2448 #: modules/websearch/doc/search-guide.webdoc:2464 #: modules/websearch/doc/search-guide.webdoc:2485 #: modules/websearch/doc/search-guide.webdoc:2507 #: modules/websearch/doc/search-guide.webdoc:2524 #: modules/websearch/doc/search-guide.webdoc:2545 #: modules/websearch/doc/search-guide.webdoc:2568 #: modules/websearch/doc/search-guide.webdoc:2585 #: modules/websearch/doc/search-guide.webdoc:2606 #: modules/websearch/doc/search-guide.webdoc:2664 #: modules/websearch/doc/search-guide.webdoc:2753 #: modules/websearch/doc/search-guide.webdoc:2766 #: modules/websearch/doc/search-guide.webdoc:2782 #: modules/websearch/doc/search-guide.webdoc:2798 #: modules/websearch/doc/search-guide.webdoc:2813 #: modules/websearch/doc/search-guide.webdoc:2833 #: modules/websearch/doc/search-guide.webdoc:2846 #: modules/websearch/doc/search-guide.webdoc:2862 #: modules/websearch/doc/search-guide.webdoc:2878 #: modules/websearch/doc/search-guide.webdoc:2893 #: modules/websearch/doc/search-guide.webdoc:2913 #: modules/websearch/doc/search-guide.webdoc:2926 #: modules/websearch/doc/search-guide.webdoc:2942 #: modules/websearch/doc/search-guide.webdoc:2958 #: modules/websearch/doc/search-guide.webdoc:2973 #: modules/websearch/doc/search-guide.webdoc:2993 #: modules/websearch/doc/search-guide.webdoc:3006 #: modules/websearch/doc/search-guide.webdoc:3022 #: modules/websearch/doc/search-guide.webdoc:3038 #: modules/websearch/doc/search-guide.webdoc:3053 #: modules/websearch/doc/search-guide.webdoc:3073 #: modules/websearch/doc/search-guide.webdoc:3086 #: modules/websearch/doc/search-guide.webdoc:3102 #: modules/websearch/doc/search-guide.webdoc:3118 #: modules/websearch/doc/search-guide.webdoc:3133 #: modules/websearch/doc/search-guide.webdoc:3168 #: modules/websearch/doc/search-guide.webdoc:3185 #: modules/websearch/doc/search-guide.webdoc:3206 #: modules/websearch/doc/search-guide.webdoc:3223 #: modules/websearch/doc/search-guide.webdoc:3243 #: modules/websearch/doc/search-guide.webdoc:3260 #: modules/websearch/doc/search-guide.webdoc:3281 #: modules/websearch/doc/search-guide.webdoc:3298 #: modules/websearch/doc/search-guide.webdoc:3318 #: modules/websearch/doc/search-guide.webdoc:3336 #: modules/websearch/doc/search-guide.webdoc:3372 #: modules/websearch/doc/search-guide.webdoc:3389 #: modules/websearch/doc/search-guide.webdoc:3405 #: modules/websearch/doc/search-guide.webdoc:3422 #: modules/websearch/doc/search-guide.webdoc:3452 #: modules/websearch/doc/search-guide.webdoc:3469 #: modules/websearch/doc/search-guide.webdoc:3485 #: modules/websearch/doc/search-guide.webdoc:3502 #: modules/websearch/doc/search-guide.webdoc:3535 #: modules/websearch/doc/search-guide.webdoc:3553 #: modules/websearch/doc/search-guide.webdoc:3570 #: modules/websearch/doc/search-guide.webdoc:3588 #: modules/websearch/doc/search-guide.webdoc:3622 #: modules/websearch/doc/search-guide.webdoc:3640 #: modules/websearch/doc/search-guide.webdoc:3656 #: modules/websearch/doc/search-guide.webdoc:3673 #: modules/websearch/doc/search-guide.webdoc:3707 #: modules/websearch/doc/search-guide.webdoc:3725 #: modules/websearch/doc/search-guide.webdoc:3741 #: modules/websearch/doc/search-guide.webdoc:3758 #: modules/websearch/doc/search-guide.webdoc:3806 #: modules/websearch/doc/search-guide.webdoc:3823 #: modules/websearch/doc/search-guide.webdoc:3839 #: modules/websearch/doc/search-guide.webdoc:3868 #: modules/websearch/doc/search-guide.webdoc:3885 #: modules/websearch/doc/search-guide.webdoc:3901 #: modules/websearch/doc/search-guide.webdoc:3930 #: modules/websearch/doc/search-guide.webdoc:3947 #: modules/websearch/doc/search-guide.webdoc:3963 #: modules/websearch/doc/search-guide.webdoc:3995 #: modules/websearch/doc/search-guide.webdoc:4012 #: modules/websearch/doc/search-guide.webdoc:4028 #: modules/websearch/doc/search-guide.webdoc:4057 #: modules/websearch/doc/search-guide.webdoc:4074 #: modules/websearch/doc/search-guide.webdoc:4090 #: modules/websearch/doc/search-guide.webdoc:4130 #: modules/websearch/doc/search-guide.webdoc:4157 #: modules/websearch/doc/search-guide.webdoc:4184 #: modules/websearch/doc/search-guide.webdoc:4212 #: modules/websearch/doc/search-guide.webdoc:4240 #: modules/websearch/doc/search-guide.webdoc:4267 #: modules/websearch/doc/search-guide.webdoc:4286 #: modules/websearch/doc/search-guide.webdoc:4306 #: modules/websearch/doc/search-guide.webdoc:4325 #: modules/websearch/doc/search-guide.webdoc:4344 #: modules/websearch/doc/search-guide.webdoc:4367 #: modules/websearch/doc/search-guide.webdoc:4388 #: modules/websearch/doc/search-guide.webdoc:4410 #: modules/websearch/doc/search-guide.webdoc:4431 #: modules/websearch/doc/search-guide.webdoc:4452 #: modules/websearch/doc/search-guide.webdoc:4475 #: modules/websearch/doc/search-guide.webdoc:4492 #: modules/websearch/doc/search-guide.webdoc:4514 #: modules/websearch/doc/search-guide.webdoc:4531 #: modules/websearch/doc/search-guide.webdoc:4554 #: modules/websearch/doc/search-guide.webdoc:4571 #: modules/websearch/doc/search-guide.webdoc:4594 #: modules/websearch/doc/search-guide.webdoc:4611 #: modules/websearch/doc/search-guide.webdoc:4633 #: modules/websearch/doc/search-guide.webdoc:4650 #: modules/websearch/doc/search-guide.webdoc:4726 #: modules/websearch/doc/search-guide.webdoc:4742 #: modules/websearch/doc/search-guide.webdoc:4761 #: modules/websearch/doc/search-guide.webdoc:4777 #: modules/websearch/doc/search-guide.webdoc:4796 #: modules/websearch/doc/search-guide.webdoc:4813 #: modules/websearch/doc/search-guide.webdoc:4833 #: modules/websearch/doc/search-guide.webdoc:4850 #: modules/websearch/doc/search-guide.webdoc:4870 #: modules/websearch/doc/search-guide.webdoc:4887 #: modules/websearch/doc/search-guide.webdoc:4951 #: modules/websearch/doc/search-guide.webdoc:4992 #: modules/websearch/doc/search-guide.webdoc:5032 #: modules/websearch/doc/search-guide.webdoc:5084 #: modules/websearch/doc/search-guide.webdoc:5138 #: modules/websearch/doc/search-guide.webdoc:5193 #: modules/websearch/doc/search-guide.webdoc:5229 #: modules/websearch/doc/search-guide.webdoc:5249 #: modules/websearch/doc/search-guide.webdoc:5268 #: modules/websearch/doc/search-guide.webdoc:5291 #: modules/websearch/doc/search-guide.webdoc:5312 #: modules/websearch/doc/search-guide.webdoc:5332 #: modules/websearch/doc/search-guide.webdoc:5355 #: modules/websearch/doc/search-guide.webdoc:5376 #: modules/websearch/doc/search-guide.webdoc:5396 #: modules/websearch/doc/search-guide.webdoc:5420 #: modules/websearch/doc/search-guide.webdoc:5442 #: modules/websearch/doc/search-guide.webdoc:5462 #: modules/websearch/doc/search-guide.webdoc:5485 #: modules/websearch/doc/search-guide.webdoc:5506 #: modules/websearch/doc/search-guide.webdoc:5526 #: modules/websearch/doc/search-guide.webdoc:5683 #: modules/websearch/doc/search-guide.webdoc:5712 #: modules/websearch/doc/search-guide.webdoc:5739 #: modules/websearch/doc/search-guide.webdoc:5763 #: modules/websearch/doc/search-guide.webdoc:5791 #: modules/websearch/doc/search-guide.webdoc:5824 #: modules/websearch/doc/search-guide.webdoc:5866 #: modules/websearch/doc/search-guide.webdoc:5897 #: modules/websearch/doc/search-guide.webdoc:5925 #: modules/websearch/doc/search-guide.webdoc:5950 #: modules/websearch/doc/search-guide.webdoc:5978 #: modules/websearch/doc/search-guide.webdoc:6013 #: modules/websearch/doc/search-guide.webdoc:6056 #: modules/websearch/doc/search-guide.webdoc:6087 #: modules/websearch/doc/search-guide.webdoc:6115 #: modules/websearch/doc/search-guide.webdoc:6142 #: modules/websearch/doc/search-guide.webdoc:6171 #: modules/websearch/doc/search-guide.webdoc:6206 #: modules/websearch/doc/search-guide.webdoc:6252 #: modules/websearch/doc/search-guide.webdoc:6284 #: modules/websearch/doc/search-guide.webdoc:6313 #: modules/websearch/doc/search-guide.webdoc:6338 #: modules/websearch/doc/search-guide.webdoc:6368 #: modules/websearch/doc/search-guide.webdoc:6404 #: modules/websearch/doc/search-guide.webdoc:6451 #: modules/websearch/doc/search-guide.webdoc:6482 #: modules/websearch/doc/search-guide.webdoc:6511 #: modules/websearch/doc/search-guide.webdoc:6537 #: modules/websearch/doc/search-guide.webdoc:6566 #: modules/websearch/doc/search-guide.webdoc:6601 #: modules/websearch/doc/search-guide.webdoc:6975 #: modules/websearch/doc/search-guide.webdoc:6994 #: modules/websearch/doc/search-guide.webdoc:7016 #: modules/websearch/doc/search-guide.webdoc:7036 #: modules/websearch/doc/search-guide.webdoc:7057 #: modules/websearch/doc/search-guide.webdoc:7077 #: modules/websearch/doc/search-guide.webdoc:7099 #: modules/websearch/doc/search-guide.webdoc:7120 #: modules/websearch/doc/search-guide.webdoc:7142 #: modules/websearch/doc/search-guide.webdoc:7162 #: modules/websearch/doc/search-guide.webdoc:7199 #: modules/websearch/doc/search-guide.webdoc:7215 #: modules/websearch/doc/search-guide.webdoc:7232 #: modules/websearch/doc/search-guide.webdoc:7251 #: modules/websearch/doc/search-guide.webdoc:7274 #: modules/websearch/doc/search-guide.webdoc:7291 #: modules/websearch/doc/search-guide.webdoc:7309 #: modules/websearch/doc/search-guide.webdoc:7329 #: modules/websearch/doc/search-guide.webdoc:7350 #: modules/websearch/doc/search-guide.webdoc:7366 #: modules/websearch/doc/search-guide.webdoc:7384 #: modules/websearch/doc/search-guide.webdoc:7404 #: modules/websearch/doc/search-guide.webdoc:7426 #: modules/websearch/doc/search-guide.webdoc:7443 #: modules/websearch/doc/search-guide.webdoc:7461 #: modules/websearch/doc/search-guide.webdoc:7482 #: modules/websearch/doc/search-guide.webdoc:7504 #: modules/websearch/doc/search-guide.webdoc:7521 #: modules/websearch/doc/search-guide.webdoc:7538 #: modules/websearch/doc/search-guide.webdoc:7560 #: modules/websearch/doc/search-tips.webdoc:38 #: modules/websearch/doc/search-tips.webdoc:74 #: modules/websearch/doc/search-tips.webdoc:120 #: modules/websearch/doc/search-tips.webdoc:177 #: modules/websearch/doc/search-tips.webdoc:204 #: modules/websearch/doc/search-tips.webdoc:232 #: modules/websearch/doc/search-tips.webdoc:291 #: modules/websearch/doc/search-tips.webdoc:339 #: modules/websearch/doc/search-tips.webdoc:350 #: modules/websearch/doc/search-tips.webdoc:374 #: modules/websearch/doc/search-tips.webdoc:398 #: modules/websearch/doc/search-tips.webdoc:441 #: modules/websearch/doc/search-tips.webdoc:488 #: modules/websearch/doc/search-tips.webdoc:511 #: modules/websearch/doc/search-tips.webdoc:533 #: modules/websearch/doc/search-tips.webdoc:574 #: modules/websearch/doc/search-tips.webdoc:595 #: modules/websearch/doc/search-tips.webdoc:620 #: modules/websearch/doc/search-tips.webdoc:664 #: modules/websearch/doc/search-tips.webdoc:693 #: modules/websearch/doc/search-tips.webdoc:721 #: modules/websearch/doc/search-tips.webdoc:744 #: modules/webstyle/doc/hacking/webstyle-webdoc-syntax.webdoc:130 #: modules/bibcirculation/lib/bibcirculation_templates.py:2928 #: modules/bibcirculation/lib/bibcirculation_templates.py:2936 #: modules/bibcirculation/lib/bibcirculation_templates.py:2944 #: modules/bibcirculation/lib/bibcirculation_templates.py:2952 #: modules/bibcirculation/lib/bibcirculation_templates.py:6152 #: modules/bibcirculation/lib/bibcirculation_templates.py:6761 #: modules/bibcirculation/lib/bibcirculation_templates.py:10252 #: modules/miscutil/lib/inveniocfg.py:653 msgid "title" msgstr "عنوان" #: modules/websearch/doc/search-tips.webdoc:79 #: modules/websearch/doc/search-tips.webdoc:125 #: modules/websearch/lib/websearch_webcoll.py:233 msgid "Narrow by collection:" msgstr "خاص براساس مجموعه" #: modules/bibformat/etc/format_templates/Default_HTML_actions.bft:5 msgid "Add to personal basket" msgstr "اضافه به سبد شخصی" #: modules/websubmit/doc/admin/websubmit-admin-guide.webdoc:20 msgid "WebSubmit Admin Guide" msgstr "" #: modules/websearch/doc/search-guide.webdoc:455 #: modules/websearch/doc/search-guide.webdoc:491 #: modules/websearch/doc/search-guide.webdoc:595 #: modules/websearch/doc/search-guide.webdoc:631 #: modules/websearch/doc/search-guide.webdoc:734 #: modules/websearch/doc/search-guide.webdoc:770 #: modules/websearch/doc/search-guide.webdoc:878 #: modules/websearch/doc/search-guide.webdoc:913 #: modules/websearch/doc/search-guide.webdoc:1022 #: modules/websearch/doc/search-guide.webdoc:1058 #: modules/websearch/lib/search_engine.py:1238 #: modules/websearch/lib/search_engine.py:1404 #: modules/websearch/lib/websearch_templates.py:1346 #: modules/websearch/lib/websearch_webcoll.py:627 msgid "OR" msgstr "یا" #: modules/bibauthorid/lib/bibauthorid_templates.py:657 msgid "Click here to review the transactions." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:709 msgid "Quit searching." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:723 msgid "" "When you merge a set of profiles, all the information stored will be " "assigned to the primary profile. This includes papers, ids or citations. " "After merging, only the primary profile will remain in the system, all other " "profiles will be automatically deleted.
" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:754 #, fuzzy msgid "Cancel merging" msgstr "لغو" #: modules/bibauthorid/lib/bibauthorid_templates.py:756 #: modules/bibauthorid/lib/bibauthorid_templates.py:3177 msgid "Merge profiles" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:963 #, fuzzy msgid "Your options" msgstr "امانت های شما" #: modules/bibauthorid/lib/bibauthorid_templates.py:973 msgid "Claim for yourself" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:977 msgid "Assign to" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:981 msgid "Search for a person to assign the paper to" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1042 #: modules/bibauthorid/lib/bibauthorid_templates.py:1136 #: modules/bibauthorid/lib/bibauthorid_templates.py:1208 msgid "Select All" msgstr "انتخاب همگی" #: modules/bibauthorid/lib/bibauthorid_templates.py:1043 #: modules/bibauthorid/lib/bibauthorid_templates.py:1137 #: modules/bibauthorid/lib/bibauthorid_templates.py:1209 msgid "Select None" msgstr "انتخاب هیچ یک" #: modules/bibauthorid/lib/bibauthorid_templates.py:1044 #: modules/bibauthorid/lib/bibauthorid_templates.py:1138 #: modules/bibauthorid/lib/bibauthorid_templates.py:1210 msgid "Invert Selection" msgstr "انتخاب معکوس" #: modules/bibauthorid/lib/bibauthorid_templates.py:1046 #: modules/bibauthorid/lib/bibauthorid_templates.py:1140 msgid "Hide successful claims" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1065 #: modules/bibauthorid/lib/bibauthorid_templates.py:1184 msgid "Paper Short Info" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1066 msgid "Author Name" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1067 #, fuzzy msgid "Affiliation" msgstr "وابستگی ها" #: modules/bibauthorid/lib/bibauthorid_templates.py:1068 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:535 #: modules/bibupload/lib/batchuploader_templates.py:237 #: modules/bibupload/lib/batchuploader_templates.py:470 #: modules/webmessage/lib/webmessage_templates.py:88 msgid "Date" msgstr "تاریخ" #: modules/bibauthorid/lib/bibauthorid_templates.py:1069 #, fuzzy msgid "Experiment" msgstr "آزمایش" #: modules/bibauthorid/lib/bibauthorid_templates.py:1070 #: modules/bibauthorid/lib/bibauthorid_templates.py:1185 #: modules/bibcirculation/lib/bibcirculation_templates.py:1171 #: modules/bibcirculation/lib/bibcirculation_templates.py:5045 #: modules/bibcirculation/lib/bibcirculation_templates.py:5376 msgid "Actions" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1090 #, fuzzy msgid "Not assigned" msgstr "مال من نیست!" #: modules/bibauthorid/lib/bibauthorid_templates.py:1106 msgid "No status information found." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1126 msgid "Operator review of user actions pending" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1171 msgid "Sorry, there are currently no records to be found in this category." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1201 msgid "Review Transaction" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1207 #, fuzzy msgid "On all pages" msgstr "بر روی همه صفحات" #: modules/bibauthorid/lib/bibauthorid_templates.py:1214 #, fuzzy msgid "With selected do" msgstr "حذف نظرات انتخاب شده" #: modules/bibauthorid/lib/bibauthorid_templates.py:1247 #, fuzzy msgid "View name variants" msgstr "مشاهده نظر" #: modules/bibauthorid/lib/bibauthorid_templates.py:1357 msgid "The author has an internal ID!" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1375 msgid "These records have been marked as not being from this person." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1376 msgid "They will be regarded in the next run of the author " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1377 msgid "disambiguation algorithm and might disappear from this listing." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1403 #: modules/bibauthorid/lib/bibauthorid_templates.py:1404 #: modules/bibauthorid/lib/bibauthorid_templates.py:1407 msgid "Not provided" msgstr "تأمین نشده" #: modules/bibauthorid/lib/bibauthorid_templates.py:1405 msgid "Not available" msgstr "غیرقابل استفاده" #: modules/bibauthorid/lib/bibauthorid_templates.py:1406 msgid "No comments" msgstr "بدون نظر " #: modules/bibauthorid/lib/bibauthorid_templates.py:1408 msgid "Not Available" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1428 msgid " Delete this ticket" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1432 msgid " Commit this entire ticket" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1452 #, fuzzy msgid "No title available" msgstr "غیرقابل استفاده" #: modules/bibauthorid/lib/bibauthorid_templates.py:1528 msgid "Make sure we match the right names!" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1530 msgid "Please select an author on each of the records that will be assigned." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1531 msgid "Papers without a name selected will be ignored in the process." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1540 msgid "Claim for person with id" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1541 msgid "This seems to be an empty profile without names associated to it yet" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1542 msgid "" "(the names will be automatically gathered when the first paper is claimed to " "this profile)." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1544 msgid "Select name for" msgstr "انتخاب نام برای" #: modules/bibauthorid/lib/bibauthorid_templates.py:1553 #: modules/bibauthorid/lib/bibauthorid_templates.py:1582 #: modules/bibauthorid/lib/bibauthorid_templates.py:1751 msgid "Error retrieving record title" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1556 msgid "Paper title: " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1558 #: modules/bibauthorid/lib/bibauthorid_templates.py:1591 msgid "Ignore" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1570 msgid "The following names have been automatically chosen:" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1575 #: modules/websubmit/lib/websubmit_templates.py:1212 msgid "For" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1586 msgid " -- With name: " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1712 msgid "Symbols legend: " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1717 #: modules/bibauthorid/lib/bibauthorid_templates.py:1776 msgid "Everything is shiny, captain!" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1718 msgid "The result of this request will be visible immediately" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1723 msgid "Confirmation needed to continue" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1724 msgid "" "The result of this request will be visible immediately but we need your " "confirmation to do so for this paper has been manually claimed before" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1729 msgid "This will create a change request for the operators" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1730 msgid "" "The result of this request will be visible upon confirmation through an " "operator" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1769 msgid "Selected name on paper" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1780 msgid "Verification needed to continue" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1784 msgid "This will create a request for the operators" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1822 msgid "Please provide your information" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1829 msgid "Please provide your first name" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1833 #: modules/bibauthorid/lib/bibauthorid_templates.py:1835 msgid "Your first name:" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1841 msgid "Please provide your last name" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1846 #: modules/bibauthorid/lib/bibauthorid_templates.py:1848 msgid "Your last name:" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1856 msgid "Please provide your eMail address" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1862 msgid "" "This eMail address is reserved by a user. Please log in or provide an " "alternative eMail address" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1867 #: modules/bibauthorid/lib/bibauthorid_templates.py:1869 msgid "Your eMail:" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1873 msgid "You may leave a comment (optional)" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1887 msgid "Continue claiming*" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1889 msgid "Confirm these changes**" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1893 msgid "!Delete the entire request!" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1906 msgid "Mark as your documents" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1921 msgid "Mark as _not_ your documents" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1932 msgid "Nothing staged as not yours" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1936 msgid "Mark as their documents" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1947 #: modules/bibauthorid/lib/bibauthorid_templates.py:1962 msgid "Nothing staged in this category" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1951 msgid "Mark as _not_ their documents" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1968 msgid " * You can come back to this page later. Nothing will be lost.
" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1969 msgid "" " ** Performs all requested changes. Changes subject to permission " "restrictions will be submitted to an operator for manual review." msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:1979 #, fuzzy msgid "Create new profile" msgstr "ایجاد گروه جدید" #: modules/bibauthorid/lib/bibauthorid_templates.py:1987 #, fuzzy msgid "Create a new Person" msgstr "ایجاد گروه جدید" #: modules/bibauthorid/lib/bibauthorid_templates.py:1999 #, fuzzy msgid "This is my profile" msgstr "این، مقاله من است!" #: modules/bibauthorid/lib/bibauthorid_templates.py:2011 msgid "Assign paper" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2026 #, fuzzy msgid "Add to merge list" msgstr "افزودن به کاربران" #: modules/bibauthorid/lib/bibauthorid_templates.py:2110 #, python-format msgid "" "We do not have a publication list for '%s'. Try using a less specific author " "name, or check back in a few days as attributions are updated frequently. " "Or you can send us feedback, at " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2187 msgid "Recent Papers" msgstr "مقاله های اخیر" #: modules/bibauthorid/lib/bibauthorid_templates.py:2198 msgid "Publication List " msgstr "سیاهه انتشار" #: modules/bibauthorid/lib/bibauthorid_templates.py:2246 msgid "Showing the" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2246 msgid "most recent documents:" msgstr "تازه ترین مدارک:" #: modules/bibauthorid/lib/bibauthorid_templates.py:2255 msgid "Sorry, there are no documents known for this person" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2325 msgid "The following profile is the one you were viewing before logging in: " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2334 #, python-format msgid "" "Out of %s paper(s) claimed to your arXiv account, %s match this profile: " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2353 msgid "" "If none of the options suggested above apply, you can look for other " "possible options from the list below:" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2921 msgid "" " Login with your " "arXiv.org account " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2926 msgid "" "You have succesfully logged in via arXiv.
You can now manage your " "profile.
" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2928 msgid "" "You have succesfully logged in via arXiv.
However the profile you are viewing is not your profile.
" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2930 #, fuzzy msgid "Manage your profile" msgstr "مدیریت اگلوهای قالب بندی" #: modules/bibauthorid/lib/bibauthorid_templates.py:2933 msgid "" "You have succesfully logged in, but
you are not " "associated to a person yet. Please use the button below to choose your " "profile

" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2935 #, fuzzy msgid "Choose your profile" msgstr "انتخاب یک فایل" #: modules/bibauthorid/lib/bibauthorid_templates.py:2938 msgid "Please log in through arXiv to manage your profile.
" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2940 msgid "Login into Inspire through arXiv.org" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2953 msgid "" " \n" " Connect this profile to an ORCiD " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2959 #, python-format msgid "" "This profile is already connected to the following ORCiD: %s
" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2961 msgid "Import your publications from ORCID" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2962 msgid "Visit your profile in ORCID" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2967 msgid "Connect an ORCiD to this profile" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2970 msgid "Suggest an ORCiD for this profile:" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:2996 msgid "" " " "Manage publications " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3001 #, fuzzy msgid "Manage publication list" msgstr "سیاهه انتشار" #: modules/bibauthorid/lib/bibauthorid_templates.py:3015 msgid " Person identifiers, internal and external " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3023 #, fuzzy msgid "add external id" msgstr "پیوند خارجی" #: modules/bibauthorid/lib/bibauthorid_templates.py:3025 #, fuzzy msgid "delete selected ids" msgstr "عدم حذف بررسی های انتخاب شده" #: modules/bibauthorid/lib/bibauthorid_templates.py:3027 msgid "Harvest missing external ids from claimed papers" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3030 msgid "suggest external id to add" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3032 msgid "suggest selected ids to delete" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3034 msgid "suggest missing ids" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3062 #, fuzzy msgid "Choose system" msgstr "انتخاب یک فایل" #: modules/bibauthorid/lib/bibauthorid_templates.py:3100 msgid "" " Automatically " "assigned publications " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3107 #, python-format msgid "" "Please wait as we are assigning %s papers from " "external systems to your Inspire profile
" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3133 msgid "" "The following publications have been successfully assigned to your profile:" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3154 msgid "Get help!" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3155 msgid " Contact " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3156 msgid "" "Please contact our user support in case you need help or you just want to " "suggest some new ideas. We will get back to you.
" msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3170 msgid "" " Merge profiles " msgstr "" #: modules/bibauthorid/lib/bibauthorid_templates.py:3174 msgid "" "If your or somebody else's publications in INSPIRE exist in multiple " "profiles, you can fix that here.
" msgstr "" -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:286 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:288 msgid "Fatal: Author ID capabilities are disabled on this system." msgstr "" -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:289 -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:326 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:291 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:328 msgid "Fatal: You are not allowed to access this functionality." msgstr "" -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:1577 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:1579 msgid "Claim this paper" msgstr "" -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:1668 -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:3187 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:1670 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:3189 msgid "" "

We're sorry. An error occurred while handling your request. Please find " "more information below:

" msgstr "" -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:1673 -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:3192 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:1675 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:3194 #: modules/websubmit/lib/websubmit_templates.py:340 msgid "Notice" msgstr "" -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:1914 -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2228 -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2234 -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2710 -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2924 -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2959 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:1916 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2230 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2236 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2712 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2926 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2961 msgid "This page is not accessible directly." msgstr "" -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2308 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2310 msgid " " msgstr "" -#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2948 +#: modules/bibauthorid/lib/bibauthorid_webinterface.py:2950 msgid "Profile management" msgstr "" #: modules/bibcatalog/lib/bibcatalog_templates.py:35 msgid "Error: No BibCatalog system configured." msgstr "" #: modules/bibcatalog/lib/bibcatalog_templates.py:39 #: modules/bibedit/lib/bibedit_webinterface.py:215 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:496 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:559 #: modules/bibknowledge/lib/bibknowledgeadmin.py:278 #: modules/webalert/lib/webalert_webinterface.py:145 #: modules/webalert/lib/webalert_webinterface.py:230 #: modules/webalert/lib/webalert_webinterface.py:364 #: modules/webalert/lib/webalert_webinterface.py:441 #: modules/webalert/lib/webalert_webinterface.py:515 #: modules/webstyle/lib/webpage.py:239 #: modules/webstyle/lib/webstyle_templates.py:659 #: modules/webstyle/lib/webstyle_templates.py:696 #: modules/webstyle/lib/webstyle_templates.py:698 msgid "Error" msgstr "خطا" #: modules/bibcatalog/lib/bibcatalog_templates.py:45 #, python-format msgid "You have %i tickets." msgstr "" #: modules/bibcatalog/lib/bibcatalog_templates.py:52 #: modules/bibknowledge/lib/bibknowledge_templates.py:167 #: modules/webcomment/lib/webcomment_templates.py:901 #: modules/webcomment/lib/webcomment_templates.py:2586 #: modules/websearch/lib/websearch_templates.py:2059 msgid "Previous" msgstr "قبلی" #: modules/bibcatalog/lib/bibcatalog_templates.py:64 msgid "show" msgstr "نمایش" #: modules/bibcatalog/lib/bibcatalog_templates.py:65 msgid "close" msgstr "بستن" #: modules/bibcatalog/lib/bibcatalog_templates.py:74 #: modules/bibknowledge/lib/bibknowledge_templates.py:165 #: modules/webcomment/lib/webcomment_templates.py:917 #: modules/webcomment/lib/webcomment_templates.py:2610 msgid "Next" msgstr "بعدی" #: modules/bibcirculation/lib/bibcirculation.py:100 #: modules/bibcirculation/lib/bibcirculation.py:127 msgid "Another user is waiting for this book." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:108 #, fuzzy msgid "Your loan has been renewed with success." msgstr "حساب شما به طور موفقیت آمیزی ایجاد شد." #: modules/bibcirculation/lib/bibcirculation.py:136 msgid "All loans have been renewed with success." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:191 #, python-format msgid "The publication date of this book is %s." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:194 #: modules/bibcirculation/lib/bibcirculation.py:196 msgid "This book has no copies in the library. " msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:198 msgid "" "If you think this book is interesting, suggest it and tell us why you " "consider this book is important. The library will consider " "your opinion and if we decide to buy the book, we will " "issue a loan for you as soon as it arrives and send it by internal mail." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:202 msgid "" "In case we decide not to buy the book, we will offer you an interlibrary loan" msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:268 msgid "This book was suggested for acquisition" msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:278 msgid "This item already has copies." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:552 #: modules/bibcirculation/lib/bibcirculation.py:681 msgid "You didn't accept the ILL conditions." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:666 #: modules/bibcirculation/lib/bibcirculation.py:710 #: modules/bibcirculation/lib/bibcirculation.py:778 msgid "" "Your ILL request has been registered and the document will be sent to you " "via internal mail." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:672 #: modules/bibcirculation/lib/bibcirculation.py:717 msgid "ILL request for books confirmation" msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:723 #: modules/bibcirculation/lib/bibcirculation.py:791 msgid "It is not possible to validate your request." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:724 #: modules/bibcirculation/lib/bibcirculation.py:792 msgid "Your office address is not available." msgstr "" #: modules/bibcirculation/lib/bibcirculation.py:725 #: modules/bibcirculation/lib/bibcirculation.py:793 #, python-format msgid "Please contact %(contact_email)s" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:113 msgid "Main navigation links" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:114 #: modules/webstyle/lib/webstyle_templates.py:92 #: modules/webstyle/lib/webstyle_templates.py:101 msgid "Home" msgstr "خانه" #: modules/bibcirculation/lib/bibcirculation_templates.py:114 msgid "Loan" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:114 #: modules/bibcirculation/lib/bibcirculation_templates.py:5887 #: modules/bibcirculation/lib/bibcirculation_templates.py:8362 msgid "Return" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:115 #: modules/bibcirculation/lib/bibcirculation_templates.py:404 #: modules/bibcirculation/lib/bibcirculation_templates.py:3143 msgid "Request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:115 msgid "Borrowers" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:116 #: modules/webbasket/lib/webbasket_templates.py:1199 msgid "Items" msgstr "آیتم ها" #: modules/bibcirculation/lib/bibcirculation_templates.py:148 #, fuzzy msgid "Loan Lists" msgstr "سیاهه ها" #: modules/bibcirculation/lib/bibcirculation_templates.py:149 msgid "Last loans" msgstr "آخرین امانت ها" #: modules/bibcirculation/lib/bibcirculation_templates.py:150 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2027 msgid "Overdue loans" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:151 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2076 msgid "Items on shelf with holds" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:152 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2125 msgid "Items on loan with holds" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:153 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2159 msgid "Overdue loans with holds" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:154 msgid "Others" msgstr "دیگران" #: modules/bibcirculation/lib/bibcirculation_templates.py:154 #: modules/bibcirculation/lib/bibcirculation_templates.py:14654 #: modules/bibcirculation/lib/bibcirculation_templates.py:15001 msgid "Libraries" msgstr "کتابخانه ها" #: modules/bibcirculation/lib/bibcirculation_templates.py:155 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5565 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5594 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5622 msgid "Add new library" msgstr "افزودن کتابخانه جدید" #: modules/bibcirculation/lib/bibcirculation_templates.py:156 msgid "Update info" msgstr "بروز رسانی اطلاعات" #: modules/bibcirculation/lib/bibcirculation_templates.py:156 msgid "Vendors" msgstr "فروشندگان" #: modules/bibcirculation/lib/bibcirculation_templates.py:157 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5970 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5998 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6025 msgid "Add new vendor" msgstr "افزودن فروشنده جدید" #: modules/bibcirculation/lib/bibcirculation_templates.py:195 #: modules/bibcirculation/lib/bibcirculation_templates.py:8121 #: modules/bibcirculation/lib/bibcirculation_templates.py:8130 msgid "ILL" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:196 msgid "Register Book request" msgstr "ثبت درخواست کتاب" #: modules/bibcirculation/lib/bibcirculation_templates.py:197 msgid "Register Article" msgstr "ثبت مقاله" #: modules/bibcirculation/lib/bibcirculation_templates.py:198 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:806 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4631 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4733 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4828 msgid "Register purchase request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:200 #, fuzzy msgid "ILL Lists" msgstr "سیاهه ها" #: modules/bibcirculation/lib/bibcirculation_templates.py:201 msgid "Purchase" msgstr "خرید" #: modules/bibcirculation/lib/bibcirculation_templates.py:202 msgid "Proposal" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:211 #: modules/webstyle/lib/webstyle_templates.py:495 #: modules/webstyle/lib/webstyle_templates.py:581 msgid "Help" msgstr "راهنما" #: modules/bibcirculation/lib/bibcirculation_templates.py:212 msgid "Admin guide" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:213 msgid "Contact Support" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:269 msgid "No messages to be displayed" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:301 msgid "This record does not exist." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:305 msgid "This record has no copies." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:312 msgid "Add a new copy" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:325 msgid "ILL services" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:329 #, python-format msgid "" "All the copies of %(strong_tag_open)s%(title)s%(strong_tag_close)s are " "missing. You can request a copy using %(strong_tag_open)s%(ill_link)s" "%(strong_tag_close)s" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:336 #: modules/bibcirculation/lib/bibcirculation_templates.py:3096 msgid "This item has no holdings." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:389 #: modules/bibcirculation/lib/bibcirculation_templates.py:1359 #: modules/bibcirculation/lib/bibcirculation_templates.py:13666 #: modules/bibcirculation/lib/bibcirculation_templates.py:13785 #: modules/bibcirculation/lib/bibcirculation_templates.py:13899 msgid "Options" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:389 #: modules/bibcirculation/lib/bibcirculation_templates.py:1164 #: modules/bibcirculation/lib/bibcirculation_templates.py:1354 #: modules/bibcirculation/lib/bibcirculation_templates.py:2498 #: modules/bibcirculation/lib/bibcirculation_templates.py:3117 #: modules/bibcirculation/lib/bibcirculation_templates.py:5040 #: modules/bibcirculation/lib/bibcirculation_templates.py:5351 #: modules/bibcirculation/lib/bibcirculation_templates.py:5715 #: modules/bibcirculation/lib/bibcirculation_templates.py:5980 #: modules/bibcirculation/lib/bibcirculation_templates.py:6071 #: modules/bibcirculation/lib/bibcirculation_templates.py:6340 #: modules/bibcirculation/lib/bibcirculation_templates.py:6504 #: modules/bibcirculation/lib/bibcirculation_templates.py:6712 #: modules/bibcirculation/lib/bibcirculation_templates.py:6992 #: modules/bibcirculation/lib/bibcirculation_templates.py:7110 #: modules/bibcirculation/lib/bibcirculation_templates.py:7349 #: modules/bibcirculation/lib/bibcirculation_templates.py:7564 #: modules/bibcirculation/lib/bibcirculation_templates.py:7668 #: modules/bibcirculation/lib/bibcirculation_templates.py:8188 #: modules/bibcirculation/lib/bibcirculation_templates.py:8462 #: modules/bibcirculation/lib/bibcirculation_templates.py:8555 #: modules/bibcirculation/lib/bibcirculation_templates.py:12257 #: modules/bibcirculation/lib/bibcirculation_templates.py:12400 #: modules/bibcirculation/lib/bibcirculation_templates.py:12485 #: modules/bibcirculation/lib/bibcirculation_templates.py:13068 #: modules/bibcirculation/lib/bibcirculation_templates.py:13222 #: modules/bibcirculation/lib/bibcirculation_templates.py:13310 #: modules/bibcirculation/lib/bibcirculation_utils.py:533 msgid "Library" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:389 #: modules/bibcirculation/lib/bibcirculation_templates.py:3117 #: modules/bibcirculation/lib/bibcirculation_templates.py:5367 #: modules/bibcirculation/lib/bibcirculation_templates.py:6344 #: modules/bibcirculation/lib/bibcirculation_templates.py:6532 #: modules/bibcirculation/lib/bibcirculation_templates.py:6714 #: modules/bibcirculation/lib/bibcirculation_templates.py:7008 #: modules/bibcirculation/lib/bibcirculation_templates.py:7159 #: modules/bibcirculation/lib/bibcirculation_templates.py:7351 #: modules/bibcirculation/lib/bibcirculation_templates.py:7580 #: modules/bibcirculation/lib/bibcirculation_templates.py:7672 #: modules/webalert/lib/webalert_templates.py:93 msgid "Collection" msgstr "مجموعه" #: modules/bibcirculation/lib/bibcirculation_templates.py:390 #: modules/bibcirculation/lib/bibcirculation_templates.py:1165 #: modules/bibcirculation/lib/bibcirculation_templates.py:1355 #: modules/bibcirculation/lib/bibcirculation_templates.py:2498 #: modules/bibcirculation/lib/bibcirculation_templates.py:3118 #: modules/bibcirculation/lib/bibcirculation_templates.py:5041 #: modules/bibcirculation/lib/bibcirculation_templates.py:5356 #: modules/bibcirculation/lib/bibcirculation_templates.py:5716 #: modules/bibcirculation/lib/bibcirculation_templates.py:5981 #: modules/bibcirculation/lib/bibcirculation_templates.py:6072 #: modules/bibcirculation/lib/bibcirculation_templates.py:6341 #: modules/bibcirculation/lib/bibcirculation_templates.py:6531 #: modules/bibcirculation/lib/bibcirculation_templates.py:6713 #: modules/bibcirculation/lib/bibcirculation_templates.py:6997 #: modules/bibcirculation/lib/bibcirculation_templates.py:7152 #: modules/bibcirculation/lib/bibcirculation_templates.py:7350 #: modules/bibcirculation/lib/bibcirculation_templates.py:7569 #: modules/bibcirculation/lib/bibcirculation_templates.py:7669 #: modules/bibcirculation/lib/bibcirculation_templates.py:8189 #: modules/bibcirculation/lib/bibcirculation_templates.py:8463 #: modules/bibcirculation/lib/bibcirculation_templates.py:8556 #: modules/bibcirculation/lib/bibcirculation_utils.py:534 msgid "Location" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:390 #: modules/bibcirculation/lib/bibcirculation_templates.py:3118 #: modules/bibcirculation/lib/bibcirculation_templates.py:5375 #: modules/bibcirculation/lib/bibcirculation_templates.py:6345 #: modules/bibcirculation/lib/bibcirculation_templates.py:6555 #: modules/bibcirculation/lib/bibcirculation_templates.py:6715 #: modules/bibcirculation/lib/bibcirculation_templates.py:7015 #: modules/bibcirculation/lib/bibcirculation_templates.py:7193 #: modules/bibcirculation/lib/bibcirculation_templates.py:7352 #: modules/bibcirculation/lib/bibcirculation_templates.py:7587 #: modules/bibcirculation/lib/bibcirculation_templates.py:7673 #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:400 #: modules/bibformat/lib/bibformat_templates.py:198 #: modules/bibformat/lib/bibformat_templates.py:726 #: modules/bibformat/lib/bibformat_templates.py:851 #: modules/bibknowledge/lib/bibknowledge_templates.py:80 #: modules/webbasket/lib/webbasket_templates.py:1612 #: modules/websession/lib/websession_templates.py:1693 #: modules/websession/lib/websession_templates.py:1767 #: modules/websession/lib/websession_templates.py:1830 #: modules/websubmit/lib/functions/Create_Upload_Files_Interface.py:471 msgid "Description" msgstr "توصیف" #: modules/bibcirculation/lib/bibcirculation_templates.py:390 #: modules/bibcirculation/lib/bibcirculation_templates.py:3118 #: modules/bibcirculation/lib/bibcirculation_templates.py:4294 #: modules/bibcirculation/lib/bibcirculation_templates.py:5361 #: modules/bibcirculation/lib/bibcirculation_templates.py:6342 #: modules/bibcirculation/lib/bibcirculation_templates.py:6562 #: modules/bibcirculation/lib/bibcirculation_templates.py:6716 #: modules/bibcirculation/lib/bibcirculation_templates.py:7002 #: modules/bibcirculation/lib/bibcirculation_templates.py:7200 #: modules/bibcirculation/lib/bibcirculation_templates.py:7353 #: modules/bibcirculation/lib/bibcirculation_templates.py:7574 #: modules/bibcirculation/lib/bibcirculation_templates.py:7670 msgid "Loan period" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:391 #: modules/bibcirculation/lib/bibcirculation_templates.py:700 #: modules/bibcirculation/lib/bibcirculation_templates.py:3119 #: modules/bibcirculation/lib/bibcirculation_templates.py:4556 #: modules/bibcirculation/lib/bibcirculation_templates.py:5348 #: modules/bibcirculation/lib/bibcirculation_templates.py:5714 #: modules/bibcirculation/lib/bibcirculation_templates.py:6339 #: modules/bibcirculation/lib/bibcirculation_templates.py:6586 #: modules/bibcirculation/lib/bibcirculation_templates.py:6717 #: modules/bibcirculation/lib/bibcirculation_templates.py:6990 #: modules/bibcirculation/lib/bibcirculation_templates.py:7225 #: modules/bibcirculation/lib/bibcirculation_templates.py:7354 #: modules/bibcirculation/lib/bibcirculation_templates.py:7562 #: modules/bibcirculation/lib/bibcirculation_templates.py:7666 #: modules/bibcirculation/lib/bibcirculation_templates.py:9213 #: modules/bibcirculation/lib/bibcirculation_templates.py:12062 #: modules/bibcirculation/lib/bibcirculation_templates.py:12854 #: modules/bibcirculation/lib/bibcirculation_templates.py:13527 #: modules/bibcirculation/lib/bibcirculation_templates.py:13662 #: modules/bibcirculation/lib/bibcirculation_templates.py:13781 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:536 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:633 #: modules/bibformat/lib/bibformat_templates.py:728 #: modules/bibformat/lib/bibformat_templates.py:852 #: modules/bibupload/lib/batchuploader_templates.py:296 #: modules/bibupload/lib/batchuploader_templates.py:338 #: modules/bibupload/lib/batchuploader_templates.py:622 #: modules/websubmit/lib/websubmit_templates.py:1267 msgid "Status" msgstr "وضعیت" #: modules/bibcirculation/lib/bibcirculation_templates.py:391 #: modules/bibcirculation/lib/bibcirculation_templates.py:626 #: modules/bibcirculation/lib/bibcirculation_templates.py:2499 #: modules/bibcirculation/lib/bibcirculation_templates.py:3119 #: modules/bibcirculation/lib/bibcirculation_templates.py:4215 #: modules/bibcirculation/lib/bibcirculation_templates.py:4292 #: modules/bibcirculation/lib/bibcirculation_templates.py:4694 #: modules/bibcirculation/lib/bibcirculation_templates.py:4871 #: modules/bibcirculation/lib/bibcirculation_templates.py:5350 #: modules/bibcirculation/lib/bibcirculation_templates.py:5834 #: modules/bibcirculation/lib/bibcirculation_templates.py:6074 #: modules/bibcirculation/lib/bibcirculation_templates.py:6991 #: modules/bibcirculation/lib/bibcirculation_templates.py:7563 #: modules/bibcirculation/lib/bibcirculation_templates.py:7667 #: modules/bibcirculation/lib/bibcirculation_templates.py:8304 #: modules/bibcirculation/lib/bibcirculation_templates.py:8558 #: modules/bibcirculation/lib/bibcirculation_templates.py:9212 #: modules/bibcirculation/lib/bibcirculation_templates.py:12301 #: modules/bibcirculation/lib/bibcirculation_templates.py:12402 #: modules/bibcirculation/lib/bibcirculation_templates.py:13112 #: modules/bibcirculation/lib/bibcirculation_templates.py:13224 #: modules/bibcirculation/lib/bibcirculation_templates.py:13530 #: modules/bibcirculation/lib/bibcirculation_utils.py:465 msgid "Due date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:391 #: modules/bibcirculation/lib/bibcirculation_templates.py:1500 #: modules/bibcirculation/lib/bibcirculation_templates.py:1739 #: modules/bibcirculation/lib/bibcirculation_templates.py:2497 #: modules/bibcirculation/lib/bibcirculation_templates.py:2829 #: modules/bibcirculation/lib/bibcirculation_templates.py:3117 #: modules/bibcirculation/lib/bibcirculation_templates.py:3389 #: modules/bibcirculation/lib/bibcirculation_templates.py:3628 #: modules/bibcirculation/lib/bibcirculation_templates.py:3864 #: modules/bibcirculation/lib/bibcirculation_templates.py:4106 #: modules/bibcirculation/lib/bibcirculation_templates.py:4290 #: modules/bibcirculation/lib/bibcirculation_templates.py:4692 #: modules/bibcirculation/lib/bibcirculation_templates.py:4869 #: modules/bibcirculation/lib/bibcirculation_templates.py:5347 #: modules/bibcirculation/lib/bibcirculation_templates.py:5717 #: modules/bibcirculation/lib/bibcirculation_templates.py:5832 #: modules/bibcirculation/lib/bibcirculation_templates.py:5979 #: modules/bibcirculation/lib/bibcirculation_templates.py:6070 #: modules/bibcirculation/lib/bibcirculation_templates.py:6338 #: modules/bibcirculation/lib/bibcirculation_templates.py:6503 #: modules/bibcirculation/lib/bibcirculation_templates.py:6711 #: modules/bibcirculation/lib/bibcirculation_templates.py:6989 #: modules/bibcirculation/lib/bibcirculation_templates.py:7109 #: modules/bibcirculation/lib/bibcirculation_templates.py:7348 #: modules/bibcirculation/lib/bibcirculation_templates.py:7561 #: modules/bibcirculation/lib/bibcirculation_templates.py:7665 #: modules/bibcirculation/lib/bibcirculation_templates.py:7821 #: modules/bibcirculation/lib/bibcirculation_templates.py:8302 #: modules/bibcirculation/lib/bibcirculation_templates.py:8462 #: modules/bibcirculation/lib/bibcirculation_templates.py:8554 #: modules/bibcirculation/lib/bibcirculation_templates.py:12214 #: modules/bibcirculation/lib/bibcirculation_templates.py:12320 #: modules/bibcirculation/lib/bibcirculation_templates.py:12418 #: modules/bibcirculation/lib/bibcirculation_templates.py:12501 msgid "Barcode" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:434 msgid "See this book on BibCirculation" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:501 #: modules/bibcirculation/lib/bibcirculation_templates.py:886 #: modules/bibcirculation/lib/bibcirculation_templates.py:1002 #: modules/bibcirculation/lib/bibcirculation_templates.py:1138 #: modules/bibcirculation/lib/bibcirculation_templates.py:1277 #: modules/bibcirculation/lib/bibcirculation_templates.py:1330 #: modules/bibcirculation/lib/bibcirculation_templates.py:1461 #: modules/bibcirculation/lib/bibcirculation_templates.py:1876 #: modules/bibcirculation/lib/bibcirculation_templates.py:1969 #: modules/bibcirculation/lib/bibcirculation_templates.py:2038 #: modules/bibcirculation/lib/bibcirculation_templates.py:2129 #: modules/bibcirculation/lib/bibcirculation_templates.py:2365 #: modules/bibcirculation/lib/bibcirculation_templates.py:2559 #: modules/bibcirculation/lib/bibcirculation_templates.py:2624 #: modules/bibcirculation/lib/bibcirculation_templates.py:2858 #: modules/bibcirculation/lib/bibcirculation_templates.py:2978 #: modules/bibcirculation/lib/bibcirculation_templates.py:3249 #: modules/bibcirculation/lib/bibcirculation_templates.py:3734 #: modules/bibcirculation/lib/bibcirculation_templates.py:4218 #: modules/bibcirculation/lib/bibcirculation_templates.py:4338 #: modules/bibcirculation/lib/bibcirculation_templates.py:4520 #: modules/bibcirculation/lib/bibcirculation_templates.py:4612 #: modules/bibcirculation/lib/bibcirculation_templates.py:4667 #: modules/bibcirculation/lib/bibcirculation_templates.py:4786 #: modules/bibcirculation/lib/bibcirculation_templates.py:4842 #: modules/bibcirculation/lib/bibcirculation_templates.py:4966 #: modules/bibcirculation/lib/bibcirculation_templates.py:5281 #: modules/bibcirculation/lib/bibcirculation_templates.py:5661 #: modules/bibcirculation/lib/bibcirculation_templates.py:5773 #: modules/bibcirculation/lib/bibcirculation_templates.py:5928 #: modules/bibcirculation/lib/bibcirculation_templates.py:6025 #: modules/bibcirculation/lib/bibcirculation_templates.py:6118 #: modules/bibcirculation/lib/bibcirculation_templates.py:6182 #: modules/bibcirculation/lib/bibcirculation_templates.py:6239 #: modules/bibcirculation/lib/bibcirculation_templates.py:6390 #: modules/bibcirculation/lib/bibcirculation_templates.py:6629 #: modules/bibcirculation/lib/bibcirculation_templates.py:6719 #: modules/bibcirculation/lib/bibcirculation_templates.py:6793 #: modules/bibcirculation/lib/bibcirculation_templates.py:6851 #: modules/bibcirculation/lib/bibcirculation_templates.py:7264 #: modules/bibcirculation/lib/bibcirculation_templates.py:7356 #: modules/bibcirculation/lib/bibcirculation_templates.py:7705 #: modules/bibcirculation/lib/bibcirculation_templates.py:7857 #: modules/bibcirculation/lib/bibcirculation_templates.py:8134 #: modules/bibcirculation/lib/bibcirculation_templates.py:8243 #: modules/bibcirculation/lib/bibcirculation_templates.py:8413 #: modules/bibcirculation/lib/bibcirculation_templates.py:8503 #: modules/bibcirculation/lib/bibcirculation_templates.py:8615 #: modules/bibcirculation/lib/bibcirculation_templates.py:8732 #: modules/bibcirculation/lib/bibcirculation_templates.py:8834 #: modules/bibcirculation/lib/bibcirculation_templates.py:8927 #: modules/bibcirculation/lib/bibcirculation_templates.py:8994 #: modules/bibcirculation/lib/bibcirculation_templates.py:9014 #: modules/bibcirculation/lib/bibcirculation_templates.py:9148 #: modules/bibcirculation/lib/bibcirculation_templates.py:9311 #: modules/bibcirculation/lib/bibcirculation_templates.py:9465 #: modules/bibcirculation/lib/bibcirculation_templates.py:9694 #: modules/bibcirculation/lib/bibcirculation_templates.py:10202 #: modules/bibcirculation/lib/bibcirculation_templates.py:10282 #: modules/bibcirculation/lib/bibcirculation_templates.py:10388 #: modules/bibcirculation/lib/bibcirculation_templates.py:10585 #: modules/bibcirculation/lib/bibcirculation_templates.py:11139 #: modules/bibcirculation/lib/bibcirculation_templates.py:11538 #: modules/bibcirculation/lib/bibcirculation_templates.py:12541 #: modules/bibcirculation/lib/bibcirculation_templates.py:13370 #: modules/bibcirculation/lib/bibcirculation_templates.py:13472 #: modules/bibcirculation/lib/bibcirculation_templates.py:14134 #: modules/bibcirculation/lib/bibcirculation_templates.py:14342 #: modules/bibcirculation/lib/bibcirculation_templates.py:14449 #: modules/bibcirculation/lib/bibcirculation_templates.py:14525 #: modules/bibcirculation/lib/bibcirculation_templates.py:14624 #: modules/bibcirculation/lib/bibcirculation_templates.py:14684 #: modules/bibcirculation/lib/bibcirculation_templates.py:14780 #: modules/bibcirculation/lib/bibcirculation_templates.py:14850 #: modules/bibcirculation/lib/bibcirculation_templates.py:14957 #: modules/bibcirculation/lib/bibcirculation_templates.py:15033 #: modules/bibcirculation/lib/bibcirculation_templates.py:15145 #: modules/bibcirculation/lib/bibcirculation_templates.py:15249 #: modules/bibcirculation/lib/bibcirculation_templates.py:15329 #: modules/bibcirculation/lib/bibcirculation_templates.py:15402 #: modules/bibcirculation/lib/bibcirculation_templates.py:15505 #: modules/bibcirculation/lib/bibcirculation_templates.py:15572 #: modules/bibcirculation/lib/bibcirculation_templates.py:15650 #: modules/bibcirculation/lib/bibcirculation_templates.py:15721 #: modules/bibcirculation/lib/bibcirculation_templates.py:15828 #: modules/bibcirculation/lib/bibcirculation_templates.py:15894 #: modules/bibcirculation/lib/bibcirculation_templates.py:15992 #: modules/bibcirculation/lib/bibcirculation_templates.py:16075 #: modules/bibknowledge/lib/bibknowledgeadmin.py:141 msgid "Back" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:501 msgid "Suggest" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:509 msgid "This item is not for loan." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:514 msgid "You already have a request on, or are in possession of this document." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:519 msgid "" "Your request has been registered and the document will be sent to you via " "internal mail." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:524 msgid "Your request has been registered." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:529 #: modules/bibcirculation/lib/bibcirculation_templates.py:537 msgid "It is not possible to validate your request. " msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:531 #: modules/bibcirculation/lib/bibcirculation_templates.py:539 #, python-format msgid "Please contact %(librarian_email)s" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:545 #: modules/bibcirculation/lib/bibcirculation_templates.py:550 msgid "Thank you for your suggestion. We will get back to you shortly." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:555 msgid "Your purchase request has been registered." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:560 msgid "Server busy. Please, try again in a few seconds." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:584 #: modules/bibcirculation/lib/bibcirculation_templates.py:8396 msgid "Renew all loans" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:589 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:174 msgid "Loans - historical overview" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:601 msgid "You don't have any book on loan." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:624 #: modules/bibcirculation/lib/bibcirculation_templates.py:698 #: modules/bibcirculation/lib/bibcirculation_templates.py:777 #: modules/bibcirculation/lib/bibcirculation_templates.py:852 #: modules/bibcirculation/lib/bibcirculation_templates.py:1163 #: modules/bibcirculation/lib/bibcirculation_templates.py:1353 #: modules/bibcirculation/lib/bibcirculation_templates.py:1635 #: modules/bibcirculation/lib/bibcirculation_templates.py:1677 #: modules/bibcirculation/lib/bibcirculation_templates.py:2497 #: modules/bibcirculation/lib/bibcirculation_templates.py:2605 #: modules/bibcirculation/lib/bibcirculation_templates.py:4555 #: modules/bibcirculation/lib/bibcirculation_templates.py:4691 #: modules/bibcirculation/lib/bibcirculation_templates.py:4868 #: modules/bibcirculation/lib/bibcirculation_templates.py:5039 #: modules/bibcirculation/lib/bibcirculation_templates.py:7818 #: modules/bibcirculation/lib/bibcirculation_templates.py:8186 #: modules/bibcirculation/lib/bibcirculation_templates.py:8301 #: modules/bibcirculation/lib/bibcirculation_templates.py:8462 #: modules/bibcirculation/lib/bibcirculation_templates.py:8553 #: modules/bibcirculation/lib/bibcirculation_templates.py:9207 #: modules/bibcirculation/lib/bibcirculation_templates.py:13525 #: modules/bibcirculation/lib/bibcirculation_templates.py:13658 #: modules/bibcirculation/lib/bibcirculation_utils.py:532 msgid "Item" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:625 #: modules/bibcirculation/lib/bibcirculation_templates.py:4693 #: modules/bibcirculation/lib/bibcirculation_templates.py:4870 #: modules/bibcirculation/lib/bibcirculation_templates.py:5833 #: modules/bibcirculation/lib/bibcirculation_templates.py:6073 #: modules/bibcirculation/lib/bibcirculation_templates.py:8557 msgid "Loaned on" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:627 #: modules/bibcirculation/lib/bibcirculation_templates.py:701 msgid "Action(s)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:639 #: modules/bibcirculation/lib/bibcirculation_templates.py:5886 #: modules/bibcirculation/lib/bibcirculation_templates.py:8361 msgid "Renew" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:673 #: modules/bibcirculation/lib/bibcirculation_templates.py:697 msgid "Your Requests" msgstr "درخواست های شما" #: modules/bibcirculation/lib/bibcirculation_templates.py:674 msgid "You don't have any request (waiting or pending)." msgstr "" "شما هیچ درخواستی (در حال انتظار یا در حال بررسی) ندارید.؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟" "pending" #: modules/bibcirculation/lib/bibcirculation_templates.py:699 #: modules/bibcirculation/lib/bibcirculation_templates.py:1170 #: modules/bibcirculation/lib/bibcirculation_templates.py:1358 #: modules/bibcirculation/lib/bibcirculation_templates.py:1681 #: modules/bibcirculation/lib/bibcirculation_templates.py:4559 #: modules/bibcirculation/lib/bibcirculation_templates.py:5044 #: modules/bibcirculation/lib/bibcirculation_templates.py:5721 #: modules/bibcirculation/lib/bibcirculation_templates.py:5984 #: modules/bibcirculation/lib/bibcirculation_templates.py:8192 #: modules/bibcirculation/lib/bibcirculation_templates.py:8464 #: modules/bibcirculation/lib/bibcirculation_templates.py:9209 #: modules/bibcirculation/lib/bibcirculation_templates.py:12195 #: modules/bibcirculation/lib/bibcirculation_templates.py:12258 #: modules/bibcirculation/lib/bibcirculation_templates.py:12401 #: modules/bibcirculation/lib/bibcirculation_templates.py:12486 #: modules/bibcirculation/lib/bibcirculation_templates.py:13003 #: modules/bibcirculation/lib/bibcirculation_templates.py:13069 #: modules/bibcirculation/lib/bibcirculation_templates.py:13222 #: modules/bibcirculation/lib/bibcirculation_templates.py:13310 #: modules/bibcirculation/lib/bibcirculation_utils.py:537 msgid "Request date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:713 #: modules/bibcirculation/lib/bibcirculation_templates.py:1416 #: modules/bibcirculation/lib/bibcirculation_templates.py:8223 #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:902 #: modules/bibedit/lib/bibeditmulti_templates.py:627 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:261 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:403 #: modules/bibformat/web/admin/bibformatadmin.py:414 #: modules/bibformat/web/admin/bibformatadmin.py:725 #: modules/bibknowledge/lib/bibknowledgeadmin.py:746 #: modules/webbasket/lib/webbasket_templates.py:1795 #: modules/webbasket/lib/webbasket_templates.py:1863 #: modules/webbasket/lib/webbasket_templates.py:1970 #: modules/webbasket/lib/webbasket_templates.py:2025 #: modules/webbasket/lib/webbasket_templates.py:2115 #: modules/webbasket/lib/webbasket_templates.py:3207 #: modules/webbasket/lib/webbasket_templates.py:4010 #: modules/webjournal/lib/webjournaladminlib.py:119 #: modules/webjournal/lib/webjournaladminlib.py:232 #: modules/websession/lib/websession_templates.py:1970 #: modules/websession/lib/websession_templates.py:2078 #: modules/websession/lib/websession_templates.py:2280 #: modules/websession/lib/websession_templates.py:2363 #: modules/websubmit/lib/websubmit_templates.py:2312 #: modules/websubmit/lib/websubmit_templates.py:2375 #: modules/websubmit/lib/websubmit_templates.py:2395 #: modules/websubmit/web/publiline.py:1228 msgid "Cancel" msgstr "لغو" #: modules/bibcirculation/lib/bibcirculation_templates.py:752 #, fuzzy msgid "Your Proposals" msgstr "گروه های شما" #: modules/bibcirculation/lib/bibcirculation_templates.py:753 msgid "You did not propose any acquisitions." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:755 #: modules/bibcirculation/lib/bibcirculation_templates.py:817 #: modules/bibcirculation/lib/bibcirculation_templates.py:1041 #: modules/bibcirculation/lib/bibcirculation_templates.py:1071 #: modules/bibcirculation/lib/bibcirculation_templates.py:2732 #: modules/bibcirculation/lib/bibcirculation_templates.py:3290 #: modules/bibcirculation/lib/bibcirculation_templates.py:3767 #: modules/bibcirculation/lib/bibcirculation_templates.py:4421 #: modules/bibcirculation/lib/bibcirculation_templates.py:5002 #: modules/bibcirculation/lib/bibcirculation_templates.py:7398 #: modules/bibcirculation/lib/bibcirculation_templates.py:9049 #: modules/bibcirculation/lib/bibcirculation_templates.py:9499 #: modules/bibcirculation/lib/bibcirculation_templates.py:14558 #: modules/bibcirculation/lib/bibcirculation_templates.py:14889 #: modules/bibcirculation/lib/bibcirculation_templates.py:15441 #: modules/bibcirculation/lib/bibcirculation_templates.py:15760 msgid "Back to home" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:776 msgid "Your Proposals under review" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:778 #: modules/bibcirculation/lib/bibcirculation_templates.py:13777 msgid "Proposal date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:853 msgid "Loaned" msgstr "امانت داده شده" #: modules/bibcirculation/lib/bibcirculation_templates.py:854 msgid "Returned" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:855 #: modules/bibcirculation/lib/bibcirculation_templates.py:4695 #: modules/bibcirculation/lib/bibcirculation_templates.py:4872 #: modules/bibcirculation/lib/bibcirculation_templates.py:5835 #: modules/bibcirculation/lib/bibcirculation_templates.py:6076 #: modules/bibcirculation/lib/bibcirculation_templates.py:8305 #: modules/bibcirculation/lib/bibcirculation_templates.py:8560 msgid "Renewals" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:933 msgid "Why do you suggest this book for the library?" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:982 msgid "Enter your period of interest" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:983 #: modules/bibcirculation/lib/bibcirculation_templates.py:1168 #: modules/bibcirculation/lib/bibcirculation_templates.py:1356 #: modules/bibcirculation/lib/bibcirculation_templates.py:1679 #: modules/bibcirculation/lib/bibcirculation_templates.py:4557 #: modules/bibcirculation/lib/bibcirculation_templates.py:5042 #: modules/bibcirculation/lib/bibcirculation_templates.py:5719 #: modules/bibcirculation/lib/bibcirculation_templates.py:5982 #: modules/bibcirculation/lib/bibcirculation_templates.py:8190 #: modules/bibcirculation/lib/bibcirculation_templates.py:8463 #: modules/bibcirculation/lib/bibcirculation_templates.py:8653 #: modules/bibcirculation/lib/bibcirculation_templates.py:11515 #: modules/bibcirculation/lib/bibcirculation_utils.py:535 msgid "From" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:983 #: modules/bibcirculation/lib/bibcirculation_templates.py:1169 #: modules/bibcirculation/lib/bibcirculation_templates.py:1357 #: modules/bibcirculation/lib/bibcirculation_templates.py:1680 #: modules/bibcirculation/lib/bibcirculation_templates.py:4558 #: modules/bibcirculation/lib/bibcirculation_templates.py:5043 #: modules/bibcirculation/lib/bibcirculation_templates.py:5720 #: modules/bibcirculation/lib/bibcirculation_templates.py:5983 #: modules/bibcirculation/lib/bibcirculation_templates.py:8191 #: modules/bibcirculation/lib/bibcirculation_templates.py:8464 #: modules/bibcirculation/lib/bibcirculation_templates.py:8655 #: modules/bibcirculation/lib/bibcirculation_templates.py:11516 #: modules/bibcirculation/lib/bibcirculation_utils.py:536 #: modules/bibknowledge/lib/bibknowledge_templates.py:363 msgid "To" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1002 #: modules/bibcirculation/lib/bibcirculation_templates.py:2625 #: modules/bibcirculation/lib/bibcirculation_templates.py:2859 #: modules/bibcirculation/lib/bibcirculation_templates.py:3249 #: modules/bibcirculation/lib/bibcirculation_templates.py:4522 #: modules/bibcirculation/lib/bibcirculation_templates.py:6719 #: modules/bibcirculation/lib/bibcirculation_templates.py:7858 #: modules/bibcirculation/lib/bibcirculation_templates.py:8835 #: modules/bibcirculation/lib/bibcirculation_templates.py:13473 #: modules/bibcirculation/lib/bibcirculation_templates.py:14349 #: modules/bibcirculation/lib/bibcirculation_templates.py:14525 #: modules/bibcirculation/lib/bibcirculation_templates.py:15250 #: modules/bibcirculation/lib/bibcirculation_templates.py:15402 #: modules/bibcirculation/lib/bibcirculation_templates.py:16039 msgid "Confirm" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1037 #, python-format msgid "You can see your library account %(x_url_open)shere%(x_url_close)s." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1115 msgid "Delete this request?" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1116 #: modules/bibcirculation/lib/bibcirculation_templates.py:1373 msgid "Request not deleted." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1137 #: modules/bibcirculation/lib/bibcirculation_templates.py:1329 msgid "No more requests are pending." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1162 #: modules/bibcirculation/lib/bibcirculation_templates.py:1352 #: modules/bibcirculation/lib/bibcirculation_templates.py:1676 #: modules/bibcirculation/lib/bibcirculation_templates.py:2360 #: modules/bibcirculation/lib/bibcirculation_templates.py:2490 #: modules/bibcirculation/lib/bibcirculation_templates.py:2722 #: modules/bibcirculation/lib/bibcirculation_templates.py:2812 #: modules/bibcirculation/lib/bibcirculation_templates.py:3058 #: modules/bibcirculation/lib/bibcirculation_templates.py:3384 #: modules/bibcirculation/lib/bibcirculation_templates.py:3623 #: modules/bibcirculation/lib/bibcirculation_templates.py:3668 #: modules/bibcirculation/lib/bibcirculation_templates.py:3859 #: modules/bibcirculation/lib/bibcirculation_templates.py:4101 #: modules/bibcirculation/lib/bibcirculation_templates.py:4145 #: modules/bibcirculation/lib/bibcirculation_templates.py:5038 #: modules/bibcirculation/lib/bibcirculation_templates.py:5228 #: modules/bibcirculation/lib/bibcirculation_templates.py:6313 #: modules/bibcirculation/lib/bibcirculation_templates.py:6470 #: modules/bibcirculation/lib/bibcirculation_templates.py:6966 #: modules/bibcirculation/lib/bibcirculation_templates.py:7538 #: modules/bibcirculation/lib/bibcirculation_templates.py:7787 #: modules/bibcirculation/lib/bibcirculation_templates.py:7946 #: modules/bibcirculation/lib/bibcirculation_templates.py:8921 #: modules/bibcirculation/lib/bibcirculation_templates.py:8970 #: modules/bibcirculation/lib/bibcirculation_templates.py:9142 #: modules/bibcirculation/lib/bibcirculation_templates.py:9379 #: modules/bibcirculation/lib/bibcirculation_templates.py:9817 #: modules/bibcirculation/lib/bibcirculation_templates.py:10185 #: modules/bibcirculation/lib/bibcirculation_templates.py:11694 #: modules/bibcirculation/lib/bibcirculation_templates.py:12044 #: modules/bibcirculation/lib/bibcirculation_templates.py:12668 #: modules/bibcirculation/lib/bibcirculation_templates.py:12807 #: modules/bibcirculation/lib/bibcirculation_templates.py:14031 #: modules/bibcirculation/lib/bibcirculation_templates.py:14254 #: modules/bibcirculation/lib/bibcirculation_templates.py:14315 #: modules/bibcirculation/lib/bibcirculation_templates.py:14416 #: modules/bibcirculation/lib/bibcirculation_templates.py:14519 #: modules/bibcirculation/lib/bibcirculation_templates.py:14746 #: modules/bibcirculation/lib/bibcirculation_templates.py:14845 #: modules/bibcirculation/lib/bibcirculation_templates.py:15121 #: modules/bibcirculation/lib/bibcirculation_templates.py:15327 #: modules/bibcirculation/lib/bibcirculation_templates.py:15397 #: modules/bibcirculation/lib/bibcirculation_templates.py:15646 #: modules/bibcirculation/lib/bibcirculation_templates.py:15717 #: modules/bibcirculation/lib/bibcirculation_templates.py:15970 #: modules/bibcirculation/lib/bibcirculation_utils.py:452 #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:398 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:194 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:255 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:345 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:398 #: modules/bibformat/lib/bibformat_templates.py:181 #: modules/bibformat/lib/bibformat_templates.py:725 #: modules/bibformat/lib/bibformat_templates.py:850 #: modules/bibknowledge/lib/bibknowledge_templates.py:79 #: modules/bibupload/lib/batchuploader_templates.py:622 #: modules/webalert/lib/webalert_templates.py:319 #: modules/websubmit/lib/functions/Create_Upload_Files_Interface.py:467 msgid "Name" msgstr "نام" #: modules/bibcirculation/lib/bibcirculation_templates.py:1166 msgid "Vol." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1167 msgid "Ed." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1229 #: modules/bibcirculation/lib/bibcirculation_templates.py:5108 #: modules/bibcirculation/lib/bibcirculation_templates.py:7707 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:175 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:324 #: modules/bibformat/lib/bibformat_templates.py:786 #: modules/bibformat/web/admin/bibformatadmin.py:414 #: modules/bibformat/web/admin/bibformatadmin.py:416 #: modules/bibformat/web/admin/bibformatadmin.py:725 #: modules/bibformat/web/admin/bibformatadmin.py:727 #: modules/bibknowledge/lib/bibknowledge_templates.py:102 #: modules/bibknowledge/lib/bibknowledge_templates.py:529 #: modules/bibknowledge/lib/bibknowledgeadmin.py:746 #: modules/bibknowledge/lib/bibknowledgeadmin.py:748 #: modules/webbasket/lib/webbasket_templates.py:3262 #: modules/webjournal/lib/webjournaladminlib.py:118 #: modules/webjournal/lib/webjournaladminlib.py:121 #: modules/webmessage/lib/webmessage_templates.py:115 msgid "Delete" msgstr "حذف" #: modules/bibcirculation/lib/bibcirculation_templates.py:1231 #: modules/bibcirculation/lib/bibcirculation_templates.py:5753 #, fuzzy msgid "Create loan" msgstr "ایجاد سبد" #: modules/bibcirculation/lib/bibcirculation_templates.py:1422 #: modules/bibcirculation/lib/bibcirculation_templates.py:5115 #: modules/bibcirculation/lib/bibcirculationadminlib.py:647 #: modules/bibcirculation/lib/bibcirculationadminlib.py:657 #: modules/bibcirculation/lib/bibcirculationadminlib.py:667 #: modules/bibcirculation/lib/bibcirculationadminlib.py:758 #, fuzzy msgid "Create Loan" msgstr "ایجاد سبد" #: modules/bibcirculation/lib/bibcirculation_templates.py:1517 #: modules/bibcirculation/lib/bibcirculation_templates.py:1756 #: modules/bibcirculation/lib/bibcirculation_templates.py:8733 msgid "Reset" msgstr "بازتنطیم" #: modules/bibcirculation/lib/bibcirculation_templates.py:1518 #: modules/bibcirculation/lib/bibcirculation_templates.py:1757 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:494 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:557 #: modules/webcomment/lib/webcomment_templates.py:2061 msgid "OK" msgstr "بسیارخوب" #: modules/bibcirculation/lib/bibcirculation_templates.py:1560 #, python-format msgid "" "The item %(x_strong_tag_open)s%(x_title)s%(x_strong_tag_close)s, with " "barcode %(x_strong_tag_open)s%(x_barcode)s%(x_strong_tag_close)s, has been " "returned with success." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1574 msgid "The next(pending) request on the returned book is shown below." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1633 msgid "Loan informations" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1634 #: modules/bibcirculation/lib/bibcirculation_templates.py:2593 #: modules/bibcirculation/lib/bibcirculation_templates.py:4554 #: modules/bibcirculation/lib/bibcirculation_templates.py:4690 #: modules/bibcirculation/lib/bibcirculation_templates.py:4867 #: modules/bibcirculation/lib/bibcirculation_templates.py:5713 #: modules/bibcirculation/lib/bibcirculation_templates.py:5831 #: modules/bibcirculation/lib/bibcirculation_templates.py:5978 #: modules/bibcirculation/lib/bibcirculation_templates.py:6069 #: modules/bibcirculation/lib/bibcirculation_templates.py:13524 #: modules/bibcirculation/lib/bibcirculation_templates.py:13657 #: modules/bibcirculation/lib/bibcirculation_utils.py:531 msgid "Borrower" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1636 #: modules/bibcirculation/lib/bibcirculation_templates.py:2088 #: modules/bibcirculation/lib/bibcirculation_templates.py:10346 #: modules/bibcirculation/lib/bibcirculation_utils.py:427 msgid "Author" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1637 #: modules/bibcirculation/lib/bibcirculation_templates.py:2729 #: modules/bibcirculation/lib/bibcirculation_templates.py:3386 #: modules/bibcirculation/lib/bibcirculation_templates.py:3625 #: modules/bibcirculation/lib/bibcirculation_templates.py:3861 #: modules/bibcirculation/lib/bibcirculation_templates.py:4103 #: modules/bibcirculation/lib/bibcirculation_templates.py:5229 #: modules/bibcirculation/lib/bibcirculation_templates.py:6317 #: modules/bibcirculation/lib/bibcirculation_templates.py:6474 #: modules/bibcirculation/lib/bibcirculation_templates.py:6970 #: modules/bibcirculation/lib/bibcirculation_templates.py:7542 #: modules/bibcirculation/lib/bibcirculation_templates.py:9383 #: modules/bibcirculation/lib/bibcirculation_templates.py:9605 #: modules/bibcirculation/lib/bibcirculation_templates.py:9820 #: modules/bibcirculation/lib/bibcirculation_templates.py:10092 #: modules/bibcirculation/lib/bibcirculation_templates.py:10517 #: modules/bibcirculation/lib/bibcirculation_templates.py:10712 #: modules/bibcirculation/lib/bibcirculation_templates.py:10961 #: modules/bibcirculation/lib/bibcirculation_templates.py:11059 #: modules/bibcirculation/lib/bibcirculation_templates.py:11209 #: modules/bibcirculation/lib/bibcirculation_templates.py:11698 #: modules/bibcirculation/lib/bibcirculation_templates.py:11795 #: modules/bibcirculation/lib/bibcirculation_templates.py:11905 #: modules/bibcirculation/lib/bibcirculation_templates.py:11989 #: modules/bibcirculation/lib/bibcirculation_templates.py:12672 #: modules/bibcirculation/lib/bibcirculation_templates.py:12777 #: modules/bibcirculation/lib/bibcirculation_utils.py:430 #: modules/miscutil/lib/dateutils.py:319 msgid "Year" msgstr "سال" #: modules/bibcirculation/lib/bibcirculation_templates.py:1638 #: modules/bibcirculation/lib/bibcirculation_templates.py:2088 #: modules/bibcirculation/lib/bibcirculation_templates.py:2730 #: modules/bibcirculation/lib/bibcirculation_templates.py:3387 #: modules/bibcirculation/lib/bibcirculation_templates.py:3626 #: modules/bibcirculation/lib/bibcirculation_templates.py:3862 #: modules/bibcirculation/lib/bibcirculation_templates.py:4104 #: modules/bibcirculation/lib/bibcirculation_templates.py:5230 #: modules/bibcirculation/lib/bibcirculation_templates.py:6319 #: modules/bibcirculation/lib/bibcirculation_templates.py:6476 #: modules/bibcirculation/lib/bibcirculation_templates.py:6972 #: modules/bibcirculation/lib/bibcirculation_templates.py:7544 #: modules/bibcirculation/lib/bibcirculation_templates.py:9385 #: modules/bibcirculation/lib/bibcirculation_templates.py:9604 #: modules/bibcirculation/lib/bibcirculation_templates.py:9821 #: modules/bibcirculation/lib/bibcirculation_templates.py:10093 #: modules/bibcirculation/lib/bibcirculation_templates.py:10346 #: modules/bibcirculation/lib/bibcirculation_templates.py:10962 #: modules/bibcirculation/lib/bibcirculation_templates.py:11058 #: modules/bibcirculation/lib/bibcirculation_templates.py:11208 #: modules/bibcirculation/lib/bibcirculation_templates.py:11700 #: modules/bibcirculation/lib/bibcirculation_templates.py:11794 #: modules/bibcirculation/lib/bibcirculation_templates.py:11904 #: modules/bibcirculation/lib/bibcirculation_templates.py:11988 #: modules/bibcirculation/lib/bibcirculation_templates.py:12674 #: modules/bibcirculation/lib/bibcirculation_templates.py:12776 msgid "Publisher" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1639 #: modules/bibcirculation/lib/bibcirculation_templates.py:2731 #: modules/bibcirculation/lib/bibcirculation_templates.py:3388 #: modules/bibcirculation/lib/bibcirculation_templates.py:3627 #: modules/bibcirculation/lib/bibcirculation_templates.py:3863 #: modules/bibcirculation/lib/bibcirculation_templates.py:4105 #: modules/bibcirculation/lib/bibcirculation_templates.py:5230 #: modules/bibcirculation/lib/bibcirculation_templates.py:6321 #: modules/bibcirculation/lib/bibcirculation_templates.py:6478 #: modules/bibcirculation/lib/bibcirculation_templates.py:6974 #: modules/bibcirculation/lib/bibcirculation_templates.py:7546 #: modules/bibcirculation/lib/bibcirculation_templates.py:9387 #: modules/bibcirculation/lib/bibcirculation_templates.py:9607 #: modules/bibcirculation/lib/bibcirculation_templates.py:9823 #: modules/bibcirculation/lib/bibcirculation_templates.py:10095 #: modules/bibcirculation/lib/bibcirculation_templates.py:10963 #: modules/bibcirculation/lib/bibcirculation_templates.py:11062 #: modules/bibcirculation/lib/bibcirculation_templates.py:11207 #: modules/bibcirculation/lib/bibcirculation_templates.py:11702 #: modules/bibcirculation/lib/bibcirculation_templates.py:11797 #: modules/bibcirculation/lib/bibcirculation_templates.py:11991 #: modules/bibcirculation/lib/bibcirculation_templates.py:12676 #: modules/bibcirculation/lib/bibcirculation_templates.py:12779 #: modules/bibcirculation/lib/bibcirculation_utils.py:429 msgid "ISBN" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1640 #: modules/bibcirculation/lib/bibcirculation_templates.py:12403 #: modules/bibcirculation/lib/bibcirculation_templates.py:13224 msgid "Return date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1675 msgid "Waiting requests" msgstr "درخواست های در حال انتظار" #: modules/bibcirculation/lib/bibcirculation_templates.py:1678 #: modules/bibcirculation/lib/bibcirculation_templates.py:8187 msgid "Request status" msgstr "وضعیت درخواست" #: modules/bibcirculation/lib/bibcirculation_templates.py:1682 msgid "Request options" msgstr "گزینه های درخواست" #: modules/bibcirculation/lib/bibcirculation_templates.py:1705 msgid "Select request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1739 #, fuzzy msgid "Return another book" msgstr "بازگشت به سبد" #: modules/bibcirculation/lib/bibcirculation_templates.py:1777 msgid "Welcome to Invenio BibCirculation Admin" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1803 #: modules/bibcirculation/lib/bibcirculation_templates.py:2193 #: modules/bibcirculation/lib/bibcirculation_templates.py:2200 #: modules/bibcirculation/lib/bibcirculation_templates.py:2207 #: modules/bibcirculation/lib/bibcirculation_templates.py:3917 #: modules/bibcirculation/lib/bibcirculation_templates.py:3924 #: modules/bibcirculation/lib/bibcirculation_templates.py:3931 #: modules/bibcirculation/lib/bibcirculation_templates.py:9878 #: modules/bibcirculation/lib/bibcirculation_templates.py:9885 #: modules/bibcirculation/lib/bibcirculation_templates.py:9892 #: modules/bibcirculation/lib/bibcirculation_templates.py:10767 #: modules/bibcirculation/lib/bibcirculation_templates.py:10774 #: modules/bibcirculation/lib/bibcirculation_templates.py:10781 #: modules/bibcirculation/lib/bibcirculation_templates.py:11311 #: modules/bibcirculation/lib/bibcirculation_templates.py:11318 #: modules/bibcirculation/lib/bibcirculation_templates.py:11325 msgid "id" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1816 msgid "register new borrower" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1847 #: modules/bibcirculation/lib/bibcirculation_templates.py:2186 #: modules/bibcirculation/lib/bibcirculation_templates.py:3435 #: modules/bibcirculation/lib/bibcirculation_templates.py:9871 #: modules/bibcirculation/lib/bibcirculation_templates.py:10760 #: modules/bibcirculation/lib/bibcirculation_templates.py:11304 msgid "Search borrower by" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1848 #: modules/bibcirculation/lib/bibcirculation_templates.py:2166 #: modules/bibcirculation/lib/bibcirculation_templates.py:2173 #: modules/bibcirculation/lib/bibcirculation_templates.py:2180 #: modules/bibcirculation/lib/bibcirculation_templates.py:2193 #: modules/bibcirculation/lib/bibcirculation_templates.py:2200 #: modules/bibcirculation/lib/bibcirculation_templates.py:2207 #: modules/bibcirculation/lib/bibcirculation_templates.py:3415 #: modules/bibcirculation/lib/bibcirculation_templates.py:3422 #: modules/bibcirculation/lib/bibcirculation_templates.py:3429 #: modules/bibcirculation/lib/bibcirculation_templates.py:3442 #: modules/bibcirculation/lib/bibcirculation_templates.py:3449 #: modules/bibcirculation/lib/bibcirculation_templates.py:3456 #: modules/bibcirculation/lib/bibcirculation_templates.py:3890 #: modules/bibcirculation/lib/bibcirculation_templates.py:3897 #: modules/bibcirculation/lib/bibcirculation_templates.py:3904 #: modules/bibcirculation/lib/bibcirculation_templates.py:3917 #: modules/bibcirculation/lib/bibcirculation_templates.py:3924 #: modules/bibcirculation/lib/bibcirculation_templates.py:3931 #: modules/bibcirculation/lib/bibcirculation_templates.py:9851 #: modules/bibcirculation/lib/bibcirculation_templates.py:9858 #: modules/bibcirculation/lib/bibcirculation_templates.py:9865 #: modules/bibcirculation/lib/bibcirculation_templates.py:9878 #: modules/bibcirculation/lib/bibcirculation_templates.py:9885 #: modules/bibcirculation/lib/bibcirculation_templates.py:9892 #: modules/bibcirculation/lib/bibcirculation_templates.py:10740 #: modules/bibcirculation/lib/bibcirculation_templates.py:10747 #: modules/bibcirculation/lib/bibcirculation_templates.py:10754 #: modules/bibcirculation/lib/bibcirculation_templates.py:10767 #: modules/bibcirculation/lib/bibcirculation_templates.py:10774 #: modules/bibcirculation/lib/bibcirculation_templates.py:10781 #: modules/bibcirculation/lib/bibcirculation_templates.py:11284 #: modules/bibcirculation/lib/bibcirculation_templates.py:11291 #: modules/bibcirculation/lib/bibcirculation_templates.py:11298 #: modules/bibcirculation/lib/bibcirculation_templates.py:11311 #: modules/bibcirculation/lib/bibcirculation_templates.py:11318 #: modules/bibcirculation/lib/bibcirculation_templates.py:11325 #: modules/bibcirculation/lib/bibcirculation_templates.py:14076 #: modules/bibcirculation/lib/bibcirculation_templates.py:14594 #: modules/bibcirculation/lib/bibcirculation_templates.py:14928 #: modules/bibcirculation/lib/bibcirculation_templates.py:15478 #: modules/bibcirculation/lib/bibcirculation_templates.py:15797 msgid "name" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1848 #: modules/bibcirculation/lib/bibcirculation_templates.py:2166 #: modules/bibcirculation/lib/bibcirculation_templates.py:2173 #: modules/bibcirculation/lib/bibcirculation_templates.py:2180 #: modules/bibcirculation/lib/bibcirculation_templates.py:2193 #: modules/bibcirculation/lib/bibcirculation_templates.py:2200 #: modules/bibcirculation/lib/bibcirculation_templates.py:2207 #: modules/bibcirculation/lib/bibcirculation_templates.py:3415 #: modules/bibcirculation/lib/bibcirculation_templates.py:3422 #: modules/bibcirculation/lib/bibcirculation_templates.py:3429 #: modules/bibcirculation/lib/bibcirculation_templates.py:3442 #: modules/bibcirculation/lib/bibcirculation_templates.py:3449 #: modules/bibcirculation/lib/bibcirculation_templates.py:3456 #: modules/bibcirculation/lib/bibcirculation_templates.py:3890 #: modules/bibcirculation/lib/bibcirculation_templates.py:3897 #: modules/bibcirculation/lib/bibcirculation_templates.py:3904 #: modules/bibcirculation/lib/bibcirculation_templates.py:3917 #: modules/bibcirculation/lib/bibcirculation_templates.py:3924 #: modules/bibcirculation/lib/bibcirculation_templates.py:3931 #: modules/bibcirculation/lib/bibcirculation_templates.py:9851 #: modules/bibcirculation/lib/bibcirculation_templates.py:9858 #: modules/bibcirculation/lib/bibcirculation_templates.py:9865 #: modules/bibcirculation/lib/bibcirculation_templates.py:9878 #: modules/bibcirculation/lib/bibcirculation_templates.py:9885 #: modules/bibcirculation/lib/bibcirculation_templates.py:9892 #: modules/bibcirculation/lib/bibcirculation_templates.py:10740 #: modules/bibcirculation/lib/bibcirculation_templates.py:10747 #: modules/bibcirculation/lib/bibcirculation_templates.py:10754 #: modules/bibcirculation/lib/bibcirculation_templates.py:10767 #: modules/bibcirculation/lib/bibcirculation_templates.py:10774 #: modules/bibcirculation/lib/bibcirculation_templates.py:10781 #: modules/bibcirculation/lib/bibcirculation_templates.py:11284 #: modules/bibcirculation/lib/bibcirculation_templates.py:11291 #: modules/bibcirculation/lib/bibcirculation_templates.py:11298 #: modules/bibcirculation/lib/bibcirculation_templates.py:11311 #: modules/bibcirculation/lib/bibcirculation_templates.py:11318 #: modules/bibcirculation/lib/bibcirculation_templates.py:11325 #: modules/bibcirculation/lib/bibcirculation_templates.py:14076 #: modules/bibcirculation/lib/bibcirculation_templates.py:14595 #: modules/bibcirculation/lib/bibcirculation_templates.py:14928 #: modules/bibcirculation/lib/bibcirculation_templates.py:15479 #: modules/bibcirculation/lib/bibcirculation_templates.py:15798 msgid "email" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1901 #: modules/bibcirculation/lib/bibcirculation_templates.py:1906 #: modules/bibcirculation/lib/bibcirculationadminlib.py:257 #: modules/bibcirculation/lib/bibcirculationadminlib.py:262 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1116 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1121 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1428 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1433 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4164 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4490 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4495 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4714 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4719 msgid "0 borrowers found." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1901 #: modules/bibcirculation/lib/bibcirculationadminlib.py:257 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1116 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1428 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4490 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4714 msgid "Search by CCID." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1905 #: modules/bibcirculation/lib/bibcirculationadminlib.py:261 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1120 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1432 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4494 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4718 msgid "Register new borrower." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:1931 msgid "Borrower(s)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2006 #: modules/bibcirculation/lib/bibcirculation_templates.py:2920 #: modules/bibcirculation/lib/bibcirculation_templates.py:6151 #: modules/bibcirculation/lib/bibcirculation_templates.py:6760 #: modules/bibcirculation/lib/bibcirculation_templates.py:10251 msgid "Search item by" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2006 #: modules/bibcirculation/lib/bibcirculation_templates.py:3383 #: modules/bibcirculation/lib/bibcirculation_templates.py:3622 #: modules/bibcirculation/lib/bibcirculation_templates.py:3858 #: modules/bibcirculation/lib/bibcirculation_templates.py:4100 #: modules/bibcirculation/lib/bibcirculation_templates.py:5228 #: modules/bibcirculation/lib/bibcirculation_templates.py:5250 #: modules/bibcirculation/lib/bibcirculation_templates.py:6312 #: modules/bibcirculation/lib/bibcirculation_templates.py:6469 #: modules/bibcirculation/lib/bibcirculation_templates.py:6965 #: modules/bibcirculation/lib/bibcirculation_templates.py:7537 #: modules/bibcirculation/lib/bibcirculation_templates.py:9377 #: modules/bibcirculation/lib/bibcirculation_templates.py:9545 #: modules/bibcirculation/lib/bibcirculation_templates.py:9816 #: modules/bibcirculation/lib/bibcirculation_templates.py:10088 #: modules/bibcirculation/lib/bibcirculation_templates.py:10704 #: modules/bibcirculation/lib/bibcirculation_templates.py:11203 #: modules/bibcirculation/lib/bibcirculation_templates.py:11693 #: modules/bibcirculation/lib/bibcirculation_templates.py:11790 #: modules/bibcirculation/lib/bibcirculation_templates.py:11895 #: modules/bibcirculation/lib/bibcirculation_templates.py:11984 #: modules/bibcirculation/lib/bibcirculation_templates.py:12667 #: modules/bibcirculation/lib/bibcirculation_templates.py:12772 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1553 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2334 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2818 msgid "Item details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2006 #: modules/bibcirculation/lib/bibcirculation_templates.py:2928 #: modules/bibcirculation/lib/bibcirculation_templates.py:2936 #: modules/bibcirculation/lib/bibcirculation_templates.py:2944 #: modules/bibcirculation/lib/bibcirculation_templates.py:2952 #: modules/bibcirculation/lib/bibcirculation_templates.py:5251 #: modules/bibcirculation/lib/bibcirculation_templates.py:10251 msgid "barcode" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2006 #: modules/bibcirculation/lib/bibcirculation_templates.py:5251 msgid "recid" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2062 msgid "0 item(s) found." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2087 #, python-format msgid "%i items found." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2087 #: modules/bibcirculation/lib/bibcirculation_templates.py:2727 #: modules/bibcirculation/lib/bibcirculation_templates.py:4289 #: modules/bibcirculation/lib/bibcirculation_templates.py:10089 #: modules/bibcirculation/lib/bibcirculation_templates.py:10345 #: modules/bibcirculation/lib/bibcirculation_templates.py:10959 #: modules/bibcirculation/lib/bibcirculation_templates.py:11055 #: modules/bibcirculation/lib/bibcirculation_templates.py:11205 #: modules/bibcirculation/lib/bibcirculation_templates.py:11791 #: modules/bibcirculation/lib/bibcirculation_templates.py:11985 #: modules/bibcirculation/lib/bibcirculation_templates.py:12773 #: modules/bibcirculation/lib/bibcirculation_templates.py:13780 #: modules/bibcirculation/lib/bibcirculation_templates.py:13897 #: modules/bibcirculation/lib/bibcirculation_utils.py:426 #: modules/webbasket/lib/webbasket_templates.py:1610 #: modules/websubmit/lib/websubmit_templates.py:2503 msgid "Title" msgstr "عنوان" #: modules/bibcirculation/lib/bibcirculation_templates.py:2089 #: modules/bibcirculation/lib/bibcirculation_templates.py:10347 msgid "# copies" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2159 #: modules/bibcirculation/lib/bibcirculation_templates.py:3408 #: modules/bibcirculation/lib/bibcirculation_templates.py:3883 #: modules/bibcirculation/lib/bibcirculation_templates.py:3910 #: modules/bibcirculation/lib/bibcirculation_templates.py:9844 #: modules/bibcirculation/lib/bibcirculation_templates.py:10733 #: modules/bibcirculation/lib/bibcirculation_templates.py:11277 msgid "Search user by" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2265 #: modules/bibcirculation/lib/bibcirculation_templates.py:3514 #: modules/bibcirculation/lib/bibcirculation_templates.py:4000 #: modules/bibcirculation/lib/bibcirculation_templates.py:9965 #: modules/bibcirculation/lib/bibcirculation_templates.py:10851 #: modules/bibcirculation/lib/bibcirculation_templates.py:11406 msgid "Select user" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2286 #: modules/bibcirculation/lib/bibcirculation_templates.py:2402 #: modules/bibcirculation/lib/bibcirculation_templates.py:2644 #: modules/bibcirculation/lib/bibcirculation_templates.py:2763 #: modules/bibcirculation/lib/bibcirculation_templates.py:2897 #: modules/bibcirculation/lib/bibcirculation_templates.py:3667 #: modules/bibcirculation/lib/bibcirculation_templates.py:4144 #: modules/bibcirculation/lib/bibcirculation_templates.py:7736 #: modules/bibcirculation/lib/bibcirculation_templates.py:7882 #: modules/bibcirculation/lib/bibcirculation_templates.py:9062 #: modules/bibcirculation/lib/bibcirculation_templates.py:10012 #: modules/bibcirculation/lib/bibcirculation_templates.py:13528 #: modules/bibcirculation/lib/bibcirculation_templates.py:13663 #: modules/bibcirculation/lib/bibcirculation_templates.py:13776 #: modules/bibupload/lib/batchuploader_templates.py:622 msgid "ID" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2289 #: modules/bibcirculation/lib/bibcirculation_templates.py:2405 #: modules/bibcirculation/lib/bibcirculation_templates.py:2647 #: modules/bibcirculation/lib/bibcirculation_templates.py:2766 #: modules/bibcirculation/lib/bibcirculation_templates.py:2900 #: modules/bibcirculation/lib/bibcirculation_templates.py:7739 #: modules/bibcirculation/lib/bibcirculation_templates.py:7885 #: modules/bibcirculation/lib/bibcirculation_templates.py:10015 msgid "CCID" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2305 #: modules/bibcirculation/lib/bibcirculation_templates.py:2488 msgid "User information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2361 #: modules/bibcirculation/lib/bibcirculation_templates.py:2491 #: modules/bibcirculation/lib/bibcirculation_templates.py:2723 #: modules/bibcirculation/lib/bibcirculation_templates.py:2813 #: modules/bibcirculation/lib/bibcirculation_templates.py:3059 #: modules/bibcirculation/lib/bibcirculation_templates.py:3669 #: modules/bibcirculation/lib/bibcirculation_templates.py:4146 #: modules/bibcirculation/lib/bibcirculation_templates.py:7788 #: modules/bibcirculation/lib/bibcirculation_templates.py:7947 #: modules/bibcirculation/lib/bibcirculation_templates.py:8924 #: modules/bibcirculation/lib/bibcirculation_templates.py:8973 #: modules/bibcirculation/lib/bibcirculation_templates.py:9143 #: modules/bibcirculation/lib/bibcirculation_templates.py:10186 #: modules/bibcirculation/lib/bibcirculation_templates.py:14032 #: modules/bibcirculation/lib/bibcirculation_templates.py:14255 #: modules/bibcirculation/lib/bibcirculation_templates.py:14316 #: modules/bibcirculation/lib/bibcirculation_templates.py:14417 #: modules/bibcirculation/lib/bibcirculation_templates.py:14522 #: modules/bibcirculation/lib/bibcirculation_templates.py:14749 #: modules/bibcirculation/lib/bibcirculation_templates.py:14848 #: modules/bibcirculation/lib/bibcirculation_templates.py:15122 #: modules/bibcirculation/lib/bibcirculation_templates.py:15328 #: modules/bibcirculation/lib/bibcirculation_templates.py:15400 #: modules/bibcirculation/lib/bibcirculation_templates.py:15649 #: modules/bibcirculation/lib/bibcirculation_templates.py:15720 #: modules/bibcirculation/lib/bibcirculation_templates.py:15971 #: modules/bibcirculation/lib/bibcirculation_utils.py:454 msgid "Address" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2362 #: modules/bibcirculation/lib/bibcirculation_templates.py:2492 #: modules/bibcirculation/lib/bibcirculation_templates.py:2724 #: modules/bibcirculation/lib/bibcirculation_templates.py:2814 #: modules/bibcirculation/lib/bibcirculation_templates.py:3060 #: modules/bibcirculation/lib/bibcirculation_templates.py:3670 #: modules/bibcirculation/lib/bibcirculation_templates.py:4147 #: modules/bibcirculation/lib/bibcirculation_templates.py:7789 #: modules/bibcirculation/lib/bibcirculation_templates.py:7948 #: modules/bibcirculation/lib/bibcirculation_templates.py:8925 #: modules/bibcirculation/lib/bibcirculation_templates.py:8974 #: modules/bibcirculation/lib/bibcirculation_templates.py:9144 #: modules/bibcirculation/lib/bibcirculation_templates.py:10187 #: modules/bibcirculation/lib/bibcirculation_templates.py:12046 #: modules/bibcirculation/lib/bibcirculation_templates.py:12809 #: modules/bibcirculation/lib/bibcirculation_utils.py:453 msgid "Mailbox" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2363 #: modules/bibcirculation/lib/bibcirculation_templates.py:2493 #: modules/bibcirculation/lib/bibcirculation_templates.py:2725 #: modules/bibcirculation/lib/bibcirculation_templates.py:2815 #: modules/bibcirculation/lib/bibcirculation_templates.py:3061 #: modules/bibcirculation/lib/bibcirculation_templates.py:3671 #: modules/bibcirculation/lib/bibcirculation_templates.py:4148 #: modules/bibcirculation/lib/bibcirculation_templates.py:7790 #: modules/bibcirculation/lib/bibcirculation_templates.py:7949 #: modules/bibcirculation/lib/bibcirculation_templates.py:8922 #: modules/bibcirculation/lib/bibcirculation_templates.py:8971 #: modules/bibcirculation/lib/bibcirculation_templates.py:9145 #: modules/bibcirculation/lib/bibcirculation_templates.py:10188 #: modules/bibcirculation/lib/bibcirculation_templates.py:12045 #: modules/bibcirculation/lib/bibcirculation_templates.py:12808 #: modules/bibcirculation/lib/bibcirculation_templates.py:14033 #: modules/bibcirculation/lib/bibcirculation_templates.py:14256 #: modules/bibcirculation/lib/bibcirculation_templates.py:14317 #: modules/bibcirculation/lib/bibcirculation_templates.py:14417 #: modules/bibcirculation/lib/bibcirculation_templates.py:14520 #: modules/bibcirculation/lib/bibcirculation_templates.py:14747 #: modules/bibcirculation/lib/bibcirculation_templates.py:14846 #: modules/bibcirculation/lib/bibcirculation_templates.py:15123 #: modules/bibcirculation/lib/bibcirculation_templates.py:15328 #: modules/bibcirculation/lib/bibcirculation_templates.py:15398 #: modules/bibcirculation/lib/bibcirculation_templates.py:15647 #: modules/bibcirculation/lib/bibcirculation_templates.py:15718 #: modules/bibcirculation/lib/bibcirculation_templates.py:15972 #: modules/bibcirculation/lib/bibcirculation_utils.py:455 #: modules/webcomment/lib/webcomment_templates.py:1738 #: modules/webcomment/lib/webcomment_templates.py:1770 msgid "Email" msgstr "ایمیل" #: modules/bibcirculation/lib/bibcirculation_templates.py:2364 #: modules/bibcirculation/lib/bibcirculation_templates.py:2494 #: modules/bibcirculation/lib/bibcirculation_templates.py:2726 #: modules/bibcirculation/lib/bibcirculation_templates.py:2816 #: modules/bibcirculation/lib/bibcirculation_templates.py:3062 #: modules/bibcirculation/lib/bibcirculation_templates.py:3672 #: modules/bibcirculation/lib/bibcirculation_templates.py:4149 #: modules/bibcirculation/lib/bibcirculation_templates.py:7791 #: modules/bibcirculation/lib/bibcirculation_templates.py:7950 #: modules/bibcirculation/lib/bibcirculation_templates.py:8923 #: modules/bibcirculation/lib/bibcirculation_templates.py:8972 #: modules/bibcirculation/lib/bibcirculation_templates.py:9146 #: modules/bibcirculation/lib/bibcirculation_templates.py:10189 #: modules/bibcirculation/lib/bibcirculation_templates.py:14034 #: modules/bibcirculation/lib/bibcirculation_templates.py:14257 #: modules/bibcirculation/lib/bibcirculation_templates.py:14318 #: modules/bibcirculation/lib/bibcirculation_templates.py:14417 #: modules/bibcirculation/lib/bibcirculation_templates.py:14521 #: modules/bibcirculation/lib/bibcirculation_templates.py:14748 #: modules/bibcirculation/lib/bibcirculation_templates.py:14847 #: modules/bibcirculation/lib/bibcirculation_templates.py:15124 #: modules/bibcirculation/lib/bibcirculation_templates.py:15328 #: modules/bibcirculation/lib/bibcirculation_templates.py:15399 #: modules/bibcirculation/lib/bibcirculation_templates.py:15648 #: modules/bibcirculation/lib/bibcirculation_templates.py:15719 #: modules/bibcirculation/lib/bibcirculation_templates.py:15973 msgid "Phone" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2365 msgid "Enter the barcode" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2366 #: modules/bibcirculation/lib/bibcirculation_templates.py:2559 #: modules/bibcirculation/lib/bibcirculation_templates.py:3734 #: modules/bibcirculation/lib/bibcirculation_templates.py:4218 #: modules/bibcirculation/lib/bibcirculation_templates.py:6629 #: modules/bibcirculation/lib/bibcirculation_templates.py:7265 #: modules/bibcirculation/lib/bibcirculation_templates.py:7356 #: modules/bibcirculation/lib/bibcirculation_templates.py:8927 #: modules/bibcirculation/lib/bibcirculation_templates.py:9014 #: modules/bibcirculation/lib/bibcirculation_templates.py:9148 #: modules/bibcirculation/lib/bibcirculation_templates.py:9465 #: modules/bibcirculation/lib/bibcirculation_templates.py:9694 #: modules/bibcirculation/lib/bibcirculation_templates.py:10202 #: modules/bibcirculation/lib/bibcirculation_templates.py:10585 #: modules/bibcirculation/lib/bibcirculation_templates.py:11139 #: modules/bibcirculation/lib/bibcirculation_templates.py:12541 #: modules/bibcirculation/lib/bibcirculation_templates.py:13370 #: modules/bibcirculation/lib/bibcirculation_templates.py:14449 #: modules/bibcirculation/lib/bibcirculation_templates.py:14780 #: modules/bibcirculation/lib/bibcirculation_templates.py:14850 #: modules/bibcirculation/lib/bibcirculation_templates.py:15329 #: modules/bibcirculation/lib/bibcirculation_templates.py:15650 #: modules/bibcirculation/lib/bibcirculation_templates.py:15721 msgid "Continue" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2370 #, fuzzy msgid "See all loans" msgstr "آخرین امانت ها" #: modules/bibcirculation/lib/bibcirculation_templates.py:2495 msgid "List of borrowed books" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2499 msgid "Write note(s)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2728 #: modules/bibcirculation/lib/bibcirculation_templates.py:3385 #: modules/bibcirculation/lib/bibcirculation_templates.py:3624 #: modules/bibcirculation/lib/bibcirculation_templates.py:3860 #: modules/bibcirculation/lib/bibcirculation_templates.py:4102 #: modules/bibcirculation/lib/bibcirculation_templates.py:5229 #: modules/bibcirculation/lib/bibcirculation_templates.py:6315 #: modules/bibcirculation/lib/bibcirculation_templates.py:6472 #: modules/bibcirculation/lib/bibcirculation_templates.py:6968 #: modules/bibcirculation/lib/bibcirculation_templates.py:7540 #: modules/bibcirculation/lib/bibcirculation_templates.py:9381 #: modules/bibcirculation/lib/bibcirculation_templates.py:9602 #: modules/bibcirculation/lib/bibcirculation_templates.py:9818 #: modules/bibcirculation/lib/bibcirculation_templates.py:10090 #: modules/bibcirculation/lib/bibcirculation_templates.py:10512 #: modules/bibcirculation/lib/bibcirculation_templates.py:10707 #: modules/bibcirculation/lib/bibcirculation_templates.py:10960 #: modules/bibcirculation/lib/bibcirculation_templates.py:11056 #: modules/bibcirculation/lib/bibcirculation_templates.py:11206 #: modules/bibcirculation/lib/bibcirculation_templates.py:11696 #: modules/bibcirculation/lib/bibcirculation_templates.py:11792 #: modules/bibcirculation/lib/bibcirculation_templates.py:11898 #: modules/bibcirculation/lib/bibcirculation_templates.py:11986 #: modules/bibcirculation/lib/bibcirculation_templates.py:12670 #: modules/bibcirculation/lib/bibcirculation_templates.py:12774 msgid "Author(s)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2734 msgid "Print loan information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2810 #: modules/bibcirculation/lib/bibcirculation_templates.py:7785 #: modules/bibcirculation/lib/bibcirculation_templates.py:7944 msgid "Personal details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2842 #: modules/bibcirculation/lib/bibcirculation_templates.py:4216 #: modules/bibcirculation/lib/bibcirculation_templates.py:7837 msgid "Write notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:2928 #: modules/bibcirculation/lib/bibcirculation_templates.py:2936 #: modules/bibcirculation/lib/bibcirculation_templates.py:2944 #: modules/bibcirculation/lib/bibcirculation_templates.py:2952 #, fuzzy msgid "Any field" msgstr "هر فیلد" #: modules/bibcirculation/lib/bibcirculation_templates.py:3013 msgid "Select item" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:3056 #: modules/bibcirculation/lib/bibcirculation_templates.py:3666 #: modules/bibcirculation/lib/bibcirculation_templates.py:4143 #: modules/bibcirculation/lib/bibcirculation_templates.py:10183 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3230 msgid "Borrower details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:3226 #: modules/bibcirculation/lib/bibcirculation_templates.py:3730 msgid "Enter the period of interest" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:3227 #: modules/bibcirculation/lib/bibcirculation_templates.py:3731 msgid "From: " msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:3229 #: modules/bibcirculation/lib/bibcirculation_templates.py:3732 msgid "To: " msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:3289 #: modules/bibcirculation/lib/bibcirculation_templates.py:3766 msgid "A new request has been registered with success." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:3415 #: modules/bibcirculation/lib/bibcirculation_templates.py:3422 #: modules/bibcirculation/lib/bibcirculation_templates.py:3429 #: modules/bibcirculation/lib/bibcirculation_templates.py:3442 #: modules/bibcirculation/lib/bibcirculation_templates.py:3449 #: modules/bibcirculation/lib/bibcirculation_templates.py:3456 #: modules/bibcirculation/lib/bibcirculation_templates.py:3890 #: modules/bibcirculation/lib/bibcirculation_templates.py:3897 #: modules/bibcirculation/lib/bibcirculation_templates.py:3904 msgid "ccid" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:3966 msgid "Please select one borrower to continue." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4213 #: modules/bibcirculation/lib/bibcirculation_templates.py:4288 #: modules/bibcirculation/lib/bibcirculation_utils.py:397 msgid "Loan information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4214 #: modules/bibcirculation/lib/bibcirculation_templates.py:4291 #: modules/bibcirculation/lib/bibcirculation_templates.py:8303 msgid "Loan date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4217 msgid "This note will be associated to this new loan, not to the borrower." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4293 #: modules/bibcirculation/lib/bibcirculation_templates.py:5837 msgid "Loan status" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4295 msgid "Requested ?" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4314 msgid "New due date: " msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4338 msgid "Submit new due date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4384 #, python-format msgid "The due date has been updated. New due date: %s" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4385 msgid "Back to borrower's loans" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4420 msgid "Notification has been sent!" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4463 msgid "Notes about loan" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4472 #: modules/bibcirculation/lib/bibcirculation_templates.py:8785 #: modules/bibcirculation/lib/bibcirculation_templates.py:11621 #: modules/bibcirculation/lib/bibcirculation_templates.py:12596 #: modules/bibcirculation/lib/bibcirculation_templates.py:13425 #: modules/bibcirculation/lib/bibcirculation_templates.py:15202 msgid "[delete]" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4519 #: modules/bibcirculation/lib/bibcirculation_templates.py:8831 #: modules/bibcirculation/lib/bibcirculation_templates.py:13471 #: modules/bibcirculation/lib/bibcirculation_templates.py:15246 msgid "Write new note" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4560 #: modules/bibcirculation/lib/bibcirculation_templates.py:5722 #: modules/bibcirculation/lib/bibcirculation_templates.py:13532 msgid "Option(s)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4596 #: modules/bibcirculation/lib/bibcirculation_templates.py:5754 msgid "Cancel hold request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4666 #: modules/bibcirculation/lib/bibcirculation_templates.py:4841 msgid "No result for your search." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4696 #: modules/bibcirculation/lib/bibcirculation_templates.py:4873 #: modules/bibcirculation/lib/bibcirculation_templates.py:6077 #: modules/bibcirculation/lib/bibcirculation_templates.py:8306 #: modules/bibcirculation/lib/bibcirculation_templates.py:8561 msgid "Overdue letters" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4697 #: modules/bibcirculation/lib/bibcirculation_templates.py:4874 msgid "Loan Notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4710 #: modules/bibcirculation/lib/bibcirculation_templates.py:4888 msgid "see notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4714 #: modules/bibcirculation/lib/bibcirculation_templates.py:4893 msgid "no notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:4746 #: modules/bibcirculation/lib/bibcirculation_templates.py:4925 #: modules/bibcirculation/lib/bibcirculation_templates.py:5904 #: modules/bibcirculation/lib/bibcirculation_templates.py:8380 msgid "Send recall" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5000 msgid "No more requests are pending or waiting." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5151 msgid "Printable format" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5231 msgid "Edit this record" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5232 msgid "Book Cover" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5250 msgid "Search another item by" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5291 msgid "Additional details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5362 #: modules/bibcirculation/lib/bibcirculation_templates.py:6343 #: modules/bibcirculation/lib/bibcirculation_templates.py:7003 #: modules/bibcirculation/lib/bibcirculation_templates.py:7575 #: modules/bibcirculation/lib/bibcirculation_templates.py:7671 msgid "No of loans" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5441 #: modules/bibcirculation/lib/bibcirculation_templates.py:5462 #: modules/bibcirculation/lib/bibcirculation_templates.py:5483 #: modules/bibcirculation/lib/bibcirculation_templates.py:5504 #: modules/bibcirculation/lib/bibcirculation_templates.py:5885 #: modules/bibcirculation/lib/bibcirculation_templates.py:8360 msgid "Select an action" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5442 #: modules/bibcirculation/lib/bibcirculation_templates.py:5463 #: modules/bibcirculation/lib/bibcirculation_templates.py:5484 #: modules/bibcirculation/lib/bibcirculation_templates.py:5505 #: modules/bibcirculation/lib/bibcirculation_templates.py:6374 #: modules/bibcirculation/lib/bibcirculation_templates.py:7993 #: modules/bibcirculation/lib/bibcirculation_templates.py:8001 #: modules/bibcirculation/lib/bibcirculation_templates.py:15128 #: modules/bibcirculation/lib/bibcirculation_templates.py:15975 #: modules/webjournal/lib/webjournal_templates.py:517 #: modules/webjournal/lib/webjournaladminlib.py:391 msgid "Update" msgstr "بروز رسانی" #: modules/bibcirculation/lib/bibcirculation_templates.py:5443 #: modules/bibcirculation/lib/bibcirculation_templates.py:5464 #: modules/bibcirculation/lib/bibcirculation_templates.py:5485 #: modules/bibcirculation/lib/bibcirculation_templates.py:5506 msgid "Add similar copy" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5444 #: modules/bibcirculation/lib/bibcirculation_templates.py:5465 #: modules/bibcirculation/lib/bibcirculation_templates.py:5486 #: modules/bibcirculation/lib/bibcirculation_templates.py:5507 #: modules/bibcirculation/lib/bibcirculation_templates.py:7984 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1103 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1144 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1182 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1210 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1250 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1283 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1330 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1359 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1677 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1713 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1758 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1803 msgid "New request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5445 #: modules/bibcirculation/lib/bibcirculation_templates.py:5466 #: modules/bibcirculation/lib/bibcirculation_templates.py:5487 #: modules/bibcirculation/lib/bibcirculation_templates.py:5508 #: modules/bibcirculation/lib/bibcirculation_templates.py:7983 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1416 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1453 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1492 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1572 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1587 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1838 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1878 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1886 msgid "New loan" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5446 #: modules/bibcirculation/lib/bibcirculation_templates.py:5467 #: modules/bibcirculation/lib/bibcirculation_templates.py:5488 #: modules/bibcirculation/lib/bibcirculation_templates.py:5509 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2766 msgid "Delete copy" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5636 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2558 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2584 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2624 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2656 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2666 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2679 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2695 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2735 msgid "Add new copy" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5637 #, python-format msgid "Hold requests and loans overview on %(date)s" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5639 #: modules/bibcirculation/lib/bibcirculation_templates.py:5643 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2375 msgid "Hold requests" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5639 #: modules/bibcirculation/lib/bibcirculation_templates.py:5640 #: modules/bibcirculation/lib/bibcirculation_templates.py:5641 #: modules/bibcirculation/lib/bibcirculation_templates.py:5644 #: modules/bibcirculation/lib/bibcirculation_templates.py:5645 #: modules/bibcirculation/lib/bibcirculation_templates.py:5646 #: modules/bibcirculation/lib/bibcirculation_templates.py:8118 #: modules/bibcirculation/lib/bibcirculation_templates.py:8120 #: modules/bibcirculation/lib/bibcirculation_templates.py:8122 #: modules/bibcirculation/lib/bibcirculation_templates.py:8124 #: modules/bibcirculation/lib/bibcirculation_templates.py:8127 #: modules/bibcirculation/lib/bibcirculation_templates.py:8129 #: modules/bibcirculation/lib/bibcirculation_templates.py:8131 #: modules/bibcirculation/lib/bibcirculation_templates.py:8133 msgid "More details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5640 #: modules/bibcirculation/lib/bibcirculation_templates.py:5644 #: modules/bibcirculation/lib/bibcirculation_templates.py:8119 #: modules/bibcirculation/lib/bibcirculation_templates.py:8128 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2532 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3652 msgid "Loans" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5641 #: modules/bibcirculation/lib/bibcirculation_templates.py:5645 #, fuzzy msgid "Purchases" msgstr "خرید" #: modules/bibcirculation/lib/bibcirculation_templates.py:5643 #: modules/bibcirculation/lib/bibcirculation_templates.py:8125 msgid "Historical overview" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5680 #: modules/bibcirculation/lib/bibcirculation_templates.py:5950 #: modules/bibcirculation/lib/bibcirculation_templates.py:8156 #: modules/bibcirculation/lib/bibcirculation_templates.py:8436 msgid "There are no requests." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5718 #, fuzzy msgid "Item Desc" msgstr "آیتم ها" #: modules/bibcirculation/lib/bibcirculation_templates.py:5794 #: modules/bibcirculation/lib/bibcirculation_templates.py:8265 #: modules/bibcirculation/lib/bibcirculation_templates.py:8525 msgid "There are no loans." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5836 #: modules/bibcirculation/lib/bibcirculation_templates.py:8707 msgid "Overdue letter" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5838 #: modules/bibcirculation/lib/bibcirculation_templates.py:8308 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2220 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2269 msgid "Loan notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5839 #: modules/bibcirculation/lib/bibcirculation_templates.py:8310 msgid "Loan options" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5853 #: modules/bibcirculation/lib/bibcirculation_templates.py:7890 #: modules/bibcirculation/lib/bibcirculation_templates.py:8325 #: modules/bibcirculation/lib/bibcirculation_templates.py:9267 #: modules/bibcirculation/lib/bibcirculation_templates.py:13976 #: modules/bibcirculation/lib/bibcirculation_templates.py:14170 #: modules/bibcirculation/lib/bibcirculation_templates.py:14185 #: modules/bibcirculation/lib/bibcirculation_templates.py:15059 #: modules/bibcirculation/lib/bibcirculation_templates.py:15921 msgid "No notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5857 #: modules/bibcirculation/lib/bibcirculation_templates.py:8330 msgid "See notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:5892 #: modules/bibcirculation/lib/bibcirculation_templates.py:5896 #: modules/bibcirculation/lib/bibcirculation_templates.py:8367 #: modules/bibcirculation/lib/bibcirculation_templates.py:8371 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1007 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1049 msgid "Change due date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:6075 #: modules/bibcirculation/lib/bibcirculation_templates.py:8559 msgid "Returned on" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:6151 #: modules/bibcirculation/lib/bibcirculation_templates.py:6760 #: modules/bibcirculation/lib/bibcirculation_templates.py:10251 #, fuzzy msgid "RecId/Item details" msgstr "جزئیات درخواست" #: modules/bibcirculation/lib/bibcirculation_templates.py:6209 #, python-format msgid "%(nb_items_found)i items found" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:6502 msgid "Update copy information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:6628 #: modules/bibcirculation/lib/bibcirculation_templates.py:6718 #: modules/bibcirculation/lib/bibcirculation_templates.py:7264 #: modules/bibcirculation/lib/bibcirculation_templates.py:7355 msgid "Expected arrival date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:6710 msgid "New copy information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:6977 #: modules/bibcirculation/lib/bibcirculation_templates.py:7549 #, python-format msgid "Copies of %s" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:7109 msgid "New copy details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:7397 #, python-format msgid "A %(x_url_open)snew copy%(x_url_close)s has been added." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:7421 msgid "Back to the record" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:7895 msgid "Notes about this borrower" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:7951 #: modules/bibcirculation/lib/bibcirculation_templates.py:8926 #: modules/bibcirculation/lib/bibcirculation_templates.py:8975 #: modules/bibcirculation/lib/bibcirculation_templates.py:14036 #: modules/bibcirculation/lib/bibcirculation_templates.py:14259 #: modules/bibcirculation/lib/bibcirculation_templates.py:14320 #: modules/bibcirculation/lib/bibcirculation_templates.py:14449 #: modules/bibcirculation/lib/bibcirculation_templates.py:14524 #: modules/bibcirculation/lib/bibcirculation_templates.py:15126 #: modules/bibcirculation/lib/bibcirculation_templates.py:15328 #: modules/bibcirculation/lib/bibcirculation_templates.py:15401 #: modules/bibcirculation/lib/bibcirculation_templates.py:15974 #: modules/webbasket/lib/webbasket_templates.py:747 #: modules/webbasket/lib/webbasket_templates.py:771 #: modules/webbasket/lib/webbasket_templates.py:856 #: modules/webbasket/lib/webbasket_templates.py:882 #: modules/webbasket/lib/webbasket_templates.py:961 #: modules/webbasket/lib/webbasket_templates.py:986 #: modules/webbasket/lib/webbasket_templates.py:1064 #: modules/webbasket/lib/webbasket_templates.py:1089 #: modules/webbasket/lib/webbasket_templates.py:2741 #: modules/webbasket/lib/webbasket_templates.py:3236 #: modules/webbasket/lib/webbasket_templates.py:3610 #: modules/webbasket/lib/webbasket_templates.py:4039 msgid "Notes" msgstr "یادداشت ها" #: modules/bibcirculation/lib/bibcirculation_templates.py:7985 msgid "New ILL request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:7986 msgid "Notify this borrower" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8115 msgid "Requests, Loans and ILL overview on" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8117 #: modules/bibcirculation/lib/bibcirculation_templates.py:8126 #: modules/bibcirculation/lib/bibcirculation_templates.py:13779 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2498 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3687 msgid "Requests" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8123 #: modules/bibcirculation/lib/bibcirculation_templates.py:8132 msgid "Proposals" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8193 msgid "Request option(s)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8307 #: modules/bibcirculation/lib/bibcirculation_templates.py:11204 #: modules/bibcirculation/lib/bibcirculation_templates.py:12910 #: modules/bibcirculation/lib/bibcirculation_templates.py:12946 #: modules/bibcirculation/lib/bibcirculation_templates.py:13068 #: modules/bibcirculation/lib/bibcirculation_templates.py:13221 #: modules/bibcirculation/lib/bibcirculation_templates.py:13309 #: modules/bibcirculation/lib/bibcirculation_templates.py:13531 #: modules/bibcirculation/lib/bibcirculation_templates.py:13665 #: modules/bibcirculation/lib/bibcirculation_templates.py:13784 #: modules/bibcirculation/lib/bibcirculation_templates.py:14035 #: modules/bibcirculation/lib/bibcirculation_templates.py:14258 #: modules/bibcirculation/lib/bibcirculation_templates.py:14319 #: modules/bibcirculation/lib/bibcirculation_templates.py:14417 #: modules/bibcirculation/lib/bibcirculation_templates.py:14523 #: modules/bibcirculation/lib/bibcirculation_templates.py:14750 #: modules/bibcirculation/lib/bibcirculation_templates.py:14849 #: modules/bibcirculation/lib/bibcirculation_templates.py:15125 msgid "Type" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8309 msgid "Loans status" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8654 msgid "CERN Library" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8680 #: modules/webmessage/lib/webmessage_templates.py:86 msgid "Subject" msgstr "موضوع" #: modules/bibcirculation/lib/bibcirculation_templates.py:8682 msgid "Message" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8683 msgid "Choose a template" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8706 msgid "Templates" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8708 msgid "Reminder" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8709 msgid "Notification" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8710 msgid "Loan recall" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8711 msgid "ILL recall" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8712 msgid "Proposal-accept" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8713 msgid "Proposal-refuse" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8714 msgid "Purchase-received-cash" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8715 msgid "Purchase-received-TID" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8716 msgid "Load" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8734 msgid "Send" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:8776 msgid "Notes about borrower" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9048 msgid "A new borrower has been registered." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9140 msgid "Borrower information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9206 msgid "ILL ID" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9208 #: modules/bibcirculation/lib/bibcirculation_templates.py:13526 #: modules/bibcirculation/lib/bibcirculation_templates.py:13660 #: modules/bibcirculation/lib/bibcirculation_templates.py:13782 msgid "Supplier" msgstr "تأمین کننده" #: modules/bibcirculation/lib/bibcirculation_templates.py:9210 #: modules/bibcirculation/lib/bibcirculation_templates.py:12197 #: modules/bibcirculation/lib/bibcirculation_templates.py:12259 #: modules/bibcirculation/lib/bibcirculation_templates.py:12401 #: modules/bibcirculation/lib/bibcirculation_templates.py:12486 #: modules/bibcirculation/lib/bibcirculation_templates.py:13005 #: modules/bibcirculation/lib/bibcirculation_templates.py:13070 #: modules/bibcirculation/lib/bibcirculation_templates.py:13223 #: modules/bibcirculation/lib/bibcirculation_templates.py:13311 msgid "Expected date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9211 #: modules/bibcirculation/lib/bibcirculation_templates.py:12301 #: modules/bibcirculation/lib/bibcirculation_templates.py:12402 #: modules/bibcirculation/lib/bibcirculation_templates.py:12487 #: modules/bibcirculation/lib/bibcirculation_templates.py:13112 #: modules/bibcirculation/lib/bibcirculation_templates.py:13223 #: modules/bibcirculation/lib/bibcirculation_templates.py:13311 msgid "Arrival date" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9214 #: modules/bibcirculation/lib/bibcirculation_templates.py:12131 #: modules/bibcirculation/lib/bibcirculation_templates.py:12235 #: modules/bibcirculation/lib/bibcirculation_templates.py:12338 #: modules/bibcirculation/lib/bibcirculation_templates.py:12438 #: modules/bibcirculation/lib/bibcirculation_templates.py:12521 #: modules/bibcirculation/lib/bibcirculation_templates.py:12932 #: modules/bibcirculation/lib/bibcirculation_templates.py:13043 #: modules/bibcirculation/lib/bibcirculation_templates.py:13157 #: modules/bibcirculation/lib/bibcirculation_templates.py:13259 #: modules/bibcirculation/lib/bibcirculation_templates.py:13347 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5829 msgid "Library notes" msgstr "یادداشت های کتابخانه " #: modules/bibcirculation/lib/bibcirculation_templates.py:9271 msgid "Notes about this ILL" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9376 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:260 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:306 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:405 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:488 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:608 msgid "Interlibrary loan request for books" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9457 #: modules/bibcirculation/lib/bibcirculation_templates.py:9638 #: modules/bibcirculation/lib/bibcirculation_templates.py:9824 #: modules/bibcirculation/lib/bibcirculation_templates.py:10136 #: modules/bibcirculation/lib/bibcirculation_templates.py:10579 #: modules/bibcirculation/lib/bibcirculation_templates.py:10715 #: modules/bibcirculation/lib/bibcirculation_templates.py:12051 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4880 msgid "ILL request details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9458 #: modules/bibcirculation/lib/bibcirculation_templates.py:9826 #: modules/bibcirculation/lib/bibcirculation_templates.py:10580 #: modules/bibcirculation/lib/bibcirculation_templates.py:10716 #: modules/bibcirculation/lib/bibcirculation_templates.py:11135 #: modules/bibcirculation/lib/bibcirculation_templates.py:11257 msgid "Period of interest - From" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9460 #: modules/bibcirculation/lib/bibcirculation_templates.py:9828 #: modules/bibcirculation/lib/bibcirculation_templates.py:10582 #: modules/bibcirculation/lib/bibcirculation_templates.py:10718 #: modules/bibcirculation/lib/bibcirculation_templates.py:11137 #: modules/bibcirculation/lib/bibcirculation_templates.py:11259 msgid "Period of interest - To" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9462 #: modules/bibcirculation/lib/bibcirculation_templates.py:9692 #: modules/bibcirculation/lib/bibcirculation_templates.py:9830 #: modules/bibcirculation/lib/bibcirculation_templates.py:10140 #: modules/bibcirculation/lib/bibcirculation_templates.py:10584 #: modules/bibcirculation/lib/bibcirculation_templates.py:10720 #: modules/bibcirculation/lib/bibcirculation_templates.py:11139 #: modules/bibcirculation/lib/bibcirculation_templates.py:11261 msgid "Additional comments" msgstr "نظرهای اضافی" #: modules/bibcirculation/lib/bibcirculation_templates.py:9463 #, python-format msgid "" "I accept the %(x_url_open)sconditions%(x_url_close)s of the service in " "particular the return of books in due time." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9464 msgid "I want this edition only." msgstr "من فقط این ویرایش را می خواهم." #: modules/bibcirculation/lib/bibcirculation_templates.py:9495 #, python-format msgid "Check your library account %(here_link)s." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9497 msgid "here" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9531 #, python-format msgid "Book does not exist in %(CFG_SITE_NAME)s" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9533 msgid "Please fill the following form." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9601 msgid "Book title" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9603 #: modules/bibcirculation/lib/bibcirculation_templates.py:9819 #: modules/bibcirculation/lib/bibcirculation_templates.py:10091 #: modules/bibcirculation/lib/bibcirculation_templates.py:11057 #: modules/bibcirculation/lib/bibcirculation_templates.py:11223 #: modules/bibcirculation/lib/bibcirculation_templates.py:11793 #: modules/bibcirculation/lib/bibcirculation_templates.py:11902 #: modules/bibcirculation/lib/bibcirculation_templates.py:11987 #: modules/bibcirculation/lib/bibcirculation_templates.py:12775 msgid "Place" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9606 #: modules/bibcirculation/lib/bibcirculation_templates.py:9822 #: modules/bibcirculation/lib/bibcirculation_templates.py:10094 #: modules/bibcirculation/lib/bibcirculation_templates.py:11060 #: modules/bibcirculation/lib/bibcirculation_templates.py:11224 #: modules/bibcirculation/lib/bibcirculation_templates.py:11796 #: modules/bibcirculation/lib/bibcirculation_templates.py:11990 #: modules/bibcirculation/lib/bibcirculation_templates.py:12778 msgid "Edition" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9638 #: modules/bibcirculation/lib/bibcirculation_templates.py:9825 #: modules/bibcirculation/lib/bibcirculation_templates.py:10137 #: modules/bibcirculation/lib/bibcirculation_templates.py:10518 #: modules/bibcirculation/lib/bibcirculation_templates.py:10713 #: modules/bibcirculation/lib/bibcirculation_templates.py:11134 #: modules/bibcirculation/lib/bibcirculation_templates.py:11256 #: modules/bibcirculation/lib/bibcirculation_templates.py:12911 #: modules/bibcirculation/lib/bibcirculation_templates.py:13023 #: modules/bibcirculation/lib/bibcirculation_templates.py:13136 #: modules/bibcirculation/lib/bibcirculation_templates.py:13239 #: modules/bibcirculation/lib/bibcirculation_templates.py:13327 msgid "Budget code" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9639 #: modules/bibcirculation/lib/bibcirculation_templates.py:10138 #: modules/bibcirculation/lib/bibcirculation_templates.py:12047 #: modules/bibcirculation/lib/bibcirculation_templates.py:12823 msgid "Period of interest (From)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9690 #: modules/bibcirculation/lib/bibcirculation_templates.py:10139 #: modules/bibcirculation/lib/bibcirculation_templates.py:12048 #: modules/bibcirculation/lib/bibcirculation_templates.py:12824 msgid "Period of interest (To)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9693 #, python-format msgid "" "Borrower accepts the %(x_url_open)sconditions%(x_url_close)s of the service " "in particular the return of books in due time." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9694 msgid "Borrower wants this edition only." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:9832 msgid "Only this edition." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10141 msgid "Only this edition" msgstr "فقط این ویرایش" #: modules/bibcirculation/lib/bibcirculation_templates.py:10233 #, python-format msgid "" "Check if the book already exists on %(CFG_SITE_NAME)s, before sending your " "ILL request." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10305 msgid "0 items found." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10388 msgid "Proceed anyway" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10509 msgid "Article details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10510 #: modules/bibcirculation/lib/bibcirculation_templates.py:10705 msgid "Periodical title" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10511 #: modules/bibcirculation/lib/bibcirculation_templates.py:10706 msgid "Article title" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10513 #: modules/bibcirculation/lib/bibcirculation_templates.py:10708 msgid "Report number" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10514 #: modules/bibcirculation/lib/bibcirculation_templates.py:10709 #: modules/bibcirculation/lib/bibcirculation_templates.py:11899 msgid "Volume" msgstr "جلد/ دوره" #: modules/bibcirculation/lib/bibcirculation_templates.py:10515 #: modules/bibcirculation/lib/bibcirculation_templates.py:10710 #: modules/bibcirculation/lib/bibcirculation_templates.py:11900 msgid "Issue" msgstr "شماره" #: modules/bibcirculation/lib/bibcirculation_templates.py:10516 #: modules/bibcirculation/lib/bibcirculation_templates.py:10711 #: modules/bibcirculation/lib/bibcirculation_templates.py:11901 msgid "Page" msgstr "صفحه" #: modules/bibcirculation/lib/bibcirculation_templates.py:10519 #: modules/bibcirculation/lib/bibcirculation_templates.py:10714 #: modules/bibcirculation/lib/bibcirculation_templates.py:11903 msgid "ISSN" msgstr "شماره استاندارد بين المللي پيايندها (شاپا)" #: modules/bibcirculation/lib/bibcirculation_templates.py:10914 msgid "" "We will process your order immediately and contact " "you as soon as the document is " "received." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10916 msgid "" "According to a decision from the Scientific Information Policy " "Board, books purchased with budget codes other than Team " "accounts will be added to the Library catalogue, with the " "indication of the purchaser." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10934 msgid "Document details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:10970 msgid "Document type" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:11061 msgid "This edition only" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:11063 #: modules/bibcirculation/lib/bibcirculation_templates.py:11225 #: modules/bibcirculation/lib/bibcirculation_templates.py:12780 msgid "Standard number" msgstr "شماره استاندارد" #: modules/bibcirculation/lib/bibcirculation_templates.py:11133 #: modules/bibcirculation/lib/bibcirculation_templates.py:11255 #: modules/bibcirculation/lib/bibcirculation_templates.py:12844 msgid "Request details" msgstr "جزئیات درخواست" #: modules/bibcirculation/lib/bibcirculation_templates.py:11134 msgid "Cash" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:11474 msgid "Search ILL request by" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:11474 msgid "ILL RecId/Item details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:11475 msgid "ILL request id" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:11475 msgid "cost" msgstr "هزینه" #: modules/bibcirculation/lib/bibcirculation_templates.py:11475 msgid "notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:11514 msgid "date restriction" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:11896 msgid "Periodical Title" msgstr "عنوان ادواری" #: modules/bibcirculation/lib/bibcirculation_templates.py:11897 msgid "Article Title" msgstr "عنوان مقاله" #: modules/bibcirculation/lib/bibcirculation_templates.py:12044 #: modules/bibcirculation/lib/bibcirculation_templates.py:12807 msgid "Borrower request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:12049 #: modules/bibcirculation/lib/bibcirculation_templates.py:12843 msgid "Borrower comments" msgstr "نظرهای امانت گیرنده" #: modules/bibcirculation/lib/bibcirculation_templates.py:12050 #: modules/bibcirculation/lib/bibcirculation_templates.py:12825 msgid "Only this edition?" msgstr "تنها این ویرایش؟" #: modules/bibcirculation/lib/bibcirculation_templates.py:12109 #: modules/bibcirculation/lib/bibcirculation_templates.py:12141 #: modules/bibcirculation/lib/bibcirculation_templates.py:12257 #: modules/bibcirculation/lib/bibcirculation_templates.py:12400 #: modules/bibcirculation/lib/bibcirculation_templates.py:12485 #: modules/bibcirculation/lib/bibcirculation_templates.py:12909 #: modules/bibcirculation/lib/bibcirculation_templates.py:12945 #: modules/bibcirculation/lib/bibcirculation_templates.py:13067 #: modules/bibcirculation/lib/bibcirculation_templates.py:13221 #: modules/bibcirculation/lib/bibcirculation_templates.py:13309 msgid "ILL request ID" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:12110 #: modules/bibcirculation/lib/bibcirculation_templates.py:12215 #: modules/bibcirculation/lib/bibcirculation_templates.py:12320 #: modules/bibcirculation/lib/bibcirculation_templates.py:12418 #: modules/bibcirculation/lib/bibcirculation_templates.py:12501 #: modules/bibcirculation/lib/bibcirculation_templates.py:12912 #: modules/bibcirculation/lib/bibcirculation_templates.py:13024 #: modules/bibcirculation/lib/bibcirculation_templates.py:13136 #: modules/bibcirculation/lib/bibcirculation_templates.py:13239 #: modules/bibcirculation/lib/bibcirculation_templates.py:13327 msgid "Previous notes" msgstr "یادداشت های قبلی" #: modules/bibcirculation/lib/bibcirculation_templates.py:12148 msgid "Library/Supplier" msgstr "کتابخانه/ تأمین کننده" #: modules/bibcirculation/lib/bibcirculation_templates.py:12199 #: modules/bibcirculation/lib/bibcirculation_templates.py:12308 #: modules/bibcirculation/lib/bibcirculation_templates.py:12404 #: modules/bibcirculation/lib/bibcirculation_templates.py:12488 #: modules/bibcirculation/lib/bibcirculation_templates.py:13007 #: modules/bibcirculation/lib/bibcirculation_templates.py:13121 #: modules/bibcirculation/lib/bibcirculation_templates.py:13226 #: modules/bibcirculation/lib/bibcirculation_templates.py:13313 #: modules/bibcirculation/lib/bibcirculation_templates.py:13661 #: modules/bibcirculation/lib/bibcirculation_templates.py:13783 #: modules/bibcirculation/lib/bibcirculation_templates.py:13898 msgid "Cost" msgstr "هزینه" #: modules/bibcirculation/lib/bibcirculation_templates.py:12830 #, fuzzy msgid "Date of request" msgstr "تاریخ درخواست" #: modules/bibcirculation/lib/bibcirculation_templates.py:12953 msgid "Vendor" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13416 msgid "Notes about ILL" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13529 msgid "Interest from" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13591 #: modules/bibcirculation/lib/bibcirculation_templates.py:13596 #: modules/bibcirculation/lib/bibcirculation_templates.py:13719 #: modules/bibcirculation/lib/bibcirculation_templates.py:13727 #: modules/bibcirculation/lib/bibcirculation_templates.py:13839 msgid "select" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13600 msgid "Inter library loan recall: " msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13605 msgid "Send Recall" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13659 #, fuzzy msgid "No. purchases" msgstr "خرید" #: modules/bibcirculation/lib/bibcirculation_templates.py:13664 msgid "Date requested" msgstr "تاریخ درخواست" #: modules/bibcirculation/lib/bibcirculation_templates.py:13778 msgid "Proposer" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13843 #: modules/bibcirculation/lib/bibcirculation_templates.py:13938 #, fuzzy msgid "Create ILL req" msgstr "ایجاد گروه جدید" #: modules/bibcirculation/lib/bibcirculation_templates.py:13893 msgid "Req.ID" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13894 #, fuzzy msgid "Requester" msgstr "ثبت" #: modules/bibcirculation/lib/bibcirculation_templates.py:13895 msgid "Period of Interest: From" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13896 msgid "Period of Interest: To" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13935 msgid "Go to Proposal" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:13981 #: modules/bibcirculation/lib/bibcirculation_templates.py:14175 #: modules/bibcirculation/lib/bibcirculation_templates.py:14190 #: modules/bibcirculation/lib/bibcirculation_templates.py:15064 msgid "Notes about this library" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14030 #: modules/bibcirculation/lib/bibcirculation_templates.py:14253 msgid "Library to be deleted" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14037 #: modules/bibcirculation/lib/bibcirculation_templates.py:14260 #: modules/bibcirculation/lib/bibcirculation_templates.py:14321 #: modules/bibcirculation/lib/bibcirculation_templates.py:15127 msgid "No of items" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14075 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5855 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5902 msgid "Search library" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14113 msgid "Select library" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14206 msgid "Please, note that this action is NOT reversible" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14265 #: modules/bibcirculation/lib/bibcirculation_templates.py:14326 msgid "Library not found" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14314 msgid "Merged library" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14416 #: modules/bibcirculation/lib/bibcirculation_templates.py:14518 msgid "New library information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14557 msgid "A new library has been registered." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14593 #: modules/bibcirculation/lib/bibcirculation_templates.py:14927 msgid "Search library by" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14745 #: modules/bibcirculation/lib/bibcirculation_templates.py:14844 msgid "Library information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14888 #: modules/bibcirculation/lib/bibcirculation_templates.py:15759 msgid "The information has been updated." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:14983 msgid "0 libraries found." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15120 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5432 msgid "Library details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15129 msgid "Duplicated library?" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15193 msgid "Notes about library" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15327 #: modules/bibcirculation/lib/bibcirculation_templates.py:15396 msgid "New vendor information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15440 msgid "A new vendor has been registered." msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15477 #: modules/bibcirculation/lib/bibcirculation_templates.py:15796 msgid "Search vendor by" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15540 #: modules/bibcirculation/lib/bibcirculation_templates.py:15863 msgid "Vendor(s)" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15645 #: modules/bibcirculation/lib/bibcirculation_templates.py:15716 msgid "Vendor information" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15926 #: modules/bibcirculation/lib/bibcirculation_templates.py:16015 msgid "Notes about this vendor" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:15969 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5946 msgid "Vendor details" msgstr "" #: modules/bibcirculation/lib/bibcirculation_templates.py:16054 msgid "Add notes" msgstr "" #: modules/bibcirculation/lib/bibcirculation_utils.py:401 msgid "This book has been sent to you:" msgstr "" #: modules/bibcirculation/lib/bibcirculation_utils.py:428 msgid "Editor" msgstr "" #: modules/bibcirculation/lib/bibcirculation_utils.py:511 msgid "List of pending hold requests" msgstr "" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:124 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:169 msgid "You are not authorized to use loans." msgstr "" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:133 #: modules/websession/lib/websession_templates.py:636 #: modules/websession/lib/websession_templates.py:765 msgid "Your Loans" msgstr "امانت های شما" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:242 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:297 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:382 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:466 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:525 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:577 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:676 #: modules/bibcirculation/lib/bibcirculation_webinterface.py:755 msgid "You are not authorized to use ill." msgstr "" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:534 msgid "Interlibrary loan request for articles" msgstr "" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:606 msgid "Wrong user id" msgstr "" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:687 msgid "Purchase request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:760 msgid "" "Payment method information is mandatory. Please, type your " "budget code or tick the 'cash' checkbox." msgstr "" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:797 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4816 msgid "Your book purchase request" msgstr "" #: modules/bibcirculation/lib/bibcirculation_webinterface.py:853 #: modules/webcomment/lib/webcomment.py:1861 #: modules/webcomment/lib/webcomment_templates.py:199 #: modules/webcomment/lib/webcomment_templates.py:2511 #: modules/websearch/lib/search_engine.py:4668 #: modules/websearch/lib/search_engine.py:5037 #: modules/websearch/lib/search_engine.py:5230 #: modules/websearch/lib/search_engine.py:5253 #: modules/websearch/lib/search_engine.py:5261 #: modules/websearch/lib/search_engine.py:5269 #: modules/websearch/lib/search_engine.py:5315 msgid "The record has been deleted." msgstr "رکورد حذف شده است." #: modules/bibcirculation/lib/bibcirculation_webinterface.py:855 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3146 #: modules/bibdocfile/lib/bibdocfile_webinterface.py:102 #: modules/bibdocfile/lib/bibdocfile_webinterface.py:146 #: modules/websearch/lib/search_engine.py:2223 #: modules/websearch/lib/search_engine.py:2232 #: modules/websearch/lib/search_engine.py:5891 #: modules/websearch/lib/search_engine.py:5953 #: modules/websearch/lib/search_engine.py:6044 msgid "Requested record does not seem to exist." msgstr "رکورد درخواست شده بنظر نمی رسد وجود داشته باشد." #: modules/bibcirculation/lib/bibcirculationadminlib.py:197 msgid "BibCirculation Admin" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:236 #, fuzzy msgid "Empty string. Please, try again." msgstr "لطفا دوباره تلاش کنید." #: modules/bibcirculation/lib/bibcirculationadminlib.py:245 #: modules/bibcirculation/lib/bibcirculationadminlib.py:490 msgid "Loan on desk" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:282 #: modules/bibcirculation/lib/bibcirculationadminlib.py:318 #: modules/bibcirculation/lib/bibcirculationadminlib.py:428 #: modules/bibcirculation/lib/bibcirculationadminlib.py:516 #: modules/bibcirculation/lib/bibcirculationadminlib.py:547 msgid "Circulation management" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:367 #: modules/bibcirculation/lib/bibcirculationadminlib.py:644 #: modules/bibcirculation/lib/bibcirculationadminlib.py:882 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1876 #, python-format msgid "" "%(x_strong_tag_open)s%(x_barcode)s%(x_strong_tag_close)s Unknown barcode." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:367 #: modules/bibcirculation/lib/bibcirculationadminlib.py:413 #: modules/bibcirculation/lib/bibcirculationadminlib.py:644 #: modules/bibcirculation/lib/bibcirculationadminlib.py:882 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1094 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1398 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1638 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1876 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3128 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3199 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3984 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4137 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4367 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4458 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4689 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5668 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5881 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6072 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6272 #, fuzzy msgid "Please, try again." msgstr "لطفا دوباره تلاش کنید." #: modules/bibcirculation/lib/bibcirculationadminlib.py:377 #, fuzzy msgid "You must select one borrower." msgstr "شما باید یک نمره را انتخاب کنید." #: modules/bibcirculation/lib/bibcirculationadminlib.py:393 #, python-format msgid "" "%(x_strong_tag_open)sWARNING:%(x_strong_tag_close)s Note that item " "%(x_strong_tag_open)s%(x_barcode)s%(x_strong_tag_close)s status is " "%(x_strong_tag_open)s%(x_status)s%(x_strong_tag_close)s" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:401 #: modules/bibcirculation/lib/bibcirculationadminlib.py:898 #, python-format msgid "" "%(x_strong_tag_open)sWARNING:%(x_strong_tag_close)s Note that item " "%(x_strong_tag_open)s%(x_barcode)s%(x_strong_tag_close)s location is " "%(x_strong_tag_open)s%(x_location)s%(x_strong_tag_close)s" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:405 #, python-format msgid "" "Another user is waiting for the book: %(x_strong_tag_open)s%(x_title)s" "%(x_strong_tag_close)s. \n" "\n" " If you want continue with this loan choose " "%(x_strong_tag_open)s[Continue]%(x_strong_tag_close)s." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:413 msgid "Empty barcode." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:483 #, python-format msgid "" "The items with barcode %(x_strong_tag_open)s%(x_barcode)s" "%(x_strong_tag_close)s are already on loan." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:501 #, python-format msgid "" "The given due date %(x_strong_tag_open)s%(x_date)s%(x_strong_tag_close)s is " "not a valid date or date format" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:543 #, python-format msgid "" "A loan for the item %(x_strong_tag_open)s%(x_title)s%(x_strong_tag_close)s, " "with barcode %(x_strong_tag_open)s%(x_barcode)s%(x_strong_tag_close)s, has " "been registered with success." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:544 msgid "You could enter the barcode for this user's next loan, if any." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:587 msgid "Loan on desk confirm" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:655 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1884 #, python-format msgid "" "The item with the barcode %(x_strong_tag_open)s%(x_barcode)s" "%(x_strong_tag_close)s is on loan." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:665 #, python-format msgid "" "The given barcode \"%(x_barcode)s\" does not correspond to requested item." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:704 #: modules/bibcirculation/lib/bibcirculationadminlib.py:799 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1902 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1976 msgid "A new loan has been registered with success." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:706 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1901 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1989 #, fuzzy msgid "Current loans" msgstr "امانت های شما" #: modules/bibcirculation/lib/bibcirculationadminlib.py:821 msgid "New Loan" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:852 #: modules/bibcirculation/lib/bibcirculationadminlib.py:925 msgid "Loan return" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:886 #, python-format msgid "" "The item the with barcode %(x_strong_tag_open)s%(x_barcode)s" "%(x_strong_tag_close)s is not on loan. Please, try again." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:971 msgid "Claim return" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:1086 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1196 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1265 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2776 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2831 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3096 #, fuzzy msgid "Item search" msgstr "اجرای جستجو" #: modules/bibcirculation/lib/bibcirculationadminlib.py:1094 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1398 #: modules/bibcirculation/lib/bibcirculationadminlib.py:1638 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3128 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3984 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4137 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4367 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4458 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4689 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5668 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6072 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6272 msgid "Empty string." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:1275 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4143 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4505 #, python-format msgid "" "The period of interest %(x_strong_tag_open)sFrom: %(x_date)s" "%(x_strong_tag_close)s is not a valid date or date format" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:1293 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4153 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4512 #, python-format msgid "" "The period of interest %(x_strong_tag_open)sTo: %(x_date)s" "%(x_strong_tag_close)s is not a valid date or date format" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:1542 #, python-format msgid "" "Item with barcode %(x_strong_tag_open)s%(x_barcode)s%(x_strong_tag_close)s " "is already on loan." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:1634 msgid "Empty borrower ID." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:1646 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3136 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4377 #, python-format msgid "" "The barcode %(x_strong_tag_open)s%(x_barcode)s%(x_strong_tag_close)s does " "not exist on BibCirculation database." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:1947 #, fuzzy msgid "List of hold requests" msgstr "ثبت درخواست کتاب" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2308 #, fuzzy msgid "Record id not valid" msgstr "رکورد یافت نشد" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2419 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2450 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3569 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3575 msgid "Loan renewed with success." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2431 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3544 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3588 #: modules/webbasket/lib/webbasket_templates.py:1862 #: modules/webcomment/lib/webcomment_templates.py:253 #: modules/webcomment/lib/webcomment_templates.py:715 #: modules/webcomment/lib/webcomment_templates.py:2114 #: modules/webcomment/lib/webcomment_templates.py:2138 #: modules/webcomment/lib/webcomment_templates.py:2164 #: modules/webmessage/lib/webmessage_templates.py:508 #: modules/websession/lib/websession_templates.py:2403 #: modules/websession/lib/websession_templates.py:2443 msgid "Yes" msgstr "بله" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2436 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3549 #: modules/webalert/lib/webalert_templates.py:318 #: modules/webcomment/lib/webcomment_templates.py:254 #: modules/webcomment/lib/webcomment_templates.py:717 #: modules/webcomment/lib/webcomment_templates.py:2114 #: modules/webcomment/lib/webcomment_templates.py:2138 #: modules/webcomment/lib/webcomment_templates.py:2164 #: modules/webmessage/lib/webmessage_templates.py:509 #: modules/websession/lib/websession_templates.py:2404 #: modules/websession/lib/websession_templates.py:2444 msgid "No" msgstr "نه" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2440 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3558 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3592 #, python-format msgid "" "Another user is waiting for this book %(x_strong_tag_open)s%(x_title)s" "%(x_strong_tag_close)s." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2442 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3560 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3594 msgid "Do you want renew this loan anyway?" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2463 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3617 #, fuzzy msgid "Loans details" msgstr "جزئیات درخواست" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2498 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2532 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3652 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3687 #, fuzzy msgid "historical overview" msgstr "نمای کلی نتایج" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2655 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2727 #, python-format msgid "The given barcode %s is already in use." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2665 msgid "The given barcode is empty." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2678 msgid "The status selected does not accept tamporary barcodes." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2762 #, fuzzy msgid "Do you really want to delete this copy of the book?" msgstr "آیا مطمئن هستند که می خواهید این گروه را حذف کنید؟" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2774 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2829 #, python-format msgid "The barcode %s was not found" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2803 #, python-format msgid "The copy with barcode %s has been deleted." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2805 #, python-format msgid "It was NOT possible to delete the copy with barcode %s" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2862 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2917 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2957 #: modules/bibcirculation/lib/bibcirculationadminlib.py:2990 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3064 #, fuzzy msgid "Update item information" msgstr "اطلاعات کلی" #: modules/bibcirculation/lib/bibcirculationadminlib.py:2948 #, python-format msgid "Barcode %s not found" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3025 #, python-format msgid "" "Item [%s] updated, but the status was not modified." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3037 #, python-format msgid "" "Item [%s] updated, but the barcode was not " "modified because it is already in use." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3040 #, python-format msgid "" "Item [%s] updated to [%s] with success." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3042 #, python-format msgid "" "Item [%s] updated, but the barcode was not " "modified because it was not found (!?)." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3162 #, fuzzy msgid "Item search result" msgstr "بازگشت به نتایج جستجو" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3199 #, fuzzy msgid "Borrower not found." msgstr "صفحه پیدا نشد" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3254 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3303 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3362 #, fuzzy msgid "Add new borrower" msgstr "افزودن کتابخانه جدید" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3284 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3420 #, fuzzy msgid "Please, insert a name" msgstr "لطفا نام یک گروه را وارد کنید." #: modules/bibcirculation/lib/bibcirculationadminlib.py:3287 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3423 msgid "Please, insert a valid email address" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3291 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3427 #, fuzzy msgid "There is already a borrower using the following email:" msgstr "این صفحه در زبان های زیر نیز قابل دسترسی است:" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3396 #: modules/bibcirculation/lib/bibcirculationadminlib.py:3447 #, fuzzy msgid "Update borrower information" msgstr "اطلاعات کلی" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3489 #, fuzzy msgid "Hold requests details" msgstr "جزئیات درخواست" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3604 msgid "All loans renewed with success." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3727 #, fuzzy msgid "ILL details" msgstr "جزئیات درخواست" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3759 msgid "ILL historical overview" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3906 #, fuzzy msgid "Borrower notes" msgstr "نظرهای امانت گیرنده" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3948 #, fuzzy msgid "New Request" msgstr "درخواست های شما" #: modules/bibcirculation/lib/bibcirculationadminlib.py:3950 #, fuzzy msgid "Borrower Search" msgstr "جستجوهای شما" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4008 #, fuzzy msgid "Borrower search result" msgstr "بازگشت به نتایج جستجو" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4058 msgid "This ILL has been created from a proposal." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4061 msgid "An ILL has been created for the user." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4063 msgid "An active ILL already exists for this user on this record." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4065 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4067 msgid "Could not create an ILL from the proposal" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4071 #, fuzzy msgid "ILL requests" msgstr "درخواست های در حال انتظار" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4103 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4198 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4247 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4292 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4478 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4538 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4702 #, fuzzy msgid "Register ILL request" msgstr "ثبت درخواست کتاب" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4286 msgid "Request not registered: wrong borrower id" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4336 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4404 #, fuzzy msgid "Register ILL Book request" msgstr "ثبت درخواست کتاب" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4432 #, fuzzy msgid "Register ILL Article request" msgstr "ثبت مقاله" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4679 #: modules/bibcirculation/lib/bibcirculationadminlib.py:4778 msgid "" "Payment method information is mandatory. Please, " "type your budget code or tick the 'cash' checkbox." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4867 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5005 #, fuzzy msgid "Borrower request details not found." msgstr "فایل درخواستی پیدا نشد" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4871 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5009 #, fuzzy msgid "Request not found." msgstr "شماره پیدا نشد" #: modules/bibcirculation/lib/bibcirculationadminlib.py:4954 msgid "ILL received: " msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5024 #, fuzzy msgid "Purchase details" msgstr "خرید" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5088 msgid "Book suggestion accepted: " msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5092 msgid "Book suggestion refused: " msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5131 msgid "Purchase received: " msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5206 #, fuzzy msgid "ILL notes" msgstr "افزودن یادداشت ها" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5231 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5380 #, fuzzy msgid "List of ILL requests" msgstr "ثبت درخواست کتاب" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5260 #, fuzzy msgid "List of purchase requests" msgstr "سیاهه سبدهای عمومی" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5284 msgid "List of requests on put aside proposals" msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5288 #, fuzzy msgid "List of proposals" msgstr "سیاهه کتاب های سفارش شده" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5319 #, fuzzy msgid "ILL search" msgstr "جستجو" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5423 #, fuzzy msgid "Library ID not found." msgstr "یادداشت های کتابخانه " #: modules/bibcirculation/lib/bibcirculationadminlib.py:5482 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5523 #, fuzzy msgid "Merge libraries" msgstr "کتابخانه ها" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5648 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5685 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5717 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5746 #: modules/bibcirculation/lib/bibcirculationadminlib.py:5776 #, fuzzy msgid "Update library information" msgstr "اطلاعات کلی" #: modules/bibcirculation/lib/bibcirculationadminlib.py:5881 msgid "Emptry string." msgstr "" #: modules/bibcirculation/lib/bibcirculationadminlib.py:6052 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6092 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6123 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6151 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6178 #, fuzzy msgid "Update vendor information" msgstr "اطلاعات کلی" #: modules/bibcirculation/lib/bibcirculationadminlib.py:6219 #, fuzzy msgid "Vendor notes" msgstr "فروشندگان" #: modules/bibcirculation/lib/bibcirculationadminlib.py:6246 #: modules/bibcirculation/lib/bibcirculationadminlib.py:6290 #, fuzzy msgid "Search vendor" msgstr "جستجوی سبدها برای" #: modules/bibclassify/lib/bibclassify_templates.py:128 #: modules/bibclassify/lib/bibclassify_templates.py:187 msgid "" "Automatically generated single, " "composite, author, and other " "keywords." msgstr "" #: modules/bibclassify/lib/bibclassify_templates.py:233 msgid "Automated keyword extraction wasn't run for this document yet." msgstr "" #: modules/bibclassify/lib/bibclassify_templates.py:233 #, fuzzy msgid "Generate keywords" msgstr "کلیدواژه های تکراری" #: modules/bibclassify/lib/bibclassify_templates.py:245 msgid "There are no suitable keywords for display in this record." msgstr "" #: modules/bibclassify/lib/bibclassify_templates.py:289 msgid "Show more..." msgstr "" #: modules/bibclassify/lib/bibclassify_templates.py:320 #, python-format msgid "Unweighted %s keywords:" msgstr "" #: modules/bibclassify/lib/bibclassify_templates.py:326 #, python-format msgid "Weighted %s keywords:" msgstr "" #: modules/bibclassify/lib/bibclassify_templates.py:365 #: modules/websearch/lib/websearchadminlib.py:3457 msgid "Keywords" msgstr "کلیدواژه ها" #: modules/bibclassify/lib/bibclassify_templates.py:367 msgid "tag cloud" msgstr "" #: modules/bibclassify/lib/bibclassify_templates.py:368 msgid "list" msgstr "" #: modules/bibclassify/lib/bibclassify_templates.py:369 msgid "XML" msgstr "" #: modules/bibclassify/lib/bibclassify_webinterface.py:141 #, python-format msgid "Unknown type: %s" msgstr "" #: modules/bibclassify/lib/bibclassify_webinterface.py:236 msgid "The site settings do not allow automatic keyword extraction" msgstr "" #: modules/bibclassify/lib/bibclassify_webinterface.py:258 msgid "" "We have registered your request, the automatedkeyword extraction will run " "after some time. Please return back in a while." msgstr "" #: modules/bibclassify/lib/bibclassify_webinterface.py:263 msgid "" "Unfortunately, we don't have a PDF fulltext for this record in the " "storage, keywords cannot be generated using an automated " "process." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:396 #: modules/websubmit/lib/functions/Create_Upload_Files_Interface.py:463 msgid "Choose a file" msgstr "انتخاب یک فایل" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:402 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:347 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:401 #: modules/webcomment/lib/webcomment_templates.py:1025 #: modules/webcomment/lib/webcomment_templates.py:1343 #: modules/webcomment/lib/webcomment_templates.py:1791 #: modules/webcomment/lib/webcomment_templates.py:2028 #: modules/websubmit/lib/functions/Create_Upload_Files_Interface.py:475 #: modules/websubmit/lib/websubmit_templates.py:2505 msgid "Comment" msgstr "نظر" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:404 #: modules/websubmit/lib/functions/Create_Upload_Files_Interface.py:479 msgid "Access" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:558 msgid "" "The file you want to edit is protected against modifications. Your action " "has not been applied" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:577 #, python-format msgid "" "The uploaded file is too small (<%i o) and has therefore not been considered" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:582 #, python-format msgid "" "The uploaded file is too big (>%i o) and has therefore not been considered" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:589 msgid "" "The uploaded file name is too long and has therefore not been considered" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:601 msgid "" "You have already reached the maximum number of files for this type of " "document" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:624 #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:635 #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:747 #, python-format msgid "A file named %s already exists. Please choose another name." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:646 #, python-format msgid "A file with format '%s' already exists. Please upload another format." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:654 #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:754 msgid "" "You are not allowed to use dot '.', slash '/', or backslash '\\\\' in file " "names. Choose a different name and upload your file again. In particular, " "note that you should not include the extension in the renaming field." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:834 msgid "Choose how you want to restrict access to this file." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:873 msgid "Add new file" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:898 msgid "You can decide to hide or not previous version(s) of this file." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:899 msgid "" "When you revise a file, the additional formats that you might have " "previously uploaded are removed, since they no longer up-to-date with the " "new file." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:900 msgid "" "Alternative formats uploaded for current version of this file will be removed" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:901 msgid "Keep previous versions" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:903 #: modules/bibknowledge/lib/bibknowledge_templates.py:207 msgid "Upload" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:904 msgid "Uploading..." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:905 #, fuzzy msgid "Please wait..." msgstr "لطفا دوباره تلاش کنید." #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:918 #: modules/bibdocfile/lib/bibdocfile_webinterface.py:465 #: modules/bibedit/lib/bibeditmulti_templates.py:362 #: modules/oaiharvest/lib/oai_harvest_admin.py:1192 msgid "Apply changes" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:923 #, python-format msgid "Need help revising or adding files to record %(recid)s" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:925 #, python-format msgid "" "Dear Support,\n" "I would need help to revise or add a file to record %(recid)s.\n" "I have attached the new version to this email.\n" "Best regards" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:930 #, python-format msgid "" "Having a problem revising a file? Send the revised version to " "%(mailto_link)s." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:933 #, python-format msgid "" "Having a problem adding or revising a file? Send the new/revised version to " "%(mailto_link)s." msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:1059 msgid "revise" msgstr "" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:1075 #: modules/oaiharvest/lib/oai_harvest_admin.py:109 #: modules/oaiharvest/lib/oai_harvest_templates.py:605 msgid "delete" msgstr "حذف" #: modules/bibdocfile/lib/bibdocfile_managedocfiles.py:1118 msgid "add format" msgstr "" #: modules/bibdocfile/lib/bibdocfile_templates.py:185 msgid "file(s)" msgstr "" #: modules/bibdocfile/lib/bibdocfile_templates.py:305 #: modules/webstyle/lib/webstyle_templates.py:807 msgid "Restricted" msgstr "" #: modules/bibdocfile/lib/bibdocfile_templates.py:310 msgid "see" msgstr "" #: modules/bibdocfile/lib/bibdocfile_templates.py:315 #: modules/bibedit/lib/bibeditmulti_templates.py:737 #: modules/websearch/lib/websearch_templates.py:2695 #: modules/websearch/lib/websearch_templates.py:2885 msgid "previous" msgstr "قبلی" #: modules/bibdocfile/lib/bibdocfile_templates.py:327 msgid "version" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:106 msgid "Requested record does not seem to have been integrated." msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:140 msgid "" "The system has encountered an error in retrieving the list of files for this " "document." msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:141 #: modules/websession/lib/websession_webinterface.py:1053 msgid "" "The error has been logged and will be taken in consideration as soon as " "possible." msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:202 #, python-format msgid "The format %s does not exist for the given version: %s" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:216 msgid "This file is restricted: " msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:228 msgid "An error has happened in trying to stream the request file." msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:231 msgid "The requested file is hidden and can not be accessed." msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:238 msgid "Requested file does not seem to exist." msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:275 msgid "Access to Fulltext" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:324 msgid "An error has happened in trying to retrieve the requested file." msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:326 msgid "Not enough information to retrieve the document" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:334 msgid "An error has happened in trying to retrieving the requested file." msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:382 msgid "Manage Document Files" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:407 #, python-format msgid "Your modifications to record #%i have been submitted" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:415 #, python-format msgid "Your modifications to record #%i have been cancelled" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:424 msgid "Edit" msgstr "ویرایش" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:425 #: modules/websearch/lib/websearch_templates.py:3666 msgid "Edit record" msgstr "ویرایش رکورد" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:441 #: modules/bibdocfile/lib/bibdocfile_webinterface.py:475 msgid "Document File Manager" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:442 #: modules/bibdocfile/lib/bibdocfile_webinterface.py:475 #, python-format msgid "Record #%i" msgstr "" #: modules/bibdocfile/lib/bibdocfile_webinterface.py:466 msgid "Cancel all changes" msgstr "لغو همه تغییرات" #: modules/bibedit/lib/bibedit_templates.py:311 msgid "Comparison of:" msgstr "" #: modules/bibedit/lib/bibedit_templates.py:312 msgid "Revision" msgstr "بازنگری" #: modules/bibedit/lib/bibedit_webinterface.py:190 msgid "Comparing two record revisions" msgstr "" #: modules/bibedit/lib/bibedit_webinterface.py:214 msgid "Failed to create a ticket" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:355 msgid "Search criteria" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:356 msgid "Output tags" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:357 msgid "Filter collection" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:358 msgid "1. Choose search criteria" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:359 msgid "" "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." msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:361 msgid "Preview results" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:613 msgid "2. Define changes" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:614 msgid "" "Specify fields and their subfields that should be changed in every record " "matching the search criteria." msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:615 msgid "Define new field action" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:616 msgid "Define new subfield action" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:617 msgid "Add new condition value" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:618 #, fuzzy msgid "Remove this condition value" msgstr "پاک کردن این مجموعه" #: modules/bibedit/lib/bibeditmulti_templates.py:619 #: modules/webalert/lib/webalert_templates.py:77 msgid "Field" msgstr "فیلد" #: modules/bibedit/lib/bibeditmulti_templates.py:620 msgid "Select action" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:621 msgid "Add field" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:622 msgid "Delete field" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:623 msgid "Update field" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:624 msgid "Add subfield" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:625 msgid "Delete subfield" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:626 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:260 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:402 #: modules/bibknowledge/lib/bibknowledge_templates.py:280 #: modules/bibknowledge/lib/bibknowledge_templates.py:528 msgid "Save" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:628 msgid "Replace substring" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:629 msgid "Replace full content" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:631 msgid "with" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:632 msgid "when subfield $$" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:633 msgid "new value" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:634 msgid "is equal to" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:635 msgid "contains" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:636 #, fuzzy msgid "does not exist" msgstr "این پیام وجود ندارد." #: modules/bibedit/lib/bibeditmulti_templates.py:637 msgid "condition" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:638 msgid "when other subfield" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:639 msgid "when subfield" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:640 msgid "Apply only to specific field instances" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:641 msgid "value" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:670 msgid "Back to Results" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:708 msgid "" "WARNING: The following records are pending execution " "in the task queue. If you proceed with the changes, " "the modifications made with other tool (e.g. BibEdit) " "to these records will be lost" msgstr "" #: modules/bibedit/lib/bibeditmulti_templates.py:736 #: modules/websearch/lib/websearch_templates.py:2690 #: modules/websearch/lib/websearch_templates.py:2880 msgid "begin" msgstr "شروع" #: modules/bibedit/lib/bibeditmulti_templates.py:738 #: modules/websearch/lib/websearch_templates.py:2061 #: modules/websearch/lib/websearch_templates.py:2707 #: modules/websearch/lib/websearch_templates.py:2897 msgid "next" msgstr "بعدی" #: modules/bibedit/lib/bibeditmulti_templates.py:784 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:515 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:571 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:590 msgid "records found" msgstr "" #: modules/bibedit/lib/bibeditmulti_webinterface.py:102 msgid "Multi-Record Editor" msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:116 #, python-format msgid "" "We are sorry, a problem has occured during the processing of your video " "upload%(submission_title)s." msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:119 #: modules/bibencode/lib/bibencode_batch_engine.py:162 #, python-format msgid "The file you uploaded was %(input_filename)s." msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:121 msgid "Your video might not be fully available until intervention." msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:123 #, python-format msgid "You can check the status of your video here: %(record_url)s." msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:125 #, python-format msgid "" "You might want to take a look at %(guidelines_url)s and modify or redo your " "submission." msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:136 #: modules/bibencode/lib/bibencode_batch_engine.py:177 msgid "the video guidelines" msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:160 #, python-format msgid "Your video submission%(submission_title)s was successfully processed." msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:164 #, python-format msgid "Your video is now available here: %(record_url)s." msgstr "" #: modules/bibencode/lib/bibencode_batch_engine.py:166 #, python-format msgid "" "If the videos quality is not as expected, you might want to take a look at " "%(guidelines_url)s and modify or redo your submission." msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter.py:500 #: modules/bibexport/lib/bibexport_method_fieldexporter.py:515 #: modules/bibexport/lib/bibexport_method_fieldexporter.py:530 #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:465 msgid "You are not authorised to access this resource." msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:116 #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:132 msgid "Export Job Overview" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:117 #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:189 msgid "New Export Job" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:118 #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:443 msgid "Export Job History" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:174 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:195 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:323 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:534 msgid "Run" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:176 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:325 msgid "New" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:196 msgid "Last run" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:256 msgid "Frequency" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:257 msgid "Output Format" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:258 msgid "Start" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:259 msgid "Output Directory" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:262 msgid "Edit Queries" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:346 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:399 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:459 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:632 #: modules/webalert/lib/webalert_templates.py:325 msgid "Query" msgstr "عبارت جستجو" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:348 msgid "Output Fields" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:400 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:460 msgid "Output fields" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:438 #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:605 msgid "Download" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:439 msgid "View as: " msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:533 msgid "Job" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:588 msgid "Total" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:652 #: modules/webcomment/lib/webcomment_templates.py:2031 msgid "Select" msgstr "انتخاب" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:653 msgid "All" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:654 msgid "None" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:672 msgid "Manually" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:674 msgid "Daily" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:676 msgid "Weekly" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_templates.py:678 msgid "Monthly" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:192 msgid "Edit Export Job" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:239 msgid "Query Results" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:246 msgid "Export Job Queries" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:320 msgid "New Query" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:325 msgid "Edit Query" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:356 msgid "Export Job Results" msgstr "" #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:389 #: modules/bibexport/lib/bibexport_method_fieldexporter_webinterface.py:423 msgid "Export Job Result" msgstr "" #: modules/bibformat/lib/bibformat_engine.py:271 #, python-format msgid "No template could be found for output format %s." msgstr "" #: modules/bibformat/lib/bibformat_engine.py:618 #: modules/bibformat/lib/bibformat_engine.py:861 #: modules/bibformat/lib/bibformatadminlib.py:1554 #, python-format msgid "Could not find format element named %s." msgstr "" #: modules/bibformat/lib/bibformat_engine.py:722 #, python-format msgid "Error when evaluating format element %s with parameters %s." msgstr "" #: modules/bibformat/lib/bibformat_engine.py:759 #, python-format msgid "" "Escape mode for format element %s could not be retrieved. Using default mode " "instead." msgstr "" #: modules/bibformat/lib/bibformat_engine.py:830 #, python-format msgid "\"nbMax\" parameter for %s must be an \"int\"." msgstr "" #: modules/bibformat/lib/bibformat_engine.py:1055 #, python-format msgid "Could not read format template named %s. %s." msgstr "" #: modules/bibformat/lib/bibformat_engine.py:1119 #, fuzzy, python-format msgid "Format element %s could not be found." msgstr "فایل درخواستی پیدا نشد" #: modules/bibformat/lib/bibformat_engine.py:1159 #, python-format msgid "Format element %s has no function named \"format\"." msgstr "" #: modules/bibformat/lib/bibformat_engine.py:1485 #, fuzzy, python-format msgid "Output format with code %s could not be found." msgstr "فایل درخواستی پیدا نشد" #: modules/bibformat/lib/bibformat_engine.py:1696 #, python-format msgid "Could not find output format named %s." msgstr "" #: modules/bibformat/lib/bibformat_engine.py:1775 #, fuzzy, python-format msgid "Could not find a fresh name for output format %s." msgstr "عدم توانایی ایجاد قالب خروجی" #: modules/bibformat/lib/bibformat_engine_unit_tests.py:852 #: modules/bibformat/lib/bibformat_engine_unit_tests.py:883 #: modules/webcomment/lib/webcomment_templates.py:1011 #: modules/webcomment/lib/webcomment_templates.py:1787 #: modules/websearch/lib/websearch_templates.py:579 msgid "Record" msgstr "رکورد" #: modules/bibformat/lib/bibformat_templates.py:128 #: modules/bibformat/lib/bibformat_templates.py:237 #: modules/bibformat/lib/bibformat_templates.py:390 #: modules/bibformat/lib/bibformat_templates.py:727 #: modules/bibformat/lib/bibformat_templates.py:859 #: modules/bibformat/lib/bibformat_templates.py:1154 #: modules/bibformat/lib/bibformat_templates.py:1260 #: modules/bibformat/lib/bibformat_templates.py:1319 #: modules/bibknowledge/lib/bibknowledge_templates.py:327 #: modules/bibknowledge/lib/bibknowledge_templates.py:587 #: modules/bibknowledge/lib/bibknowledge_templates.py:656 #: modules/webcomment/lib/webcomment_templates.py:1619 #: modules/webjournal/lib/webjournal_templates.py:200 #: modules/webjournal/lib/webjournal_templates.py:572 #: modules/webjournal/lib/webjournal_templates.py:713 #: modules/weblinkback/lib/weblinkback_templates.py:278 msgid "Menu" msgstr "فهرست" #: modules/bibformat/lib/bibformat_templates.py:130 #: modules/bibformat/lib/bibformat_templates.py:238 #: modules/bibformat/lib/bibformat_templates.py:391 #: modules/bibknowledge/lib/bibknowledge_templates.py:323 #: modules/bibknowledge/lib/bibknowledge_templates.py:586 #: modules/bibknowledge/lib/bibknowledge_templates.py:655 msgid "Close Editor" msgstr "بستن ویرایشگر" #: modules/bibformat/lib/bibformat_templates.py:131 #: modules/bibformat/lib/bibformat_templates.py:239 #: modules/bibformat/lib/bibformat_templates.py:403 msgid "Modify Template Attributes" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:132 #: modules/bibformat/lib/bibformat_templates.py:240 #: modules/bibformat/lib/bibformat_templates.py:392 msgid "Template Editor" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:133 #: modules/bibformat/lib/bibformat_templates.py:241 #: modules/bibformat/lib/bibformat_templates.py:404 #: modules/bibformat/lib/bibformat_templates.py:975 #: modules/bibformat/lib/bibformat_templates.py:1153 #: modules/bibformat/lib/bibformat_templates.py:1259 msgid "Check Dependencies" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:199 msgid "Update Format Attributes" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:395 #: modules/websubmit/lib/websubmit_templates.py:842 msgid "Your modifications will not be saved." msgstr "" #: modules/bibformat/lib/bibformat_templates.py:427 msgid "Show Documentation" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:428 #: modules/bibformat/lib/bibformat_templates.py:473 msgid "Hide Documentation" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:729 #: modules/bibformat/lib/bibformat_templates.py:853 msgid "Last Modification Date" msgstr "تاریخ آخرین اصلاح" #: modules/bibformat/lib/bibformat_templates.py:730 #: modules/bibformat/lib/bibformat_templates.py:854 #: modules/bibknowledge/lib/bibknowledge_templates.py:81 #: modules/webalert/lib/webalert_templates.py:326 #: modules/webalert/lib/webalert_templates.py:463 #: modules/webmessage/lib/webmessage_templates.py:89 #: modules/websubmit/lib/websubmit_templates.py:1266 msgid "Action" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:732 #: modules/bibformat/lib/bibformat_templates.py:856 #: modules/bibformat/lib/bibformat_templates.py:1320 #: modules/bibformat/web/admin/bibformatadmin.py:108 #: modules/bibformat/web/admin/bibformatadmin.py:171 #: modules/bibformat/web/admin/bibformatadmin.py:245 #: modules/bibformat/web/admin/bibformatadmin.py:295 #: modules/bibformat/web/admin/bibformatadmin.py:393 #: modules/bibformat/web/admin/bibformatadmin.py:1009 msgid "Manage Output Formats" msgstr "مدیریت قالب های خروجی" #: modules/bibformat/lib/bibformat_templates.py:733 #: modules/bibformat/lib/bibformat_templates.py:857 #: modules/bibformat/lib/bibformat_templates.py:1321 #: modules/bibformat/web/admin/bibformatadmin.py:474 #: modules/bibformat/web/admin/bibformatadmin.py:509 #: modules/bibformat/web/admin/bibformatadmin.py:583 #: modules/bibformat/web/admin/bibformatadmin.py:631 #: modules/bibformat/web/admin/bibformatadmin.py:705 #: modules/bibformat/web/admin/bibformatadmin.py:1031 msgid "Manage Format Templates" msgstr "مدیریت اگلوهای قالب بندی" #: modules/bibformat/lib/bibformat_templates.py:734 #: modules/bibformat/lib/bibformat_templates.py:858 #: modules/bibformat/lib/bibformat_templates.py:1322 #: modules/bibformat/web/admin/bibformatadmin.py:898 #: modules/bibformat/web/admin/bibformatadmin.py:922 #: modules/bibformat/web/admin/bibformatadmin.py:957 #: modules/bibformat/web/admin/bibformatadmin.py:1050 msgid "Format Elements Documentation" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:809 msgid "Add New Format Template" msgstr "فزودن الگوی قالب بندی جدید" #: modules/bibformat/lib/bibformat_templates.py:810 msgid "Check Format Templates Extensively" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:849 msgid "Code" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:932 msgid "Add New Output Format" msgstr "افزودن قالب خروجی جدید" #: modules/bibformat/lib/bibformat_templates.py:971 msgid "menu" msgstr "فهرست" #: modules/bibformat/lib/bibformat_templates.py:972 #: modules/bibformat/lib/bibformat_templates.py:1150 #: modules/bibformat/lib/bibformat_templates.py:1256 msgid "Close Output Format" msgstr "بستن قالب خروجی جدید" #: modules/bibformat/lib/bibformat_templates.py:973 #: modules/bibformat/lib/bibformat_templates.py:1151 #: modules/bibformat/lib/bibformat_templates.py:1257 msgid "Rules" msgstr "قوانین" #: modules/bibformat/lib/bibformat_templates.py:974 #: modules/bibformat/lib/bibformat_templates.py:1152 #: modules/bibformat/lib/bibformat_templates.py:1258 msgid "Modify Output Format Attributes" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:1062 #: modules/bibformat/lib/bibformatadminlib.py:565 msgid "Remove Rule" msgstr "حذف قوانین" #: modules/bibformat/lib/bibformat_templates.py:1104 #: modules/bibformat/lib/bibformatadminlib.py:572 msgid "Add New Rule" msgstr "افزودن قانون جدید" #: modules/bibformat/lib/bibformat_templates.py:1105 #: modules/bibformat/lib/bibformatadminlib.py:569 msgid "Save Changes" msgstr "ذخیره تغییرات" #: modules/bibformat/lib/bibformat_templates.py:1679 msgid "No problem found with format" msgstr "" #: modules/bibformat/lib/bibformat_templates.py:1681 msgid "An error has been found" msgstr "یک خطا پیدا شده است" #: modules/bibformat/lib/bibformat_templates.py:1683 msgid "The following errors have been found" msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:62 #: modules/bibformat/web/admin/bibformatadmin.py:77 msgid "BibFormat Admin" msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:354 #: modules/bibformat/lib/bibformatadminlib.py:393 #: modules/bibformat/lib/bibformatadminlib.py:395 msgid "Test with record:" msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:355 msgid "Enter a search query here." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:406 #, fuzzy, python-format msgid "No Record Found for %s." msgstr "رکورد یافت نشد" #: modules/bibformat/lib/bibformatadminlib.py:1279 #, python-format msgid "Tag specification \"%s\" must end with column \":\" at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1286 #, python-format msgid "Tag specification \"%s\" must start with \"tag\" at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1293 #, python-format msgid "\"tag\" must be lowercase in \"%s\" at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1303 #: modules/bibformat/lib/bibformatadminlib.py:1317 #, python-format msgid "Should be \"tag field_number:\" at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1311 #, python-format msgid "Invalid tag \"%s\" at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1326 #, python-format msgid "Condition \"%s\" is outside a tag specification at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1334 #, python-format msgid "Condition \"%s\" can only have a single separator --- at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1342 #: modules/bibformat/lib/bibformatadminlib.py:1375 #, python-format msgid "Template \"%s\" does not exist at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1353 #, python-format msgid "Missing column \":\" after \"default\" in \"%s\" at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1360 #, python-format msgid "" "Default template specification \"%s\" must start with \"default :\" at line " "%s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1367 #, python-format msgid "\"default\" keyword must be lowercase in \"%s\" at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1383 #, python-format msgid "Line %s could not be understood at line %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1389 #: modules/bibformat/web/admin/bibformatadmin.py:187 #: modules/bibformat/web/admin/bibformatadmin.py:258 #: modules/bibformat/web/admin/bibformatadmin.py:308 #: modules/bibformat/web/admin/bibformatadmin.py:1013 #, python-format msgid "Output format %s cannot not be read. %s" msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1418 #, python-format msgid "" "Could not find a name specified in tag \"\" inside format template %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1427 #, python-format msgid "" "Could not find a description specified in tag \"\" inside " "format template %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1442 #, python-format msgid "Format template %s calls undefined element \"%s\"." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1451 #, python-format msgid "" "Format template %s calls unreadable element \"%s\". Check element file " "permissions." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1457 #, python-format msgid "Cannot load element \"%s\" in template %s. Check element code." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1481 #, python-format msgid "Format element %s uses unknown parameter \"%s\" in format template %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1498 #, python-format msgid "Could not read format template named %s. %s" msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1540 #, python-format msgid "Error in format element %s. %s." msgstr "" #: modules/bibformat/lib/bibformatadminlib.py:1546 #: modules/bibformat/web/admin/bibformatadmin.py:1055 #, python-format msgid "Format element %s cannot not be read. %s" msgstr "" #: modules/bibformat/lib/elements/bfe_aid_authors.py:285 #: modules/bibformat/lib/elements/bfe_authors.py:235 #: modules/bibformat/lib/elements/bfe_authors_parent.py:317 msgid "Hide" msgstr "پنهان" #: modules/bibformat/lib/elements/bfe_aid_authors.py:286 #: modules/bibformat/lib/elements/bfe_authors.py:236 #: modules/bibformat/lib/elements/bfe_authors_parent.py:318 #, python-format msgid "Show all %i authors" msgstr "" #: modules/bibformat/lib/elements/bfe_arxiv_link.py:36 msgid "This article on arXiv.org" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_author.py:61 msgid "Name variant(s)" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_author.py:66 #: modules/bibformat/lib/elements/bfe_authority_journal.py:62 #: modules/bibformat/lib/elements/bfe_authority_subject.py:62 #: modules/webstyle/lib/webdoc_webinterface.py:164 msgid "See also" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_author_publications.py:75 #: modules/bibformat/lib/elements/bfe_authority_publications.py:88 #, fuzzy msgid "Publication(s)" msgstr "سیاهه انتشار" #: modules/bibformat/lib/elements/bfe_authority_control_no.py:113 msgid "Control Number(s)" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_institute.py:59 #: modules/bibformat/lib/elements/bfe_authority_journal.py:57 #: modules/bibformat/lib/elements/bfe_authority_subject.py:57 msgid "Variant(s)" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_institute.py:80 msgid "Parent" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_institute.py:82 msgid "Children" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_institute.py:84 msgid "Predecessor" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_institute.py:86 msgid "Successor" msgstr "" #: modules/bibformat/lib/elements/bfe_authority_links.py:81 #, fuzzy msgid "Useful links" msgstr "پیوندهای خارجی" #: modules/bibformat/lib/elements/bfe_edit_files.py:50 msgid "Manage Files of This Record" msgstr "مدیریت فایل های این رکورد" #: modules/bibformat/lib/elements/bfe_edit_record.py:46 msgid "Edit This Record" msgstr "ویرایش این رکورد" #: modules/bibformat/lib/elements/bfe_fulltext.py:88 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:74 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:77 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:115 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:118 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:135 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:137 msgid "Download fulltext" msgstr "دانلود تمام متن" #: modules/bibformat/lib/elements/bfe_fulltext.py:97 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:63 msgid "additional files" msgstr "فایل های اضافی" #: modules/bibformat/lib/elements/bfe_fulltext.py:133 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:122 #, python-format msgid "%(x_sitename)s link" msgstr "" #: modules/bibformat/lib/elements/bfe_fulltext.py:133 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:122 #, python-format msgid "%(x_sitename)s links" msgstr "" #: modules/bibformat/lib/elements/bfe_fulltext.py:142 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:140 msgid "external link" msgstr "پیوند خارجی" #: modules/bibformat/lib/elements/bfe_fulltext.py:142 #: modules/bibformat/lib/elements/bfe_fulltext_mini.py:140 msgid "external links" msgstr "پیوندهای خارجی" #: modules/bibformat/lib/elements/bfe_fulltext.py:254 #: modules/bibformat/lib/elements/bfe_fulltext.py:258 #: modules/bibformat/lib/elements/bfe_fulltext.py:312 msgid "Fulltext" msgstr "متن کامل" #: modules/bibformat/lib/elements/bfe_plots.py:90 #, fuzzy msgid "Show more plots" msgstr "نمایش نظرها" #: modules/bibformat/lib/elements/bfe_sciencewise.py:99 msgid "Add this document to your ScienceWise.info bookmarks" msgstr "" #: modules/bibformat/lib/elements/bfe_sciencewise.py:112 msgid "Add this article to your ScienceWise.info bookmarks" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:57 msgid "" "Cannot write in etc/bibformat dir of your Invenio installation. Check " "directory permission." msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:190 #: modules/bibformat/web/admin/bibformatadmin.py:261 #: modules/bibformat/web/admin/bibformatadmin.py:311 #: modules/bibformat/web/admin/bibformatadmin.py:1016 msgid "Restricted Output Format" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:213 #: modules/bibformat/web/admin/bibformatadmin.py:544 #: modules/bibknowledge/lib/bibknowledgeadmin.py:562 msgid "Ok" msgstr "خوب" #: modules/bibformat/web/admin/bibformatadmin.py:215 #, python-format msgid "Output Format %s Rules" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:273 #, python-format msgid "Output Format %s Attributes" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:321 #, python-format msgid "Output Format %s Dependencies" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:393 msgid "Delete Output Format" msgstr "حذف قالب خروجی" #: modules/bibformat/web/admin/bibformatadmin.py:443 msgid "Cannot create output format" msgstr "عدم توانایی ایجاد قالب خروجی" #: modules/bibformat/web/admin/bibformatadmin.py:523 #: modules/bibformat/web/admin/bibformatadmin.py:598 #: modules/bibformat/web/admin/bibformatadmin.py:1035 #, python-format msgid "Format template %s cannot not be read. %s" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:526 #: modules/bibformat/web/admin/bibformatadmin.py:601 #: modules/bibformat/web/admin/bibformatadmin.py:1038 msgid "Restricted Format Template" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:549 #, python-format msgid "Format Template %s" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:609 #, python-format msgid "Format Template %s Attributes" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:643 #, python-format msgid "Format Template %s Dependencies" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:705 msgid "Delete Format Template" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:931 #, python-format msgid "Format Element %s Dependencies" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:964 #, python-format msgid "Test Format Element %s" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:1027 #, python-format msgid "Validation of Output Format %s" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:1046 #, python-format msgid "Validation of Format Template %s" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:1058 msgid "Restricted Format Element" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:1065 #, python-format msgid "Validation of Format Element %s" msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:1069 msgid "No format specified for validation. Please specify one." msgstr "" #: modules/bibformat/web/admin/bibformatadmin.py:1072 msgid "Format Validation" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:51 #, python-format msgid "" "Limit display to knowledge bases matching %(keyword_field)s in their rules " "and descriptions" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:89 msgid "No Knowledge Base" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:148 msgid "Add New Knowledge Base" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:149 msgid "Configure a dynamic KB" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:150 msgid "Add New Taxonomy" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:191 msgid "This knowledge base already has a taxonomy file." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:192 msgid "If you upload another file, the current version will be replaced." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:194 #, python-format msgid "The current taxonomy can be accessed with this URL: %s" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:197 #, python-format msgid "Please upload the RDF file for taxonomy %s" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:234 msgid "Please configure" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:235 msgid "" "A dynamic knowledge base is a list of values of a " "given field. The list is generated dynamically by " "searching the records using a search expression." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:239 msgid "" "Example: Your records contain field 270__a for the " "name and address of the author's institute. If you " "set the field to '270__a' and the expression to " "'270__a:*Paris*', a list of institutes in Paris " "will be created." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:244 msgid "" "If the expression is empty, a list of all values in " "270__a will be created." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:246 msgid "" "If the expression contains '%', like '270__a:*%*', " "it will be replaced by a search string when the " "knowledge base is used." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:249 msgid "" "You can enter a collection name if the expression " "should be evaluated in a specific collection." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:251 msgid "" "Example 1: Your records contain field 270__a for " "the name and address of the author's institute. If " "you set the field to '270__a' and the expression to " "'270__a:*Paris*', a list of institutes in Paris " "will be created." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:256 msgid "" "Example 2: Return the institute's name (100__a) when " "the user gives its postal code " "(270__a): Set field to 100__a, expression to 270__a:" "*%*." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:260 msgid "Any collection" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:282 msgid "Exporting: " msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:324 #: modules/bibknowledge/lib/bibknowledge_templates.py:588 #: modules/bibknowledge/lib/bibknowledge_templates.py:657 msgid "Knowledge Base Mappings" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:325 #: modules/bibknowledge/lib/bibknowledge_templates.py:589 #: modules/bibknowledge/lib/bibknowledge_templates.py:658 msgid "Knowledge Base Attributes" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:326 #: modules/bibknowledge/lib/bibknowledge_templates.py:590 #: modules/bibknowledge/lib/bibknowledge_templates.py:659 msgid "Knowledge Base Dependencies" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:347 msgid "" "Here you can add new mappings to this base and " "change the base attributes." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:362 msgid "Map From" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:425 msgid "Search for a mapping" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:480 msgid "Knowledge base is empty" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:545 msgid "You can get a these mappings in textual format by: " msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:547 msgid "And the KBA version by:" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:627 msgid "Update Base Attributes" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:670 msgid "This knowledge base is not used in any format elements." msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:700 #, python-format msgid "Your rule: %s" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:702 #, python-format msgid "" "The left side of the rule (%s) already appears in these knowledge bases:" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:705 #, python-format msgid "" "The right side of the rule (%s) already appears in these knowledge bases:" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:719 msgid "Please select action" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:720 msgid "Replace the selected rules with this rule" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:721 msgid "Add this rule in the current knowledge base" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:722 msgid "Cancel: do not add this rule" msgstr "" #: modules/bibknowledge/lib/bibknowledge_templates.py:755 msgid "" "It is not possible to have two rules with the same left side in the same " "knowledge base." msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:71 msgid "BibKnowledge Admin" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:91 msgid "Knowledge Bases" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:105 #: modules/bibknowledge/lib/bibknowledgeadmin.py:116 #: modules/bibknowledge/lib/bibknowledgeadmin.py:128 msgid "Cannot upload file" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:106 msgid "You have not selected a file to upload" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:140 #, python-format msgid "File %s uploaded." msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:142 msgid "File uploaded" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:171 #: modules/bibknowledge/lib/bibknowledgeadmin.py:215 #: modules/bibknowledge/lib/bibknowledgeadmin.py:265 #: modules/bibknowledge/lib/bibknowledgeadmin.py:302 #: modules/bibknowledge/lib/bibknowledgeadmin.py:355 #: modules/bibknowledge/lib/bibknowledgeadmin.py:464 #: modules/bibknowledge/lib/bibknowledgeadmin.py:523 #: modules/bibknowledge/lib/bibknowledgeadmin.py:589 #: modules/bibknowledge/lib/bibknowledgeadmin.py:685 #: modules/bibknowledge/lib/bibknowledgeadmin.py:702 #: modules/bibknowledge/lib/bibknowledgeadmin.py:717 #: modules/bibknowledge/lib/bibknowledgeadmin.py:753 msgid "Manage Knowledge Bases" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:184 #: modules/bibknowledge/lib/bibknowledgeadmin.py:229 #: modules/bibknowledge/lib/bibknowledgeadmin.py:315 #: modules/bibknowledge/lib/bibknowledgeadmin.py:369 #: modules/bibknowledge/lib/bibknowledgeadmin.py:477 #: modules/bibknowledge/lib/bibknowledgeadmin.py:542 #: modules/bibknowledge/lib/bibknowledgeadmin.py:729 msgid "Unknown Knowledge Base" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:191 #, python-format msgid "Knowledge Base %s" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:238 #, python-format msgid "Knowledge Base %s Attributes" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:324 #, python-format msgid "Knowledge Base %s Dependencies" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:406 msgid "Left side exists" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:414 msgid "Right side exists" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:591 msgid "Knowledge base name missing" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:611 msgid "Unknown knowledge base" msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:612 msgid "There is no knowledge base with that name." msgstr "" #: modules/bibknowledge/lib/bibknowledgeadmin.py:717 msgid "Delete Knowledge Base" msgstr "" #: modules/bibrank/lib/bibrank_citation_grapher.py:208 msgid "Citation history:" msgstr "تاریخچه استنادی" #: modules/bibrank/lib/bibrank_downloads_grapher.py:87 msgid "Download history:" msgstr "تاریخچه دانلود" #: modules/bibrank/lib/bibrank_downloads_grapher.py:110 msgid "Download user distribution:" msgstr "" #: modules/bibsort/lib/bibsortadminlib.py:367 #, fuzzy msgid "BibSort Guide" msgstr "راهنمای واگذاری" #: modules/bibsword/lib/bibsword_webinterface.py:157 msgid "BibSword Admin Interface" msgstr "" #: modules/bibsword/lib/bibsword_webinterface.py:171 #: modules/bibsword/lib/bibsword_webinterface.py:277 #: modules/bibsword/lib/bibsword_webinterface.py:301 #: modules/bibsword/lib/bibsword_webinterface.py:330 msgid "Export with BibSword: Step 2/4" msgstr "" #: modules/bibsword/lib/bibsword_webinterface.py:222 #: modules/bibsword/lib/bibsword_webinterface.py:233 #: modules/bibsword/lib/bibsword_webinterface.py:291 msgid "Export with BibSword: Step 1/4" msgstr "" #: modules/bibsword/lib/bibsword_webinterface.py:315 #: modules/bibsword/lib/bibsword_webinterface.py:343 #: modules/bibsword/lib/bibsword_webinterface.py:374 msgid "Export with BibSword: Step 3/4" msgstr "" #: modules/bibsword/lib/bibsword_webinterface.py:358 #: modules/bibsword/lib/bibsword_webinterface.py:389 msgid "Export with BibSword: Step 4/4" msgstr "" #: modules/bibsword/lib/bibsword_webinterface.py:434 msgid "Export with BibSword: Acknowledgement" msgstr "" #: modules/bibupload/lib/batchuploader_engine.py:272 msgid "More than one possible recID, ambiguous behaviour" msgstr "" #: modules/bibupload/lib/batchuploader_engine.py:272 msgid "No records match that file name" msgstr "" #: modules/bibupload/lib/batchuploader_engine.py:273 msgid "File already exists" msgstr "" #: modules/bibupload/lib/batchuploader_engine.py:273 msgid "A file with the same name and format already exists" msgstr "" #: modules/bibupload/lib/batchuploader_engine.py:274 #, python-format msgid "No rights to upload to collection '%s'" msgstr "" #: modules/bibupload/lib/batchuploader_engine.py:568 #: modules/bibupload/lib/batchuploader_engine.py:581 #, python-format msgid "" "The user '%(x_user)s' is not authorized to modify collection '%(x_coll)s'" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:233 msgid "Select file to upload" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:234 msgid "File type" msgstr "نوع فایل" #: modules/bibupload/lib/batchuploader_templates.py:235 #: modules/bibupload/lib/batchuploader_templates.py:468 msgid "Upload mode" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:236 #: modules/bibupload/lib/batchuploader_templates.py:469 msgid "Upload later? then select:" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:238 #: modules/bibupload/lib/batchuploader_templates.py:471 #: modules/bibupload/lib/batchuploader_templates.py:622 #: modules/webstyle/lib/webstyle_templates.py:746 msgid "Time" msgstr "زمان" #: modules/bibupload/lib/batchuploader_templates.py:239 #: modules/bibupload/lib/batchuploader_templates.py:466 #: modules/bibupload/lib/batchuploader_templates.py:472 #: modules/websession/lib/websession_templates.py:268 #: modules/websession/lib/websession_templates.py:271 #: modules/websession/lib/websession_templates.py:1210 msgid "Example" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:240 #: modules/bibupload/lib/batchuploader_templates.py:475 msgid "Email logs to" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:241 #: modules/bibupload/lib/batchuploader_templates.py:473 #, python-format msgid "All fields with %(x_fmt_open)s*%(x_fmt_close)s are mandatory" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:243 #: modules/bibupload/lib/batchuploader_templates.py:464 msgid "Upload priority" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:244 msgid "Skip upload simulation" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:266 #, python-format msgid "" "Your file has been successfully queued. You can check your " "%(x_url1_open)supload history%(x_url1_close)s or %(x_url2_open)ssubmit " "another file%(x_url2_close)s" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:280 msgid "No metadata files have been uploaded yet." msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:293 #: modules/bibupload/lib/batchuploader_templates.py:335 msgid "Submit time" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:294 #: modules/bibupload/lib/batchuploader_templates.py:336 msgid "File name" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:295 #: modules/bibupload/lib/batchuploader_templates.py:337 msgid "Execution time" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:322 msgid "No document files have been uploaded yet." msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:377 #: modules/bibupload/lib/batchuploader_webinterface.py:103 #: modules/bibupload/lib/batchuploader_webinterface.py:230 #: modules/bibupload/lib/batchuploader_webinterface.py:283 msgid "Metadata batch upload" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:380 #: modules/bibupload/lib/batchuploader_webinterface.py:127 #: modules/bibupload/lib/batchuploader_webinterface.py:165 msgid "Document batch upload" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:383 #: modules/bibupload/lib/batchuploader_webinterface.py:312 msgid "Upload history" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:386 msgid "Daemon monitor" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:465 msgid "Input directory" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:467 msgid "Filename matching" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:492 msgid "The job is scheduled to run on" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:538 msgid "You are about to submit a" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:539 msgid "file with name" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:540 #, fuzzy msgid "and content" msgstr "افزودن نظرها" #: modules/bibupload/lib/batchuploader_templates.py:541 msgid "This file will be uploaded with priority" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:542 msgid "and in mode" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:543 msgid "Do you want to submit the changes?" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:544 msgid "record(s) will be affected" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:567 #, python-format msgid "%s documents have been found." msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:569 msgid "The following files have been successfully queued:" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:574 msgid "The following errors have occurred:" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:581 msgid "" "Some files could not be moved to DONE folder. Please remove them manually." msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:583 msgid "All uploaded files were moved to DONE folder." msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:593 #, python-format msgid "" "Using %(x_fmt_open)sweb interface upload%(x_fmt_close)s, actions are " "executed a single time." msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:595 #, python-format msgid "" "Check the %(x_url_open)sBatch Uploader daemon help page%(x_url_close)s for " "executing these actions periodically." msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:600 msgid "Metadata folders" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:622 msgid "Progress" msgstr "پیشرفت" #: modules/bibupload/lib/batchuploader_templates.py:624 msgid "Last BibSched tasks:" msgstr "" #: modules/bibupload/lib/batchuploader_templates.py:633 msgid "Next scheduled BibSched run:" msgstr "" #: modules/bibupload/lib/batchuploader_webinterface.py:168 msgid "Document batch upload result" msgstr "" #: modules/bibupload/lib/batchuploader_webinterface.py:228 msgid "Upload successful" msgstr "فراگذاری موفقیت آمیز" #: modules/bibupload/lib/batchuploader_webinterface.py:338 msgid "Batch Uploader: Daemon monitor" msgstr "" #: modules/miscutil/lib/dateutils.py:106 modules/miscutil/lib/dateutils.py:133 #: modules/miscutil/lib/dateutils_unit_tests.py:128 #: modules/webbasket/lib/webbasket.py:210 #: modules/webbasket/lib/webbasket.py:851 #: modules/webbasket/lib/webbasket.py:968 #: modules/websession/lib/webuser.py:312 #: modules/webstyle/lib/webstyle_templates.py:655 msgid "N/A" msgstr "" #: modules/miscutil/lib/dateutils.py:196 msgid "Sun" msgstr "یکشنبه" #: modules/miscutil/lib/dateutils.py:197 msgid "Mon" msgstr "دوشنبه" #: modules/miscutil/lib/dateutils.py:198 msgid "Tue" msgstr "سه شنبه" #: modules/miscutil/lib/dateutils.py:199 msgid "Wed" msgstr "چهارشنبه" #: modules/miscutil/lib/dateutils.py:200 msgid "Thu" msgstr "پنج شنبه" #: modules/miscutil/lib/dateutils.py:201 msgid "Fri" msgstr "جمعه" #: modules/miscutil/lib/dateutils.py:202 msgid "Sat" msgstr "شنبه" #: modules/miscutil/lib/dateutils.py:204 msgid "Sunday" msgstr "یکشنبه" #: modules/miscutil/lib/dateutils.py:205 msgid "Monday" msgstr "دوشنبه" #: modules/miscutil/lib/dateutils.py:206 msgid "Tuesday" msgstr "سه شنبه" #: modules/miscutil/lib/dateutils.py:207 msgid "Wednesday" msgstr "چهارشنبه" #: modules/miscutil/lib/dateutils.py:208 msgid "Thursday" msgstr "پنج شنبه" #: modules/miscutil/lib/dateutils.py:209 msgid "Friday" msgstr "جمعه" #: modules/miscutil/lib/dateutils.py:210 msgid "Saturday" msgstr "شنبه" #: modules/miscutil/lib/dateutils.py:224 modules/miscutil/lib/dateutils.py:238 msgid "Month" msgstr "ماه" #: modules/miscutil/lib/dateutils.py:225 msgid "Jan" msgstr "ژانویه" #: modules/miscutil/lib/dateutils.py:226 msgid "Feb" msgstr "فوریه" #: modules/miscutil/lib/dateutils.py:227 msgid "Mar" msgstr "مارچ" #: modules/miscutil/lib/dateutils.py:228 msgid "Apr" msgstr "آپریل" #: modules/miscutil/lib/dateutils.py:229 msgid "May" msgstr "مِی" #: modules/miscutil/lib/dateutils.py:230 msgid "Jun" msgstr "ژوئن" #: modules/miscutil/lib/dateutils.py:231 #: modules/miscutil/lib/dateutils_unit_tests.py:108 msgid "Jul" msgstr "ژولای" #: modules/miscutil/lib/dateutils.py:232 msgid "Aug" msgstr "آوگوست" #: modules/miscutil/lib/dateutils.py:233 msgid "Sep" msgstr "سپتامبر" #: modules/miscutil/lib/dateutils.py:234 msgid "Oct" msgstr "اکتبر" #: modules/miscutil/lib/dateutils.py:235 msgid "Nov" msgstr "نوامبر" #: modules/miscutil/lib/dateutils.py:236 msgid "Dec" msgstr "دسامبر" #: modules/miscutil/lib/dateutils.py:239 #: modules/websearch/lib/search_engine.py:1160 #: modules/websearch/lib/websearch_templates.py:1385 msgid "January" msgstr "ژانویه" #: modules/miscutil/lib/dateutils.py:240 #: modules/websearch/lib/search_engine.py:1160 #: modules/websearch/lib/websearch_templates.py:1385 msgid "February" msgstr "فوریه" #: modules/miscutil/lib/dateutils.py:241 #: modules/websearch/lib/search_engine.py:1160 #: modules/websearch/lib/websearch_templates.py:1385 msgid "March" msgstr "مارچ" #: modules/miscutil/lib/dateutils.py:242 #: modules/websearch/lib/search_engine.py:1160 #: modules/websearch/lib/websearch_templates.py:1385 msgid "April" msgstr "آپریل" #: modules/miscutil/lib/dateutils.py:243 #: modules/websearch/lib/search_engine.py:1161 #: modules/websearch/lib/websearch_templates.py:1386 msgid "May " msgstr "" #: modules/miscutil/lib/dateutils.py:244 #: modules/websearch/lib/search_engine.py:1161 #: modules/websearch/lib/websearch_templates.py:1386 msgid "June" msgstr "ژوئن" #: modules/miscutil/lib/dateutils.py:245 #: modules/websearch/lib/search_engine.py:1161 #: modules/websearch/lib/websearch_templates.py:1386 msgid "July" msgstr "زولای" #: modules/miscutil/lib/dateutils.py:246 #: modules/websearch/lib/search_engine.py:1161 #: modules/websearch/lib/websearch_templates.py:1386 msgid "August" msgstr "آوگوست" #: modules/miscutil/lib/dateutils.py:247 #: modules/websearch/lib/search_engine.py:1162 #: modules/websearch/lib/websearch_templates.py:1387 msgid "September" msgstr "سپتامبر" #: modules/miscutil/lib/dateutils.py:248 #: modules/websearch/lib/search_engine.py:1162 #: modules/websearch/lib/websearch_templates.py:1387 msgid "October" msgstr "اکتبر" #: modules/miscutil/lib/dateutils.py:249 #: modules/websearch/lib/search_engine.py:1162 #: modules/websearch/lib/websearch_templates.py:1387 msgid "November" msgstr "نوامبر" #: modules/miscutil/lib/dateutils.py:250 #: modules/websearch/lib/search_engine.py:1162 #: modules/websearch/lib/websearch_templates.py:1387 msgid "December" msgstr "دسامبر" #: modules/miscutil/lib/dateutils.py:268 msgid "Day" msgstr "روز" #: modules/miscutil/lib/errorlib_webinterface.py:64 #: modules/miscutil/lib/errorlib_webinterface.py:69 #: modules/miscutil/lib/errorlib_webinterface.py:74 #: modules/miscutil/lib/errorlib_webinterface.py:79 msgid "Sorry" msgstr "" #: modules/miscutil/lib/errorlib_webinterface.py:65 #: modules/miscutil/lib/errorlib_webinterface.py:70 #: modules/miscutil/lib/errorlib_webinterface.py:75 #: modules/miscutil/lib/errorlib_webinterface.py:80 #, python-format msgid "Cannot send error request, %s parameter missing." msgstr "" #: modules/miscutil/lib/errorlib_webinterface.py:98 msgid "The error report has been sent." msgstr "" #: modules/miscutil/lib/errorlib_webinterface.py:99 msgid "Many thanks for helping us to improve the service." msgstr "" #: modules/miscutil/lib/errorlib_webinterface.py:101 msgid "Use the back button of your browser to return to the previous page." msgstr "" #: modules/miscutil/lib/errorlib_webinterface.py:103 msgid "Thank you!" msgstr "متشکرم!" #: modules/miscutil/lib/inveniocfg.py:664 msgid "journal" msgstr "مجله" #: modules/miscutil/lib/inveniocfg.py:666 msgid "record ID" msgstr "" #: modules/miscutil/lib/inveniocfg.py:679 msgid "word similarity" msgstr "" #: modules/miscutil/lib/inveniocfg.py:680 msgid "journal impact factor" msgstr "ضریب تأثیر مجله" #: modules/miscutil/lib/inveniocfg.py:681 msgid "times cited" msgstr "دفعات استناد شده" #: modules/miscutil/lib/inveniocfg.py:682 msgid "time-decay cite count" msgstr "" #: modules/miscutil/lib/inveniocfg.py:683 msgid "all-time-best cite rank" msgstr "" #: modules/miscutil/lib/inveniocfg.py:684 msgid "time-decay cite rank" msgstr "" #: modules/miscutil/lib/mailutils.py:241 #, python-format msgid "" "The system is not attempting to send an email from %s, to %s, with body %s." msgstr "" #: modules/miscutil/lib/mailutils.py:274 #, python-format msgid "" "Error in connecting to the SMPT server waiting %s seconds. Exception is %s, " "while sending email from %s to %s with body %s." msgstr "" #: modules/miscutil/lib/mailutils.py:291 #, python-format msgid "Error while sending an email: %s" msgstr "" #: modules/miscutil/lib/mailutils.py:292 #, python-format msgid "" "\n" "Error while sending an email.\n" "Reason: %s\n" "Details: %s\n" "Sender: \"%s\"\n" "Recipient(s): \"%s\"\n" "\n" "The content of the mail was as follows:\n" "%s" msgstr "" #: modules/miscutil/lib/mailutils.py:307 #, python-format msgid "Error in sending email from %s to %s with body %s." msgstr "" #: modules/miscutil/lib/mailutils.py:323 modules/miscutil/lib/mailutils.py:337 #: modules/webcomment/lib/webcomment_templates.py:2256 msgid "Hello:" msgstr "" #: modules/miscutil/lib/mailutils.py:355 modules/miscutil/lib/mailutils.py:376 msgid "Best regards" msgstr "" #: modules/miscutil/lib/mailutils.py:357 modules/miscutil/lib/mailutils.py:378 msgid "Need human intervention? Contact" msgstr "نیازمند به مداخله انسانی دارید؟ تماس بگیرید" #: modules/oaiharvest/lib/oai_harvest_admin.py:105 #: modules/oaiharvest/lib/oai_harvest_templates.py:601 msgid "edit" msgstr "ویرایش" #: modules/oaiharvest/lib/oai_harvest_admin.py:113 #: modules/oaiharvest/lib/oai_harvest_templates.py:609 msgid "test" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:117 #: modules/oaiharvest/lib/oai_harvest_templates.py:613 msgid "history" msgstr "تاریخچه" #: modules/oaiharvest/lib/oai_harvest_admin.py:121 #: modules/oaiharvest/lib/oai_harvest_templates.py:617 msgid "harvest" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:130 msgid "Overview of sources" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:131 msgid "Harvesting status" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:145 msgid "Not Set" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:146 msgid "never" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:147 #: modules/webalert/lib/webalert_templates.py:219 #: modules/webalert/lib/webalert_templates.py:332 msgid "daily" msgstr "روزانه" #: modules/oaiharvest/lib/oai_harvest_admin.py:148 #: modules/webalert/lib/webalert_templates.py:218 #: modules/webalert/lib/webalert_templates.py:335 msgid "weekly" msgstr "هفتگی" #: modules/oaiharvest/lib/oai_harvest_admin.py:149 #: modules/webalert/lib/webalert_templates.py:217 #: modules/webalert/lib/webalert_templates.py:337 msgid "monthly" msgstr "ماهانه" #: modules/oaiharvest/lib/oai_harvest_admin.py:153 msgid "Never harvested" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:177 msgid "View Holding Pen" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:199 #: modules/oaiharvest/lib/oai_harvest_admin.py:469 #: modules/oaiharvest/lib/oai_harvest_admin.py:1110 msgid "No OAI source ID selected." msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:287 #: modules/oaiharvest/lib/oai_harvest_admin.py:411 #: modules/oaiharvest/lib/oai_harvest_admin.py:459 msgid "Go back to the OAI sources overview" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:370 msgid "Try again with another url" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:377 msgid "Continue anyway" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:861 msgid "Return to the month view" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:874 #: modules/oaiharvest/lib/oai_harvest_admin.py:929 #: modules/websubmit/lib/websubmit_templates.py:527 msgid "Next page" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:889 #: modules/oaiharvest/lib/oai_harvest_admin.py:941 #: modules/websubmit/lib/websubmit_templates.py:507 msgid "Previous page" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1115 msgid "Record OAI IDs ( as recognized by the data source )" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1138 msgid "Harvest" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1158 msgid "Error while submitting harvesting task. Contact administrators." msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1160 msgid "No OAI IDs defined. Provide at least one ID." msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1185 msgid "Compare with original" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1199 #: modules/oaiharvest/lib/oai_harvest_admin.py:1244 msgid "Delete from holding pen" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1217 msgid "Error when retrieving the Holding Pen entry" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1225 msgid "Error when retrieving the record" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1233 msgid "" "Error when formatting the Holding Pen entry. Probably its content is broken" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1238 msgid "Accept Holding Pen version" msgstr "" #: modules/oaiharvest/lib/oai_harvest_admin.py:1258 msgid "Record deleted from the holding pen" msgstr "" #: modules/oaiharvest/lib/oai_harvest_templates.py:64 #: modules/oaiharvest/lib/oai_harvest_templates.py:81 msgid "See Guide" msgstr "راهنما را ببینید" #: modules/oaiharvest/lib/oai_harvest_templates.py:92 msgid "OAI sources currently present in the database" msgstr "" #: modules/oaiharvest/lib/oai_harvest_templates.py:93 msgid "No OAI sources currently present in the database" msgstr "" #: modules/oaiharvest/lib/oai_harvest_templates.py:103 msgid "Next oaiharvest task" msgstr "" #: modules/oaiharvest/lib/oai_harvest_templates.py:104 msgid "scheduled time:" msgstr "" #: modules/oaiharvest/lib/oai_harvest_templates.py:105 msgid "current status:" msgstr "وضعیت جاری:" #: modules/oaiharvest/lib/oai_harvest_templates.py:106 msgid "No oaiharvest task currently scheduled." msgstr "" #: modules/oaiharvest/lib/oai_harvest_templates.py:305 msgid "successfully validated" msgstr "" #: modules/oaiharvest/lib/oai_harvest_templates.py:306 msgid "does not seem to be a OAI-compliant baseURL" msgstr "" #: modules/oaiharvest/lib/oai_harvest_templates.py:435 msgid "View next entries..." msgstr "مشاهده مدخل های بعدی ..." #: modules/oaiharvest/lib/oai_harvest_templates.py:492 msgid "previous month" msgstr "ماه پیشین" #: modules/oaiharvest/lib/oai_harvest_templates.py:499 msgid "next month" msgstr "ماه آینده" #: modules/oaiharvest/lib/oai_harvest_templates.py:594 msgid "main Page" msgstr "صفحه اصلی" #: modules/oairepository/lib/oai_repository_admin.py:150 #: modules/oairepository/lib/oai_repository_admin.py:246 #: modules/oairepository/lib/oai_repository_admin.py:319 #: modules/oairepository/lib/oai_repository_admin.py:369 msgid "Return to main selection" msgstr "" #: modules/oairepository/lib/oai_repository_admin.py:352 #, python-format msgid "" "Do you want to touch the OAI set %s? Note that this will force all clients " "to re-harvest the whole set." msgstr "" #: modules/oairepository/lib/oai_repository_admin.py:360 #, fuzzy msgid "OAI set does not exist." msgstr "این پیام وجود ندارد." #: modules/oairepository/lib/oai_repository_admin.py:363 #, python-format msgid "OAI set %s touched." msgstr "" #: modules/webaccess/lib/access_control_config.py:653 #: modules/websession/lib/websession_templates.py:1262 #: modules/websession/lib/webuser.py:1020 #: modules/websession/lib/webuser.py:1029 #: modules/websession/lib/webuser.py:1030 msgid "Run Record Editor" msgstr "" #: modules/webaccess/lib/access_control_config.py:654 #: modules/websession/lib/websession_templates.py:1264 msgid "Run Multi-Record Editor" msgstr "" #: modules/webaccess/lib/access_control_config.py:655 #: modules/websession/lib/websession_templates.py:1300 #: modules/websession/lib/webuser.py:1021 #: modules/websession/lib/webuser.py:1031 #: modules/websession/lib/webuser.py:1032 msgid "Run Document File Manager" msgstr "" #: modules/webaccess/lib/access_control_config.py:656 #: modules/websession/lib/websession_templates.py:1270 msgid "Run Record Merger" msgstr "" #: modules/webaccess/lib/access_control_config.py:657 msgid "Run BibSword client" msgstr "" #: modules/webaccess/lib/access_control_config.py:658 #: modules/websession/lib/websession_templates.py:1278 msgid "Configure BibKnowledge" msgstr "" #: modules/webaccess/lib/access_control_config.py:659 #: modules/websession/lib/websession_templates.py:1276 msgid "Configure BibFormat" msgstr "" #: modules/webaccess/lib/access_control_config.py:660 #: modules/websession/lib/websession_templates.py:1280 msgid "Configure OAI Harvest" msgstr "" #: modules/webaccess/lib/access_control_config.py:661 #: modules/websession/lib/websession_templates.py:1282 msgid "Configure OAI Repository" msgstr "" #: modules/webaccess/lib/access_control_config.py:662 #: modules/websession/lib/websession_templates.py:1284 msgid "Configure BibIndex" msgstr "" #: modules/webaccess/lib/access_control_config.py:663 #: modules/websession/lib/websession_templates.py:1286 msgid "Configure BibRank" msgstr "" #: modules/webaccess/lib/access_control_config.py:664 #: modules/websession/lib/websession_templates.py:1288 msgid "Configure WebAccess" msgstr "" #: modules/webaccess/lib/access_control_config.py:665 #: modules/websession/lib/websession_templates.py:1290 msgid "Configure WebComment" msgstr "" #: modules/webaccess/lib/access_control_config.py:666 #: modules/websession/lib/websession_templates.py:1292 msgid "Configure WebLinkback" msgstr "" #: modules/webaccess/lib/access_control_config.py:667 #: modules/websession/lib/websession_templates.py:1296 msgid "Configure WebSearch" msgstr "" #: modules/webaccess/lib/access_control_config.py:668 #: modules/websession/lib/websession_templates.py:1298 msgid "Configure WebSubmit" msgstr "" #: modules/webaccess/lib/access_control_config.py:669 #: modules/websession/lib/websession_templates.py:1294 msgid "Configure WebJournal" msgstr "" #: modules/webaccess/lib/access_control_config.py:670 #: modules/websession/lib/websession_templates.py:1302 msgid "Configure BibSort" msgstr "" #: modules/webaccess/lib/access_control_config.py:671 #: modules/websession/lib/websession_templates.py:1268 msgid "Run BibCirculation" msgstr "" #: modules/webaccess/lib/access_control_config.py:672 #: modules/websession/lib/websession_templates.py:1274 msgid "Run Batch Uploader" msgstr "" #: modules/webaccess/lib/access_control_config.py:673 #: modules/websession/lib/websession_templates.py:1304 msgid "Run Info Space Manager" msgstr "" #: modules/webaccess/lib/access_control_config.py:674 msgid "Run Person/Author Manager" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3746 #, python-format msgid "Your account on '%s' has been activated" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3747 #, python-format msgid "Your account earlier created on '%s' has been activated:" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3749 #: modules/webaccess/lib/webaccessadmin_lib.py:3762 #: modules/webaccess/lib/webaccessadmin_lib.py:3788 msgid "Username/Email:" msgstr "نام کاربری/ پست الکترونیکی:" #: modules/webaccess/lib/webaccessadmin_lib.py:3750 #: modules/webaccess/lib/webaccessadmin_lib.py:3763 msgid "Password:" msgstr "کلمه عبور:" #: modules/webaccess/lib/webaccessadmin_lib.py:3760 #, python-format msgid "Account created on '%s'" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3761 #, python-format msgid "An account has been created for you on '%s':" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3773 #, python-format msgid "Account rejected on '%s'" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3774 #, python-format msgid "Your request for an account has been rejected on '%s':" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3776 #, python-format msgid "Username/Email: %s" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3786 #, python-format msgid "Account deleted on '%s'" msgstr "" #: modules/webaccess/lib/webaccessadmin_lib.py:3787 #, python-format msgid "Your account on '%s' has been deleted:" msgstr "" #: modules/webalert/lib/htmlparser.py:186 #: modules/webbasket/lib/webbasket_templates.py:736 #: modules/webbasket/lib/webbasket_templates.py:844 #: modules/webbasket/lib/webbasket_templates.py:950 #: modules/webbasket/lib/webbasket_templates.py:1053 #: modules/webbasket/lib/webbasket_templates.py:2748 #: modules/webbasket/lib/webbasket_templates.py:3617 #: modules/websearch/lib/websearch_templates.py:1805 #: modules/websearch/lib/websearch_templates.py:3598 #: modules/websearch/lib/websearch_templates.py:3604 #: modules/websearch/lib/websearch_templates.py:3609 #: modules/websearch/lib/websearch_templates.py:5069 msgid "Detailed record" msgstr "" #: modules/webalert/lib/htmlparser.py:187 #: modules/websearch/lib/websearch_templates.py:1808 #: modules/websearch/lib/websearch_templates.py:3616 #: modules/webstyle/lib/webstyle_templates.py:930 msgid "Similar records" msgstr "رکوردهای مشابه" #: modules/webalert/lib/htmlparser.py:188 msgid "Cited by" msgstr "استناد داده شده به وسیله" #: modules/webalert/lib/webalert.py:54 #, python-format msgid "You already have an alert named %s." msgstr "" #: modules/webalert/lib/webalert.py:111 msgid "unknown" msgstr "نامشخص" #: modules/webalert/lib/webalert.py:163 modules/webalert/lib/webalert.py:217 #: modules/webalert/lib/webalert.py:303 modules/webalert/lib/webalert.py:341 msgid "You do not have rights for this operation." msgstr "" #: modules/webalert/lib/webalert.py:198 msgid "You already have an alert defined for the specified query and basket." msgstr "" #: modules/webalert/lib/webalert.py:221 modules/webalert/lib/webalert.py:345 msgid "The alert name cannot be empty." msgstr "نام هشدار نمی تواند خالی باشد." #: modules/webalert/lib/webalert.py:226 msgid "You are not the owner of this basket." msgstr "شما مالک این سبد نیستید." #: modules/webalert/lib/webalert.py:237 #, python-format msgid "The alert %s has been added to your profile." msgstr "" #: modules/webalert/lib/webalert.py:376 #, python-format msgid "The alert %s has been successfully updated." msgstr "" #: modules/webalert/lib/webalert.py:428 #, python-format msgid "" "You have made %(x_nb)s queries. A %(x_url_open)sdetailed list%(x_url_close)s " "is available with a possibility to (a) view search results and (b) subscribe " "to an automatic email alerting service for these queries." msgstr "" #: modules/webalert/lib/webalert_templates.py:75 msgid "Pattern" msgstr "" #: modules/webalert/lib/webalert_templates.py:79 msgid "Pattern 1" msgstr "" #: modules/webalert/lib/webalert_templates.py:81 msgid "Field 1" msgstr "" #: modules/webalert/lib/webalert_templates.py:83 msgid "Pattern 2" msgstr "" #: modules/webalert/lib/webalert_templates.py:85 msgid "Field 2" msgstr "" #: modules/webalert/lib/webalert_templates.py:87 msgid "Pattern 3" msgstr "" #: modules/webalert/lib/webalert_templates.py:89 msgid "Field 3" msgstr "" #: modules/webalert/lib/webalert_templates.py:91 msgid "Collections" msgstr "مجموعه ها" #: modules/webalert/lib/webalert_templates.py:114 msgid "You own the following alerts:" msgstr "" #: modules/webalert/lib/webalert_templates.py:115 msgid "alert name" msgstr "" #: modules/webalert/lib/webalert_templates.py:123 msgid "SHOW" msgstr "" #: modules/webalert/lib/webalert_templates.py:172 msgid "" "This alert will notify you each time/only if a new item satisfies the " "following query:" msgstr "" #: modules/webalert/lib/webalert_templates.py:173 msgid "QUERY" msgstr "" #: modules/webalert/lib/webalert_templates.py:211 msgid "Alert identification name:" msgstr "" #: modules/webalert/lib/webalert_templates.py:213 msgid "Search-checking frequency:" msgstr "" #: modules/webalert/lib/webalert_templates.py:220 msgid "Send notification email?" msgstr "ارسال ایمیل اطلاع دهی؟" #: modules/webalert/lib/webalert_templates.py:223 #: modules/webalert/lib/webalert_templates.py:340 msgid "yes" msgstr "بله" #: modules/webalert/lib/webalert_templates.py:224 #: modules/webalert/lib/webalert_templates.py:342 msgid "no" msgstr "نه" #: modules/webalert/lib/webalert_templates.py:225 #, python-format msgid "if %(x_fmt_open)sno%(x_fmt_close)s you must specify a basket" msgstr "" #: modules/webalert/lib/webalert_templates.py:227 msgid "Store results in basket?" msgstr "ذخیره نتایج در سبد؟" #: modules/webalert/lib/webalert_templates.py:248 msgid "SET ALERT" msgstr "تنظیم هشدار" #: modules/webalert/lib/webalert_templates.py:249 msgid "CLEAR DATA" msgstr "" #: modules/webalert/lib/webalert_templates.py:300 #, python-format msgid "" "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." msgstr "" #: modules/webalert/lib/webalert_templates.py:320 msgid "Search checking frequency" msgstr "" #: modules/webalert/lib/webalert_templates.py:321 msgid "Notification by email" msgstr "اطلاع دهی به وسیله پست الکترونیکی" #: modules/webalert/lib/webalert_templates.py:322 msgid "Result in basket" msgstr "نتیجه در سبد" #: modules/webalert/lib/webalert_templates.py:323 msgid "Date last run" msgstr "" #: modules/webalert/lib/webalert_templates.py:324 msgid "Creation date" msgstr "تاریخ ایجاد" #: modules/webalert/lib/webalert_templates.py:368 #: modules/webbasket/lib/webbasket_templates.py:2130 msgid "no basket" msgstr "بدون سبد" #: modules/webalert/lib/webalert_templates.py:385 #: modules/websearch/lib/services/SubmissionNameSearchService.py:132 msgid "Modify" msgstr "اصلاح کردن" #: modules/webalert/lib/webalert_templates.py:391 #: modules/webjournal/lib/webjournaladminlib.py:233 #: modules/webjournal/lib/webjournaladminlib.py:239 msgid "Remove" msgstr "حذف کردن" #: modules/webalert/lib/webalert_templates.py:393 #: modules/webalert/lib/webalert_templates.py:483 msgid "Execute search" msgstr "اجرای جستجو" #: modules/webalert/lib/webalert_templates.py:399 #, python-format msgid "You have defined %s alerts." msgstr "" #: modules/webalert/lib/webalert_templates.py:437 #, python-format msgid "" "You have not executed any search yet. Please go to the %(x_url_open)ssearch " "interface%(x_url_close)s first." msgstr "" #: modules/webalert/lib/webalert_templates.py:446 #, python-format msgid "" "You have performed %(x_nb1)s searches (%(x_nb2)s different questions) during " "the last 30 days or so." msgstr "" #: modules/webalert/lib/webalert_templates.py:451 #, python-format msgid "Here are the %s most popular searches." msgstr "" #: modules/webalert/lib/webalert_templates.py:462 msgid "Question" msgstr "سؤال" #: modules/webalert/lib/webalert_templates.py:466 msgid "Last Run" msgstr "" #: modules/webalert/lib/webalert_templates.py:484 msgid "Set new alert" msgstr "تنظیم هشدار جدید" #: modules/webalert/lib/webalert_webinterface.py:76 #: modules/webalert/lib/webalert_webinterface.py:139 #: modules/webalert/lib/webalert_webinterface.py:224 #: modules/webalert/lib/webalert_webinterface.py:302 #: modules/webalert/lib/webalert_webinterface.py:358 #: modules/webalert/lib/webalert_webinterface.py:435 #: modules/webalert/lib/webalert_webinterface.py:509 msgid "You are not authorized to use alerts." msgstr "" #: modules/webalert/lib/webalert_webinterface.py:79 msgid "Popular Searches" msgstr "جستجوی های رایج" #: modules/webalert/lib/webalert_webinterface.py:81 #: modules/websession/lib/websession_templates.py:605 #: modules/websession/lib/websession_templates.py:779 msgid "Your Searches" msgstr "جستجوهای شما" #: modules/webalert/lib/webalert_webinterface.py:98 #: modules/webalert/lib/webalert_webinterface.py:150 #: modules/webalert/lib/webalert_webinterface.py:183 #: modules/webalert/lib/webalert_webinterface.py:235 #: modules/webalert/lib/webalert_webinterface.py:268 #: modules/webalert/lib/webalert_webinterface.py:319 #: modules/webalert/lib/webalert_webinterface.py:369 #: modules/webalert/lib/webalert_webinterface.py:395 #: modules/webalert/lib/webalert_webinterface.py:446 #: modules/webalert/lib/webalert_webinterface.py:472 #: modules/webalert/lib/webalert_webinterface.py:520 #: modules/webalert/lib/webalert_webinterface.py:548 #: modules/webbasket/lib/webbasket.py:2519 #: modules/webbasket/lib/webbasket_webinterface.py:801 #: modules/webbasket/lib/webbasket_webinterface.py:903 #: modules/webbasket/lib/webbasket_webinterface.py:1027 #: modules/webbasket/lib/webbasket_webinterface.py:1117 #: modules/webbasket/lib/webbasket_webinterface.py:1261 #: modules/webcomment/lib/webcomment_webinterface.py:918 #: modules/webmessage/lib/webmessage_templates.py:466 #: modules/websession/lib/websession_templates.py:760 #: modules/websession/lib/websession_templates.py:2517 #: modules/websession/lib/websession_webinterface.py:229 #: modules/websession/lib/websession_webinterface.py:252 #: modules/websession/lib/websession_webinterface.py:339 #: modules/websession/lib/websession_webinterface.py:597 #: modules/websession/lib/websession_webinterface.py:620 #: modules/websession/lib/websession_webinterface.py:648 #: modules/websession/lib/websession_webinterface.py:664 #: modules/websession/lib/websession_webinterface.py:716 #: modules/websession/lib/websession_webinterface.py:739 #: modules/websession/lib/websession_webinterface.py:765 #: modules/websession/lib/websession_webinterface.py:838 #: modules/websession/lib/websession_webinterface.py:915 #: modules/websession/lib/websession_webinterface.py:961 #: modules/websession/lib/websession_webinterface.py:992 #: modules/websession/lib/websession_webinterface.py:1064 #: modules/websession/lib/websession_webinterface.py:1142 #: modules/websession/lib/websession_webinterface.py:1183 #: modules/websession/lib/websession_webinterface.py:1259 #: modules/websession/lib/websession_webinterface.py:1297 #: modules/websession/lib/websession_webinterface.py:1340 #: modules/websession/lib/websession_webinterface.py:1378 #: modules/websession/lib/websession_webinterface.py:1412 #: modules/websession/lib/websession_webinterface.py:1464 #: modules/websubmit/web/publiline.py:136 #: modules/websubmit/web/publiline.py:157 #: modules/websubmit/web/yourapprovals.py:91 #: modules/websubmit/web/yoursubmissions.py:157 msgid "Your Account" msgstr "حساب شما" #: modules/webalert/lib/webalert_webinterface.py:100 #, python-format msgid "%s Personalize, Display searches" msgstr "" #: modules/webalert/lib/webalert_webinterface.py:101 #: modules/webalert/lib/webalert_webinterface.py:153 #: modules/webalert/lib/webalert_webinterface.py:186 #: modules/webalert/lib/webalert_webinterface.py:238 #: modules/webalert/lib/webalert_webinterface.py:271 #: modules/webalert/lib/webalert_webinterface.py:322 #: modules/webalert/lib/webalert_webinterface.py:372 #: modules/webalert/lib/webalert_webinterface.py:398 #: modules/webalert/lib/webalert_webinterface.py:449 #: modules/webalert/lib/webalert_webinterface.py:475 #: modules/webalert/lib/webalert_webinterface.py:523 #: modules/webalert/lib/webalert_webinterface.py:551 #: modules/webcomment/lib/webcomment_webinterface.py:921 #: modules/websession/lib/websession_webinterface.py:232 #: modules/websession/lib/websession_webinterface.py:255 #: modules/websession/lib/websession_webinterface.py:341 #: modules/websession/lib/websession_webinterface.py:599 #: modules/websession/lib/websession_webinterface.py:622 #: modules/websession/lib/websession_webinterface.py:651 #: modules/websession/lib/websession_webinterface.py:667 #: modules/websession/lib/websession_webinterface.py:685 #: modules/websession/lib/websession_webinterface.py:695 #: modules/websession/lib/websession_webinterface.py:718 #: modules/websession/lib/websession_webinterface.py:741 #: modules/websession/lib/websession_webinterface.py:767 #, python-format msgid "%s, personalize" msgstr "" #: modules/webalert/lib/webalert_webinterface.py:152 #: modules/webalert/lib/webalert_webinterface.py:185 #: modules/webalert/lib/webalert_webinterface.py:237 #: modules/webalert/lib/webalert_webinterface.py:371 #: modules/webalert/lib/webalert_webinterface.py:448 #: modules/webalert/lib/webalert_webinterface.py:522 #, python-format msgid "%s Personalize, Set a new alert" msgstr "" #: modules/webalert/lib/webalert_webinterface.py:178 msgid "Set a new alert" msgstr "تنظیم یک هشدار جدید" #: modules/webalert/lib/webalert_webinterface.py:263 msgid "Modify alert settings" msgstr "اصلاح تنظیمات هشدار" #: modules/webalert/lib/webalert_webinterface.py:270 #, python-format msgid "%s Personalize, Modify alert settings" msgstr "" #: modules/webalert/lib/webalert_webinterface.py:314 #: modules/websession/lib/websession_templates.py:628 msgid "Your Alerts" msgstr "هشدارهای شما" #: modules/webalert/lib/webalert_webinterface.py:321 #: modules/webalert/lib/webalert_webinterface.py:397 #: modules/webalert/lib/webalert_webinterface.py:474 #: modules/webalert/lib/webalert_webinterface.py:550 #, python-format msgid "%s Personalize, Display alerts" msgstr "" #: modules/webalert/lib/webalert_webinterface.py:390 #: modules/webalert/lib/webalert_webinterface.py:467 #: modules/webalert/lib/webalert_webinterface.py:543 msgid "Display alerts" msgstr "هشدارهای نمایش" #: modules/webauthorlist/lib/authorlist_webinterface.py:86 #: modules/webauthorlist/lib/authorlist_webinterface.py:111 msgid "Author List Manager" msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:182 #: modules/websearch/lib/websearch_templates.py:4577 msgid "Name variants" msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:228 #: modules/websearch/lib/websearch_templates.py:4588 msgid "No Name Variants" msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:271 #: modules/websearch/lib/websearch_templates.py:4600 msgid "downloaded" msgstr "دانلود شده" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:272 #: modules/websearch/lib/websearch_templates.py:4601 msgid "times" msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:321 #: modules/websearch/lib/websearch_templates.py:4596 msgid "Papers" msgstr "مقاله ها" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:350 #: modules/websearch/lib/websearch_templates.py:4667 msgid "No Keywords" msgstr "بدون کلیدواژه ها" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:356 #: modules/websearch/lib/websearch_templates.py:4670 msgid "Frequent keywords" msgstr "کلیدواژه های تکراری" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:385 msgid "No Subject categories" msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:392 msgid "Subject categories" msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:447 #, fuzzy msgid "No Collaborations" msgstr "مجموعه ها" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:453 #, fuzzy msgid "Collaborations" msgstr "مجموعه ها" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:477 #: modules/websearch/lib/websearch_templates.py:4642 msgid "unknown affiliation" msgstr "وابستگی نامشخص" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:489 #: modules/websearch/lib/websearch_templates.py:4649 msgid "No Affiliations" msgstr "بدون وابستگی ها" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:496 #: modules/websearch/lib/websearch_templates.py:4651 msgid "Affiliations" msgstr "وابستگی ها" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:502 msgid "Frequent co-authors (excluding collaborations)" msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:532 #: modules/websearch/lib/websearch_templates.py:4686 msgid "No Frequent Co-authors" msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:546 #, fuzzy, python-format msgid "Citations%s" msgstr "استنادها" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:572 msgid " Publications per year " msgstr "" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:573 #, fuzzy msgid "No Publication Graph" msgstr "سیاهه انتشار" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:601 #, fuzzy msgid " Publications list " msgstr "سیاهه انتشار" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:635 #, fuzzy msgid "No publications available" msgstr "هیچ اطلاعاتی قابل استفاده نیست" #: modules/webauthorprofile/lib/webauthorprofile_templates.py:758 msgid "Recompute Now!" msgstr "" #: modules/webbasket/lib/webbasket.py:106 #: modules/webbasket/lib/webbasket.py:2317 #: modules/webbasket/lib/webbasket.py:2347 msgid "" "The selected public basket does not exist or you do not have access to it." msgstr "سبد عمومی انتخاب شده وجود ندارد یا اینکه شما دسترسی به آن را ندارید." #: modules/webbasket/lib/webbasket.py:114 msgid "Please select a valid public basket from the list of public baskets." msgstr "لطفا یک سبد عمومی درست از سیاهه سبدهای عمومی انتخاب کنید. " #: modules/webbasket/lib/webbasket.py:137 #: modules/webbasket/lib/webbasket.py:300 #: modules/webbasket/lib/webbasket.py:1003 msgid "The selected item does not exist or you do not have access to it." msgstr "آیتم انتخاب شده وجود ندارد یا اینکه شما دسترسی به آن را ندارید." #: modules/webbasket/lib/webbasket.py:143 msgid "Returning to the public basket view." msgstr "بازگشت به نمای سبد عمومی." #: modules/webbasket/lib/webbasket.py:418 #: modules/webbasket/lib/webbasket.py:477 #: modules/webbasket/lib/webbasket.py:1550 #: modules/webbasket/lib/webbasket.py:1615 msgid "You do not have permission to write notes to this item." msgstr "شما اجازه نوشتن یادداشت برای این آیتم را ندارید." #: modules/webbasket/lib/webbasket.py:431 #: modules/webbasket/lib/webbasket.py:1562 msgid "" "The note you are quoting does not exist or you do not have access to it." msgstr "" #: modules/webbasket/lib/webbasket.py:487 #: modules/webbasket/lib/webbasket.py:1627 msgid "You must fill in both the subject and the body of the note." msgstr "" #: modules/webbasket/lib/webbasket.py:590 #: modules/webbasket/lib/webbasket.py:655 #: modules/webbasket/lib/webbasket.py:694 #: modules/webbasket/lib/webbasket.py:2850 msgid "The selected topic does not exist or you do not have access to it." msgstr "" #: modules/webbasket/lib/webbasket.py:622 #: modules/webbasket/lib/webbasket.py:724 #: modules/webbasket/lib/webbasket.py:2877 #: modules/webbasket/lib/webbasket.py:2885 #: modules/webbasket/lib/webbasket.py:2892 msgid "The selected basket does not exist or you do not have access to it." msgstr "" #: modules/webbasket/lib/webbasket.py:715 msgid "The selected basket is no longer public." msgstr "" #: modules/webbasket/lib/webbasket.py:1680 msgid "You do not have permission to delete this note." msgstr "شما مجوز حذف این یدداشت را ندارید." #: modules/webbasket/lib/webbasket.py:1691 msgid "" "The note you are deleting does not exist or you do not have access to it." msgstr "" #: modules/webbasket/lib/webbasket.py:1793 #, python-format msgid "Sorry, you do not have sufficient rights to add record #%i." msgstr "" #: modules/webbasket/lib/webbasket.py:1799 msgid "Some of the items were not added due to lack of sufficient rights." msgstr "" #: modules/webbasket/lib/webbasket.py:1816 msgid "Please provide a title for the external source." msgstr "" #: modules/webbasket/lib/webbasket.py:1822 msgid "Please provide a description for the external source." msgstr "" #: modules/webbasket/lib/webbasket.py:1828 msgid "Please provide a url for the external source." msgstr "" #: modules/webbasket/lib/webbasket.py:1837 msgid "The url you have provided is not valid." msgstr "" #: modules/webbasket/lib/webbasket.py:1844 msgid "" "The url you have provided is not valid: The request contains bad syntax or " "cannot be fulfilled." msgstr "" #: modules/webbasket/lib/webbasket.py:1851 msgid "" "The url you have provided is not valid: The server failed to fulfil an " "apparently valid request." msgstr "" #: modules/webbasket/lib/webbasket.py:1900 #: modules/webbasket/lib/webbasket.py:1912 #: modules/webbasket/lib/webbasket.py:2044 #: modules/webbasket/lib/webbasket.py:2113 msgid "Sorry, you do not have sufficient rights on this basket." msgstr "" #: modules/webbasket/lib/webbasket.py:1922 msgid "No records to add." msgstr "بدون رکوردی برای افزودن" #: modules/webbasket/lib/webbasket.py:1969 msgid "" "Some items could not be moved. The destination basket already contains those " "items." msgstr "" #: modules/webbasket/lib/webbasket.py:1971 #: modules/webbasket/lib/webbasket.py:2822 msgid "Cannot add items to the selected basket. Invalid parameters." msgstr "" #: modules/webbasket/lib/webbasket.py:1981 #, fuzzy msgid "Untitled basket" msgstr "ویرایش سبد" #: modules/webbasket/lib/webbasket.py:1981 msgid "Untitled topic" msgstr "" #: modules/webbasket/lib/webbasket.py:1983 msgid "" "A default topic and basket have been automatically created. Edit them to " "rename them as you see fit." msgstr "" #: modules/webbasket/lib/webbasket.py:2264 msgid "Please provide a name for the new basket." msgstr "لطفا یک نام برای سبد جدید قرار دهید." #: modules/webbasket/lib/webbasket.py:2270 msgid "Please select an existing topic or create a new one." msgstr "" #: modules/webbasket/lib/webbasket.py:2309 msgid "" "You cannot subscribe to this basket, you are the either owner or you have " "already subscribed." msgstr "" #: modules/webbasket/lib/webbasket.py:2339 msgid "" "You cannot unsubscribe from this basket, you are the either owner or you " "have already unsubscribed." msgstr "" #: modules/webbasket/lib/webbasket.py:2431 #: modules/webbasket/lib/webbasket.py:2549 #: modules/webbasket/lib/webbasket_templates.py:106 #: modules/webbasket/lib/webbasket_templates.py:156 #: modules/webbasket/lib/webbasket_templates.py:162 #: modules/webbasket/lib/webbasket_templates.py:639 #: modules/webbasket/lib/webbasket_templates.py:696 msgid "Personal baskets" msgstr "سبدهای شخصی" #: modules/webbasket/lib/webbasket.py:2457 #: modules/webbasket/lib/webbasket.py:2566 #: modules/webbasket/lib/webbasket_templates.py:172 #: modules/webbasket/lib/webbasket_templates.py:204 #: modules/webbasket/lib/webbasket_templates.py:210 #: modules/webbasket/lib/webbasket_templates.py:649 #: modules/webbasket/lib/webbasket_templates.py:802 msgid "Group baskets" msgstr "سبدهای گروهی" #: modules/webbasket/lib/webbasket.py:2485 msgid "Others' baskets" msgstr "سبدهای دیگر" #: modules/webbasket/lib/webbasket.py:2522 #: modules/websession/lib/websession_templates.py:613 #: modules/websession/lib/websession_templates.py:768 msgid "Your Baskets" msgstr "سبدهای شما" #: modules/webbasket/lib/webbasket.py:2527 #: modules/webbasket/lib/webbasket_webinterface.py:1294 #: modules/webbasket/lib/webbasket_webinterface.py:1379 #: modules/webbasket/lib/webbasket_webinterface.py:1421 #: modules/webbasket/lib/webbasket_webinterface.py:1480 msgid "List of public baskets" msgstr "سیاهه سبدهای عمومی" #: modules/webbasket/lib/webbasket.py:2538 #: modules/webbasket/lib/webbasket_webinterface.py:425 msgid "Search baskets" msgstr "جستجوی سبدها" #: modules/webbasket/lib/webbasket.py:2543 #: modules/webbasket/lib/webbasket_webinterface.py:737 #: modules/websearch/lib/websearch_templates.py:3041 #: modules/websearch/lib/websearch_templates.py:3254 msgid "Add to basket" msgstr "افزودن به سبد" #: modules/webbasket/lib/webbasket.py:2583 #: modules/webbasket/lib/webbasket_templates.py:223 #: modules/webbasket/lib/webbasket_templates.py:229 #: modules/webbasket/lib/webbasket_templates.py:659 #: modules/webbasket/lib/webbasket_templates.py:911 msgid "Public baskets" msgstr "سبدهای عمومی" #: modules/webbasket/lib/webbasket.py:2613 #, python-format msgid "" "You have %(x_nb_perso)s personal baskets and are subscribed to " "%(x_nb_group)s group baskets and %(x_nb_public)s other users public baskets." msgstr "" #: modules/webbasket/lib/webbasket.py:2799 #: modules/webbasket/lib/webbasket.py:2837 msgid "" "The category you have selected does not exist. Please select a valid " "category." msgstr "" #: modules/webbasket/lib/webbasket.py:2863 msgid "The selected group does not exist or you do not have access to it." msgstr "" #: modules/webbasket/lib/webbasket.py:2916 msgid "The selected output format is not available or is invalid." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:92 msgid "" "You have no personal or group baskets or are subscribed to any public " "baskets." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:93 #, python-format msgid "" "You may want to start by %(x_url_open)screating a new basket%(x_url_close)s." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:117 #: modules/webbasket/lib/webbasket_templates.py:183 msgid "Back to Your Baskets" msgstr "بازگشت به سبدهای شما" #: modules/webbasket/lib/webbasket_templates.py:123 #: modules/webbasket/lib/webbasket_templates.py:364 #: modules/webbasket/lib/webbasket_webinterface.py:1264 msgid "Create basket" msgstr "ایجاد سبد" #: modules/webbasket/lib/webbasket_templates.py:129 #: modules/webbasket/lib/webbasket_webinterface.py:1139 msgid "Edit topic" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:586 msgid "Search baskets for" msgstr "جستجوی سبدها برای" #: modules/webbasket/lib/webbasket_templates.py:587 msgid "Search also in notes (where allowed)" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:593 #, python-format msgid "%(x_search_for_term)s in %(x_collection_list)s" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:630 msgid "Results overview" msgstr "نمای کلی نتایج" #: modules/webbasket/lib/webbasket_templates.py:631 #: modules/webbasket/lib/webbasket_templates.py:698 #: modules/webbasket/lib/webbasket_templates.py:804 #: modules/webbasket/lib/webbasket_templates.py:913 #: modules/webbasket/lib/webbasket_templates.py:1017 #, python-format msgid "%i matching items" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:669 #: modules/webbasket/lib/webbasket_templates.py:1015 msgid "All public baskets" msgstr "همه سبدهای عمومی" #: modules/webbasket/lib/webbasket_templates.py:685 msgid "No items found." msgstr "هیچ آیتمی یافت نشد." #: modules/webbasket/lib/webbasket_templates.py:715 #: modules/webbasket/lib/webbasket_templates.py:821 #: modules/webbasket/lib/webbasket_templates.py:927 #: modules/webbasket/lib/webbasket_templates.py:1030 #, python-format msgid "In %(x_linked_basket_name)s" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:747 #: modules/webbasket/lib/webbasket_templates.py:771 #: modules/webbasket/lib/webbasket_templates.py:856 #: modules/webbasket/lib/webbasket_templates.py:882 #: modules/webbasket/lib/webbasket_templates.py:961 #: modules/webbasket/lib/webbasket_templates.py:986 #: modules/webbasket/lib/webbasket_templates.py:1064 #: modules/webbasket/lib/webbasket_templates.py:1089 #: modules/webbasket/lib/webbasket_templates.py:2741 #: modules/webbasket/lib/webbasket_templates.py:3610 msgid "Add a note..." msgstr "افزودن یک یادداشت..." #: modules/webbasket/lib/webbasket_templates.py:1196 #: modules/webbasket/lib/webbasket_webinterface.py:1312 #: modules/webbasket/lib/webbasket_webinterface.py:1436 #: modules/webbasket/lib/webbasket_webinterface.py:1495 msgid "Public basket" msgstr "سبد عمومی" #: modules/webbasket/lib/webbasket_templates.py:1197 msgid "Owner" msgstr "مالک" #: modules/webbasket/lib/webbasket_templates.py:1198 msgid "Last update" msgstr "آخرین به روز رسانی" #: modules/webbasket/lib/webbasket_templates.py:1200 msgid "Views" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1282 msgid "There is currently no publicly accessible basket" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1304 #, python-format msgid "" "Displaying public baskets %(x_from)i - %(x_to)i out of " "%(x_total_public_basket)i public baskets in total." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1371 #: modules/webbasket/lib/webbasket_templates.py:1395 #, python-format msgid "%(x_title)s, by %(x_name)s on %(x_date)s:" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1374 #: modules/webbasket/lib/webbasket_templates.py:1398 #: modules/webcomment/lib/webcomment.py:1622 #, python-format msgid "%(x_name)s wrote on %(x_date)s:" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1454 msgid "Select topic" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1470 #: modules/webbasket/lib/webbasket_templates.py:1885 #: modules/webbasket/lib/webbasket_templates.py:1894 msgid "Choose topic" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1472 #: modules/webbasket/lib/webbasket_templates.py:1896 msgid "or create a new one" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1472 msgid "Create new topic" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1474 #: modules/webbasket/lib/webbasket_templates.py:1882 msgid "Basket name" msgstr "نام سبد" #: modules/webbasket/lib/webbasket_templates.py:1477 msgid "Create a new basket" msgstr "ایجاد یک سبد جدید" #: modules/webbasket/lib/webbasket_templates.py:1538 msgid "Create new basket" msgstr "ایجاد سبد جدید" #: modules/webbasket/lib/webbasket_templates.py:1608 #: modules/webbasket/lib/webbasket_templates.py:2670 #: modules/webbasket/lib/webbasket_templates.py:3049 #: modules/webbasket/lib/webbasket_templates.py:3550 #: modules/webbasket/lib/webbasket_templates.py:3866 msgid "External item" msgstr "آیتم خروجی" #: modules/webbasket/lib/webbasket_templates.py:1609 msgid "" "Provide a url for the external item you wish to add and fill in a title and " "description" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1614 msgid "URL" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1645 #, python-format msgid "%i items have been successfully added to your basket" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1646 #, python-format msgid "Proceed to the %(x_url_open)sbasket%(x_url_close)s" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1651 #, python-format msgid " or return to your %(x_url_open)sprevious basket%(x_url_close)s" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1655 #, python-format msgid " or go %(x_url_open)sback%(x_url_close)s" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1768 #, python-format msgid "Adding %i items to your baskets" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1769 #, python-format msgid "" "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" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1787 msgid "Optionally, add a note to each one of these items" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1788 msgid "Optionally, add a note to this item" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1794 msgid "Add items" msgstr "افزودن آیتم ها" #: modules/webbasket/lib/webbasket_templates.py:1818 msgid "Are you sure you want to delete this basket?" msgstr "آیا برای حذف این سبد مطمئن هستید؟" #: modules/webbasket/lib/webbasket_templates.py:1820 #, python-format msgid "%i users are subscribed to this basket." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1822 #, python-format msgid "%i user groups are subscribed to this basket." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1824 #, python-format msgid "You have set %i alerts on this basket." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1899 #: modules/webbasket/lib/webbasket_templates.py:1995 msgid "General settings" msgstr "تنظیمات کلی" #: modules/webbasket/lib/webbasket_templates.py:1914 #: modules/webbasket/lib/webbasket_templates.py:2089 #: modules/webbasket/lib/webbasket_templates.py:2116 msgid "Add group" msgstr "افزودن گروه" #: modules/webbasket/lib/webbasket_templates.py:1919 msgid "Manage group rights" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1931 msgid "Manage global sharing rights" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1936 #: modules/webbasket/lib/webbasket_templates.py:2002 #: modules/webbasket/lib/webbasket_templates.py:2363 #: modules/webbasket/lib/webbasket_templates.py:2445 msgid "Delete basket" msgstr "حذف سبد" #: modules/webbasket/lib/webbasket_templates.py:1960 #, python-format msgid "Editing basket %(x_basket_name)s" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:1969 #: modules/webbasket/lib/webbasket_templates.py:2024 msgid "Save changes" msgstr "ذخیره تغییرات" #: modules/webbasket/lib/webbasket_templates.py:1990 msgid "Topic name" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2019 #, python-format msgid "Editing topic: %(x_topic_name)s" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2036 #: modules/webbasket/lib/webbasket_templates.py:2051 msgid "No rights" msgstr "بدون حقوق" #: modules/webbasket/lib/webbasket_templates.py:2038 #: modules/webbasket/lib/webbasket_templates.py:2053 msgid "View records" msgstr "دیدن رکوردها" #: modules/webbasket/lib/webbasket_templates.py:2040 #: modules/webbasket/lib/webbasket_templates.py:2042 #: modules/webbasket/lib/webbasket_templates.py:2055 #: modules/webbasket/lib/webbasket_templates.py:2057 #: modules/webbasket/lib/webbasket_templates.py:2059 #: modules/webbasket/lib/webbasket_templates.py:2061 #: modules/webbasket/lib/webbasket_templates.py:2063 #: modules/webbasket/lib/webbasket_templates.py:2065 msgid "and" msgstr "و" #: modules/webbasket/lib/webbasket_templates.py:2040 msgid "view comments" msgstr "دیدن نظرها" #: modules/webbasket/lib/webbasket_templates.py:2042 msgid "add comments" msgstr "افزودن نظرها" #: modules/webbasket/lib/webbasket_templates.py:2055 msgid "view notes" msgstr "دیدن یادداشت ها" #: modules/webbasket/lib/webbasket_templates.py:2057 msgid "add notes" msgstr "افزودن یادداشت ها" #: modules/webbasket/lib/webbasket_templates.py:2059 msgid "add records" msgstr "افزودن رکوردها" #: modules/webbasket/lib/webbasket_templates.py:2061 msgid "delete notes" msgstr "پاک کردن یادداشت ها" #: modules/webbasket/lib/webbasket_templates.py:2063 msgid "remove records" msgstr "حذف رکوردها" #: modules/webbasket/lib/webbasket_templates.py:2065 msgid "manage sharing rights" msgstr "مدیریت حقوق اشتراک" #: modules/webbasket/lib/webbasket_templates.py:2087 msgid "You are not a member of a group." msgstr "شما عضوی از یک گروه نیستید" #: modules/webbasket/lib/webbasket_templates.py:2109 msgid "Sharing basket to a new group" msgstr "اشترات سبد برای یک گروه جدید" #: modules/webbasket/lib/webbasket_templates.py:2138 #: modules/websession/lib/websession_templates.py:662 msgid "" "You are logged in as a guest user, so your baskets will disappear at the end " "of the current session." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2139 #: modules/webbasket/lib/webbasket_templates.py:2154 #: modules/websession/lib/websession_templates.py:665 #, python-format msgid "" "If you wish you can %(x_url_open)slogin or register here%(x_url_close)s." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2153 #: modules/websession/lib/websession_webinterface.py:288 #: modules/websession/lib/websession_webinterface.py:315 msgid "This functionality is forbidden to guest users." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2206 #: modules/webcomment/lib/webcomment_templates.py:1012 msgid "Back to search results" msgstr "بازگشت به نتایج جستجو" #: modules/webbasket/lib/webbasket_templates.py:2337 #: modules/webbasket/lib/webbasket_templates.py:3390 #, python-format msgid "%i items" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2338 #: modules/webbasket/lib/webbasket_templates.py:3392 #, python-format msgid "%i notes" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2338 #: modules/webbasket/lib/webbasket_templates.py:3392 msgid "no notes yet" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2341 #, python-format msgid "%i subscribers" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2343 #: modules/webbasket/lib/webbasket_templates.py:3394 msgid "last update" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2355 #: modules/webbasket/lib/webbasket_templates.py:2437 msgid "Add item" msgstr "افزودن آیتم" #: modules/webbasket/lib/webbasket_templates.py:2360 #: modules/webbasket/lib/webbasket_templates.py:2442 #: modules/webbasket/lib/webbasket_webinterface.py:1049 msgid "Edit basket" msgstr "ویرایش سبد" #: modules/webbasket/lib/webbasket_templates.py:2368 #: modules/webbasket/lib/webbasket_templates.py:2450 #: modules/webbasket/lib/webbasket_templates.py:3402 #: modules/webbasket/lib/webbasket_templates.py:3455 msgid "Unsubscribe from basket" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2455 msgid "This basket is publicly accessible at the following address:" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2517 msgid "This basket does not contain any records yet." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2551 msgid "You do not have sufficient rights to view this basket's content." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2594 msgid "Move item up" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2598 msgid "You cannot move this item up" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2612 msgid "Move item down" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2616 msgid "You cannot move this item down" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2630 #: modules/webbasket/lib/webbasket_templates.py:3546 msgid "Copy item" msgstr "کپی آیتم" #: modules/webbasket/lib/webbasket_templates.py:2648 #, fuzzy msgid "Move item" msgstr "حذف آیتم" #: modules/webbasket/lib/webbasket_templates.py:2664 msgid "Remove item" msgstr "حذف آیتم" #: modules/webbasket/lib/webbasket_templates.py:2738 #: modules/webbasket/lib/webbasket_templates.py:3094 #: modules/webbasket/lib/webbasket_templates.py:3607 #: modules/webbasket/lib/webbasket_templates.py:3907 msgid "This record does not seem to exist any more" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2854 #: modules/webbasket/lib/webbasket_templates.py:3698 #, python-format msgid "Item %(x_item_index)i of %(x_item_total)i" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:2867 #: modules/webbasket/lib/webbasket_templates.py:2870 #: modules/webbasket/lib/webbasket_templates.py:2954 #: modules/webbasket/lib/webbasket_templates.py:2957 #: modules/webbasket/lib/webbasket_templates.py:3708 #: modules/webbasket/lib/webbasket_templates.py:3711 #: modules/webbasket/lib/webbasket_templates.py:3783 #: modules/webbasket/lib/webbasket_templates.py:3786 msgid "Previous item" msgstr "آیتم پیشین" #: modules/webbasket/lib/webbasket_templates.py:2882 #: modules/webbasket/lib/webbasket_templates.py:2885 #: modules/webbasket/lib/webbasket_templates.py:2969 #: modules/webbasket/lib/webbasket_templates.py:2972 #: modules/webbasket/lib/webbasket_templates.py:3720 #: modules/webbasket/lib/webbasket_templates.py:3723 #: modules/webbasket/lib/webbasket_templates.py:3795 #: modules/webbasket/lib/webbasket_templates.py:3798 msgid "Next item" msgstr "آیتم پسین" #: modules/webbasket/lib/webbasket_templates.py:2895 #: modules/webbasket/lib/webbasket_templates.py:2982 #: modules/webbasket/lib/webbasket_templates.py:3730 #: modules/webbasket/lib/webbasket_templates.py:3805 msgid "Return to basket" msgstr "بازگشت به سبد" #: modules/webbasket/lib/webbasket_templates.py:3042 #: modules/webbasket/lib/webbasket_templates.py:3859 msgid "The item you have selected does not exist." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:3070 #: modules/webbasket/lib/webbasket_templates.py:3883 msgid "You do not have sufficient rights to view this item's notes." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:3103 msgid "You do not have sufficient rights to view this item." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:3210 #: modules/webbasket/lib/webbasket_templates.py:3220 #: modules/webbasket/lib/webbasket_templates.py:4013 #: modules/webbasket/lib/webbasket_templates.py:4023 #: modules/webbasket/lib/webbasket_webinterface.py:489 #: modules/webbasket/lib/webbasket_webinterface.py:1556 msgid "Add a note" msgstr "افزودن یک یادداشت" #: modules/webbasket/lib/webbasket_templates.py:3211 #: modules/webbasket/lib/webbasket_templates.py:4014 msgid "Add note" msgstr "افزودن یادداشت" #: modules/webbasket/lib/webbasket_templates.py:3257 #: modules/webbasket/lib/webbasket_templates.py:4060 #: modules/webcomment/lib/webcomment_templates.py:410 #: modules/webmessage/lib/webmessage_templates.py:111 msgid "Reply" msgstr "پاسخ دادن" #: modules/webbasket/lib/webbasket_templates.py:3287 #: modules/webbasket/lib/webbasket_templates.py:4085 #, python-format msgid "%(x_title)s, by %(x_name)s on %(x_date)s" msgstr "" #: modules/webbasket/lib/webbasket_templates.py:3289 #: modules/webbasket/lib/webbasket_templates.py:4087 #: modules/websession/lib/websession_templates.py:198 #: modules/websession/lib/websession_templates.py:272 #: modules/websession/lib/websession_templates.py:323 #: modules/websession/lib/websession_templates.py:1078 #: modules/websession/lib/websession_templates.py:1211 msgid "Note" msgstr "یادداشت" #: modules/webbasket/lib/webbasket_templates.py:3399 #: modules/webbasket/lib/webbasket_templates.py:3452 msgid "Subscribe to basket" msgstr "آبونه شدن سبد" #: modules/webbasket/lib/webbasket_templates.py:3458 msgid "This public basket belongs to the user " msgstr "" #: modules/webbasket/lib/webbasket_templates.py:3482 msgid "This public basket belongs to you." msgstr "" #: modules/webbasket/lib/webbasket_templates.py:3505 msgid "Basket is empty" msgstr "سبد خالی است" #: modules/webbasket/lib/webbasket_templates.py:4265 msgid "All your baskets" msgstr "همه سبدهای شما" #: modules/webbasket/lib/webbasket_templates.py:4268 #: modules/webbasket/lib/webbasket_templates.py:4361 msgid "Your personal baskets" msgstr "سبدهای شخصی شما" #: modules/webbasket/lib/webbasket_templates.py:4273 #, fuzzy msgid "All your topics" msgstr "همه سبدهای شما" #: modules/webbasket/lib/webbasket_templates.py:4284 #: modules/webbasket/lib/webbasket_templates.py:4384 msgid "Your group baskets" msgstr "سبدهای گروهی شما" #: modules/webbasket/lib/webbasket_templates.py:4289 #, fuzzy msgid "All your groups" msgstr "گروه های شما" #: modules/webbasket/lib/webbasket_templates.py:4300 msgid "Your public baskets" msgstr "سبدهای عمومی شما" #: modules/webbasket/lib/webbasket_templates.py:4304 #, fuzzy msgid "All your public baskets" msgstr "همه سبدهای عمومی" #: modules/webbasket/lib/webbasket_templates.py:4309 msgid "All the public baskets" msgstr "همه سبدهای عمومی" #: modules/webbasket/lib/webbasket_templates.py:4342 #, fuzzy msgid "Please select a basket..." msgstr "لطفا یک شماره انتخاب کنید" #: modules/webbasket/lib/webbasket_webinterface.py:158 #: modules/webbasket/lib/webbasket_webinterface.py:318 #: modules/webbasket/lib/webbasket_webinterface.py:403 #: modules/webbasket/lib/webbasket_webinterface.py:467 #: modules/webbasket/lib/webbasket_webinterface.py:534 #: modules/webbasket/lib/webbasket_webinterface.py:611 #: modules/webbasket/lib/webbasket_webinterface.py:699 #: modules/webbasket/lib/webbasket_webinterface.py:777 #: modules/webbasket/lib/webbasket_webinterface.py:864 #: modules/webbasket/lib/webbasket_webinterface.py:969 #: modules/webbasket/lib/webbasket_webinterface.py:1088 #: modules/webbasket/lib/webbasket_webinterface.py:1186 #: modules/webbasket/lib/webbasket_webinterface.py:1417 #: modules/webbasket/lib/webbasket_webinterface.py:1476 #: modules/webbasket/lib/webbasket_webinterface.py:1537 #: modules/webbasket/lib/webbasket_webinterface.py:1598 msgid "You are not authorized to use baskets." msgstr "شما برای استفاده این سبدها اجازه ندارید" #: modules/webbasket/lib/webbasket_webinterface.py:169 msgid "You are not authorized to view this attachment" msgstr "شما برای دیدن این ضمیمه اجازه ندارید" #: modules/webbasket/lib/webbasket_webinterface.py:360 msgid "Display baskets" msgstr "نمایش سبدها" #: modules/webbasket/lib/webbasket_webinterface.py:561 #: modules/webbasket/lib/webbasket_webinterface.py:635 #: modules/webbasket/lib/webbasket_webinterface.py:1621 msgid "Display item and notes" msgstr "نمایش آیتم و یادداشت ها" #: modules/webbasket/lib/webbasket_webinterface.py:822 msgid "Delete a basket" msgstr "حذف یک سبد" #: modules/webbasket/lib/webbasket_webinterface.py:882 #, fuzzy msgid "Move record to basket" msgstr "کپی رکورد به سبد" #: modules/webbasket/lib/webbasket_webinterface.py:885 msgid "Copy record to basket" msgstr "کپی رکورد به سبد" #: modules/webcomment/lib/webcomment.py:167 #: modules/webcomment/lib/webcomment.py:211 msgid "Bad page number --> showing first page." msgstr "" #: modules/webcomment/lib/webcomment.py:175 #, fuzzy msgid "Bad number of results per page --> showing 10 results per page." msgstr "تعداد نتایج جستجو در هر صفحه" #: modules/webcomment/lib/webcomment.py:184 msgid "Bad display order --> showing most helpful first." msgstr "" #: modules/webcomment/lib/webcomment.py:193 msgid "Bad display order --> showing oldest first." msgstr "" #: modules/webcomment/lib/webcomment.py:237 #: modules/webcomment/lib/webcomment.py:1596 #: modules/webcomment/lib/webcomment.py:1649 msgid "Comments on records have been disallowed by the administrator." msgstr "" #: modules/webcomment/lib/webcomment.py:245 #: modules/webcomment/lib/webcomment.py:268 #: modules/webcomment/lib/webcomment.py:1436 #: modules/webcomment/lib/webcomment.py:1457 msgid "Your feedback has been recorded, many thanks." msgstr "" #: modules/webcomment/lib/webcomment.py:252 msgid "You have already reported an abuse for this comment." msgstr "" #: modules/webcomment/lib/webcomment.py:259 msgid "The comment you have reported no longer exists." msgstr "" #: modules/webcomment/lib/webcomment.py:275 msgid "Sorry, you have already voted. This vote has not been recorded." msgstr "" #: modules/webcomment/lib/webcomment.py:282 msgid "" "You have been subscribed to this discussion. From now on, you will receive " "an email whenever a new comment is posted." msgstr "" #: modules/webcomment/lib/webcomment.py:289 msgid "You have been unsubscribed from this discussion." msgstr "" #: modules/webcomment/lib/webcomment.py:1188 #, python-format msgid "Record %i" msgstr "" #: modules/webcomment/lib/webcomment.py:1199 #, python-format msgid "%(report_number)s\"%(title)s\" has been reviewed" msgstr "" #: modules/webcomment/lib/webcomment.py:1203 #, python-format msgid "%(report_number)s\"%(title)s\" has been commented" msgstr "" #: modules/webcomment/lib/webcomment.py:1424 #, python-format msgid "%s is an invalid record ID" msgstr "" #: modules/webcomment/lib/webcomment.py:1443 #: modules/webcomment/lib/webcomment.py:1464 msgid "Your feedback could not be recorded, please try again." msgstr "" #: modules/webcomment/lib/webcomment.py:1572 #, python-format msgid "%s is an invalid user ID." msgstr "" #: modules/webcomment/lib/webcomment.py:1606 msgid "Cannot reply to a review." msgstr "" #: modules/webcomment/lib/webcomment.py:1661 msgid "You must enter a title." msgstr "شما باید یک عنوان را وارد کنید." #: modules/webcomment/lib/webcomment.py:1668 msgid "You must choose a score." msgstr "شما باید یک نمره را انتخاب کنید." #: modules/webcomment/lib/webcomment.py:1675 msgid "You must enter a text." msgstr "شما باید یک متن را وارد کنید." #: modules/webcomment/lib/webcomment.py:1692 msgid "You already wrote a review for this record." msgstr "شما قبلا یک بررسی برای این رکورد نوشته اید." #: modules/webcomment/lib/webcomment.py:1711 msgid "You already posted a comment short ago. Please retry later." msgstr "" #: modules/webcomment/lib/webcomment.py:1723 msgid "Failed to insert your comment to the database. Please try again." msgstr "" #: modules/webcomment/lib/webcomment.py:1737 msgid "Unknown action --> showing you the default add comment form." msgstr "" #: modules/webcomment/lib/webcomment.py:1864 #, python-format msgid "Record ID %s does not exist in the database." msgstr "" #: modules/webcomment/lib/webcomment.py:1872 msgid "No record ID was given." msgstr "" #: modules/webcomment/lib/webcomment.py:1880 #, python-format msgid "Record ID %s is an invalid ID." msgstr "" #: modules/webcomment/lib/webcomment.py:1888 #, python-format msgid "Record ID %s is not a number." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:83 #: modules/webcomment/lib/webcomment_templates.py:939 #: modules/websubmit/lib/websubmit_templates.py:2451 msgid "Write a comment" msgstr "یک نظر بنویسید" #: modules/webcomment/lib/webcomment_templates.py:99 #, python-format msgid "%(x_nb)i Comments for round \"%(x_name)s\"" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:102 #, fuzzy, python-format msgid "%(x_nb)i Comments" msgstr "نظرها" #: modules/webcomment/lib/webcomment_templates.py:140 #, python-format msgid "Showing the latest %i comments:" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:153 #: modules/webcomment/lib/webcomment_templates.py:179 msgid "Discuss this document" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:180 #: modules/webcomment/lib/webcomment_templates.py:952 msgid "Start a discussion about any aspect of this document." msgstr "شروع یک بحث درباره هر جنبه ای از این مدرک." #: modules/webcomment/lib/webcomment_templates.py:197 #, python-format msgid "Sorry, the record %s does not seem to exist." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:201 #, python-format msgid "Sorry, %s is not a valid ID value." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:203 msgid "Sorry, no record ID was provided." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:207 #, python-format msgid "You may want to start browsing from %s" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:265 #: modules/webcomment/lib/webcomment_templates.py:757 #: modules/webcomment/lib/webcomment_templates.py:767 #, python-format msgid "%(x_nb)i comments for round \"%(x_name)s\"" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:295 #: modules/webcomment/lib/webcomment_templates.py:862 msgid "Was this review helpful?" msgstr "آیا این بررسی سودمند بود؟" #: modules/webcomment/lib/webcomment_templates.py:306 #: modules/webcomment/lib/webcomment_templates.py:343 #: modules/webcomment/lib/webcomment_templates.py:939 msgid "Write a review" msgstr "یک بررسی بنویسید" #: modules/webcomment/lib/webcomment_templates.py:313 #: modules/webcomment/lib/webcomment_templates.py:926 #: modules/webcomment/lib/webcomment_templates.py:2185 #, python-format msgid "Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:316 #, python-format msgid "Readers found the following %s reviews to be most helpful." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:319 #: modules/webcomment/lib/webcomment_templates.py:342 #, python-format msgid "View all %s reviews" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:338 #: modules/webcomment/lib/webcomment_templates.py:360 #: modules/webcomment/lib/webcomment_templates.py:2226 msgid "Rate this document" msgstr "این مدرک را درجه بندی کنید" #: modules/webcomment/lib/webcomment_templates.py:361 #, fuzzy msgid "Be the first to review this document.
" msgstr "نخستین فرد بررسی کننده این مدرک باشید." #: modules/webcomment/lib/webcomment_templates.py:405 #: modules/webcomment/lib/webcomment_templates.py:466 #: modules/webcomment/lib/webcomment_templates.py:808 #, fuzzy msgid "Close" msgstr "بستن" #: modules/webcomment/lib/webcomment_templates.py:412 #: modules/webcomment/lib/webcomment_templates.py:863 msgid "Report abuse" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:426 msgid "Undelete comment" msgstr "بازگرداندن نظر" #: modules/webcomment/lib/webcomment_templates.py:435 #: modules/webcomment/lib/webcomment_templates.py:437 msgid "Delete comment" msgstr "حذف نظر" #: modules/webcomment/lib/webcomment_templates.py:443 msgid "Unreport comment" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:454 msgid "Attached file" msgstr "فایل پیوست شده" #: modules/webcomment/lib/webcomment_templates.py:454 msgid "Attached files" msgstr "فایل های پیوست شده" #: modules/webcomment/lib/webcomment_templates.py:466 #: modules/webcomment/lib/webcomment_templates.py:807 msgid "Open" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:535 #, python-format msgid "Reviewed by %(x_nickname)s on %(x_date)s" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:539 #, python-format msgid "%(x_nb_people)s out of %(x_nb_total)s people found this review useful" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:561 msgid "Undelete review" msgstr "بازگرداندن بررسی" #: modules/webcomment/lib/webcomment_templates.py:570 msgid "Delete review" msgstr "حذف بررسی" #: modules/webcomment/lib/webcomment_templates.py:576 msgid "Unreport review" msgstr "بررسی گزارش نشده" #: modules/webcomment/lib/webcomment_templates.py:683 #: modules/webcomment/lib/webcomment_templates.py:699 #: modules/webcomment/lib/webcomment_webinterface.py:245 #: modules/webcomment/lib/webcomment_webinterface.py:462 #: modules/websubmit/lib/websubmit_templates.py:2449 msgid "Comments" msgstr "نظرها" #: modules/webcomment/lib/webcomment_templates.py:684 #: modules/webcomment/lib/webcomment_templates.py:700 #: modules/webcomment/lib/webcomment_webinterface.py:245 #: modules/webcomment/lib/webcomment_webinterface.py:462 msgid "Reviews" msgstr "بررسی ها" #: modules/webcomment/lib/webcomment_templates.py:943 #, python-format msgid "There is a total of %s reviews" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:945 #, python-format msgid "There is a total of %s comments" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:957 msgid "Be the first to review this document." msgstr "نخستین فرد بررسی کننده این مدرک باشید." #: modules/webcomment/lib/webcomment_templates.py:976 msgid "Subscribe" msgstr "مشترک شدن" #: modules/webcomment/lib/webcomment_templates.py:994 msgid "Unsubscribe" msgstr "مشترک نشدن" #: modules/webcomment/lib/webcomment_templates.py:1021 #: modules/webcomment/lib/webcomment_templates.py:1089 msgid "review" msgstr "بررسی" #: modules/webcomment/lib/webcomment_templates.py:1021 #: modules/webcomment/lib/webcomment_templates.py:1089 msgid "comment" msgstr "نظر" #: modules/webcomment/lib/webcomment_templates.py:1024 #: modules/webcomment/lib/webcomment_templates.py:2028 msgid "Review" msgstr "بررسی" #: modules/webcomment/lib/webcomment_templates.py:1087 msgid "Viewing" msgstr "دیدن" #: modules/webcomment/lib/webcomment_templates.py:1088 msgid "Page:" msgstr "صفحه:" #: modules/webcomment/lib/webcomment_templates.py:1102 msgid "You are not authorized to comment or review." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1272 #, python-format msgid "Note: Your nickname, %s, will be displayed as author of this comment." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1276 #: modules/webcomment/lib/webcomment_templates.py:1393 #, python-format msgid "" "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." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1293 msgid "Once logged in, authorized users can also attach files." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1308 msgid "Optionally, attach a file to this comment" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1309 msgid "Optionally, attach files to this comment" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1310 msgid "Max one file" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1311 #, python-format msgid "Max %i files" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1312 #, python-format msgid "Max %(x_nb_bytes)s per file" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1327 msgid "Send me an email when a new comment is posted" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1342 #: modules/webcomment/lib/webcomment_templates.py:1464 msgid "Article" msgstr "مقاله" #: modules/webcomment/lib/webcomment_templates.py:1344 msgid "Add comment" msgstr "افزودن نظر" #: modules/webcomment/lib/webcomment_templates.py:1388 #, python-format msgid "" "Note: Your nickname, %s, will be displayed as the author of this review." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1465 msgid "Rate this article" msgstr "این مقاله را درجه بندی کنید" #: modules/webcomment/lib/webcomment_templates.py:1466 msgid "Select a score" msgstr "یک نمره انتخاب کنید" #: modules/webcomment/lib/webcomment_templates.py:1467 msgid "Give a title to your review" msgstr "یک عنوان به بررسی تان بدهید" #: modules/webcomment/lib/webcomment_templates.py:1468 msgid "Write your review" msgstr "بررسی تان را بنویسید" #: modules/webcomment/lib/webcomment_templates.py:1473 msgid "Add review" msgstr "افزودن بررسی" #: modules/webcomment/lib/webcomment_templates.py:1483 #: modules/webcomment/lib/webcomment_webinterface.py:507 msgid "Add Review" msgstr "افزودن بررسی" #: modules/webcomment/lib/webcomment_templates.py:1505 msgid "Your review was successfully added." msgstr "بررسی شما به صورت موفقیت آمیزی اضافه شد." #: modules/webcomment/lib/webcomment_templates.py:1507 msgid "Your comment was successfully added." msgstr "نظر شما به صورت موفقیت آمیزی اضافه شد." #: modules/webcomment/lib/webcomment_templates.py:1510 msgid "Back to record" msgstr "برگشت به رکورد" #: modules/webcomment/lib/webcomment_templates.py:1512 #, python-format msgid "" "You can also view all the comments you have submitted so far on " "\"%(x_url_open)sYour Comments%(x_url_close)s\" page." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1592 #: modules/webcomment/web/admin/webcommentadmin.py:171 msgid "View most commented records" msgstr "دیدن رکوردهای دارای بیشترین نظر" #: modules/webcomment/lib/webcomment_templates.py:1594 #: modules/webcomment/web/admin/webcommentadmin.py:207 msgid "View latest commented records" msgstr "دیدن آخرین رکوردهای نظرداده شده" #: modules/webcomment/lib/webcomment_templates.py:1596 #: modules/webcomment/web/admin/webcommentadmin.py:140 msgid "View all comments reported as abuse" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1600 #: modules/webcomment/web/admin/webcommentadmin.py:170 msgid "View most reviewed records" msgstr "دیدن رکوردهای دارای بیشترین بررسی" #: modules/webcomment/lib/webcomment_templates.py:1602 #: modules/webcomment/web/admin/webcommentadmin.py:206 msgid "View latest reviewed records" msgstr "دیدن آخرین رکوردهای بررسی شده" #: modules/webcomment/lib/webcomment_templates.py:1604 #: modules/webcomment/web/admin/webcommentadmin.py:140 msgid "View all reviews reported as abuse" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1612 msgid "View all users who have been reported" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1614 msgid "Guide" msgstr "راهنما" #: modules/webcomment/lib/webcomment_templates.py:1616 msgid "Comments and reviews are disabled" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1636 msgid "" "Please enter the ID of the comment/review so that you can view it before " "deciding whether to delete it or not" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1660 msgid "Comment ID:" msgstr "هویت نظر:" #: modules/webcomment/lib/webcomment_templates.py:1661 msgid "Or enter a record ID to list all the associated comments/reviews:" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1662 msgid "Record ID:" msgstr "هویت رکورد:" #: modules/webcomment/lib/webcomment_templates.py:1664 msgid "View Comment" msgstr "مشاهده نظر" #: modules/webcomment/lib/webcomment_templates.py:1685 msgid "There have been no reports so far." msgstr "تا کنون هیچ گزارشی وجود نداشته است." #: modules/webcomment/lib/webcomment_templates.py:1689 #, python-format msgid "View all %s reported comments" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1692 #, python-format msgid "View all %s reported reviews" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1729 msgid "" "Here is a list, sorted by total number of reports, of all users who have had " "a comment reported at least once." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1737 #: modules/webcomment/lib/webcomment_templates.py:1766 #: modules/websession/lib/websession_templates.py:264 #: modules/websession/lib/websession_templates.py:1206 msgid "Nickname" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1739 #: modules/webcomment/lib/webcomment_templates.py:1768 msgid "User ID" msgstr "هویت کاربر" #: modules/webcomment/lib/webcomment_templates.py:1741 msgid "Number positive votes" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1742 msgid "Number negative votes" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1743 msgid "Total number votes" msgstr "کل تعداد رأی ها" #: modules/webcomment/lib/webcomment_templates.py:1744 msgid "Total number of reports" msgstr "کل تعداد گزارش ها" #: modules/webcomment/lib/webcomment_templates.py:1745 msgid "View all user's reported comments/reviews" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1778 #, python-format msgid "This review has been reported %i times" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:1780 #, python-format msgid "This comment has been reported %i times" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2029 msgid "Written by" msgstr "نوشته شده به وسیله" #: modules/webcomment/lib/webcomment_templates.py:2030 msgid "General informations" msgstr "اطلاعات کلی" #: modules/webcomment/lib/webcomment_templates.py:2045 msgid "Delete selected reviews" msgstr "حذف بررسی های انتخاب شده" #: modules/webcomment/lib/webcomment_templates.py:2046 #: modules/webcomment/lib/webcomment_templates.py:2053 msgid "Suppress selected abuse report" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2047 msgid "Undelete selected reviews" msgstr "عدم حذف بررسی های انتخاب شده" #: modules/webcomment/lib/webcomment_templates.py:2051 msgid "Undelete selected comments" msgstr "عدم حذف نظرات انتخاب شده" #: modules/webcomment/lib/webcomment_templates.py:2052 msgid "Delete selected comments" msgstr "حذف نظرات انتخاب شده" #: modules/webcomment/lib/webcomment_templates.py:2067 #, python-format msgid "Here are the reported reviews of user %s" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2069 #, python-format msgid "Here are the reported comments of user %s" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2073 #, python-format msgid "Here is review %s" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2075 #, python-format msgid "Here is comment %s" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2078 #, python-format msgid "Here is review %(x_cmtID)s written by user %(x_user)s" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2080 #, python-format msgid "Here is comment %(x_cmtID)s written by user %(x_user)s" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2086 msgid "Here are all reported reviews sorted by the most reported" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2088 msgid "Here are all reported comments sorted by the most reported" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2093 #, python-format msgid "Here are all reviews for record %i, sorted by the most reported" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2094 msgid "Show comments" msgstr "نمایش نظرها" #: modules/webcomment/lib/webcomment_templates.py:2096 #, python-format msgid "Here are all comments for record %i, sorted by the most reported" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2097 msgid "Show reviews" msgstr "نمایش بررسی ها" #: modules/webcomment/lib/webcomment_templates.py:2122 #: modules/webcomment/lib/webcomment_templates.py:2146 #: modules/webcomment/lib/webcomment_templates.py:2172 msgid "comment ID" msgstr "هویت نظر" #: modules/webcomment/lib/webcomment_templates.py:2122 msgid "successfully deleted" msgstr "به طور موفقیت آمیزی حذف شد" #: modules/webcomment/lib/webcomment_templates.py:2146 msgid "successfully undeleted" msgstr "به طور موفقیت آمیزی حذف نشد" #: modules/webcomment/lib/webcomment_templates.py:2172 msgid "successfully suppressed abuse report" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2189 msgid "Not yet reviewed" msgstr "هنوز بررسی نشده است" #: modules/webcomment/lib/webcomment_templates.py:2257 #, python-format msgid "" "The following review was sent to %(CFG_SITE_NAME)s by %(user_nickname)s:" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2258 #, python-format msgid "" "The following comment was sent to %(CFG_SITE_NAME)s by %(user_nickname)s:" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2285 msgid "This is an automatic message, please don't reply to it." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2287 #, python-format msgid "To post another comment, go to <%(x_url)s> instead." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2292 #, python-format msgid "To specifically reply to this comment, go to <%(x_url)s>" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2297 #, python-format msgid "To unsubscribe from this discussion, go to <%(x_url)s>" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2301 #, python-format msgid "For any question, please use <%(CFG_SITE_SUPPORT_EMAIL)s>" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2368 msgid "Your comment will be lost." msgstr "نظر شما گم خواهد شد." #: modules/webcomment/lib/webcomment_templates.py:2406 #, fuzzy msgid "Oldest comment first" msgstr "حذف نظرها" #: modules/webcomment/lib/webcomment_templates.py:2407 #, fuzzy msgid "Latest comment first" msgstr "دیدن آخرین رکوردهای نظرداده شده" #: modules/webcomment/lib/webcomment_templates.py:2408 msgid "Group by record, oldest commented first" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2409 #, fuzzy msgid "Group by record, latest commented first" msgstr "دیدن آخرین رکوردهای نظرداده شده" #: modules/webcomment/lib/webcomment_templates.py:2411 #, fuzzy msgid "Records and comments" msgstr "افزودن نظرها" #: modules/webcomment/lib/webcomment_templates.py:2412 #, fuzzy msgid "Records only" msgstr "رکورد" #: modules/webcomment/lib/webcomment_templates.py:2413 #, fuzzy msgid "Comments only" msgstr "نظرها" #: modules/webcomment/lib/webcomment_templates.py:2415 #: modules/webcomment/lib/webcomment_templates.py:2416 #: modules/webcomment/lib/webcomment_templates.py:2417 #: modules/webcomment/lib/webcomment_templates.py:2418 #, fuzzy, python-format msgid "%s items" msgstr "افزودن آیتم ها" #: modules/webcomment/lib/webcomment_templates.py:2419 #, fuzzy msgid "All items" msgstr "افزودن آیتم ها" #: modules/webcomment/lib/webcomment_templates.py:2422 msgid "Below is the list of the comments you have submitted so far." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2426 msgid "" "You have not yet submitted any comment in the document \"discussion\" tab." msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2429 #: modules/webcomment/lib/webcomment_templates.py:2437 #: modules/webcomment/lib/webcomment_templates.py:2445 msgid "You might find other comments here: " msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2457 msgid "" "You have not yet submitted any comment. Browse documents from the search " "interface and take part to discussions!" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2485 #, fuzzy msgid "Display" msgstr "هشدارهای نمایش" #: modules/webcomment/lib/webcomment_templates.py:2486 #, fuzzy msgid "Order by" msgstr "کتاب های سفارش شده" #: modules/webcomment/lib/webcomment_templates.py:2487 msgid "Per page" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2491 #, fuzzy msgid "Display options" msgstr "هشدارهای نمایش" #: modules/webcomment/lib/webcomment_templates.py:2492 #: modules/webjournal/lib/webjournal_templates.py:454 #: modules/webjournal/lib/webjournaladminlib.py:326 #: modules/webjournal/lib/webjournaladminlib.py:343 #: modules/weblinkback/lib/weblinkback_templates.py:209 msgid "Refresh" msgstr "تازه کردن" #: modules/webcomment/lib/webcomment_templates.py:2521 msgid "Comment deleted by the moderator" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2523 msgid "You have deleted this comment: it is not visible by other users" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2530 #, fuzzy msgid "(in reply to a comment)" msgstr "یک نظر بنویسید" #: modules/webcomment/lib/webcomment_templates.py:2535 msgid "See comment on discussion page" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2574 #, python-format msgid "%i comments found in total (not shown on this page)" msgstr "" #: modules/webcomment/lib/webcomment_templates.py:2576 #, fuzzy, python-format msgid "%i items found in total" msgstr "هیچ آیتمی یافت نشد." #: modules/webcomment/lib/webcomment_webinterface.py:269 #: modules/webcomment/lib/webcomment_webinterface.py:526 msgid "Record Not Found" msgstr "رکورد یافت نشد" #: modules/webcomment/lib/webcomment_webinterface.py:347 #: modules/webcomment/lib/webcomment_webinterface.py:587 #: modules/webcomment/lib/webcomment_webinterface.py:664 msgid "Specified comment does not belong to this record" msgstr "" #: modules/webcomment/lib/webcomment_webinterface.py:356 #: modules/webcomment/lib/webcomment_webinterface.py:593 #: modules/webcomment/lib/webcomment_webinterface.py:670 #, fuzzy msgid "You do not have access to the specified comment" msgstr "شما مجوز حذف این یدداشت را ندارید." #: modules/webcomment/lib/webcomment_webinterface.py:427 #, python-format msgid "" "The size of file \\\"%s\\\" (%s) is larger than maximum allowed file size " "(%s). Select files again." msgstr "" #: modules/webcomment/lib/webcomment_webinterface.py:509 #: modules/websubmit/lib/websubmit_templates.py:2445 #: modules/websubmit/lib/websubmit_templates.py:2446 msgid "Add Comment" msgstr "افزودن نظر" #: modules/webcomment/lib/webcomment_webinterface.py:598 msgid "You cannot vote for a deleted comment" msgstr "" #: modules/webcomment/lib/webcomment_webinterface.py:675 msgid "You cannot report a deleted comment" msgstr "" #: modules/webcomment/lib/webcomment_webinterface.py:822 #: modules/webcomment/lib/webcomment_webinterface.py:862 msgid "Page Not Found" msgstr "صفحه پیدا نشد" #: modules/webcomment/lib/webcomment_webinterface.py:823 msgid "The requested comment could not be found" msgstr "" #: modules/webcomment/lib/webcomment_webinterface.py:843 msgid "You cannot access files of a deleted comment" msgstr "" #: modules/webcomment/lib/webcomment_webinterface.py:863 msgid "The requested file could not be found" msgstr "فایل درخواستی پیدا نشد" #: modules/webcomment/lib/webcomment_webinterface.py:906 #, fuzzy msgid "You are not authorized to use comments." msgstr "شما برای استفاده این سبدها اجازه ندارید" #: modules/webcomment/lib/webcomment_webinterface.py:908 #: modules/websession/lib/websession_templates.py:621 #: modules/websession/lib/websession_templates.py:774 #, fuzzy msgid "Your Comments" msgstr "نظرها" #: modules/webcomment/lib/webcomment_webinterface.py:920 #, python-format msgid "%s View your previously submitted comments" msgstr "" #: modules/webcomment/lib/webcommentadminlib.py:122 msgid "Invalid comment ID." msgstr "" #: modules/webcomment/lib/webcommentadminlib.py:142 #, python-format msgid "Comment ID %s does not exist." msgstr "" #: modules/webcomment/lib/webcommentadminlib.py:156 #, python-format msgid "Record ID %s does not exist." msgstr "" #: modules/webcomment/web/admin/webcommentadmin.py:45 #: modules/webcomment/web/admin/webcommentadmin.py:59 #: modules/webcomment/web/admin/webcommentadmin.py:83 #: modules/webcomment/web/admin/webcommentadmin.py:126 #: modules/webcomment/web/admin/webcommentadmin.py:164 #: modules/webcomment/web/admin/webcommentadmin.py:192 #: modules/webcomment/web/admin/webcommentadmin.py:228 #: modules/webcomment/web/admin/webcommentadmin.py:266 msgid "WebComment Admin" msgstr "" #: modules/webcomment/web/admin/webcommentadmin.py:50 #: modules/webcomment/web/admin/webcommentadmin.py:88 #: modules/webcomment/web/admin/webcommentadmin.py:131 #: modules/webcomment/web/admin/webcommentadmin.py:197 #: modules/webcomment/web/admin/webcommentadmin.py:233 #: modules/webcomment/web/admin/webcommentadmin.py:271 #: modules/websearch/lib/websearch_webinterface.py:833 #: modules/websearch/lib/websearch_webinterface.py:946 #: modules/websession/lib/websession_webinterface.py:1058 #: modules/webstyle/lib/webstyle_templates.py:661 msgid "Internal Error" msgstr "خطای درونی" #: modules/webcomment/web/admin/webcommentadmin.py:102 msgid "Delete/Undelete Reviews" msgstr "حذف/ عدم حذف بررسی ها" #: modules/webcomment/web/admin/webcommentadmin.py:102 msgid "Delete/Undelete Comments" msgstr "حذف/ عدم حذف نظرات" #: modules/webcomment/web/admin/webcommentadmin.py:102 msgid " or Suppress abuse reports" msgstr "" #: modules/webcomment/web/admin/webcommentadmin.py:242 msgid "View all reported users" msgstr "نمایش تمام کاربران گزارش شده" #: modules/webcomment/web/admin/webcommentadmin.py:289 msgid "Delete comments" msgstr "حذف نظرها" #: modules/webcomment/web/admin/webcommentadmin.py:292 msgid "Suppress abuse reports" msgstr "" #: modules/webcomment/web/admin/webcommentadmin.py:295 msgid "Undelete comments" msgstr "بازگرداندن نظرها" #: modules/webjournal/lib/elements/bfe_webjournal_archive.py:106 msgid "Archive" msgstr "آرشیو" #: modules/webjournal/lib/elements/bfe_webjournal_archive.py:133 msgid "Select Year:" msgstr "انتخاب سال:" #: modules/webjournal/lib/elements/bfe_webjournal_archive.py:139 msgid "Select Issue:" msgstr "انتخاب شماره:" #: modules/webjournal/lib/elements/bfe_webjournal_archive.py:143 msgid "Select Date:" msgstr "انتخاب تاریخ" #: modules/webjournal/lib/elements/bfe_webjournal_article_author.py:45 #, python-format msgid "About your article at %(url)s" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_article_body.py:232 msgid "Did you know?" msgstr "آیا شما می دانید؟" #: modules/webjournal/lib/elements/bfe_webjournal_article_body.py:264 #, python-format msgid "" "Hi,\n" "\n" "Have a look at the following article:\n" "<%(url)s>" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_article_body.py:271 msgid "Send this article" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_imprint.py:122 msgid "Issue No." msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py:92 msgid "News Articles" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py:93 msgid "Official News" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py:94 msgid "Training and Development" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py:95 msgid "General Information" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py:96 msgid "Announcements" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py:97 msgid "Training" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py:98 msgid "Events" msgstr "رخدادها" #: modules/webjournal/lib/elements/bfe_webjournal_main_navigation.py:99 msgid "Staff Association" msgstr "" #: modules/webjournal/lib/elements/bfe_webjournal_rss.py:141 msgid "Subscribe by RSS" msgstr "" #: modules/webjournal/lib/webjournal_config.py:64 msgid "Page not found" msgstr "صفحه پیدا نشد" #: modules/webjournal/lib/webjournal_config.py:65 msgid "The requested page does not exist" msgstr "صفحه درخواستی وجود ندارد" #: modules/webjournal/lib/webjournal_config.py:96 msgid "No journal articles" msgstr "نبود مقاله های مجله" #: modules/webjournal/lib/webjournal_config.py:97 #: modules/webjournal/lib/webjournal_config.py:138 msgid "Problem with the configuration of this journal" msgstr "" #: modules/webjournal/lib/webjournal_config.py:137 msgid "No journal issues" msgstr "نبود شماره های مجله" #: modules/webjournal/lib/webjournal_config.py:176 msgid "Journal article error" msgstr "" #: modules/webjournal/lib/webjournal_config.py:177 msgid "We could not know which article you were looking for" msgstr "" #: modules/webjournal/lib/webjournal_config.py:211 msgid "No journals available" msgstr "" #: modules/webjournal/lib/webjournal_config.py:212 msgid "We could not provide you any journals" msgstr "" #: modules/webjournal/lib/webjournal_config.py:213 msgid "" "It seems that there are no journals defined on this server. Please contact " "support if this is not right." msgstr "" #: modules/webjournal/lib/webjournal_config.py:239 msgid "Select a journal on this server" msgstr "" #: modules/webjournal/lib/webjournal_config.py:240 msgid "We couldn't guess which journal you are looking for" msgstr "" #: modules/webjournal/lib/webjournal_config.py:241 msgid "" "You did not provide an argument for a journal name. Please select the " "journal you want to read in the list below." msgstr "" #: modules/webjournal/lib/webjournal_config.py:268 msgid "No current issue" msgstr "نبود شماره جاری" #: modules/webjournal/lib/webjournal_config.py:269 msgid "We could not find any informtion on the current issue" msgstr "" #: modules/webjournal/lib/webjournal_config.py:270 msgid "" "The configuration for the current issue seems to be empty. Try providing an " "issue number or check with support." msgstr "" #: modules/webjournal/lib/webjournal_config.py:298 msgid "Issue number badly formed" msgstr "" #: modules/webjournal/lib/webjournal_config.py:299 msgid "We could not read the issue number you provided" msgstr "" #: modules/webjournal/lib/webjournal_config.py:329 msgid "Archive date badly formed" msgstr "" #: modules/webjournal/lib/webjournal_config.py:330 msgid "We could not read the archive date you provided" msgstr "" #: modules/webjournal/lib/webjournal_config.py:365 msgid "No popup record" msgstr "" #: modules/webjournal/lib/webjournal_config.py:366 msgid "We could not deduce the popup article you requested" msgstr "" #: modules/webjournal/lib/webjournal_config.py:399 msgid "Update error" msgstr "خطای بروزرسانی" #: modules/webjournal/lib/webjournal_config.py:400 #: modules/webjournal/lib/webjournal_config.py:431 msgid "There was an internal error" msgstr "" #: modules/webjournal/lib/webjournal_config.py:430 msgid "Journal publishing DB error" msgstr "" #: modules/webjournal/lib/webjournal_config.py:463 msgid "Journal issue error" msgstr "خطای شماره مجله" #: modules/webjournal/lib/webjournal_config.py:464 msgid "Issue not found" msgstr "شماره پیدا نشد" #: modules/webjournal/lib/webjournal_config.py:494 msgid "Journal ID error" msgstr "خطای تعیین هویت مجله" #: modules/webjournal/lib/webjournal_config.py:495 msgid "We could not find the id for this journal in the Database" msgstr "" #: modules/webjournal/lib/webjournal_config.py:527 #: modules/webjournal/lib/webjournal_config.py:529 #, python-format msgid "Category \"%(category_name)s\" not found" msgstr "" #: modules/webjournal/lib/webjournal_config.py:531 msgid "Sorry, this category does not exist for this journal and issue." msgstr "" #: modules/webjournal/lib/webjournal_templates.py:50 msgid "Available Journals" msgstr "" #: modules/webjournal/lib/webjournal_templates.py:59 #: modules/webjournal/lib/webjournal_templates.py:98 #, python-format msgid "Contact %(x_url_open)sthe administrator%(x_url_close)s" msgstr "" #: modules/webjournal/lib/webjournal_templates.py:178 msgid "Regeneration Error" msgstr "" #: modules/webjournal/lib/webjournal_templates.py:179 msgid "" "The issue could not be correctly regenerated. Please contact your " "administrator." msgstr "" #: modules/webjournal/lib/webjournal_templates.py:305 #, python-format msgid "If you cannot read this email please go to %(x_journal_link)s" msgstr "" #: modules/webjournal/lib/webjournal_templates.py:452 #: modules/webjournal/lib/webjournal_templates.py:701 #: modules/webjournal/lib/webjournaladminlib.py:326 #: modules/webjournal/lib/webjournaladminlib.py:346 msgid "Add" msgstr "افزودن" #: modules/webjournal/lib/webjournal_templates.py:453 #: modules/webjournal/lib/webjournaladminlib.py:365 msgid "Publish" msgstr "منتشر کردن" #: modules/webjournal/lib/webjournal_templates.py:696 msgid "Apply" msgstr "اجرا کردن" #: modules/webjournal/lib/webjournaladminlib.py:377 msgid "Please select an issue" msgstr "لطفا یک شماره انتخاب کنید" #: modules/webjournal/lib/widgets/bfe_webjournal_widget_seminars.py:92 #: modules/webjournal/lib/widgets/bfe_webjournal_widget_seminars.py:110 #: modules/webjournal/lib/widgets/bfe_webjournal_widget_weather.py:125 #: modules/webjournal/lib/widgets/bfe_webjournal_widget_weather.py:140 msgid "No information available" msgstr "هیچ اطلاعاتی قابل استفاده نیست" #: modules/webjournal/lib/widgets/bfe_webjournal_widget_seminars.py:115 msgid "No seminars today" msgstr "" #: modules/webjournal/lib/widgets/bfe_webjournal_widget_seminars.py:329 msgid "What's on today" msgstr "" #: modules/webjournal/lib/widgets/bfe_webjournal_widget_seminars.py:330 msgid "Seminars of the week" msgstr "سمینارهای هفته" #: modules/webjournal/lib/widgets/bfe_webjournal_widget_weather.py:224 msgid "Under the CERN sky" msgstr "" #: modules/webjournal/lib/widgets/bfe_webjournal_widget_whatsNew.py:167 msgid "There are no new articles for the moment" msgstr "" #: modules/webjournal/lib/widgets/bfe_webjournal_widget_whatsNew.py:299 msgid "What's new" msgstr "" #: modules/webjournal/web/admin/webjournaladmin.py:76 msgid "WebJournal Admin" msgstr "" #: modules/webjournal/web/admin/webjournaladmin.py:118 #, python-format msgid "Administrate %(journal_name)s" msgstr "" #: modules/webjournal/web/admin/webjournaladmin.py:157 msgid "Feature a record" msgstr "" #: modules/webjournal/web/admin/webjournaladmin.py:219 msgid "Email Alert System" msgstr "" #: modules/webjournal/web/admin/webjournaladmin.py:275 msgid "Issue regenerated" msgstr "" #: modules/webjournal/web/admin/webjournaladmin.py:275 #, fuzzy msgid "Regenerate Issue" msgstr "انتخاب شماره:" #: modules/webjournal/web/admin/webjournaladmin.py:328 msgid "Publishing Interface" msgstr "" #: modules/webjournal/web/admin/webjournaladmin.py:354 msgid "Add Journal" msgstr "افزودن مجله" #: modules/webjournal/web/admin/webjournaladmin.py:356 msgid "Edit Settings" msgstr "ویرایش تنظیمات" #: modules/weblinkback/lib/weblinkback_templates.py:50 msgid "Trackback URL: " msgstr "" #: modules/weblinkback/lib/weblinkback_templates.py:80 #, fuzzy msgid "to review" msgstr "1 بررسی" #: modules/weblinkback/lib/weblinkback_templates.py:99 #, python-format msgid "Linkbacks%s: %s" msgstr "" #: modules/weblinkback/lib/weblinkback_templates.py:159 #, fuzzy msgid "Submitted on" msgstr "واگذاری" #: modules/weblinkback/lib/weblinkback_templates.py:170 #: modules/websubmit/web/publiline.py:1134 #: modules/websubmit/web/publiline.py:1194 msgid "Approve" msgstr "تأیید کردن" #: modules/weblinkback/lib/weblinkback_templates.py:172 #: modules/websubmit/web/publiline.py:1135 #: modules/websubmit/web/publiline.py:1195 msgid "Reject" msgstr "رد کردن" #: modules/weblinkback/lib/weblinkback_templates.py:197 msgid "View last" msgstr "" #: modules/weblinkback/lib/weblinkback_templates.py:261 #: modules/weblinkback/web/admin/weblinkbackadmin.py:134 msgid "Pending Linkbacks" msgstr "" #: modules/weblinkback/lib/weblinkback_templates.py:267 #, fuzzy msgid "Recent Linkbacks" msgstr "مقاله های اخیر" #: modules/weblinkback/lib/weblinkback_templates.py:273 #: modules/weblinkback/web/admin/weblinkbackadmin.py:83 msgid "Linkback Whitelist/Blacklist Manager" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:85 #: modules/weblinkback/lib/weblinkbackadminlib.py:184 #, fuzzy msgid "Unknown error" msgstr "نامشخص" #: modules/weblinkback/lib/weblinkbackadminlib.py:87 msgid "The URL already exists in one of the lists" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:89 #: modules/weblinkback/lib/weblinkbackadminlib.py:186 #, fuzzy msgid "Invalid action" msgstr "انتخاب معکوس" #: modules/weblinkback/lib/weblinkbackadminlib.py:91 msgid "Invalid URL, might contain spaces" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:120 msgid "Whitelist" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:121 msgid "linkback requests from these URLs will be approved automatically." msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:122 msgid "Blacklist" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:123 msgid "" "linkback requests from these URLs will be refused automatically, no data " "will be saved." msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:124 msgid "" "All URLs in these lists are checked for containment (infix) in any linkback " "request URL. A whitelist match has precedence over a blacklist match." msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:127 msgid "Add URL" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:134 msgid "There are no URLs in both lists." msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:136 msgid "Reduce the amount of future pending linkback requests" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:198 msgid "Pending linkbacks" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:199 msgid "" "these linkbacks are not visible to users, they must be approved or rejected." msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:208 msgid "Reduce the amount of currently pending linkback requests" msgstr "" #: modules/weblinkback/lib/weblinkbackadminlib.py:210 msgid "Currently only pending linkbacks are supported." msgstr "" #: modules/weblinkback/web/admin/weblinkbackadmin.py:46 #: modules/weblinkback/web/admin/weblinkbackadmin.py:54 #: modules/weblinkback/web/admin/weblinkbackadmin.py:75 #: modules/weblinkback/web/admin/weblinkbackadmin.py:101 #: modules/weblinkback/web/admin/weblinkbackadmin.py:126 #: modules/weblinkback/web/admin/weblinkbackadmin.py:152 msgid "WebLinkback Admin" msgstr "" #: modules/webmessage/lib/webmessage.py:58 #: modules/webmessage/lib/webmessage.py:137 #: modules/webmessage/lib/webmessage.py:203 msgid "Sorry, this message is not in your mailbox." msgstr "" #: modules/webmessage/lib/webmessage.py:75 #: modules/webmessage/lib/webmessage.py:219 msgid "This message does not exist." msgstr "این پیام وجود ندارد." #: modules/webmessage/lib/webmessage.py:144 msgid "The message could not be deleted." msgstr "این پیام نمی تواند حذف شود." #: modules/webmessage/lib/webmessage.py:146 msgid "The message was successfully deleted." msgstr "پیام به طور موفقیت آمیزی حذف شد." #: modules/webmessage/lib/webmessage.py:162 msgid "Your mailbox has been emptied." msgstr "صندوق پستی شما خالی دشه است." #: modules/webmessage/lib/webmessage.py:368 #, python-format msgid "The chosen date (%(x_year)i/%(x_month)i/%(x_day)i) is invalid." msgstr "" #: modules/webmessage/lib/webmessage.py:377 msgid "Please enter a user name or a group name." msgstr "لطفا نام کاربری یا نام یک گروه را وارد کنید" #: modules/webmessage/lib/webmessage.py:381 #, python-format msgid "" "Your message is too long, please shorten it. Maximum size allowed is %i " "characters." msgstr "" #: modules/webmessage/lib/webmessage.py:396 #, python-format msgid "Group %s does not exist." msgstr "" #: modules/webmessage/lib/webmessage.py:421 #, python-format msgid "User %s does not exist." msgstr "" #: modules/webmessage/lib/webmessage.py:434 #: modules/webmessage/lib/webmessage_webinterface.py:145 #: modules/webmessage/lib/webmessage_webinterface.py:242 msgid "Write a message" msgstr "نوشتن یک پیام" #: modules/webmessage/lib/webmessage.py:449 msgid "" "Your message could not be sent to the following recipients as it would " "exceed their quotas:" msgstr "" #: modules/webmessage/lib/webmessage.py:453 msgid "Your message has been sent." msgstr "پیام شما ارسال شده است." #: modules/webmessage/lib/webmessage.py:458 #: modules/webmessage/lib/webmessage_templates.py:472 #: modules/webmessage/lib/webmessage_webinterface.py:87 #: modules/webmessage/lib/webmessage_webinterface.py:311 #: modules/webmessage/lib/webmessage_webinterface.py:357 #: modules/websession/lib/websession_templates.py:762 msgid "Your Messages" msgstr "پیام های شما" #: modules/webmessage/lib/webmessage_templates.py:87 msgid "Sender" msgstr "ارسال کننده" #: modules/webmessage/lib/webmessage_templates.py:96 msgid "No messages" msgstr "بدون پیام" #: modules/webmessage/lib/webmessage_templates.py:100 msgid "No subject" msgstr "بدون موضوع" #: modules/webmessage/lib/webmessage_templates.py:146 msgid "Write new message" msgstr "نوشتن پیام جدید" #: modules/webmessage/lib/webmessage_templates.py:147 msgid "Delete All" msgstr "حذف همه" #: modules/webmessage/lib/webmessage_templates.py:189 msgid "Re:" msgstr "" #: modules/webmessage/lib/webmessage_templates.py:281 msgid "Send later?" msgstr "بعد ارسال شود؟" #: modules/webmessage/lib/webmessage_templates.py:282 #: modules/websubmit/lib/websubmit_templates.py:2857 msgid "To:" msgstr "به:" #: modules/webmessage/lib/webmessage_templates.py:283 msgid "Users" msgstr "کاربران" #: modules/webmessage/lib/webmessage_templates.py:284 msgid "Groups" msgstr "گروه ها" #: modules/webmessage/lib/webmessage_templates.py:285 #: modules/webmessage/lib/webmessage_templates.py:447 #: modules/websubmit/lib/websubmit_templates.py:2858 msgid "Subject:" msgstr "موضوع:" #: modules/webmessage/lib/webmessage_templates.py:286 #: modules/websubmit/lib/websubmit_templates.py:2859 msgid "Message:" msgstr "پیام:" #: modules/webmessage/lib/webmessage_templates.py:287 #: modules/websubmit/lib/websubmit_templates.py:2860 msgid "SEND" msgstr "ارسال" #: modules/webmessage/lib/webmessage_templates.py:446 msgid "From:" msgstr "از:" #: modules/webmessage/lib/webmessage_templates.py:448 msgid "Sent on:" msgstr "" #: modules/webmessage/lib/webmessage_templates.py:449 msgid "Received on:" msgstr "" #: modules/webmessage/lib/webmessage_templates.py:450 msgid "Sent to:" msgstr "ارسال به:" #: modules/webmessage/lib/webmessage_templates.py:451 msgid "Sent to groups:" msgstr "ارسال به گروه ها:" #: modules/webmessage/lib/webmessage_templates.py:452 msgid "REPLY" msgstr "پاسخ دادن" #: modules/webmessage/lib/webmessage_templates.py:453 msgid "DELETE" msgstr "حذف کردن" #: modules/webmessage/lib/webmessage_templates.py:506 msgid "Are you sure you want to empty your whole mailbox?" msgstr "آیا مطمئن هستید که می خواهید کل صندوق پستی خود را خالی کنید؟" #: modules/webmessage/lib/webmessage_templates.py:582 #, python-format msgid "Quota used: %(x_nb_used)i messages out of max. %(x_nb_total)i" msgstr "" #: modules/webmessage/lib/webmessage_templates.py:600 msgid "Please select one or more:" msgstr "لطفا یکی یا بیشتر را انتخاب کنید:" #: modules/webmessage/lib/webmessage_templates.py:631 msgid "Add to users" msgstr "افزودن به کاربران" #: modules/webmessage/lib/webmessage_templates.py:633 msgid "Add to groups" msgstr "افزودن به گروه ها " #: modules/webmessage/lib/webmessage_templates.py:636 msgid "No matching user" msgstr "" #: modules/webmessage/lib/webmessage_templates.py:638 #: modules/websession/lib/websession_templates.py:2008 msgid "No matching group" msgstr "" #: modules/webmessage/lib/webmessage_templates.py:675 msgid "Find users or groups:" msgstr "یافتن کاربران یا گروه ها:" #: modules/webmessage/lib/webmessage_templates.py:676 msgid "Find a user" msgstr "یافتن یک کاربر" #: modules/webmessage/lib/webmessage_templates.py:677 msgid "Find a group" msgstr "یافتن یک گروه" #: modules/webmessage/lib/webmessage_templates.py:692 #, python-format msgid "You have %(x_nb_new)s new messages out of %(x_nb_total)s messages" msgstr "" #: modules/webmessage/lib/webmessage_webinterface.py:82 #: modules/webmessage/lib/webmessage_webinterface.py:134 #: modules/webmessage/lib/webmessage_webinterface.py:228 #: modules/webmessage/lib/webmessage_webinterface.py:305 #: modules/webmessage/lib/webmessage_webinterface.py:351 #: modules/webmessage/lib/webmessage_webinterface.py:397 msgid "You are not authorized to use messages." msgstr "" #: modules/webmessage/lib/webmessage_webinterface.py:403 msgid "Read a message" msgstr "خواندن یک پیام" #: modules/websearch/lib/search_engine.py:947 #: modules/websearch/lib/search_engine.py:980 #: modules/websearch/lib/search_engine.py:5939 #: modules/websearch/lib/search_engine.py:6033 msgid "Search Results" msgstr "نتایج جستجو" #: modules/websearch/lib/search_engine.py:1152 #: modules/websearch/lib/websearch_templates.py:1369 msgid "any day" msgstr "هر روزی" #: modules/websearch/lib/search_engine.py:1158 #: modules/websearch/lib/websearch_templates.py:1381 msgid "any month" msgstr "هر ماهی" #: modules/websearch/lib/search_engine.py:1167 #: modules/websearch/lib/websearch_templates.py:1396 msgid "any year" msgstr "هر سالی" #: modules/websearch/lib/search_engine.py:1213 #: modules/websearch/lib/search_engine.py:1232 msgid "any publisher or journal" msgstr "" #: modules/websearch/lib/search_engine.py:1213 #: modules/websearch/lib/search_engine.py:1232 msgid "any public collection" msgstr "هر مجموعه عمومی" #: modules/websearch/lib/search_engine.py:1217 msgid "remove this publisher or journal" msgstr "" #: modules/websearch/lib/search_engine.py:1217 msgid "remove this collection" msgstr "پاک کردن این مجموعه" #: modules/websearch/lib/search_engine.py:1228 msgid "add another publisher or journal" msgstr "" #: modules/websearch/lib/search_engine.py:1228 msgid "add another collection" msgstr "افزودن مجوعه دیگر" #: modules/websearch/lib/search_engine.py:1238 #: modules/websearch/lib/websearch_webcoll.py:627 msgid "rank by" msgstr "رتبه بندی به وسیله" #: modules/websearch/lib/search_engine.py:1382 #: modules/websearch/lib/websearch_webcoll.py:597 msgid "latest first" msgstr "" #: modules/websearch/lib/search_engine.py:2016 msgid "No values found." msgstr "هیچ مقداری یافت نشد." #: modules/websearch/lib/search_engine.py:2141 msgid "" "Full-text search is currently available for all arXiv papers, many theses, a " "few report series and some journal articles" msgstr "" #: modules/websearch/lib/search_engine.py:2143 #, python-format msgid "" "Warning: figure caption search is only available for a subset of papers " "mostly from %(x_range_from_year)s-%(x_range_to_year)s." msgstr "" #: modules/websearch/lib/search_engine.py:2151 #, python-format msgid "There is no index %s. Searching for %s in all fields." msgstr "" #: modules/websearch/lib/search_engine.py:2155 #, python-format msgid "Instead searching %s." msgstr "" #: modules/websearch/lib/search_engine.py:2161 msgid "Search term too generic, displaying only partial results..." msgstr "" #: modules/websearch/lib/search_engine.py:2165 msgid "" "Search term after reference operator too generic, displaying only partial " "results..." msgstr "" #: modules/websearch/lib/search_engine.py:2169 msgid "" "Search term after citedby operator too generic, displaying only partial " "results..." msgstr "" #: modules/websearch/lib/search_engine.py:2173 msgid "" "No phrase index available for fulltext yet, looking for word combination..." msgstr "" #: modules/websearch/lib/search_engine.py:2213 #, python-format msgid "No exact match found for %(x_query1)s, using %(x_query2)s instead..." msgstr "" #: modules/websearch/lib/search_engine.py:2352 msgid "" "Search syntax misunderstood. Ignoring all parentheses in the query. If this " "doesn't help, please check your search and try again." msgstr "" #: modules/websearch/lib/search_engine.py:3161 #, python-format msgid "" "No match found in collection %(x_collection)s. Other collections gave " "%(x_url_open)s%(x_nb_hits)d hits%(x_url_close)s." msgstr "" #: modules/websearch/lib/search_engine.py:3178 msgid "" "No public collection matched your query. If you were looking for a hidden " "document, please type the correct URL for this record." msgstr "" #: modules/websearch/lib/search_engine.py:3182 msgid "" "No public collection matched your query. If you were looking for a non-" "public document, please choose the desired restricted collection first." msgstr "" #: modules/websearch/lib/search_engine.py:3289 msgid "Your search did not match any records. Please try again." msgstr "" #: modules/websearch/lib/search_engine.py:3298 msgid "No match found, please enter different search terms." msgstr "" #: modules/websearch/lib/search_engine.py:3304 #, python-format msgid "There are no records referring to %s." msgstr "" #: modules/websearch/lib/search_engine.py:3306 #, fuzzy, python-format msgid "There are no records modified by %s." msgstr "تا کنون هیچ گزارشی وجود نداشته است." #: modules/websearch/lib/search_engine.py:3308 #, python-format msgid "There are no records cited by %s." msgstr "" #: modules/websearch/lib/search_engine.py:3313 #, python-format msgid "No word index is available for %s." msgstr "" #: modules/websearch/lib/search_engine.py:3324 #, python-format msgid "No phrase index is available for %s." msgstr "" #: modules/websearch/lib/search_engine.py:3363 #, python-format msgid "" "Search term %(x_term)s inside index %(x_index)s did not match any record. " "Nearest terms in any collection are:" msgstr "" #: modules/websearch/lib/search_engine.py:3367 #, python-format msgid "" "Search term %s did not match any record. Nearest terms in any collection are:" msgstr "" #: modules/websearch/lib/search_engine.py:4249 #: modules/websearch/lib/search_engine.py:4391 #, python-format msgid "" "Sorry, %s does not seem to be a valid sort option. The records will not be " "sorted." msgstr "" #: modules/websearch/lib/search_engine.py:4379 #, python-format msgid "" "Sorry, sorting is allowed on sets of up to %d records only. Using default " "sort order." msgstr "" #: modules/websearch/lib/search_engine.py:4671 #: modules/websearch/lib/search_engine.py:5041 #, fuzzy, python-format msgid "The record %d replaces it." msgstr "رکورد حذف شده است." #: modules/websearch/lib/search_engine.py:4897 msgid "Use different search terms." msgstr "اصطلاحات جستجوی مختلفی را استفاده کنید." #: modules/websearch/lib/search_engine.py:6317 msgid "No match within your time limits, discarding this condition..." msgstr "" #: modules/websearch/lib/search_engine.py:6351 msgid "No match within your search limits, discarding this condition..." msgstr "" #: modules/websearch/lib/services/CollectionNameSearchService.py:51 msgid "Looking for a particular collection? Try:" msgstr "" #: modules/websearch/lib/services/CollectionNameSearchService.py:113 #: modules/websearch/lib/services/CollectionNameSearchService.py:114 #, fuzzy msgid "submit" msgstr "واگذاری" #: modules/websearch/lib/services/FAQKBService.py:37 msgid "You might be interested in:" msgstr "" #: modules/websearch/lib/services/JournalHintService.py:99 msgid "Were you looking for this paper?" msgstr "" #: modules/websearch/lib/services/JournalHintService.py:105 #, python-format msgid "Were you looking for a journal reference? Try: %(x_href)s" msgstr "" #: modules/websearch/lib/services/SubmissionNameSearchService.py:61 msgid "Looking for a particular submission? Try:" msgstr "" #: modules/websearch/lib/services/SubmissionNameSearchService.py:130 #: modules/webstyle/lib/webstyle_templates.py:493 #: modules/webstyle/lib/webstyle_templates.py:579 #: modules/websubmit/lib/websubmit_engine.py:737 #: modules/websubmit/lib/websubmit_engine.py:1226 #: modules/websubmit/lib/websubmit_engine.py:1282 #: modules/websubmit/lib/websubmit_engine.py:1559 msgid "Submit" msgstr "واگذاری" #: modules/websearch/lib/services/SubmissionNameSearchService.py:131 #, fuzzy msgid "Revise" msgstr "بررسی" #: modules/websearch/lib/services/WeatherService.py:62 #, fuzzy msgid "weather" msgstr "دیگران" #: modules/websearch/lib/websearch_external_collections.py:148 msgid "in" msgstr "در" #: modules/websearch/lib/websearch_external_collections_templates.py:51 msgid "" "Haven't found what you were looking for? Try your search on other servers:" msgstr "" #: modules/websearch/lib/websearch_external_collections_templates.py:79 msgid "External collections results overview:" msgstr "" #: modules/websearch/lib/websearch_external_collections_templates.py:119 msgid "Search timed out." msgstr "" #: modules/websearch/lib/websearch_external_collections_templates.py:120 msgid "" "The external search engine has not responded in time. You can check its " "results here:" msgstr "" #: modules/websearch/lib/websearch_external_collections_templates.py:146 #: modules/websearch/lib/websearch_external_collections_templates.py:154 #: modules/websearch/lib/websearch_external_collections_templates.py:168 msgid "No results found." msgstr "نتایج یافت نشد." #: modules/websearch/lib/websearch_external_collections_templates.py:150 #, python-format msgid "%s results found" msgstr "" #: modules/websearch/lib/websearch_external_collections_templates.py:152 #, python-format msgid "%s seconds" msgstr "" #: modules/websearch/lib/websearch_templates.py:474 #, python-format msgid "Search on %(x_CFG_SITE_NAME_INTL)s" msgstr "" #: modules/websearch/lib/websearch_templates.py:733 #: modules/websearch/lib/websearch_templates.py:882 #: modules/websearch/lib/websearch_templates.py:1147 #, python-format msgid "Search %s records for:" msgstr "" #: modules/websearch/lib/websearch_templates.py:785 msgid "less" msgstr "" #: modules/websearch/lib/websearch_templates.py:786 #: modules/websearch/lib/websearch_templates.py:1703 #: modules/websearch/lib/websearch_templates.py:4279 #: modules/websearch/lib/websearch_templates.py:4356 #: modules/websearch/lib/websearch_templates.py:4450 msgid "more" msgstr "بیشتر" #: modules/websearch/lib/websearch_templates.py:791 #, python-format msgid "Example: %(x_sample_search_query)s" msgstr "" #: modules/websearch/lib/websearch_templates.py:803 #: modules/websearch/lib/websearch_templates.py:2337 #, python-format msgid "Search in %(x_collection_name)s" msgstr "" #: modules/websearch/lib/websearch_templates.py:807 #: modules/websearch/lib/websearch_templates.py:2341 msgid "Search everywhere" msgstr "" #: modules/websearch/lib/websearch_templates.py:841 #: modules/websearch/lib/websearch_templates.py:918 #: modules/websearch/lib/websearch_templates.py:1151 #: modules/websearch/lib/websearch_templates.py:2171 #: modules/websearch/lib/websearch_templates.py:2309 #: modules/websearch/lib/websearch_templates.py:2367 msgid "Advanced Search" msgstr "جستجوی پیشرفته" #: modules/websearch/lib/websearch_templates.py:979 #, python-format msgid "Search %s records for" msgstr "" #: modules/websearch/lib/websearch_templates.py:1030 #: modules/websearch/lib/websearch_templates.py:2225 msgid "Simple Search" msgstr "جستجوی ساده" #: modules/websearch/lib/websearch_templates.py:1063 msgid "Search options:" msgstr "گزینه های جستجو" #: modules/websearch/lib/websearch_templates.py:1110 #: modules/websearch/lib/websearch_templates.py:2463 msgid "Added/modified since:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1111 #: modules/websearch/lib/websearch_templates.py:2464 msgid "until:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1116 #: modules/websearch/lib/websearch_templates.py:2506 msgid "Sort by:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1117 #: modules/websearch/lib/websearch_templates.py:2507 msgid "Display results:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1118 #: modules/websearch/lib/websearch_templates.py:2508 msgid "Output format:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1240 #: modules/websearch/lib/websearch_templates.py:1265 #, fuzzy msgid "Add to Search" msgstr "افزودن به کاربران" #: modules/websearch/lib/websearch_templates.py:1423 msgid "Added since:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1424 msgid "Modified since:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1521 msgid "restricted" msgstr "" #: modules/websearch/lib/websearch_templates.py:1549 msgid "Search also:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1620 msgid "" "This collection is restricted. If you are authorized to access it, please " "click on the Search button." msgstr "" #: modules/websearch/lib/websearch_templates.py:1635 msgid "" "This is a hosted external collection. Please click on the Search button to " "see its content." msgstr "" #: modules/websearch/lib/websearch_templates.py:1650 msgid "This collection does not contain any document yet." msgstr "" #: modules/websearch/lib/websearch_templates.py:1821 #: modules/websearch/lib/websearch_templates.py:3629 #, python-format msgid "Cited by %i records" msgstr "" #: modules/websearch/lib/websearch_templates.py:1887 #, python-format msgid "Words nearest to %(x_word)s inside %(x_field)s in any collection are:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1890 #, python-format msgid "Words nearest to %(x_word)s in any collection are:" msgstr "" #: modules/websearch/lib/websearch_templates.py:1982 msgid "Hits" msgstr "" #: modules/websearch/lib/websearch_templates.py:2412 msgid "collections" msgstr "مجموعه ها" #: modules/websearch/lib/websearch_templates.py:2434 msgid "Limit to:" msgstr "" #: modules/websearch/lib/websearch_templates.py:2476 #: modules/websearch/lib/websearch_webcoll.py:645 msgid "results" msgstr "نتایج" #: modules/websearch/lib/websearch_templates.py:2512 #: modules/websearch/lib/websearch_webcoll.py:615 msgid "asc." msgstr "" #: modules/websearch/lib/websearch_templates.py:2515 #: modules/websearch/lib/websearch_webcoll.py:616 msgid "desc." msgstr "" #: modules/websearch/lib/websearch_templates.py:2521 #: modules/websearch/lib/websearch_webcoll.py:660 msgid "single list" msgstr "" #: modules/websearch/lib/websearch_templates.py:2524 #: modules/websearch/lib/websearch_webcoll.py:659 msgid "split by collection" msgstr "" #: modules/websearch/lib/websearch_templates.py:2562 msgid "MARC tag" msgstr "برچسب مارک" #: modules/websearch/lib/websearch_templates.py:2658 #: modules/websearch/lib/websearch_templates.py:2663 #: modules/websearch/lib/websearch_templates.py:2841 #: modules/websearch/lib/websearch_templates.py:2853 #: modules/websearch/lib/websearch_templates.py:3193 #: modules/websearch/lib/websearch_templates.py:3204 #, python-format msgid "%s records found" msgstr "" #: modules/websearch/lib/websearch_templates.py:2714 #: modules/websearch/lib/websearch_templates.py:2904 msgid "end" msgstr "پایان" #: modules/websearch/lib/websearch_templates.py:2734 #: modules/websearch/lib/websearch_templates.py:2924 msgid "jump to record:" msgstr "پرش به رکورد" #: modules/websearch/lib/websearch_templates.py:2747 #: modules/websearch/lib/websearch_templates.py:2937 #, python-format msgid "Search took %s seconds." msgstr "" #: modules/websearch/lib/websearch_templates.py:3141 #, python-format msgid "" "%(x_fmt_open)sResults overview:%(x_fmt_close)s Found %(x_nb_records)s " "records in %(x_nb_seconds)s seconds." msgstr "" #: modules/websearch/lib/websearch_templates.py:3153 #, python-format msgid "%(x_fmt_open)sResults overview%(x_fmt_close)s" msgstr "" #: modules/websearch/lib/websearch_templates.py:3161 #, python-format msgid "" "%(x_fmt_open)sResults overview:%(x_fmt_close)s Found at least " "%(x_nb_records)s records in %(x_nb_seconds)s seconds." msgstr "" #: modules/websearch/lib/websearch_templates.py:3207 #, fuzzy msgid "Show less collections" msgstr "مجموعه ها" #: modules/websearch/lib/websearch_templates.py:3208 #, fuzzy msgid "Show all collections" msgstr "مجموعه ها" #: modules/websearch/lib/websearch_templates.py:3265 msgid "No results found..." msgstr "هیچ نتیجه ای یافت نشد ..." #: modules/websearch/lib/websearch_templates.py:3314 msgid "Less suggestions" msgstr "" #: modules/websearch/lib/websearch_templates.py:3315 #, fuzzy msgid "More suggestions" msgstr "واگذاری های شما" #: modules/websearch/lib/websearch_templates.py:3348 msgid "" "Boolean query returned no hits. Please combine your search terms differently." msgstr "" #: modules/websearch/lib/websearch_templates.py:3380 msgid "See also: similar author names" msgstr "" #: modules/websearch/lib/websearch_templates.py:3630 msgid "Cited by 1 record" msgstr "به وسیله 1 رکورد استناد شده" #: modules/websearch/lib/websearch_templates.py:3645 #, python-format msgid "%i comments" msgstr "" #: modules/websearch/lib/websearch_templates.py:3646 msgid "1 comment" msgstr "1 نظر" #: modules/websearch/lib/websearch_templates.py:3656 #, python-format msgid "%i reviews" msgstr "" #: modules/websearch/lib/websearch_templates.py:3657 msgid "1 review" msgstr "1 بررسی" #: modules/websearch/lib/websearch_templates.py:3889 #: modules/websearch/lib/websearch_webinterface.py:849 #, python-format msgid "Collection %s Not Found" msgstr "" #: modules/websearch/lib/websearch_templates.py:3901 #: modules/websearch/lib/websearch_webinterface.py:845 #, python-format msgid "Sorry, collection %s does not seem to exist." msgstr "" #: modules/websearch/lib/websearch_templates.py:3903 #: modules/websearch/lib/websearch_webinterface.py:846 #, python-format msgid "You may want to start browsing from %s." msgstr "" #: modules/websearch/lib/websearch_templates.py:3930 #, python-format msgid "" "Set up a personal %(x_url1_open)semail alert%(x_url1_close)s\n" " or subscribe to the %(x_url2_open)sRSS feed" "%(x_url2_close)s." msgstr "" #: modules/websearch/lib/websearch_templates.py:3937 #, python-format msgid "Subscribe to the %(x_url2_open)sRSS feed%(x_url2_close)s." msgstr "" #: modules/websearch/lib/websearch_templates.py:3946 msgid "Interested in being notified about new results for this query?" msgstr "" #: modules/websearch/lib/websearch_templates.py:4012 #: modules/websearch/lib/websearch_templates.py:4037 #: modules/websearch/lib/websearch_templates.py:4087 msgid "Back to search" msgstr "بازگشت به جستجو" #: modules/websearch/lib/websearch_templates.py:4047 #: modules/websearch/lib/websearch_templates.py:4063 #: modules/websearch/lib/websearch_templates.py:4079 #, python-format msgid "%s of" msgstr "" #: modules/websearch/lib/websearch_templates.py:4272 msgid "People who downloaded this document also downloaded:" msgstr "" "افرادی که این مدرک را دانلود کرده اند همچنین موارد زیر را دانلود کرده اند:" #: modules/websearch/lib/websearch_templates.py:4288 msgid "People who viewed this page also viewed:" msgstr "افرادی که این صفحه را دیده اند همچنین صفحه های زیر را دیده اند:" #: modules/websearch/lib/websearch_templates.py:4342 #, python-format msgid "Cited by: %s records" msgstr "" #: modules/websearch/lib/websearch_templates.py:4443 #, python-format msgid "Co-cited with: %s records" msgstr "" #: modules/websearch/lib/websearch_templates.py:4485 #, python-format msgid ".. of which self-citations: %s records" msgstr "" #: modules/websearch/lib/websearch_templates.py:4629 msgid "No Papers" msgstr "بدون مقاله ها" #: modules/websearch/lib/websearch_templates.py:4675 msgid "Frequent co-authors" msgstr "" #: modules/websearch/lib/websearch_templates.py:4710 msgid "This is me. Verify my publication list." msgstr "" #: modules/websearch/lib/websearch_templates.py:4749 msgid "Citations:" msgstr "استنادها" #: modules/websearch/lib/websearch_templates.py:4753 msgid "No Citation Information available" msgstr "هیچ اطلاعات استنادی قابل استفاده نیست" #: modules/websearch/lib/websearch_templates.py:4834 #, fuzzy msgid "Back to citesummary" msgstr "بازگشت به جستجو" #: modules/websearch/lib/websearch_templates.py:4845 #, python-format msgid "

%(msg)s

" msgstr "" #: modules/websearch/lib/websearch_templates.py:4852 msgid "Exclude self-citations" msgstr "" #: modules/websearch/lib/websearch_templates.py:4866 msgid "Citation summary results" msgstr "" #: modules/websearch/lib/websearch_templates.py:4871 #, fuzzy msgid "Total number of papers analyzed:" msgstr "تعداد کل مقالات قابل استناد تحلیل شده:" #: modules/websearch/lib/websearch_templates.py:4895 msgid "Total number of citations:" msgstr "تعداد کل استنادها:" #: modules/websearch/lib/websearch_templates.py:4902 msgid "Average citations per paper:" msgstr "میانگین استنادهای هر مقاله:" #: modules/websearch/lib/websearch_templates.py:4913 #, fuzzy msgid "Total number of citations excluding self-citations" msgstr "تعداد کل استنادها:" #: modules/websearch/lib/websearch_templates.py:4926 #, fuzzy msgid "Average citations per paper excluding self-citations" msgstr "میانگین استنادهای هر مقاله:" #: modules/websearch/lib/websearch_templates.py:4945 msgid "Breakdown of papers by citations:" msgstr "" #: modules/websearch/lib/websearch_templates.py:4987 #, fuzzy msgid "Citation metrics" msgstr "سنجه های استنادی" #: modules/websearch/lib/websearch_templates.py:5064 #, python-format msgid "" "For some unknown reason multiple records matching the specified DOI \"%s\" " "have been found." msgstr "" #: modules/websearch/lib/websearch_templates.py:5065 #: modules/webstyle/lib/webstyle_templates.py:1014 msgid "The system administrators have been alerted." msgstr "" #: modules/websearch/lib/websearch_templates.py:5066 msgid "In the meantime you can pick one of the retrieved candidates:" msgstr "" #: modules/websearch/lib/websearch_webcoll.py:228 msgid "Focus on:" msgstr "" #: modules/websearch/lib/websearch_webcoll.py:231 #, fuzzy msgid "Narrow by publisher/journal:" msgstr "خاص براساس مجموعه" #: modules/websearch/lib/websearch_webcoll.py:235 msgid "Latest additions:" msgstr "" #: modules/websearch/lib/websearch_webcoll.py:659 msgid "split by publisher/journal" msgstr "" #: modules/websearch/lib/websearch_webcoll.py:681 msgid "brief" msgstr "خلاصه" #: modules/websearch/lib/websearch_webinterface.py:414 msgid "You are not authorized to view this area." msgstr "" #: modules/websearch/lib/websearch_webinterface.py:851 #: modules/websearch/lib/websearch_webinterface.py:970 msgid "Not found" msgstr "پیدا نشد" #: modules/websearch/lib/websearch_webinterface.py:964 #, fuzzy, python-format msgid "Sorry, DOI %s could not be resolved." msgstr "این پیام نمی تواند حذف شود." #: modules/websearch/lib/websearch_webinterface.py:968 #, fuzzy, python-format msgid "DOI \"%s\" Not Found" msgstr "صفحه پیدا نشد" #: modules/websearch/lib/websearch_webinterface.py:983 #, python-format msgid "Found multiple records matching DOI %s" msgstr "" #: modules/websearch/lib/websearch_webinterface.py:985 msgid "Found multiple records matching DOI" msgstr "" #: modules/websearch/lib/websearchadminlib.py:3454 msgid "Information" msgstr "اطلاعات" #: modules/websearch/lib/websearchadminlib.py:3455 msgid "References" msgstr "ارجاعات" #: modules/websearch/lib/websearchadminlib.py:3456 msgid "Citations" msgstr "استنادها" #: modules/websearch/lib/websearchadminlib.py:3458 msgid "Discussion" msgstr "بحث" #: modules/websearch/lib/websearchadminlib.py:3459 msgid "Usage statistics" msgstr "آمارهای استفاده" #: modules/websearch/lib/websearchadminlib.py:3460 msgid "Files" msgstr "" #: modules/websearch/lib/websearchadminlib.py:3461 msgid "Plots" msgstr "" #: modules/websearch/lib/websearchadminlib.py:3462 msgid "Holdings" msgstr "موجودی" #: modules/websearch/lib/websearchadminlib.py:3463 msgid "Linkbacks" msgstr "" #: modules/websearch/lib/websearchadminlib.py:3464 #, fuzzy msgid "HepData" msgstr "داده" #: modules/websession/lib/webaccount.py:117 #, python-format msgid "" "You are logged in as guest. You may want to %(x_url_open)slogin" "%(x_url_close)s as a regular user." msgstr "" #: modules/websession/lib/webaccount.py:121 #, python-format msgid "" "The %(x_fmt_open)sguest%(x_fmt_close)s users need to %(x_url_open)sregister" "%(x_url_close)s first" msgstr "" #: modules/websession/lib/webaccount.py:126 msgid "No queries found" msgstr "" #: modules/websession/lib/webaccount.py:397 msgid "" "This collection is restricted. If you think you have right to access it, " "please authenticate yourself." msgstr "" #: modules/websession/lib/webaccount.py:398 msgid "" "This file is restricted. If you think you have right to access it, please " "authenticate yourself." msgstr "" #: modules/websession/lib/webaccount.py:399 msgid "The OpenID identifier is invalid" msgstr "" #: modules/websession/lib/webaccount.py:400 msgid "" "python-openid package must be installed: run make install-openid-package or " "download manually from https://github.com/openid/python-openid/" msgstr "" #: modules/websession/lib/webaccount.py:400 #: modules/websession/lib/webaccount.py:401 #: modules/websession/lib/webaccount.py:402 #, python-format msgid "Please inform the administator" msgstr "" #: modules/websession/lib/webaccount.py:401 msgid "" "rauth package must be installed: run make install-oauth-package or download " "manually from https://github.com/litl/rauth/" msgstr "" #: modules/websession/lib/webaccount.py:402 msgid "The configuration isn't set properly" msgstr "" #: modules/websession/lib/webaccount.py:403 msgid "Cannot connect the provider. Please try again later." msgstr "" #: modules/websession/lib/webgroup.py:158 #: modules/websession/lib/webgroup.py:432 msgid "Please enter a group name." msgstr "لطفا نام یک گروه را وارد کنید." #: modules/websession/lib/webgroup.py:168 #: modules/websession/lib/webgroup.py:442 msgid "Please enter a valid group name." msgstr "" #: modules/websession/lib/webgroup.py:178 #: modules/websession/lib/webgroup.py:452 msgid "Please choose a group join policy." msgstr "" #: modules/websession/lib/webgroup.py:188 #: modules/websession/lib/webgroup.py:462 msgid "Group name already exists. Please choose another group name." msgstr "" #: modules/websession/lib/webgroup.py:260 msgid "You are already member of the group." msgstr "شما قبلا عضو گروه بوده اید." #: modules/websession/lib/webgroup.py:302 msgid "Please select only one group." msgstr "لطفا تنها یک گروه را انتخاب کنید." #: modules/websession/lib/webgroup.py:359 msgid "Please select one group." msgstr "لطفا یک گروه را انتخاب کنید." #: modules/websession/lib/webgroup.py:384 #: modules/websession/lib/webgroup.py:399 #: modules/websession/lib/webgroup.py:510 #: modules/websession/lib/webgroup.py:555 #: modules/websession/lib/webgroup.py:570 #: modules/websession/lib/webgroup.py:604 #: modules/websession/lib/webgroup.py:644 #: modules/websession/lib/webgroup.py:711 msgid "Sorry, there was an error with the database." msgstr "" #: modules/websession/lib/webgroup.py:391 #: modules/websession/lib/webgroup.py:562 msgid "Sorry, you do not have sufficient rights on this group." msgstr "" #: modules/websession/lib/webgroup.py:499 msgid "The group has already been deleted." msgstr "گروه قبلا حذف شده است." #: modules/websession/lib/webgroup.py:611 msgid "Please choose a member if you want to remove him from the group." msgstr "اگر می خواهید عضوی از گروه را حذف کنید لطفا وی را انتخاب کنید." #: modules/websession/lib/webgroup.py:651 msgid "" "Please choose a user from the list if you want him to be added to the group." msgstr "" "اگر می خواهید کاربری را به گروه اضافه کنید لطفا وی را از سیاهه انتخاب کنید." #: modules/websession/lib/webgroup.py:664 msgid "The user is already member of the group." msgstr "کاربر قبلا عضو گروه بوده است." #: modules/websession/lib/webgroup.py:718 msgid "" "Please choose a user from the list if you want him to be removed from " "waiting list." msgstr "" "اگر می خواهید کاربری را از سیاهه انتظار حذف کنید لطفا وی را از سیاهه انتخاب " "کنید." #: modules/websession/lib/webgroup.py:731 msgid "The user request for joining group has already been rejected." msgstr "درخواست کاربر برای پیوستن گروه قبلا رد شده است." #: modules/websession/lib/webgroup_dblayer.py:314 #: modules/websession/lib/webuser.py:310 msgid "user" msgstr "کاربر" #: modules/websession/lib/websession_templates.py:96 msgid "External account settings" msgstr "" #: modules/websession/lib/websession_templates.py:98 #, python-format msgid "" "You can consult the list of your external groups directly in the " "%(x_url_open)sgroups page%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_templates.py:102 msgid "External user groups" msgstr "گروه های کاربری بیرونی" #: modules/websession/lib/websession_templates.py:133 msgid "API keys" msgstr "" #: modules/websession/lib/websession_templates.py:137 msgid "These are your current API keys" msgstr "" #: modules/websession/lib/websession_templates.py:162 #, fuzzy msgid "Description: " msgstr "توصیف" #: modules/websession/lib/websession_templates.py:163 #, fuzzy msgid "Status: " msgstr "وضعیت" #: modules/websession/lib/websession_templates.py:166 msgid "API key" msgstr "" #: modules/websession/lib/websession_templates.py:167 #, fuzzy msgid "Delete key" msgstr "حذف سبد" #: modules/websession/lib/websession_templates.py:195 msgid "If you want to create a new API key, please enter a description for it" msgstr "" #: modules/websession/lib/websession_templates.py:196 msgid "Description for the new API key" msgstr "" #: modules/websession/lib/websession_templates.py:197 #: modules/websession/lib/websession_templates.py:270 #: modules/websession/lib/websession_templates.py:318 #: modules/websession/lib/websession_templates.py:1208 msgid "mandatory" msgstr "الزامی" #: modules/websession/lib/websession_templates.py:199 msgid "" "The description should be something meaningful for you to recognize the API " "key" msgstr "" #: modules/websession/lib/websession_templates.py:200 #, fuzzy msgid "Create new key" msgstr "ایجاد سبد جدید" #: modules/websession/lib/websession_templates.py:262 msgid "" "If you want to change your email or set for the first time your nickname, " "please set new values in the form below." msgstr "" #: modules/websession/lib/websession_templates.py:263 msgid "Edit login credentials" msgstr "" #: modules/websession/lib/websession_templates.py:269 msgid "New email address" msgstr "نشانی پست الکترونیکی جدید" #: modules/websession/lib/websession_templates.py:273 msgid "Set new values" msgstr "مقادیر جدید را تنظیم کنید" #: modules/websession/lib/websession_templates.py:277 msgid "" "Since this is considered as a signature for comments and reviews, once set " "it can not be changed." msgstr "" #: modules/websession/lib/websession_templates.py:317 msgid "" "If you want to change your password, please enter the old one and set the " "new value in the form below." msgstr "" #: modules/websession/lib/websession_templates.py:319 msgid "Old password" msgstr "کلمه عبور قدیم" #: modules/websession/lib/websession_templates.py:320 msgid "New password" msgstr "کلمه عبور جدید" #: modules/websession/lib/websession_templates.py:322 #: modules/websession/lib/websession_templates.py:1209 msgid "optional" msgstr "انتخابی" #: modules/websession/lib/websession_templates.py:324 #: modules/websession/lib/websession_templates.py:1212 msgid "The password phrase may contain punctuation, spaces, etc." msgstr "" #: modules/websession/lib/websession_templates.py:325 msgid "You must fill the old password in order to set a new one." msgstr "" #: modules/websession/lib/websession_templates.py:326 msgid "Retype password" msgstr "کلمه عبور را دوباره تایپ کنید" #: modules/websession/lib/websession_templates.py:327 msgid "Set new password" msgstr "کلمه عبور جدید را بگذارید" #: modules/websession/lib/websession_templates.py:332 #, python-format msgid "" "If you are using a lightweight CERN account you can\n" " %(x_url_open)sreset the password%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_templates.py:338 #, python-format msgid "" "You can change or reset your CERN account password by means of the " "%(x_url_open)sCERN account system%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_templates.py:364 msgid "Edit cataloging interface settings" msgstr "" #: modules/websession/lib/websession_templates.py:365 #: modules/websession/lib/websession_templates.py:1063 msgid "Username" msgstr "نام کاربری" #: modules/websession/lib/websession_templates.py:366 #: modules/websession/lib/websession_templates.py:1064 #: modules/websession/lib/websession_templates.py:1207 msgid "Password" msgstr "کلمه عبور" #: modules/websession/lib/websession_templates.py:367 #: modules/websession/lib/websession_templates.py:394 #: modules/websession/lib/websession_templates.py:422 #: modules/websession/lib/websession_templates.py:459 msgid "Update settings" msgstr "بروزرسانی تنظیمات" #: modules/websession/lib/websession_templates.py:382 msgid "Edit language-related settings" msgstr "" #: modules/websession/lib/websession_templates.py:393 msgid "Select desired language of the web interface." msgstr "" #: modules/websession/lib/websession_templates.py:409 #, fuzzy msgid "Edit profiling settings" msgstr "ویرایش تنظیمات" #: modules/websession/lib/websession_templates.py:413 msgid "Disabled" msgstr "" #: modules/websession/lib/websession_templates.py:417 msgid "Enabled" msgstr "" #: modules/websession/lib/websession_templates.py:441 msgid "Edit search-related settings" msgstr "" #: modules/websession/lib/websession_templates.py:442 msgid "Show the latest additions box" msgstr "" #: modules/websession/lib/websession_templates.py:444 msgid "Show collection help boxes" msgstr "" #: modules/websession/lib/websession_templates.py:460 msgid "Number of search results per page" msgstr "تعداد نتایج جستجو در هر صفحه" #: modules/websession/lib/websession_templates.py:493 msgid "Edit login method" msgstr "" #: modules/websession/lib/websession_templates.py:494 msgid "" "Please select which login method you would like to use to authenticate " "yourself" msgstr "" #: modules/websession/lib/websession_templates.py:495 #: modules/websession/lib/websession_templates.py:510 msgid "Select method" msgstr "" #: modules/websession/lib/websession_templates.py:529 #, python-format msgid "" "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." msgstr "" #: modules/websession/lib/websession_templates.py:551 #: modules/websession/lib/websession_templates.py:1205 msgid "Email address" msgstr "نشانی پست الکترونیکی" #: modules/websession/lib/websession_templates.py:552 msgid "Send password reset link" msgstr "" #: modules/websession/lib/websession_templates.py:556 #, python-format msgid "" "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." msgstr "" #: modules/websession/lib/websession_templates.py:559 msgid "" "Note that if you have been using an external login system, then we cannot do " "anything and you have to ask there." msgstr "" #: modules/websession/lib/websession_templates.py:560 #, python-format msgid "" "Alternatively, you can ask %s to change your login system from external to " "internal." msgstr "" #: modules/websession/lib/websession_templates.py:587 #, python-format msgid "" "%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." msgstr "" #: modules/websession/lib/websession_templates.py:597 #: modules/websession/lib/websession_webinterface.py:332 msgid "Your Settings" msgstr "تنظیمات شما" #: modules/websession/lib/websession_templates.py:598 msgid "" "Set or change your account email address or password. Specify your " "preferences about the look and feel of the interface." msgstr "" #: modules/websession/lib/websession_templates.py:606 msgid "View all the searches you performed during the last 30 days." msgstr "" #: modules/websession/lib/websession_templates.py:614 msgid "" "With baskets you can define specific collections of items, store interesting " "records you want to access later or share with others." msgstr "" #: modules/websession/lib/websession_templates.py:622 msgid "Display all the comments you have submitted so far." msgstr "" #: modules/websession/lib/websession_templates.py:629 msgid "" "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." msgstr "" #: modules/websession/lib/websession_templates.py:637 msgid "" "Check out book you have on loan, submit borrowing requests, etc. Requires " "CERN ID." msgstr "" #: modules/websession/lib/websession_templates.py:664 msgid "" "You are logged in as a guest user, so your alerts will disappear at the end " "of the current session." msgstr "" #: modules/websession/lib/websession_templates.py:687 #, python-format msgid "" "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." msgstr "" #: modules/websession/lib/websession_templates.py:771 #, python-format msgid "" "You can consult the list of %(x_url_open)syour comments%(x_url_close)s " "submitted so far." msgstr "" #: modules/websession/lib/websession_templates.py:776 msgid "Your Alert Searches" msgstr "" #: modules/websession/lib/websession_templates.py:782 #, python-format msgid "" "You can consult the list of %(x_url_open)syour groups%(x_url_close)s you are " "administering or are a member of." msgstr "" #: modules/websession/lib/websession_templates.py:785 #: modules/websession/lib/websession_templates.py:2515 #: modules/websession/lib/websession_webinterface.py:1503 msgid "Your Groups" msgstr "گروه های شما" #: modules/websession/lib/websession_templates.py:788 #, python-format msgid "" "You can consult the list of %(x_url_open)syour submissions%(x_url_close)s " "and inquire about their status." msgstr "" #: modules/websession/lib/websession_templates.py:791 #: modules/websubmit/web/yoursubmissions.py:154 msgid "Your Submissions" msgstr "واگذاری های شما" #: modules/websession/lib/websession_templates.py:794 #, python-format msgid "" "You can consult the list of %(x_url_open)syour approvals%(x_url_close)s with " "the documents you approved or refereed." msgstr "" #: modules/websession/lib/websession_templates.py:797 #: modules/websubmit/web/yourapprovals.py:88 msgid "Your Approvals" msgstr "" #: modules/websession/lib/websession_templates.py:801 #, python-format msgid "You can consult the list of %(x_url_open)syour tickets%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_templates.py:804 msgid "Your Tickets" msgstr "" #: modules/websession/lib/websession_templates.py:806 #: modules/websession/lib/websession_webinterface.py:714 msgid "Your Administrative Activities" msgstr "" #: modules/websession/lib/websession_templates.py:833 msgid "Try again" msgstr "مجدد تلاش کنید" #: modules/websession/lib/websession_templates.py:855 #, python-format msgid "" "Somebody (possibly you) coming from %(x_ip_address)s has asked\n" "for a password reset at %(x_sitename)s\n" "for the account \"%(x_email)s\"." msgstr "" #: modules/websession/lib/websession_templates.py:863 msgid "If you want to reset the password for this account, please go to:" msgstr "" #: modules/websession/lib/websession_templates.py:869 #: modules/websession/lib/websession_templates.py:906 msgid "in order to confirm the validity of this request." msgstr "" #: modules/websession/lib/websession_templates.py:870 #: modules/websession/lib/websession_templates.py:907 #, python-format msgid "" "Please note that this URL will remain valid for about %(days)s days only." msgstr "" #: modules/websession/lib/websession_templates.py:892 #, python-format msgid "" "Somebody (possibly you) coming from %(x_ip_address)s has asked\n" "to register a new account at %(x_sitename)s\n" "for the email address \"%(x_email)s\"." msgstr "" #: modules/websession/lib/websession_templates.py:900 msgid "If you want to complete this account registration, please go to:" msgstr "" #: modules/websession/lib/websession_templates.py:926 #, python-format msgid "Okay, a password reset link has been emailed to %s." msgstr "" #: modules/websession/lib/websession_templates.py:941 msgid "Deleting your account" msgstr "حذف حساب شما" #: modules/websession/lib/websession_templates.py:955 msgid "You are no longer recognized by our system." msgstr "" #: modules/websession/lib/websession_templates.py:957 #, python-format msgid "" "You are still recognized by the centralized\n" " %(x_fmt_open)sSSO%(x_fmt_close)s system. You can\n" " %(x_url_open)slogout from SSO%(x_url_close)s, too." msgstr "" #: modules/websession/lib/websession_templates.py:964 #, python-format msgid "If you wish you can %(x_url_open)slogin here%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_templates.py:997 msgid "If you already have an account, please login using the form below." msgstr "" #: modules/websession/lib/websession_templates.py:1001 #, python-format msgid "" "If you don't own a CERN account yet, you can register a %(x_url_open)snew " "CERN lightweight account%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_templates.py:1004 #, python-format msgid "" "If you don't own an account yet, please %(x_url_open)sregister" "%(x_url_close)s an internal account." msgstr "" #: modules/websession/lib/websession_templates.py:1012 #, python-format msgid "If you don't own an account yet, please contact %s." msgstr "" #: modules/websession/lib/websession_templates.py:1036 msgid "Login method:" msgstr "روش ورود:" #: modules/websession/lib/websession_templates.py:1065 msgid "Remember login on this computer." msgstr "" #: modules/websession/lib/websession_templates.py:1066 #: modules/websession/lib/websession_templates.py:1363 #: modules/websession/lib/websession_webinterface.py:116 #: modules/websession/lib/websession_webinterface.py:211 #: modules/websession/lib/websession_webinterface.py:837 #: modules/websession/lib/websession_webinterface.py:960 msgid "login" msgstr "ورود" #: modules/websession/lib/websession_templates.py:1071 #: modules/websession/lib/websession_webinterface.py:618 msgid "Lost your password?" msgstr "کلمه عبورتان را از دست داده اید؟" #: modules/websession/lib/websession_templates.py:1079 msgid "You can use your nickname or your email address to login." msgstr "" #: modules/websession/lib/websession_templates.py:1112 msgid "" "Your request is valid. Please set the new desired password in the following " "form." msgstr "" #: modules/websession/lib/websession_templates.py:1135 msgid "Set a new password for" msgstr "تنظیم یک کلمه عبور جدید برای" #: modules/websession/lib/websession_templates.py:1136 msgid "Type the new password" msgstr "کلمه عبور جدید را تایپ کنید" #: modules/websession/lib/websession_templates.py:1137 msgid "Type again the new password" msgstr "کلمه عبور جدید را دوباره تایپ کنید" #: modules/websession/lib/websession_templates.py:1138 msgid "Set the new password" msgstr "تنظیم کلمه عبور جدید" #: modules/websession/lib/websession_templates.py:1160 msgid "Please enter your email address and desired nickname and password:" msgstr "" #: modules/websession/lib/websession_templates.py:1162 msgid "" "It will not be possible to use the account before it has been verified and " "activated." msgstr "" #: modules/websession/lib/websession_templates.py:1213 msgid "Retype Password" msgstr "تایپ دوباره کلمه عبور" #: modules/websession/lib/websession_templates.py:1214 #: modules/websession/lib/websession_webinterface.py:1063 msgid "register" msgstr "ثبت" #: modules/websession/lib/websession_templates.py:1215 #, python-format msgid "" "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." msgstr "" #: modules/websession/lib/websession_templates.py:1219 #, python-format msgid "" "It is not possible to create an account yourself. Contact %s if you want an " "account." msgstr "" #: modules/websession/lib/websession_templates.py:1245 #, python-format msgid "" "You seem to be a guest user. You have to %(x_url_open)slogin%(x_url_close)s " "first." msgstr "" #: modules/websession/lib/websession_templates.py:1251 msgid "You are not authorized to access administrative functions." msgstr "" #: modules/websession/lib/websession_templates.py:1254 #, python-format msgid "You are enabled to the following roles: %(x_role)s." msgstr "" #: modules/websession/lib/websession_templates.py:1266 msgid "Run Author List Manager" msgstr "" #: modules/websession/lib/websession_templates.py:1272 msgid "Run BibSword Client" msgstr "" #: modules/websession/lib/websession_templates.py:1306 msgid "Here are some interesting web admin links for you:" msgstr "" #: modules/websession/lib/websession_templates.py:1308 #, python-format msgid "" "For more admin-level activities, see the complete %(x_url_open)sAdmin Area" "%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_templates.py:1361 msgid "guest" msgstr "مهمان" #: modules/websession/lib/websession_templates.py:1375 msgid "logout" msgstr "خروج" #: modules/websession/lib/websession_templates.py:1425 #: modules/webstyle/lib/webstyle_templates.py:494 #: modules/webstyle/lib/webstyle_templates.py:580 msgid "Personalize" msgstr "شخص کردن" #: modules/websession/lib/websession_templates.py:1433 msgid "Your account" msgstr "حساب شما" #: modules/websession/lib/websession_templates.py:1439 msgid "Your alerts" msgstr "هشدارهای شما" #: modules/websession/lib/websession_templates.py:1445 msgid "Your approvals" msgstr "تأییدهای شما" #: modules/websession/lib/websession_templates.py:1451 msgid "Your baskets" msgstr "سبدهای شما" #: modules/websession/lib/websession_templates.py:1457 #, fuzzy msgid "Your comments" msgstr "بدون نظر " #: modules/websession/lib/websession_templates.py:1463 msgid "Your groups" msgstr "گروه های شما" #: modules/websession/lib/websession_templates.py:1469 msgid "Your loans" msgstr "امانت های شما" #: modules/websession/lib/websession_templates.py:1475 msgid "Your messages" msgstr "پیام های شما" #: modules/websession/lib/websession_templates.py:1481 msgid "Your submissions" msgstr "واگذاری های شما" #: modules/websession/lib/websession_templates.py:1487 msgid "Your searches" msgstr "جستجوهای شما" #: modules/websession/lib/websession_templates.py:1540 msgid "Administration" msgstr "مدیریت" #: modules/websession/lib/websession_templates.py:1556 msgid "Statistics" msgstr "آمارها" #: modules/websession/lib/websession_templates.py:1673 msgid "You are an administrator of the following groups:" msgstr "شما اداره کننده گروه های ذیل هستید:" #: modules/websession/lib/websession_templates.py:1693 #: modules/websession/lib/websession_templates.py:1767 #: modules/websession/lib/websession_templates.py:1830 #: modules/websubmit/lib/websubmit_templates.py:2789 #: modules/websubmit/lib/websubmit_templates.py:2795 msgid "Group" msgstr "گروه" #: modules/websession/lib/websession_templates.py:1700 msgid "You are not an administrator of any groups." msgstr "شما اداره کننده هیچ گروهی نیستید" #: modules/websession/lib/websession_templates.py:1707 msgid "Edit group" msgstr "ویرایش گروه" #: modules/websession/lib/websession_templates.py:1714 #, python-format msgid "Edit %s members" msgstr "" #: modules/websession/lib/websession_templates.py:1737 #: modules/websession/lib/websession_templates.py:1877 #: modules/websession/lib/websession_templates.py:1879 #: modules/websession/lib/websession_webinterface.py:1559 msgid "Create new group" msgstr "ایجاد گروه جدید" #: modules/websession/lib/websession_templates.py:1751 msgid "You are a member of the following groups:" msgstr "شما عضوی از گروه های ذیل هستید:" #: modules/websession/lib/websession_templates.py:1774 msgid "You are not a member of any groups." msgstr "شما عضو هیچ گروهی نیستید." #: modules/websession/lib/websession_templates.py:1798 msgid "Join new group" msgstr "به گروه جدیدی بپیوندید" #: modules/websession/lib/websession_templates.py:1799 #: modules/websession/lib/websession_templates.py:2351 #: modules/websession/lib/websession_templates.py:2362 msgid "Leave group" msgstr "گروه را ترک کنید" #: modules/websession/lib/websession_templates.py:1814 msgid "You are a member of the following external groups:" msgstr "شما عضوی از گروهای بیرونی ذیل هستید:" #: modules/websession/lib/websession_templates.py:1837 msgid "You are not a member of any external groups." msgstr "شما عضو هیچ گروه بیرونی نیستید." #: modules/websession/lib/websession_templates.py:1885 msgid "Update group" msgstr "بروز رسانی گروه" #: modules/websession/lib/websession_templates.py:1887 #, python-format msgid "Edit group %s" msgstr "" #: modules/websession/lib/websession_templates.py:1889 msgid "Delete group" msgstr "حذف گروه" #: modules/websession/lib/websession_templates.py:1962 msgid "Group name:" msgstr "نام گروه:" #: modules/websession/lib/websession_templates.py:1964 msgid "Group description:" msgstr "توصیف گروه:" #: modules/websession/lib/websession_templates.py:1965 msgid "Group join policy:" msgstr "خط مشی پیوستن به گروه:" #: modules/websession/lib/websession_templates.py:2006 #: modules/websession/lib/websession_templates.py:2079 #: modules/websession/lib/websession_templates.py:2220 #: modules/websession/lib/websession_templates.py:2229 #: modules/websession/lib/websession_templates.py:2349 #: modules/websession/lib/websession_templates.py:2461 msgid "Please select:" msgstr "لطفا انتخاب کنید:" #: modules/websession/lib/websession_templates.py:2072 msgid "Join group" msgstr "به گروه بپیوندید" #: modules/websession/lib/websession_templates.py:2074 msgid "or find it" msgstr "یا آن را بیابید" #: modules/websession/lib/websession_templates.py:2075 msgid "Choose group:" msgstr "انتخاب گروه:" #: modules/websession/lib/websession_templates.py:2077 msgid "Find group" msgstr "یافتن گروه" #: modules/websession/lib/websession_templates.py:2225 msgid "Remove member" msgstr "پاک کردن عضو" #: modules/websession/lib/websession_templates.py:2227 msgid "No members." msgstr "بدون اعضاء." #: modules/websession/lib/websession_templates.py:2237 msgid "Accept member" msgstr "پذیرش عضو" #: modules/websession/lib/websession_templates.py:2237 msgid "Reject member" msgstr "رد عضو" #: modules/websession/lib/websession_templates.py:2239 msgid "No members awaiting approval." msgstr "بدون اعضاء منتظر تأیید." #: modules/websession/lib/websession_templates.py:2241 #: modules/websession/lib/websession_templates.py:2275 msgid "Current members" msgstr "اعضاء جاری" #: modules/websession/lib/websession_templates.py:2242 #: modules/websession/lib/websession_templates.py:2276 msgid "Members awaiting approval" msgstr "اعضاء منتظر تأیید" #: modules/websession/lib/websession_templates.py:2243 #: modules/websession/lib/websession_templates.py:2277 msgid "Invite new members" msgstr "دعوت اعضاء جدید" #: modules/websession/lib/websession_templates.py:2248 #, python-format msgid "Invitation to join \"%s\" group" msgstr "" #: modules/websession/lib/websession_templates.py:2249 #, python-format msgid "" "Hello:\n" "\n" "I think you might be interested in joining the group \"%(x_name)s\".\n" "You can join by clicking here: %(x_url)s.\n" "\n" "Best regards.\n" msgstr "" #: modules/websession/lib/websession_templates.py:2263 #, python-format msgid "" "If you want to invite new members to join your group, please use the " "%(x_url_open)sweb message%(x_url_close)s system." msgstr "" #: modules/websession/lib/websession_templates.py:2267 #, python-format msgid "Group: %s" msgstr "" #: modules/websession/lib/websession_templates.py:2350 msgid "Group list" msgstr "سیاهه گروه" #: modules/websession/lib/websession_templates.py:2353 msgid "You are not member of any group." msgstr "شما عضو هیچ گروهی نیستید." #: modules/websession/lib/websession_templates.py:2401 msgid "Are you sure you want to delete this group?" msgstr "آیا مطمئن هستند که می خواهید این گروه را حذف کنید؟" #: modules/websession/lib/websession_templates.py:2441 msgid "Are you sure you want to leave this group?" msgstr "آیا مطمئن هستید که می خواهید این گروه را ترک کنید؟" #: modules/websession/lib/websession_templates.py:2457 msgid "Visible and open for new members" msgstr "آشکار و آزاد برای اعضاء جدید" #: modules/websession/lib/websession_templates.py:2459 msgid "Visible but new members need approval" msgstr "آشکار اما اعضاء جدید نیاز به تأیید دارند" #: modules/websession/lib/websession_templates.py:2544 #, python-format msgid "Group %s: New membership request" msgstr "" #: modules/websession/lib/websession_templates.py:2548 #, python-format msgid "A user wants to join the group %s." msgstr "" #: modules/websession/lib/websession_templates.py:2549 #, python-format msgid "" "Please %(x_url_open)saccept or reject%(x_url_close)s this user's request." msgstr "" #: modules/websession/lib/websession_templates.py:2566 #, python-format msgid "Group %s: Join request has been accepted" msgstr "" #: modules/websession/lib/websession_templates.py:2567 #, python-format msgid "Your request for joining group %s has been accepted." msgstr "" #: modules/websession/lib/websession_templates.py:2569 #, python-format msgid "Group %s: Join request has been rejected" msgstr "" #: modules/websession/lib/websession_templates.py:2570 #, python-format msgid "Your request for joining group %s has been rejected." msgstr "" #: modules/websession/lib/websession_templates.py:2573 #: modules/websession/lib/websession_templates.py:2591 #, python-format msgid "You can consult the list of %(x_url_open)syour groups%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_templates.py:2587 #, python-format msgid "Group %s has been deleted" msgstr "" #: modules/websession/lib/websession_templates.py:2589 #, python-format msgid "Group %s has been deleted by its administrator." msgstr "" #: modules/websession/lib/websession_templates.py:2606 #, python-format msgid "" "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)." msgstr "" #: modules/websession/lib/websession_templates.py:2625 msgid "" "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." msgstr "" #: modules/websession/lib/websession_templates.py:2631 msgid "" "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." msgstr "" #: modules/websession/lib/websession_templates.py:2637 msgid "" "Warning: The password set for the Invenio admin user is currently empty. For " "security purposes, it is strongly recommended that you add a password." msgstr "" #: modules/websession/lib/websession_templates.py:2643 msgid "" "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." msgstr "" #: modules/websession/lib/websession_templates.py:2649 msgid "" "A newer version of Invenio is available for download. You may want to visit " msgstr "" "نسخه جدیدتری از اینونیو برای دانلود در دسترس است. شما ممکن است خواهان دیدن " "آن باشید." #: modules/websession/lib/websession_templates.py:2656 msgid "" "Cannot download or parse release notes from http://invenio-software.org/repo/" "invenio/tree/RELEASE-NOTES" msgstr "" #: modules/websession/lib/websession_templates.py:2661 #, python-format msgid "" "Your e-mail is auto-generated by the system. Please change your e-mail from " "account settings." msgstr "" #: modules/websession/lib/websession_webinterface.py:98 msgid "Mail Cookie Service" msgstr "" #: modules/websession/lib/websession_webinterface.py:108 msgid "Role authorization request" msgstr "" #: modules/websession/lib/websession_webinterface.py:108 msgid "This request for an authorization has already been authorized." msgstr "" #: modules/websession/lib/websession_webinterface.py:111 #, python-format msgid "" "You have successfully obtained an authorization as %(x_role)s! This " "authorization will last until %(x_expiration)s and until you close your " "browser if you are a guest user." msgstr "" #: modules/websession/lib/websession_webinterface.py:129 msgid "You have confirmed the validity of your email address!" msgstr "" #: modules/websession/lib/websession_webinterface.py:132 #: modules/websession/lib/websession_webinterface.py:142 msgid "Please, wait for the administrator to enable your account." msgstr "" #: modules/websession/lib/websession_webinterface.py:136 #: modules/websession/lib/websession_webinterface.py:145 #, python-format msgid "You can now go to %(x_url_open)syour account page%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_webinterface.py:137 #: modules/websession/lib/websession_webinterface.py:146 msgid "Email address successfully activated" msgstr "نشانی پست الکترونیکی به طور موفقیت آمیز فعال شد." #: modules/websession/lib/websession_webinterface.py:140 msgid "You have already confirmed the validity of your email address!" msgstr "" #: modules/websession/lib/websession_webinterface.py:149 msgid "" "This request for confirmation of an email address is not valid or is expired." msgstr "" #: modules/websession/lib/websession_webinterface.py:154 msgid "This request for an authorization is not valid or is expired." msgstr "" #: modules/websession/lib/websession_webinterface.py:167 msgid "Reset password" msgstr "بازتنظیم کلمه عبور" #: modules/websession/lib/websession_webinterface.py:173 msgid "This request for resetting a password has already been used." msgstr "" #: modules/websession/lib/websession_webinterface.py:176 msgid "This request for resetting a password is not valid or is expired." msgstr "" #: modules/websession/lib/websession_webinterface.py:181 msgid "This request for resetting the password is not valid or is expired." msgstr "" #: modules/websession/lib/websession_webinterface.py:194 msgid "The two provided passwords aren't equal." msgstr "دو کلمه عبور قرار داده شده، یکسان نیستند." #: modules/websession/lib/websession_webinterface.py:209 msgid "The password was successfully set! You can now proceed with the login." msgstr "" #: modules/websession/lib/websession_webinterface.py:340 #, python-format msgid "%s Personalize, Your Settings" msgstr "" #: modules/websession/lib/websession_webinterface.py:409 #: modules/websession/lib/websession_webinterface.py:483 #: modules/websession/lib/websession_webinterface.py:545 #: modules/websession/lib/websession_webinterface.py:558 #: modules/websession/lib/websession_webinterface.py:571 msgid "Settings edited" msgstr "تنظیمات ویرایش شده" #: modules/websession/lib/websession_webinterface.py:411 #: modules/websession/lib/websession_webinterface.py:482 #: modules/websession/lib/websession_webinterface.py:523 #: modules/websession/lib/websession_webinterface.py:547 #: modules/websession/lib/websession_webinterface.py:560 #: modules/websession/lib/websession_webinterface.py:566 msgid "Show account" msgstr "نمایش حساب" #: modules/websession/lib/websession_webinterface.py:415 msgid "Unable to change login method." msgstr "نمی تواند روش ورود را تغییر دهد." #: modules/websession/lib/websession_webinterface.py:423 msgid "Switched to internal login method." msgstr "به روش ورود درونی تعویض شد." #: modules/websession/lib/websession_webinterface.py:424 msgid "" "Please note that if this is the first time that you are using this account " "with the internal login method then the system has set for you a randomly " "generated password. Please click the following button to obtain a password " "reset request link sent to you via email:" msgstr "" #: modules/websession/lib/websession_webinterface.py:432 msgid "Send Password" msgstr "ارسال کلمه عبور" #: modules/websession/lib/websession_webinterface.py:440 #, python-format msgid "" "Unable to switch to external login method %s, because your email address is " "unknown." msgstr "" #: modules/websession/lib/websession_webinterface.py:444 #, python-format msgid "" "Unable to switch to external login method %s, because your email address is " "unknown to the external login system." msgstr "" #: modules/websession/lib/websession_webinterface.py:448 msgid "Login method successfully selected." msgstr "روش ورود به طور موفقیت آمیز انتخاب شد." #: modules/websession/lib/websession_webinterface.py:450 #, python-format msgid "" "The external login method %s does not support email address based logins. " "Please contact the site administrators." msgstr "" #: modules/websession/lib/websession_webinterface.py:462 #, fuzzy msgid "Your nickname has not been updated" msgstr "صندوق پستی شما خالی دشه است." #: modules/websession/lib/websession_webinterface.py:476 msgid "Settings successfully edited." msgstr "تنظیمات به طور موفقیت آمیز ویرایش شد." #: modules/websession/lib/websession_webinterface.py:477 #, python-format msgid "" "Note that if you have changed your email address, you will have to " "%(x_url_open)sreset your password%(x_url_close)s anew." msgstr "" #: modules/websession/lib/websession_webinterface.py:485 #: modules/websession/lib/websession_webinterface.py:1033 #, python-format msgid "Desired nickname %s is invalid." msgstr "" #: modules/websession/lib/websession_webinterface.py:486 #: modules/websession/lib/websession_webinterface.py:492 #: modules/websession/lib/websession_webinterface.py:505 #: modules/websession/lib/websession_webinterface.py:527 #: modules/websession/lib/websession_webinterface.py:533 #: modules/websession/lib/websession_webinterface.py:1024 #: modules/websession/lib/websession_webinterface.py:1029 #: modules/websession/lib/websession_webinterface.py:1034 #: modules/websession/lib/websession_webinterface.py:1045 msgid "Please try again." msgstr "لطفا دوباره تلاش کنید." #: modules/websession/lib/websession_webinterface.py:488 #: modules/websession/lib/websession_webinterface.py:494 #: modules/websession/lib/websession_webinterface.py:501 #: modules/websession/lib/websession_webinterface.py:507 #: modules/websession/lib/websession_webinterface.py:529 #: modules/websession/lib/websession_webinterface.py:535 #: modules/websession/lib/websession_webinterface.py:590 msgid "Edit settings" msgstr "ویرایش تنظیمات" #: modules/websession/lib/websession_webinterface.py:489 #: modules/websession/lib/websession_webinterface.py:495 #: modules/websession/lib/websession_webinterface.py:502 #: modules/websession/lib/websession_webinterface.py:508 #: modules/websession/lib/websession_webinterface.py:592 msgid "Editing settings failed" msgstr "ویرایش تنظیمات عمل نکرد" #: modules/websession/lib/websession_webinterface.py:491 #: modules/websession/lib/websession_webinterface.py:1028 #, python-format msgid "Supplied email address %s is invalid." msgstr "" #: modules/websession/lib/websession_webinterface.py:497 #: modules/websession/lib/websession_webinterface.py:1038 #, python-format msgid "Supplied email address %s already exists in the database." msgstr "" #: modules/websession/lib/websession_webinterface.py:499 #: modules/websession/lib/websession_webinterface.py:1040 msgid "Or please try again." msgstr "یا لطفا دوباره تلاش کنید." #: modules/websession/lib/websession_webinterface.py:504 #, python-format msgid "Desired nickname %s is already in use." msgstr "" #: modules/websession/lib/websession_webinterface.py:513 msgid "Users cannot edit passwords on this site." msgstr "کاربران نمی توانند کلمه های عبور این سایت را ویرایش کنند." #: modules/websession/lib/websession_webinterface.py:521 msgid "Password successfully edited." msgstr "کلمه عبور به طور موفقیت آمیز ویرایش شد." #: modules/websession/lib/websession_webinterface.py:524 msgid "Password edited" msgstr "کلمه عبور ویرایش شد" #: modules/websession/lib/websession_webinterface.py:526 #: modules/websession/lib/websession_webinterface.py:1023 msgid "Both passwords must match." msgstr "هر دو کلمه عبور باید منطبق باشند." #: modules/websession/lib/websession_webinterface.py:530 #: modules/websession/lib/websession_webinterface.py:536 msgid "Editing password failed" msgstr "ویرایش کلمه عبور عمل نکرد" #: modules/websession/lib/websession_webinterface.py:532 msgid "Wrong old password inserted." msgstr "کلمه عبور قدیمی اشتباه وارد شده است." #: modules/websession/lib/websession_webinterface.py:548 #: modules/websession/lib/websession_webinterface.py:561 #: modules/websession/lib/websession_webinterface.py:575 #: modules/websession/lib/websession_webinterface.py:583 msgid "User settings saved correctly." msgstr "تنظیمات کاربری به طور صحیح ذخیره شد." #: modules/websession/lib/websession_webinterface.py:568 msgid "Editing bibcatalog authorization failed" msgstr "" #: modules/websession/lib/websession_webinterface.py:569 msgid "Empty username or password" msgstr "خالی کردن نام کاربری یا کلمه عبور" #: modules/websession/lib/websession_webinterface.py:586 msgid "Unable to update settings." msgstr "نمی توان تنظیمات را بروزرسانی کرد." #: modules/websession/lib/websession_webinterface.py:647 msgid "" "Cannot send password reset request since you are using external " "authentication system." msgstr "" #: modules/websession/lib/websession_webinterface.py:663 msgid "The entered email address does not exist in the database." msgstr "" #: modules/websession/lib/websession_webinterface.py:677 msgid "Password reset request for" msgstr "درخواست بازتنظیم کلمه عبور برای" #: modules/websession/lib/websession_webinterface.py:681 msgid "" "The entered email address is incorrect, please check that it is written " "correctly (e.g. johndoe@example.com)." msgstr "" #: modules/websession/lib/websession_webinterface.py:682 msgid "Incorrect email address" msgstr "نشانی پست الکترونیکی نادرست" #: modules/websession/lib/websession_webinterface.py:692 msgid "Reset password link sent" msgstr "لینک بازتنظیم کلمه عبور ارسال شد" #: modules/websession/lib/websession_webinterface.py:737 msgid "Delete Account" msgstr "حذف حساب" #: modules/websession/lib/websession_webinterface.py:763 msgid "Logout" msgstr "خروج" #: modules/websession/lib/websession_webinterface.py:836 #: modules/websession/lib/websession_webinterface.py:913 #: modules/websession/lib/websession_webinterface.py:959 #: modules/websession/lib/websession_webinterface.py:1134 #: modules/websession/lib/websession_webinterface.py:1175 #: modules/websession/lib/websession_webinterface.py:1251 #: modules/websession/lib/websession_webinterface.py:1289 #: modules/websession/lib/websession_webinterface.py:1332 #: modules/websession/lib/websession_webinterface.py:1370 #: modules/websession/lib/websession_webinterface.py:1404 msgid "Login" msgstr "ورود" #: modules/websession/lib/websession_webinterface.py:990 msgid "Register" msgstr "ثبت" #: modules/websession/lib/websession_webinterface.py:993 #: modules/websession/lib/websession_webinterface.py:1065 #, python-format msgid "%s Personalize, Main page" msgstr "" #: modules/websession/lib/websession_webinterface.py:1010 msgid "Your account has been successfully created." msgstr "حساب شما به طور موفقیت آمیزی ایجاد شد." #: modules/websession/lib/websession_webinterface.py:1011 msgid "Account created" msgstr "حساب ایجاد شد" #: modules/websession/lib/websession_webinterface.py:1013 msgid "" "In order to confirm its validity, an email message containing an account " "activation key has been sent to the given email address." msgstr "" #: modules/websession/lib/websession_webinterface.py:1014 msgid "" "Please follow instructions presented there in order to complete the account " "registration process." msgstr "" #: modules/websession/lib/websession_webinterface.py:1016 msgid "" "A second email will be sent when the account has been activated and can be " "used." msgstr "" #: modules/websession/lib/websession_webinterface.py:1019 #, python-format msgid "You can now access your %(x_url_open)saccount%(x_url_close)s." msgstr "" #: modules/websession/lib/websession_webinterface.py:1026 #: modules/websession/lib/websession_webinterface.py:1031 #: modules/websession/lib/websession_webinterface.py:1036 #: modules/websession/lib/websession_webinterface.py:1042 #: modules/websession/lib/websession_webinterface.py:1047 #: modules/websession/lib/websession_webinterface.py:1051 #: modules/websession/lib/websession_webinterface.py:1055 #: modules/websession/lib/websession_webinterface.py:1060 msgid "Registration failure" msgstr "ثبت نام شکست خورد" #: modules/websession/lib/websession_webinterface.py:1044 #, python-format msgid "Desired nickname %s already exists in the database." msgstr "" #: modules/websession/lib/websession_webinterface.py:1049 msgid "Users cannot register themselves, only admin can register them." msgstr "" #: modules/websession/lib/websession_webinterface.py:1053 msgid "" "The site is having troubles in sending you an email for confirming your " "email address." msgstr "" #: modules/websession/lib/websession_webinterface.py:1462 msgid "Your tickets" msgstr "" #: modules/websession/lib/websession_webinterface.py:1498 #: modules/websession/lib/websession_webinterface.py:1539 #: modules/websession/lib/websession_webinterface.py:1598 #: modules/websession/lib/websession_webinterface.py:1660 #: modules/websession/lib/websession_webinterface.py:1717 #: modules/websession/lib/websession_webinterface.py:1788 msgid "You are not authorized to use groups." msgstr "" #: modules/websession/lib/websession_webinterface.py:1623 msgid "Join New Group" msgstr "پیوستن گروه جدید" #: modules/websession/lib/websession_webinterface.py:1675 msgid "Leave Group" msgstr "ترک گروه" #: modules/websession/lib/websession_webinterface.py:1745 msgid "Edit Group" msgstr "ویرایش گروه" #: modules/websession/lib/websession_webinterface.py:1817 msgid "Edit group members" msgstr "ویرایش اعضاء گروه" #: modules/websession/lib/webuser.py:153 msgid "Database problem" msgstr "مشکل پایگاه داده" #: modules/websession/lib/webuser.py:486 #, python-format msgid "Account registration at %s" msgstr "" #: modules/websession/lib/webuser.py:840 msgid "New account on" msgstr "" #: modules/websession/lib/webuser.py:842 msgid "PLEASE ACTIVATE" msgstr "لطفا فعال کنید" #: modules/websession/lib/webuser.py:843 msgid "A new account has been created on" msgstr "" #: modules/websession/lib/webuser.py:845 msgid " and is awaiting activation" msgstr "و منتظر فعال سازی است" #: modules/websession/lib/webuser.py:847 msgid " Username/Email" msgstr "نام کاربری/ پست الکترونیکی" #: modules/websession/lib/webuser.py:848 msgid "You can approve or reject this account request at" msgstr "" #: modules/webstyle/lib/webdoc.py:566 #, python-format msgid "%(category)s Pages" msgstr "" #: modules/webstyle/lib/webdoc_info_webinterface.py:206 #: modules/webstyle/lib/webdoc_webinterface.py:187 #, python-format msgid "Sorry, page %s does not seem to exist." msgstr "" #: modules/webstyle/lib/webdoc_webinterface.py:144 #: modules/webstyle/lib/webdoc_webinterface.py:149 msgid "Admin Pages" msgstr "" #: modules/webstyle/lib/webdoc_webinterface.py:146 #: modules/webstyle/lib/webdoc_webinterface.py:150 msgid "Help Pages" msgstr "" #: modules/webstyle/lib/webdoc_webinterface.py:148 #: modules/webstyle/lib/webdoc_webinterface.py:151 msgid "Hacking Pages" msgstr "" #: modules/webstyle/lib/webdoc_webinterface.py:157 msgid "Hacking Invenio" msgstr "" #: modules/webstyle/lib/webdoc_webinterface.py:159 msgid "Latest modifications:" msgstr "" #: modules/webstyle/lib/webdoc_webinterface.py:162 #, python-format msgid "This is the table of contents of the %(x_category)s pages." msgstr "" #: modules/webstyle/lib/webdoc_webinterface.py:179 #, python-format msgid "Page %s Not Found" msgstr "" #: modules/webstyle/lib/webdoc_webinterface.py:190 #, python-format msgid "" "You may want to look at the %(x_url_open)s%(x_category)s pages" "%(x_url_close)s." msgstr "" #: modules/webstyle/lib/webpage.py:253 msgid "Warning" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:545 msgid "Last updated" msgstr "آخرین بروزرسانی" #: modules/webstyle/lib/webstyle_templates.py:583 msgid "Powered by" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:584 msgid "Maintained by" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:630 msgid "This site is also available in the following languages:" msgstr "این صفحه در زبان های زیر نیز قابل دسترسی است:" #: modules/webstyle/lib/webstyle_templates.py:663 msgid "Browser" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:685 msgid "System Error" msgstr "خطای سیستم" #: modules/webstyle/lib/webstyle_templates.py:700 msgid "Traceback" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:747 msgid "Client" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:749 msgid "Please send an error report to the administrator." msgstr "لطفا گزارش خطا را به مدیر ارسال کنید." #: modules/webstyle/lib/webstyle_templates.py:750 msgid "Send error report" msgstr "ارسال گزارش خطا" #: modules/webstyle/lib/webstyle_templates.py:754 #, python-format msgid "Please contact %s quoting the following information:" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:809 msgid "Restricted (Processing Record)" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:942 #, python-format msgid "" "Record created %(x_date_creation)s, last modified %(x_date_modification)s" msgstr "" #: modules/webstyle/lib/webstyle_templates.py:1013 msgid "The server encountered an error while dealing with your request." msgstr "" #: modules/webstyle/lib/webstyle_templates.py:1015 #, python-format msgid "In case of doubt, please contact %(x_admin_email)s." msgstr "" #: modules/websubmit/lib/functions/Shared_Functions.py:200 msgid "" "Note that your submission has been inserted into the bibliographic task " "queue and is waiting for execution.\n" msgstr "" #: modules/websubmit/lib/functions/Shared_Functions.py:203 #, python-format msgid "" "The task queue is currently running in automatic mode, and there are " "currently %s tasks waiting to be executed. Your record should be available " "within a few minutes and searchable within an hour or thereabouts.\n" msgstr "" #: modules/websubmit/lib/functions/Shared_Functions.py:205 msgid "" "Because of a human intervention or a temporary problem, the task queue is " "currently set to the manual mode. Your submission is well registered but may " "take longer than usual before it is fully integrated and searchable.\n" msgstr "" #: modules/websubmit/lib/websubmit_engine.py:195 #: modules/websubmit/lib/websubmit_engine.py:872 msgid "Not enough information to go ahead with the submission." msgstr "" #: modules/websubmit/lib/websubmit_engine.py:201 #: modules/websubmit/lib/websubmit_engine.py:282 #: modules/websubmit/lib/websubmit_engine.py:292 #: modules/websubmit/lib/websubmit_engine.py:385 #: modules/websubmit/lib/websubmit_engine.py:435 #: modules/websubmit/lib/websubmit_engine.py:886 #: modules/websubmit/lib/websubmit_engine.py:924 #: modules/websubmit/lib/websubmit_engine.py:1005 #: modules/websubmit/lib/websubmit_engine.py:1052 msgid "Invalid parameters" msgstr "" #: modules/websubmit/lib/websubmit_engine.py:207 #: modules/websubmit/lib/websubmit_engine.py:878 msgid "Invalid doctype and act parameters" msgstr "" #: modules/websubmit/lib/websubmit_engine.py:238 #: modules/websubmit/lib/websubmit_engine.py:848 #: modules/websubmit/lib/websubmit_engine.py:913 #, python-format msgid "Unable to find the submission directory for the action: %s" msgstr "" #: modules/websubmit/lib/websubmit_engine.py:247 #: modules/websubmit/lib/websubmit_engine.py:1095 msgid "Unknown document type" msgstr "" #: modules/websubmit/lib/websubmit_engine.py:253 #: modules/websubmit/lib/websubmit_engine.py:1101 msgid "Unknown action" msgstr "" #: modules/websubmit/lib/websubmit_engine.py:261 #: modules/websubmit/lib/websubmit_engine.py:953 msgid "Unable to determine the number of submission pages." msgstr "" #: modules/websubmit/lib/websubmit_engine.py:302 #: modules/websubmit/lib/websubmit_engine.py:932 msgid "" "Unable to create a directory for this submission. The administrator has been " "alerted." msgstr "" #: modules/websubmit/lib/websubmit_engine.py:441 #: modules/websubmit/lib/websubmit_engine.py:1059 msgid "Cannot create submission directory. The administrator has been alerted." msgstr "" #: modules/websubmit/lib/websubmit_engine.py:463 #: modules/websubmit/lib/websubmit_engine.py:1081 msgid "No file uploaded?" msgstr "" #: modules/websubmit/lib/websubmit_engine.py:505 #: modules/websubmit/lib/websubmit_engine.py:508 #: modules/websubmit/lib/websubmit_engine.py:646 #: modules/websubmit/lib/websubmit_engine.py:649 msgid "Unknown form field found on submission page." msgstr "" #: modules/websubmit/lib/websubmit_engine.py:1139 msgid "" "A serious function-error has been encountered. Adminstrators have been " "alerted.
Please not that this might be due to wrong characters " "inserted into the form (e.g. by copy and pasting some text from a PDF " "file)." msgstr "" #: modules/websubmit/lib/websubmit_engine.py:1501 #, python-format msgid "Unable to find document type: %s" msgstr "" #: modules/websubmit/lib/websubmit_engine.py:1537 #, fuzzy msgid "You are not authorized to access this submission interface." msgstr "شما برای دیدن این ضمیمه اجازه ندارید" #: modules/websubmit/lib/websubmit_engine.py:1808 msgid "The chosen action is not supported by the document type." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:67 msgid "Login to display all document types you can access" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:93 msgid "Document types available for submission" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:94 msgid "Please select the type of document you want to submit" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:112 msgid "No document types available." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:295 msgid "Please select a category" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:341 msgid "Select a category and then click on an action button." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:364 msgid "" "To continue with a previously interrupted submission, enter an access number " "into the box below:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:366 msgid "GO" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:471 #: modules/websubmit/lib/websubmit_templates.py:942 msgid "SUMMARY" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:513 msgid "Submission number" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:542 #: modules/websubmit/lib/websubmit_templates.py:983 msgid "Are you sure you want to quit this submission?" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:544 #: modules/websubmit/lib/websubmit_templates.py:984 #: modules/websubmit/lib/websubmit_templates.py:993 msgid "Back to main menu" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:547 msgid "" "This is your submission access number. It can be used to continue with an " "interrupted submission in case of problems." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:548 msgid "Mandatory fields appear in red in the SUMMARY window." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:695 #, python-format msgid "The field %s is mandatory." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:695 msgid "Please make a choice in the select box" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:709 msgid "Please press a button." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:717 #, python-format msgid "The field %s is mandatory. Please fill it in." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:794 #, python-format msgid "The field %(field)s is mandatory." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:795 msgid "Going back to page" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:933 msgid "finished!" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:941 msgid "end of action" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:965 msgid "Submission no" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1036 #, python-format msgid "" "Here is the %(x_action)s function list for %(x_doctype)s documents at level " "%(x_step)s" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1041 msgid "Function" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1042 msgid "Score" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1043 msgid "Running function" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1049 #, python-format msgid "Function %s does not exist." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1088 msgid "You must now" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1213 msgid "all types of document" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1268 msgid "Subm.No." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1269 msgid "Reference" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1271 msgid "First access" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1272 msgid "Last access" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1282 msgid "Are you sure you want to delete this submission?" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1283 #, python-format msgid "Delete submission %(x_id)s in %(x_docname)s" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1307 msgid "Reference not yet given" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1378 msgid "Refereed Documents" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1388 msgid "You are a general referee" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1394 msgid "You are a referee for category:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1433 #: modules/websubmit/lib/websubmit_templates.py:1478 msgid "List of refereed types of documents" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1434 #: modules/websubmit/lib/websubmit_templates.py:1479 msgid "" "Select one of the following types of documents to check the documents status" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1447 msgid "Go to specific approval workflow" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1535 msgid "List of refereed categories" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1536 #: modules/websubmit/lib/websubmit_templates.py:1685 msgid "Please choose a category" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1556 #: modules/websubmit/lib/websubmit_templates.py:1597 #: modules/websubmit/lib/websubmit_templates.py:1708 #: modules/websubmit/lib/websubmit_templates.py:1766 #: modules/websubmit/lib/websubmit_templates.py:1832 #: modules/websubmit/lib/websubmit_templates.py:1957 msgid "Pending" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1562 #: modules/websubmit/lib/websubmit_templates.py:1600 #: modules/websubmit/lib/websubmit_templates.py:1715 #: modules/websubmit/lib/websubmit_templates.py:1769 #: modules/websubmit/lib/websubmit_templates.py:1833 #: modules/websubmit/lib/websubmit_templates.py:1958 msgid "Approved" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1568 #: modules/websubmit/lib/websubmit_templates.py:1602 #: modules/websubmit/lib/websubmit_templates.py:1603 #: modules/websubmit/lib/websubmit_templates.py:1722 #: modules/websubmit/lib/websubmit_templates.py:1771 #: modules/websubmit/lib/websubmit_templates.py:1772 #: modules/websubmit/lib/websubmit_templates.py:1834 #: modules/websubmit/lib/websubmit_templates.py:1959 msgid "Rejected" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1596 #: modules/websubmit/lib/websubmit_templates.py:1765 msgid "Key" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1599 #: modules/websubmit/lib/websubmit_templates.py:1768 msgid "Waiting for approval" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1601 #: modules/websubmit/lib/websubmit_templates.py:1770 msgid "Already approved" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1604 #: modules/websubmit/lib/websubmit_templates.py:1775 msgid "Some documents are pending." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1649 msgid "List of refereing categories" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1729 #: modules/websubmit/lib/websubmit_templates.py:1773 #: modules/websubmit/lib/websubmit_templates.py:1774 #: modules/websubmit/lib/websubmit_templates.py:1960 msgid "Cancelled" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1829 #: modules/websubmit/lib/websubmit_templates.py:1917 msgid "List of refereed documents" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1830 #: modules/websubmit/lib/websubmit_templates.py:1954 msgid "Click on a report number for more information." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1831 #: modules/websubmit/lib/websubmit_templates.py:1956 msgid "Report Number" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1919 msgid "List of publication documents" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:1921 msgid "List of direct approval documents" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2094 msgid "Your request has been sent to the referee." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2110 #: modules/websubmit/lib/websubmit_templates.py:2232 #: modules/websubmit/lib/websubmit_templates.py:2543 #: modules/websubmit/lib/websubmit_templates.py:2727 msgid "Title:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2116 #: modules/websubmit/lib/websubmit_templates.py:2239 #: modules/websubmit/lib/websubmit_templates.py:2549 #: modules/websubmit/lib/websubmit_templates.py:2733 msgid "Author:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2124 #: modules/websubmit/lib/websubmit_templates.py:2248 #: modules/websubmit/lib/websubmit_templates.py:2557 #: modules/websubmit/lib/websubmit_templates.py:2741 msgid "More information:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2125 #: modules/websubmit/lib/websubmit_templates.py:2249 #: modules/websubmit/lib/websubmit_templates.py:2558 #: modules/websubmit/lib/websubmit_templates.py:2742 msgid "Click here" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2134 msgid "Approval note:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2139 #, python-format msgid "" "This document is still %(x_fmt_open)swaiting for approval%(x_fmt_close)s." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2142 #: modules/websubmit/lib/websubmit_templates.py:2163 #: modules/websubmit/lib/websubmit_templates.py:2172 msgid "It was first sent for approval on:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2144 #: modules/websubmit/lib/websubmit_templates.py:2146 #: modules/websubmit/lib/websubmit_templates.py:2165 #: modules/websubmit/lib/websubmit_templates.py:2167 #: modules/websubmit/lib/websubmit_templates.py:2174 #: modules/websubmit/lib/websubmit_templates.py:2176 msgid "Last approval email was sent on:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2147 msgid "" "You can send an approval request email again by clicking the following " "button:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2149 #: modules/websubmit/web/publiline.py:366 msgid "Send Again" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2150 msgid "WARNING! Upon confirmation, an email will be sent to the referee." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2153 msgid "" "As a referee for this document, you may approve or reject it from the " "submission interface" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2155 msgid "Approve/Reject" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2161 #, python-format msgid "This document has been %(x_fmt_open)sapproved%(x_fmt_close)s." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2162 msgid "Its approved reference is:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2168 msgid "It was approved on:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2170 #, python-format msgid "This document has been %(x_fmt_open)srejected%(x_fmt_close)s." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2177 msgid "It was rejected on:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2261 #: modules/websubmit/lib/websubmit_templates.py:2316 #: modules/websubmit/lib/websubmit_templates.py:2379 msgid "It has first been asked for refereing process on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2264 #: modules/websubmit/lib/websubmit_templates.py:2318 msgid "Last request e-mail was sent to the publication committee chair on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2268 msgid "A referee has been selected by the publication committee on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2271 #: modules/websubmit/lib/websubmit_templates.py:2334 msgid "No referee has been selected yet." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2273 #: modules/websubmit/lib/websubmit_templates.py:2336 msgid "Select a referee" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2278 msgid "" "The referee has sent his final recommendations to the publication committee " "on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2281 #: modules/websubmit/lib/websubmit_templates.py:2342 msgid "No recommendation from the referee yet." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2283 #: modules/websubmit/lib/websubmit_templates.py:2293 #: modules/websubmit/lib/websubmit_templates.py:2344 #: modules/websubmit/lib/websubmit_templates.py:2352 #: modules/websubmit/lib/websubmit_templates.py:2360 msgid "Send a recommendation" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2288 #: modules/websubmit/lib/websubmit_templates.py:2356 msgid "" "The publication committee has sent his final recommendations to the project " "leader on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2291 #: modules/websubmit/lib/websubmit_templates.py:2358 msgid "No recommendation from the publication committee yet." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2298 #: modules/websubmit/lib/websubmit_templates.py:2364 #: modules/websubmit/lib/websubmit_templates.py:2384 msgid "It has been cancelled by the author on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2302 #: modules/websubmit/lib/websubmit_templates.py:2367 #: modules/websubmit/lib/websubmit_templates.py:2387 msgid "It has been approved by the project leader on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2305 #: modules/websubmit/lib/websubmit_templates.py:2369 #: modules/websubmit/lib/websubmit_templates.py:2389 msgid "It has been rejected by the project leader on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2308 #: modules/websubmit/lib/websubmit_templates.py:2371 #: modules/websubmit/lib/websubmit_templates.py:2391 msgid "No final decision taken yet." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2310 #: modules/websubmit/lib/websubmit_templates.py:2373 #: modules/websubmit/lib/websubmit_templates.py:2393 #: modules/websubmit/web/publiline.py:1136 #: modules/websubmit/web/publiline.py:1146 msgid "Take a decision" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2321 msgid "" "An editorial board has been selected by the publication committee on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2323 msgid "Add an author list" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2326 msgid "No editorial board has been selected yet." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2328 msgid "Select an editorial board" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2332 msgid "A referee has been selected by the editorial board on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2340 msgid "" "The referee has sent his final recommendations to the editorial board on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2348 msgid "" "The editorial board has sent his final recommendations to the publication " "committee on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2350 msgid "No recommendation from the editorial board yet." msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2381 msgid "Last request e-mail was sent to the project leader on the " msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2475 msgid "Comments overview" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2597 msgid "search for user" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2599 msgid "search for users" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2602 #: modules/websubmit/lib/websubmit_templates.py:2604 #: modules/websubmit/lib/websubmit_templates.py:2657 #: modules/websubmit/lib/websubmit_templates.py:2659 msgid "select user" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2613 msgid "connected" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2616 msgid "add this user" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2666 msgid "remove this user" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2780 msgid "User" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2861 #: modules/websubmit/web/publiline.py:1133 msgid "Select:" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2862 msgid "approve" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2863 msgid "reject" msgstr "" #: modules/websubmit/lib/websubmit_templates.py:2898 #, python-brace-format msgid "Use '\\$' delimiters to write LaTeX markup. Eg: \\$e=mc^{2}\\$" msgstr "" #: modules/websubmit/lib/websubmit_webinterface.py:709 msgid "Sorry, invalid arguments" msgstr "" #: modules/websubmit/lib/websubmit_webinterface.py:716 #, fuzzy msgid "Note: the requested submission has already been completed" msgstr "درخواست کاربر برای پیوستن گروه قبلا رد شده است." #: modules/websubmit/lib/websubmit_webinterface.py:720 msgid "" "Sorry, you don't seem to have initiated a submission with the provided " "access number" msgstr "" #: modules/websubmit/lib/websubmit_webinterface.py:738 msgid "Submissions are not available" msgstr "" #: modules/websubmit/lib/websubmit_webinterface.py:742 msgid "Sorry, 'sub' parameter missing..." msgstr "" #: modules/websubmit/lib/websubmit_webinterface.py:745 msgid "Sorry. Cannot analyse parameter" msgstr "" #: modules/websubmit/lib/websubmit_webinterface.py:827 msgid "Sorry, invalid URL..." msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:3905 #, python-format msgid "" "Unable to move field at position %s to position %s on page %s of submission " "'%s%s' - Invalid Field Position Numbers" msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:3916 #, python-format msgid "" "Unable to swap field at position %s with field at position %s on page %s of " "submission %s - could not move field at position %s to temporary field " "location" msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:3927 #, python-format msgid "" "Unable to swap field at position %s with field at position %s on page %s of " "submission %s - could not move field at position %s to position %s. Please " "ask Admin to check that a field was not stranded in a temporary position" msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:3938 #, python-format msgid "" "Unable to swap field at position %s with field at position %s on page %s of " "submission %s - could not move field that was located at position %s to " "position %s from temporary position. Field is now stranded in temporary " "position and must be corrected manually by an Admin" msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:3949 #, python-format msgid "" "Unable to move field at position %s to position %s on page %s of submission " "%s - could not decrement the position of the fields below position %s. Tried " "to recover - please check that field ordering is not broken" msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:3960 #, python-format msgid "" "Unable to move field at position %s to position %s on page %s of submission " "%s%s - could not increment the position of the fields at and below position " "%s. The field that was at position %s is now stranded in a temporary " "position." msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:3971 #, python-format msgid "" "Moved field from position %s to position %s on page %s of submission '%s%s'." msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:3992 #, python-format msgid "Unable to delete field at position %s from page %s of submission '%s'" msgstr "" #: modules/websubmit/lib/websubmitadmin_engine.py:4002 #, python-format msgid "Unable to delete field at position %s from page %s of submission '%s%s'" msgstr "" #: modules/websubmit/web/approve.py:53 msgid "approve.py: cannot determine document reference" msgstr "" #: modules/websubmit/web/approve.py:56 msgid "approve.py: cannot find document in database" msgstr "" #: modules/websubmit/web/publiline.py:133 msgid "Document Approval Workflow" msgstr "" #: modules/websubmit/web/publiline.py:154 msgid "Approval and Refereeing Workflow" msgstr "" #: modules/websubmit/web/publiline.py:333 #: modules/websubmit/web/publiline.py:434 #: modules/websubmit/web/publiline.py:660 msgid "Approval has never been requested for this document." msgstr "" #: modules/websubmit/web/publiline.py:356 #: modules/websubmit/web/publiline.py:358 #: modules/websubmit/web/publiline.py:460 #: modules/websubmit/web/publiline.py:685 msgid "Unable to display document." msgstr "" #: modules/websubmit/web/publiline.py:689 #: modules/websubmit/web/publiline.py:813 #: modules/websubmit/web/publiline.py:928 #: modules/websubmit/web/publiline.py:992 #: modules/websubmit/web/publiline.py:1033 #: modules/websubmit/web/publiline.py:1089 #: modules/websubmit/web/publiline.py:1152 #: modules/websubmit/web/publiline.py:1202 msgid "Action unauthorized for this document." msgstr "" #: modules/websubmit/web/publiline.py:692 #: modules/websubmit/web/publiline.py:816 #: modules/websubmit/web/publiline.py:931 #: modules/websubmit/web/publiline.py:995 #: modules/websubmit/web/publiline.py:1036 #: modules/websubmit/web/publiline.py:1092 #: modules/websubmit/web/publiline.py:1155 #: modules/websubmit/web/publiline.py:1205 msgid "Action unavailable for this document." msgstr "" #: modules/websubmit/web/publiline.py:702 msgid "Adding users to the editorial board" msgstr "" #: modules/websubmit/web/publiline.py:730 #: modules/websubmit/web/publiline.py:853 msgid "no qualified users, try new search." msgstr "" #: modules/websubmit/web/publiline.py:732 #: modules/websubmit/web/publiline.py:855 msgid "hits" msgstr "" #: modules/websubmit/web/publiline.py:732 #: modules/websubmit/web/publiline.py:855 msgid "too many qualified users, specify more narrow search." msgstr "" #: modules/websubmit/web/publiline.py:732 #: modules/websubmit/web/publiline.py:855 msgid "limit" msgstr "" #: modules/websubmit/web/publiline.py:748 msgid "users in brackets are already attached to the role, try another one..." msgstr "" #: modules/websubmit/web/publiline.py:754 msgid "Removing users from the editorial board" msgstr "" #: modules/websubmit/web/publiline.py:790 msgid "Validate the editorial board selection" msgstr "" #: modules/websubmit/web/publiline.py:835 msgid "Referee selection" msgstr "" #: modules/websubmit/web/publiline.py:921 msgid "Come back to the document" msgstr "" #: modules/websubmit/web/publiline.py:1106 msgid "Back to the document" msgstr "" #: modules/websubmit/web/publiline.py:1233 msgid "Wrong action for this document." msgstr "" #: modules/websubmit/web/yourapprovals.py:57 msgid "You are not authorized to use approval system." msgstr "" #~ msgid "Additional Citation Metrics" #~ msgstr "سنجه های استنادی اضافی" #~ msgid "Acquisitions" #~ msgstr "فراهم آوری" #~ msgid "Order new book" #~ msgstr "سفارش کتاب جدید" #~ msgid "the beginning" #~ msgstr "آغاز" #~ msgid "now" #~ msgstr "اکنون" #~ msgid "Rejected!" #~ msgstr "رد شده!" #~ msgid "Your papers" #~ msgstr "مقالات شما" #~ msgid "Not your papers" #~ msgstr "مقالات غیر شما" #~ msgid "These are mine!" #~ msgstr "این ها مال من هستند!" #~ msgid "These are not mine!" #~ msgstr "اینها مال من نیستند!" #~ msgid "Mine!" #~ msgstr "مال من!" #~ msgid "This is not my paper!" #~ msgstr "این، مقاله من نیست!" #~ msgid "Not Mine." #~ msgstr "مال من نیست" #~ msgid "Marked as my paper!" #~ msgstr "نشانه گذاری به عنوان مقاله من" #~ msgid "Not Mine!" #~ msgstr "مال من نیست!" #~ msgid "But this is mine!" #~ msgstr "اما این مال من است!" #~ msgid "But this is my paper!" #~ msgstr "اما این مقاله ام است!" #~ msgid "Welcome!" #~ msgstr "خوش آمدید!" #~ msgid "Info" #~ msgstr "اطلاعات" #~ msgid "YES!" #~ msgstr "بله!" diff --git a/invenio/legacy/bibdocfile/api.py b/invenio/legacy/bibdocfile/api.py index dcc9d7c9b..008e87f89 100644 --- a/invenio/legacy/bibdocfile/api.py +++ b/invenio/legacy/bibdocfile/api.py @@ -1,4945 +1,4947 @@ # This file is part of Invenio. # Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 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. from __future__ import print_function """ 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,InvenioBibDocFileError @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 from six.moves import cPickle import base64 import binascii import cgi import sys import copy try: import magic if hasattr(magic, "open"): CFG_HAS_MAGIC = 1 if not hasattr(magic, "MAGIC_MIME_TYPE"): ## Patching RHEL6/CentOS6 version magic.MAGIC_MIME_TYPE = 16 elif hasattr(magic, "Magic"): CFG_HAS_MAGIC = 2 except ImportError: CFG_HAS_MAGIC = 0 from flask import current_app from datetime import datetime from mimetypes import MimeTypes from thread import get_ident from six import iteritems from weakref import ref from urlparse import urlsplit, parse_qs from invenio.utils import 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.utils.shell import escape_shell_arg, run_shell_command from invenio.legacy.dbquery import run_sql, DatabaseError from invenio.ext.logging import register_exception from invenio.legacy.bibrecord import record_get_field_instances, \ field_get_subfield_values, field_get_subfield_instances, \ encode_for_xml from invenio.utils.url import create_url, make_user_agent_string from invenio.utils.text import nice_size from invenio.modules.access.engine import acc_authorize_action from invenio.modules.access.control import acc_is_user_in_role, acc_get_role_id from invenio.modules.access.firerole import compile_role_definition, acc_firerole_check_user from invenio.modules.access.local_config import SUPERADMINROLE, CFG_WEBACCESS_WARNING_MSGS from invenio.config import CFG_SITE_URL, \ CFG_WEBDIR, CFG_BIBDOCFILE_FILEDIR,\ CFG_BIBDOCFILE_ADDITIONAL_KNOWN_FILE_EXTENSIONS, \ CFG_BIBDOCFILE_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_SITE_RECORD, CFG_PYLIBDIR, \ CFG_BIBUPLOAD_FFT_ALLOWED_EXTERNAL_URLS, \ CFG_BIBDOCFILE_ENABLE_BIBDOCFSINFO_CACHE, \ CFG_BIBDOCFILE_ADDITIONAL_KNOWN_MIMETYPES, \ CFG_BIBDOCFILE_PREFERRED_MIMETYPES_MAPPING, \ CFG_BIBCATALOG_SYSTEM from invenio.legacy.bibcatalog.api import BIBCATALOG_SYSTEM from invenio.legacy.bibdocfile.config import CFG_BIBDOCFILE_ICON_SUBFORMAT_RE, \ CFG_BIBDOCFILE_DEFAULT_ICON_SUBFORMAT from invenio.utils.hash import md5 from invenio.legacy.bibdocfile.registry import plugins import invenio.legacy.template def _plugin_bldr(plugin_code): """Preparing the plugin dictionary structure.""" if not plugin_code.__name__.split('.')[-1].startswith('bom_'): return ret = {} ret['create_instance'] = getattr(plugin_code, "create_instance", None) ret['supports'] = getattr(plugin_code, "supports", None) return ret _CFG_BIBDOC_PLUGINS = None def get_plugins(): """Lazy loading of plugins.""" global _CFG_BIBDOC_PLUGINS if _CFG_BIBDOC_PLUGINS is None: _CFG_BIBDOC_PLUGINS = filter(None, map( _plugin_bldr, plugins)) return _CFG_BIBDOC_PLUGINS bibdocfile_templates = invenio.legacy.template.load('bibdocfile') # 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 #: 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' ) DBG_LOG_QUERIES = False #: constant used if FFT correct with the obvious meaning. KEEP_OLD_VALUE = 'KEEP-OLD-VALUE' _CFG_BIBUPLOAD_FFT_ALLOWED_EXTERNAL_URLS = [(re.compile(_regex), _headers) for _regex, _headers in CFG_BIBUPLOAD_FFT_ALLOWED_EXTERNAL_URLS] _mimes = MimeTypes(strict=False) _mimes.suffix_map.update({'.tbz2' : '.tar.bz2'}) _mimes.encodings_map.update({'.bz2' : 'bzip2'}) if CFG_BIBDOCFILE_ADDITIONAL_KNOWN_MIMETYPES: for key, value in iteritems(CFG_BIBDOCFILE_ADDITIONAL_KNOWN_MIMETYPES): _mimes.add_type(key, value) del key, value _magic_cookies = {} if CFG_HAS_MAGIC == 1: 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), magic.MAGIC_MIME_TYPE: magic.open(magic.MAGIC_MIME_TYPE), } for key in _magic_cookies[thread_id].keys(): _magic_cookies[thread_id][key].load() return _magic_cookies[thread_id] elif CFG_HAS_MAGIC == 2: def _magic_wrapper(local_path, mime=True, mime_encoding=False): thread_id = get_ident() if (thread_id, mime, mime_encoding) not in _magic_cookies: magic_object = _magic_cookies[thread_id, mime, mime_encoding] = magic.Magic(mime=mime, mime_encoding=mime_encoding) else: magic_object = _magic_cookies[thread_id, mime, mime_encoding] return magic_object.from_file(local_path) # pylint: disable=E1103 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_BIBDOCFILE_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 InvenioBibDocFileError(Exception): """ Exception raised in case of errors related to fulltext files. """ pass class InvenioBibdocfileUnauthorizedURL(InvenioBibDocFileError): """ Exception raised in case of errors related to fulltext files. """ ## NOTE: this is a legacy Exception pass def _val_or_null(val, eq_name = None, q_str = None, q_args = None): """ Auxiliary function helpful while building WHERE clauses of SQL queries that should contain field=val or field is val If optional parameters q_str and q_args are provided, lists are updated if val == None, a statement of the form "eq_name is Null" is returned otherwise, otherwise the function returns a parametrised comparison "eq_name=%s" with val as an argument added to the query args list. Using parametrised queries diminishes the likelihood of having SQL injection. @param val Value to compare with @type val @param eq_name The name of the database column @type eq_name string @param q_str Query string builder - list of clauses that should be connected by AND operator @type q_str list @param q_args Query arguments list. This list will be applied as a second argument of run_sql command @type q_args list @result string of a single part of WHERE clause @rtype string """ res = "" if eq_name != None: res += eq_name if val == None: if eq_name != None: res += " is " res += "NULL" if q_str != None: q_str.append(res) return res else: if eq_name != None: res += "=" res += "%s" if q_str != None: q_str.append(res) if q_args != None: q_args.append(str(val)) return res def _sql_generate_conjunctive_where(to_process): """Generating WHERE clause of a SQL statement, consisting of conjunction of declared terms. Terms are defined by the to_process argument. the method creates appropriate entries different in the case, value should be NULL (None in the list) and in the case of not-none arguments. In the second case, parametrised query is generated decreasing the chance of an SQL-injection. @param to_process List of tuples (value, database_column) @type to_process list""" q_str = [] q_args = [] for entry in to_process: q_str.append(_val_or_null(entry[0], eq_name = entry[1], q_args = q_args)) return (" AND ".join(q_str), q_args) 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(docformat, 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 not docformat: return '' if allow_subformat: subformat = docformat[docformat.rfind(';'):] docformat = docformat[:docformat.rfind(';')] else: subformat = '' if docformat and docformat[0] != '.': docformat = '.' + docformat if CFG_BIBDOCFILE_STRONG_FORMAT_NORMALIZATION: if docformat not in ('.Z', '.H', '.C', '.CC'): docformat = docformat.lower() docformat = { '.jpg' : '.jpeg', '.htm' : '.html', '.tif' : '.tiff' }.get(docformat, docformat) return docformat + 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 should be guessed. @type url: string @return: the recognized extension or '.bin' if it's impossible to recognize it. @rtype: string """ def guess_via_magic(local_path): try: if CFG_HAS_MAGIC == 1: magic_cookie = _get_magic_cookies()[magic.MAGIC_MIME_TYPE] mimetype = magic_cookie.file(local_path) elif CFG_HAS_MAGIC == 2: mimetype = _magic_wrapper(local_path, mime=True, mime_encoding=False) if CFG_HAS_MAGIC: if mimetype in CFG_BIBDOCFILE_PREFERRED_MIMETYPES_MAPPING: return normalize_format(CFG_BIBDOCFILE_PREFERRED_MIMETYPES_MAPPING[mimetype]) else: return normalize_format(_mimes.guess_extension(mimetype)) except Exception: pass ## 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): ## The URL corresponds to a local file, so we can safely consider ## traditional extensions after the dot. ext = decompose_file(url, skip_version=True, only_known_extensions=False)[2] if ext.startswith('.'): return ext ## No extensions? Let's use Magic. ext = guess_via_magic(url) if ext: return ext else: ## Since the URL is remote, let's try to perform a HEAD request ## and see the corresponding headers try: response = open_url(url, head_request=True) except (InvenioBibdocfileUnauthorizedURL, urllib2.URLError): return ".bin" ext = get_format_from_http_response(response) if ext: return ext if CFG_HAS_MAGIC: ## Last solution: let's download the remote resource ## and use the Python magic library to guess the extension filename = "" try: try: filename = download_url(url, docformat='') ext = guess_via_magic(filename) if ext: return ext except Exception: pass finally: if os.path.exists(filename): ## Let's free space os.remove(filename) return ".bin" _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, extension, subformat=None, version=None, storagename=None): """ Construct back a fullpath given the separate components. @param @param storagename Name under which the file should be stored in the filesystem @type storagename string @return a fullpath to the file @rtype string """ 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 if not storagename: storagename = "content" return os.path.join(dirname, storagename + extension + subformat + version) def compose_format(extension, subformat=None): """ Construct the format string """ if not extension.startswith("."): extension = ".%s" % extension if subformat: if not subformat.startswith(";"): subformat = ";%s" % subformat else: subformat = "" return extension + subformat 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 basename and extension. @rtype: (dirname, basename, 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 basename, extension and version. @rtype: (dirname, basename, 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(docformat): """ @return the subformat if any. @rtype: string >>> get_subformat_from_format('foo;bar') 'bar' >>> get_subformat_from_format('foo') '' """ try: return docformat[docformat.rindex(';') + 1:] except ValueError: return '' def get_superformat_from_format(docformat): """ @return the superformat if any. @rtype: string >>> get_superformat_from_format('foo;bar') 'foo' >>> get_superformat_from_format('foo') 'foo' """ try: return docformat[:docformat.rindex(';')] except ValueError: return docformat 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(object): """ 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): try: self.id = int(recid) except ValueError: raise ValueError("BibRecDocs: recid is %s but must be an integer." % repr(recid)) self.human_readable = human_readable self.deleted_too = deleted_too self.attachment_types = {} # dictionary docname->attachment type self._bibdocs = [] self.dirty = True @property def bibdocs(self): if self.dirty: self.build_bibdoc_list() return self._bibdocs 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 (docname, (bibdoc, dummy)) in self.bibdocs.items(): out += str(docname) + ":" + 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 correxsponding record has been deleted. @rtype: bool """ from invenio.legacy.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.legacy.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\n' for subfield, value in field_get_subfield_instances(field): out += '\t\t%s\n' % (subfield, encode_for_xml(value)) out += '\t\n' for afile in self.list_latest_files(list_hidden=False): out += '\t\n' url = afile.get_url() description = afile.get_description() comment = afile.get_comment() if url: out += '\t\t%s\n' % encode_for_xml(url) if description: out += '\t\t%s\n' % encode_for_xml(description) if comment: out += '\t\t%s\n' % encode_for_xml(comment) out += '\t\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.values(): 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.values(): 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 brbd.id_bibdoc, brbd.docname, brbd.type FROM bibrec_bibdoc as brbd JOIN bibdoc as bd ON bd.id=brbd.id_bibdoc WHERE brbd.id_bibrec=%s ORDER BY brbd.docname ASC""", (self.id,)) else: res = run_sql("""SELECT brbd.id_bibdoc, brbd.docname, brbd.type FROM bibrec_bibdoc as brbd JOIN bibdoc as bd ON bd.id=brbd.id_bibdoc WHERE brbd.id_bibrec=%s AND bd.status<>'DELETED' ORDER BY brbd.docname ASC""", (self.id,)) for row in res: cur_doc = BibDoc.create_instance(docid=row[0], recid=self.id, human_readable=self.human_readable) self._bibdocs[row[1]] = (cur_doc, row[2]) self.dirty = False def list_bibdocs_by_names(self, doctype=None): """ Returns the dictionary of all bibdocs object belonging to a recid. Keys in the dictionary are names of documetns and values are BibDoc objects. If C{doctype} is set, it returns just the bibdocs of that doctype. @param doctype: the optional doctype. @type doctype: string @return: the dictionary of bibdocs. @rtype: dictionary of Dcname -> BibDoc """ if not doctype: return dict((k, v) for (k, (v, _)) in iteritems(self.bibdocs)) res = {} for (docname, (doc, attachmenttype)) in iteritems(self.bibdocs): if attachmenttype == doctype: res[docname] = doc return res def list_bibdocs(self, doctype=None, rel_type=None): """ 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 """ return [bibdoc for (bibdoc, rtype) in self.bibdocs.values() if (not doctype or doctype == bibdoc.doctype) and (rel_type is None or rel_type == rtype)] def get_bibdoc_names(self, doctype=None): """ Returns all the names of the documents associated with the bibrec. 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 [docname for (docname, dummy) in self.list_bibdocs_by_names(doctype).items()] def check_file_exists(self, path, f_format): """ 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 and afile.format == f_format] 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 InvenioBibDocFileError: 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(): docformat = bibdocfile.get_format() if bibdoc1.format_already_exists_p(docformat): raise InvenioBibDocFileError('Format %s already exists in bibdoc %s of record %s. It\'s impossible to merge bibdoc %s into it.' % (docformat, 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(): docformat = bibdocfile.get_format() comment = bibdocfile.get_comment() description = bibdocfile.get_description() bibdoc1.add_file_new_format(bibdocfile.get_full_path(), description=description, comment=comment, docformat=docformat) ## Finally deleting old bibdoc2 bibdoc2.delete() self.dirty = True 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 InvenioBibDocFileError: if the C{docname} does not corresponds to a document attached to this record. """ if docname in self.bibdocs: return self.bibdocs[docname][0].id raise InvenioBibDocFileError, "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 InvenioBibDocFileError: if the C{docid} does not corresponds to a document attached to this record. """ for (docname, (bibdoc, _)) in self.bibdocs.items(): if bibdoc.id == docid: return docname raise InvenioBibDocFileError, "Recid '%s' is not connected with a " \ "docid '%s'" % (self.id, docid) def change_name(self, newname, oldname=None, docid=None): """ Renames document of a given name. @param newname: the new name. @type newname: string @raise InvenioBibDocFileError: if the new name corresponds to a document already attached to the record owning this document. """ if not oldname and not docid: raise StandardError("Trying to rename unspecified document") if not oldname: oldname = self.get_docname(docid) if not docid: docid = self.get_docid(oldname) doc, atttype = self.bibdocs[oldname] newname = normalize_docname(newname) res = run_sql("SELECT id_bibdoc FROM bibrec_bibdoc WHERE id_bibrec=%s AND docname=%s", (self.id, newname)) if res: raise InvenioBibDocFileError, "A bibdoc called %s already exists for recid %s" % (newname, self.id) doc.change_name(self.id, newname) # updating the record structure del self._bibdocs[oldname] self._bibdocs[newname] = (doc, atttype) 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 """ return docname in self.bibdocs.keys() def get_bibdoc(self, docname): """ @return: the bibdoc with a particular docname associated with this recid""" if docname in self.bibdocs: return self.bibdocs[docname][0] raise InvenioBibDocFileError, "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 """ if docname in self.bibdocs: self.bibdocs[docname][0].delete() self.dirty = True 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 InvenioBibDocFileError: 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 InvenioBibDocFileError, \ "%s has already a bibdoc with docname %s" % (self.id, docname) else: bibdoc = BibDoc.create_instance(recid=self.id, doctype=doctype, docname=docname, human_readable=self.human_readable) self.dirty = True return bibdoc except Exception as e: register_exception() raise InvenioBibDocFileError(str(e)) def add_new_file(self, fullpath, doctype="Main", docname=None, never_fail=False, description=None, comment=None, docformat=None, flags=None, modification_date=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 InvenioBibDocFileError: in case of error. """ if docname is None: docname = decompose_file(fullpath)[1] if docformat is None: docformat = decompose_file(fullpath)[2] docname = normalize_docname(docname) try: bibdoc = self.get_bibdoc(docname) except InvenioBibDocFileError: # bibdoc doesn't already exists! bibdoc = self.add_bibdoc(doctype, docname, False) bibdoc.add_file_new_version(fullpath, description=description, comment=comment, docformat=docformat, flags=flags, modification_date=modification_date) else: try: bibdoc.add_file_new_format(fullpath, description=description, comment=comment, docformat=docformat, flags=flags, modification_date=modification_date) except InvenioBibDocFileError as dummy: # Format already exist! if never_fail: bibdoc = self.add_bibdoc(doctype, docname, True) bibdoc.add_file_new_version(fullpath, description=description, comment=comment, docformat=docformat, flags=flags, modification_date=modification_date) else: raise return bibdoc def add_new_version(self, fullpath, docname=None, description=None, comment=None, docformat=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 InvenioBibDocFileError: 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 docformat is None: docformat = decompose_file(fullpath)[2] if flags is None: flags = [] if 'pdfa' in get_subformat_from_format(docformat).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, docformat=docformat, flags=flags) return bibdoc def add_new_format(self, fullpath, docname=None, description=None, comment=None, docformat=None, flags=None, modification_date=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 InvenioBibDocFileError: in case the same format already exists. """ if docname is None: docname = decompose_file(fullpath)[1] if docformat is None: docformat = decompose_file(fullpath)[2] if flags is None: flags = [] if 'pdfa' in get_subformat_from_format(docformat).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, docformat=docformat, flags=flags, modification_date=modification_date) return bibdoc def list_latest_files(self, doctype=None, 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 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 InvenioBibDocFileError: 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): from invenio.config import CFG_CERN_SITE, CFG_INSPIRE_SITE, CFG_BIBDOCFILE_AFS_VOLUME_PATTERN, CFG_BIBDOCFILE_AFS_VOLUME_QUOTA if os.path.realpath(bibdoc.basedir).startswith('/afs') and (CFG_CERN_SITE or CFG_INSPIRE_SITE): ## We are on AFS at CERN! Let's allocate directories the CERN/AFS way. E.g. ## $ afs_admin create -q 1000000 /afs/cern.ch/project/cds/files/g40 p.cds.g40 ## NOTE: This might be extended to use low-level OpenAFS CLI tools ## so that this technique could be extended to other AFS users outside CERN. mount_point = os.path.dirname(os.path.realpath(bibdoc.basedir)) if not os.path.exists(mount_point): volume = CFG_BIBDOCFILE_AFS_VOLUME_PATTERN % os.path.basename(mount_point) quota = str(CFG_BIBDOCFILE_AFS_VOLUME_QUOTA) exit_code, stdout, stderr = run_shell_command("afs_admin create -q %s %s %s", (quota, mount_point, volume)) if exit_code or stderr: raise IOError("Error in creating AFS mount point %s with quota %s and volume %s: exit_code=%s. Captured stdout:\n: %s\nCaptured stderr:\n: %s" % (mount_point, quota, volume, exit_code, stdout, stderr)) for filename in os.listdir(bibdoc.basedir): if filename[0] != '.' and ';' in filename: name, version = filename.rsplit(';', 1) try: version = int(version) except ValueError: # Strange name register_exception() raise InvenioBibDocFileError, "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 docformat = name[len(file_strip_ext(name)):] docformat = normalize_format(docformat) if version not in versions: 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 as e: register_exception() raise InvenioBibDocFileError, "Error in renaming '%s' to '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, new_name), e) if docformat in versions[version]: new_bibdocs.append((new_name, version)) else: versions[version][docformat] = new_name counter += 1 elif filename[0] != '.': # Strange name register_exception() raise InvenioBibDocFileError, "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(0o022) 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 as e: register_exception() raise InvenioBibDocFileError, e os.umask(old_umask) if not versions: bibdoc.delete() self.dirty = True else: for version, formats in iteritems(versions): if zero_version_bug: version += 1 for docformat, filename in iteritems(formats): destination = '%s%s;%i' % (docname, docformat, version) try: shutil.move('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, destination)) except Exception as e: register_exception() raise InvenioBibDocFileError, "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 as e: register_exception() raise InvenioBibDocFileError, "Error in creating .recid and .type file for '%s' folder: '%s'" % (bibdoc.basedir, e) 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 as e: register_exception() raise InvenioBibDocFileError, "Error in removing '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), e) Md5Folder(bibdoc.basedir).update(only_new=False) bibdoc._build_file_list() for (bibdoc, dummyatttype) in self.bibdocs.values(): if not run_sql('SELECT data_value FROM bibdocmoreinfo WHERE id_bibdoc=%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 as e: register_exception() raise InvenioBibDocFileError, "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_BIBDOCFILE_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 InvenioBibDocFileError: 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, docformat, version = decompose_file_with_version(filename) except Exception: raise InvenioBibDocFileError('Incorrect filename "%s" for docname %s for recid %i' % (filename, docname, self.id)) if '%s%s;%i' % (correct_docname, docformat, 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, docformat, 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, docformat, version))) Md5Folder(bibdoc.basedir).update() bibdoc.touch('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.dirty = True self.uniformize_bibdoc(proposed_docname) try: self.merge_bibdocs(docname, proposed_docname) except InvenioBibDocFileError: return False else: run_sql('UPDATE bibdoc SET docname=%s WHERE id=%s', (correct_docname, bibdoc.id)) self.dirty = True 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 = self.get_docname(bibdoc.id) if docname in docnames: new_docname = self.propose_unique_docname(self.get_docname(bibdoc.id)) self.change_name(docid=bibdoc.id, newname=new_docname) self.merge_bibdocs(docname, new_docname) docnames.add(docname) def get_text(self, extract_text_if_necessary=True): """ @return: concatenated texts of all bibdocs separated by " ": string """ texts = [] for bibdoc in self.list_bibdocs(): if hasattr(bibdoc, 'has_text'): if extract_text_if_necessary and not bibdoc.has_text(require_up_to_date=True): perform_ocr = hasattr(bibdoc, 'is_ocr_required') and bibdoc.is_ocr_required() from invenio.legacy.bibsched.bibtask import write_message write_message("... will extract words from %s %s" % (bibdoc, perform_ocr and 'with OCR' or ''), verbose=2) bibdoc.extract_text(perform_ocr=perform_ocr) texts.append(bibdoc.get_text()) return " ".join(texts) class BibDoc(object): """ 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 InvenioBibDocFileError: in case of error. """ @staticmethod def create_new_document(doc_type="Main", rec_links=None): if rec_links is None: rec_links = [] status = '' doc_id = run_sql("INSERT INTO bibdoc (status, creation_date, modification_date, doctype) " "values(%s,NOW(),NOW(), %s)", (status, doc_type)) if not doc_id: raise InvenioBibDocFileError, "New docid cannot be created" # creating the representation on disk ... preparing the directory try: BibDoc.prepare_basedir(doc_id) except Exception as e: run_sql('DELETE FROM bibdoc WHERE id=%s', (doc_id, )) register_exception(alert_admin=True) raise InvenioBibDocFileError, e # the object has been created: linking to bibliographical records doc = BibDoc(doc_id) for link in rec_links: if "rec_id" in link and link["rec_id"]: rec_id = link["rec_id"] doc_name = normalize_docname(link["doc_name"]) a_type = link["a_type"] doc.attach_to_record(rec_id, str(a_type), str(doc_name)) return doc_id def __init__(self, docid, human_readable=False, initial_data=None): """Constructor of a bibdoc. At least the docid or the recid/docname pair is needed. specifying recid, docname and doctype without specifying docid results in attaching newly created document to a record """ # docid is known, the document already exists res2 = run_sql("SELECT id_bibrec, type, docname FROM bibrec_bibdoc WHERE id_bibdoc=%s", (docid,)) self.bibrec_types = [(r[0], r[1], r[2]) for r in res2 ] # just in case the result was behaving like tuples but was something else if not res2: # fake attachment self.bibrec_types = [(0, None, "fake_name_for_unattached_document")] if initial_data is None: initial_data = BibDoc._retrieve_data(docid) self._docfiles = [] self.__md5s = None self._related_files = {} self.human_readable = human_readable self.cd = initial_data["cd"] # creation date self.md = initial_data["md"] # modification date self.td = initial_data["td"] # text extraction date # should be moved from here !!!! self.bibrec_links = initial_data["bibrec_links"] self.id = initial_data["id"] self.status = initial_data["status"] self.basedir = initial_data["basedir"] self.doctype = initial_data["doctype"] self.storagename = initial_data["storagename"] # the old docname -> now used as a storage name for old records self.more_info = BibDocMoreInfo(self.id) self.dirty = True self.dirty_related_files = True self.last_action = 'init' def __del__(self): if self.dirty and self.last_action != 'init': ## The object is dirty and we did something more than initializing it self._build_file_list() @property def docfiles(self): if self.dirty: self._build_file_list(self.last_action) self.dirty = False return self._docfiles @property def related_files(self): if self.dirty_related_files: self._build_related_file_list() self.dirty_related_files = False return self._related_files @staticmethod def prepare_basedir(doc_id): """Prepares the directory serving as root of a BibDoc""" basedir = _make_base_dir(doc_id) # we create the corresponding storage directory if not os.path.exists(basedir): from invenio.config import CFG_CERN_SITE, CFG_INSPIRE_SITE, CFG_BIBDOCFILE_AFS_VOLUME_PATTERN, CFG_BIBDOCFILE_AFS_VOLUME_QUOTA if os.path.realpath(basedir).startswith('/afs') and (CFG_CERN_SITE or CFG_INSPIRE_SITE): ## We are on AFS at CERN! Let's allocate directories the CERN/AFS way. E.g. ## $ afs_admin create -q 1000000 /afs/cern.ch/project/cds/files/g40 p.cds.g40 ## NOTE: This might be extended to use low-level OpenAFS CLI tools ## so that this technique could be extended to other AFS users outside CERN. mount_point = os.path.dirname(os.path.realpath(basedir)) if not os.path.exists(mount_point): volume = CFG_BIBDOCFILE_AFS_VOLUME_PATTERN % os.path.basename(mount_point) quota = str(CFG_BIBDOCFILE_AFS_VOLUME_QUOTA) exit_code, stdout, stderr = run_shell_command("afs_admin create -q %s %s %s", (quota, mount_point, volume)) if exit_code or stderr: raise IOError("Error in creating AFS mount point %s with quota %s and volume %s: exit_code=%s. Captured stdout:\n: %s\nCaptured stderr:\n: %s" % (mount_point, quota, volume, exit_code, stdout, stderr)) old_umask = os.umask(022) os.makedirs(basedir) os.umask(old_umask) def _update_additional_info_files(self): """Update the hidden file in the document directory ... the file contains all links to records""" try: reclinks_fd = open("%s/.reclinks" % (self.basedir, ), "w") reclinks_fd.write("RECID DOCNAME TYPE\n") for link in self.bibrec_links: reclinks_fd.write("%(recid)s %(docname)s %(doctype)s\n" % link) reclinks_fd.close() except Exception as e: register_exception(alert_admin=True) raise InvenioBibDocFileError, e @staticmethod def _retrieve_data(docid = None): """ Filling information about a document from the database entry """ container = {} container["bibrec_links"] = [] container["id"] = docid container["basedir"] = _make_base_dir(container["id"]) # retrieving links betwen records and documents res = run_sql("SELECT id_bibrec, type, docname FROM bibrec_bibdoc WHERE id_bibdoc=%s", (str(docid),), 1) if res: for r in res: container["bibrec_links"].append({"recid": r[0], "doctype": r[1], "docname": r[2]}) # gather the other information res = run_sql("SELECT status, creation_date, modification_date, text_extraction_date, doctype, docname FROM bibdoc WHERE id=%s LIMIT 1", (docid,), 1) if res: container["status"] = res[0][0] container["cd"] = res[0][1] container["md"] = res[0][2] container["td"] = res[0][3] container["doctype"] = res[0][4] container["storagename"] = res[0][5] else: # this bibdoc doesn't exist raise InvenioBibDocFileError, "The docid %s does not exist." % docid # retreiving all available formats fprefix = container["storagename"] or "content" try: if CFG_BIBDOCFILE_ENABLE_BIBDOCFSINFO_CACHE: ## We take all extensions from the existing formats in the DB. container["extensions"] = set([ext[0] for ext in run_sql("SELECT format FROM bibdocfsinfo WHERE id_bibdoc=%s", (docid, ))]) else: ## We take all the extensions by listing the directory content, stripping name ## and version. container["extensions"] = set([fname[len(fprefix):].rsplit(";", 1)[0] for fname in filter(lambda x: x.startswith(fprefix), os.listdir(container["basedir"]))]) except OSError: container["extensions"] = [] current_app.logger.warning("Could not retrieve available formats", exc_info=True) return container @staticmethod def create_instance(docid=None, recid=None, docname=None, doctype='Fulltext', a_type = '', human_readable=False): """ Parameters of an attachement to the record: a_type, recid, docname @param a_type Type of the attachment to the record (by default Main) @type a_type String @param doctype Type of the document itself (by default Fulltext) @type doctype String """ # first try to retrieve existing record based on obtained data data = None extensions = [] if docid is not None: data = BibDoc._retrieve_data(docid) doctype = data["doctype"] extensions = data["extensions"] # Loading an appropriate plugin (by default a generic BibDoc) used_plugin = None for plugin in get_plugins(): if plugin['supports'](doctype, extensions): used_plugin = plugin if not a_type: a_type = doctype or 'Main' if not docid: rec_links = [] if recid: rec_links.append({"rec_id": recid, "doc_name" : docname, "a_type": a_type}) if used_plugin and 'create_new' in used_plugin: docid = used_plugin['create_new'](doctype, rec_links) else: docid = BibDoc.create_new_document(doctype, rec_links) if used_plugin: return used_plugin['create_instance'](docid=docid, human_readable=human_readable, initial_data=data) return BibDoc(docid=docid, human_readable=human_readable, initial_data=data) def attach_to_record(self, recid, a_type, docname): """ Attaches given document to a record given by its identifier. @param recid The identifier of the record @type recid Integer @param a_type Function of a document in the record @type a_type String @param docname Name of a document inside of a record @type docname String """ run_sql("INSERT INTO bibrec_bibdoc (id_bibrec, id_bibdoc, type, docname) VALUES (%s,%s,%s,%s)", (str(recid), str(self.id), a_type, docname)) self._update_additional_info_files() def __repr__(self): """ @return: the canonical string representation of the C{BibDoc}. @rtype: string """ return 'BibDoc(%s, %s, %s)' % (repr(self.id), repr(self.doctype), repr(self.human_readable)) def format_recids(self): """Returns a string representation of related record ids""" if len(self.bibrec_links) == 1: return self.bibrec_links[0]["recid"] return "[" + ",".join([str(el["recid"]) for el in self.bibrec_links]) + "]" def __str__(self): """ @return: an easy to be I{grepped} string representation of the whole C{BibDoc} content. @rtype: string """ recids = self.format_recids() out = '%s:%i:::doctype=%s\n' % (recids, self.id, self.doctype) out += '%s:%i:::status=%s\n' % (recids, self.id, self.status) out += '%s:%i:::basedir=%s\n' % (recids, self.id, self.basedir) out += '%s:%i:::creation date=%s\n' % (recids, self.id, self.cd) out += '%s:%i:::modification date=%s\n' % (recids, self.id, self.md) out += '%s:%i:::text extraction date=%s\n' % (recids, self.id, self.td) out += '%s:%i:::total file attached=%s\n' % (recids, self.id, len(self.docfiles)) if self.human_readable: out += '%s:%i:::total size latest version=%s\n' % (recids, self.id, nice_size(self.get_total_size_latest_version())) out += '%s:%i:::total size all files=%s\n' % (recids, self.id, nice_size(self.get_total_size())) else: out += '%s:%i:::total size latest version=%s\n' % (recids, self.id, self.get_total_size_latest_version()) out += '%s:%i:::total size all files=%s\n' % (recids, self.id, self.get_total_size()) for docfile in self.docfiles: out += str(docfile) return out def get_md5s(self): """ @return: an instance of the Md5Folder class to access MD5 information of the current BibDoc @rtype: Md5Folder """ if self.__md5s is None: self.__md5s = Md5Folder(self.basedir) return self.__md5s md5s = property(get_md5s) def format_already_exists_p(self, docformat): """ @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 """ docformat = normalize_format(docformat) for afile in self.list_latest_files(): if docformat == afile.get_format(): return True return False def get_status(self): """ @return: the status information. @rtype: string """ return self.status @staticmethod def get_fileprefix(basedir, storagename=None): fname = "%s" % (storagename or "content", ) return os.path.join(basedir, fname ) def get_filepath(self, docformat, version): """ Generaters the path inside of the filesystem where the document should be stored. @param format The format of the document @type format string @param version version to be stored in the file @type version string TODO: this should be completely replaced. File storage (and so, also path building) should be abstracted from BibDoc and be using loadable extensions @param format Format of the document to be stored @type format string @param version Version of the document to be stored @type version String @return Full path to the file encoding a particular version and format of the document @trype string """ return "%s%s;%i" % (BibDoc.get_fileprefix(self.basedir, self.storagename), docformat, version) def get_docname(self): """Obsolete !! (will return empty String for new format documents""" return self.storagename def get_doctype(self, recid): """Retrieves the type of this document in the scope of a given recid""" link_types = [attachement["doctype"] for attachement in self.bibrec_links if str(attachement["recid"]) == str(recid)] if link_types: return link_types[0] return "" def touch(self, action=''): """ 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, )) self.dirty = True self.last_action = action def change_doctype(self, new_doctype): """ Modify the doctype of a BibDoc """ run_sql('UPDATE bibdoc SET doctype=%s WHERE id=%s', (new_doctype, self.id)) run_sql('UPDATE bibrec_bibdoc SET type=%s WHERE id_bibdoc=%s', (new_doctype, self.id)) self.dirty = True 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 InvenioBibDocFileError: in case the reserved word 'DELETED' is used. """ if new_status != KEEP_OLD_VALUE: if new_status == 'DELETED': raise InvenioBibDocFileError('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('status') def add_file_new_version(self, filename, description=None, comment=None, docformat=None, flags=None, modification_date=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 InvenioBibDocFileError: in case of error. """ 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 InvenioBibDocFileError, "%s seems to be empty" % filename if docformat is None: docformat = decompose_file(filename)[2] else: docformat = normalize_format(docformat) destination = self.get_filepath(docformat, myversion) if run_sql("SELECT id_bibdoc FROM bibdocfsinfo WHERE id_bibdoc=%s AND version=%s AND format=%s", (self.id, myversion, docformat)): raise InvenioBibDocFileError("According to the database a file of format %s is already attached to the docid %s" % (docformat, self.id)) try: shutil.copyfile(filename, destination) os.chmod(destination, 0644) if modification_date: # if the modification time of the file needs to be changed update_modification_date_of_file(destination, modification_date) except Exception as e: register_exception() raise InvenioBibDocFileError("Encountered an exception while copying '%s' to '%s': '%s'" % (filename, destination, e)) self.more_info.set_description(description, docformat, myversion) self.more_info.set_comment(comment, docformat, myversion) if flags is None: flags = [] if 'pdfa' in get_subformat_from_format(docformat).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(): docformat = afile.get_format() version = afile.get_version() if version < myversion: self.more_info.set_flag('HIDDEN', docformat, myversion) else: self.more_info.set_flag(flag, docformat, myversion) else: raise InvenioBibDocFileError("'%s' does not exists!" % filename) self.touch('newversion') Md5Folder(self.basedir).update() just_added_file = self.get_file(docformat, myversion) run_sql("INSERT INTO bibdocfsinfo(id_bibdoc, version, format, last_version, cd, md, checksum, filesize, mime) VALUES(%s, %s, %s, true, %s, %s, %s, %s, %s)", (self.id, myversion, docformat, just_added_file.cd, just_added_file.md, just_added_file.get_checksum(), just_added_file.get_size(), just_added_file.mime)) run_sql("UPDATE bibdocfsinfo SET last_version=false WHERE id_bibdoc=%s AND version<%s", (self.id, myversion)) def add_file_new_format(self, filename, version=None, description=None, comment=None, docformat=None, flags=None, modification_date=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 InvenioBibDocFileError: if the given format already exists. """ 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 InvenioBibDocFileError, "%s seems to be empty" % filename if docformat is None: docformat = decompose_file(filename)[2] else: docformat = normalize_format(docformat) if run_sql("SELECT id_bibdoc FROM bibdocfsinfo WHERE id_bibdoc=%s AND version=%s AND format=%s", (self.id, version, docformat)): raise InvenioBibDocFileError("According to the database a file of format %s is already attached to the docid %s" % (docformat, self.id)) destination = self.get_filepath(docformat, version) if os.path.exists(destination): raise InvenioBibDocFileError, "A file for docid '%s' already exists for the format '%s'" % (str(self.id), docformat) try: shutil.copyfile(filename, destination) os.chmod(destination, 0644) if modification_date: # if the modification time of the file needs to be changed update_modification_date_of_file(destination, modification_date) except Exception, e: register_exception() raise InvenioBibDocFileError, "Encountered an exception while copying '%s' to '%s': '%s'" % (filename, destination, e) self.more_info.set_comment(comment, docformat, version) self.more_info.set_description(description, docformat, version) if flags is None: flags = [] if 'pdfa' in get_subformat_from_format(docformat).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, docformat, version) else: raise InvenioBibDocFileError, "'%s' does not exists!" % filename Md5Folder(self.basedir).update() self.touch('newformat') just_added_file = self.get_file(docformat, version) run_sql("INSERT INTO bibdocfsinfo(id_bibdoc, version, format, last_version, cd, md, checksum, filesize, mime) VALUES(%s, %s, %s, true, %s, %s, %s, %s, %s)", (self.id, version, docformat, just_added_file.cd, just_added_file.md, just_added_file.get_checksum(), just_added_file.get_size(), just_added_file.mime)) def change_docformat(self, oldformat, newformat): """ Renames a format name on disk and in all BibDoc structures. The change will touch only the last version files. The change will take place only if the newformat doesn't already exist. @param oldformat: the format that needs to be renamed @type oldformat: string @param newformat: the format new name @type newformat: string """ oldformat = normalize_format(oldformat) newformat = normalize_format(newformat) if self.format_already_exists_p(newformat): # same format already exists in the latest files, abort return for bibdocfile in self.list_latest_files(): if bibdocfile.get_format() == oldformat: # change format -> rename x.oldformat -> x.newformat dirname, base, docformat, version = decompose_file_with_version(bibdocfile.get_full_path()) os.rename(bibdocfile.get_full_path(), os.path.join(dirname, '%s%s;%i' %(base, newformat, version))) Md5Folder(self.basedir).update() self.touch('rename') self._sync_to_db() return 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 as dummy: register_exception() Md5Folder(self.basedir).update() self.touch('purge') run_sql("DELETE FROM bibdocfsinfo WHERE id_bibdoc=%s AND version<%s", (self.id, version)) 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. """ self.more_info.delete() 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 INTO hstDOCUMENT(action, id_bibdoc, doctimestamp) VALUES("EXPUNGE", %s, NOW())', (self.id, )) run_sql('DELETE FROM bibdocfsinfo WHERE id_bibdoc=%s', (self.id, )) del self._docfiles del self.id del self.cd del self.md del self.td del self.basedir del self.doctype del self.bibrec_links 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 InvenioBibDocFileError: in case of errors """ version = int(version) docfiles = self.list_version_files(version) if docfiles: self.add_file_new_version(docfiles[0].get_full_path(), description=docfiles[0].get_description(), comment=docfiles[0].get_comment(), docformat=docfiles[0].get_format(), flags=docfiles[0].flags) for docfile in docfiles[1:]: self.add_file_new_format(docfile.filename, description=docfile.get_description(), comment=docfile.get_comment(), docformat=docfile.get_format(), flags=docfile.flags) 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.legacy.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 re.match('%s/%s/[0-9]+/files/' % (CFG_SITE_URL, CFG_SITE_RECORD), url): ## 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, docformat = decompose_bibdocfile_url(url) brd = BibRecDocs(self.id) if docname == brd.get_docname(self.id): description = field_get_subfield_values(field, 'y') if description: local_description[docformat] = description[0] comment = field_get_subfield_values(field, 'z') if comment: local_comment[docformat] = comment[0] ## Let's update the tables version = self.get_latest_version() for docfile in self.list_latest_files(): docformat = docfile.get_format() if docformat in local_comment: self.set_comment(local_comment[docformat], docformat, version) else: self.set_comment(global_comment, docformat, version) if docformat in local_description: self.set_description(local_description[docformat], docformat, version) else: self.set_description(global_description, docformat, version) self.dirty = True def get_icon(self, subformat_re=CFG_BIBDOCFILE_ICON_SUBFORMAT_RE, display_hidden=True): """ @param subformat_re: by default the convention is that L{CFG_BIBDOCFILE_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 CFG_BIBDOCFILE_DEFAULT_ICON_SUBFORMAT or, if this does not exist, the smallest size icon of this document, or None if no 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. """ icons = [] for docfile in self.list_latest_files(list_hidden=display_hidden): subformat = docfile.get_subformat() if subformat.lower() == CFG_BIBDOCFILE_DEFAULT_ICON_SUBFORMAT.lower(): # If it's the default icon subformat, return it return docfile if subformat_re.match(subformat): icons.append((docfile.get_size(), docfile)) if icons: # Sort by size, retrieve the smallest one icons.sort() return icons[0][1] return None def add_icon(self, filename, docformat=None, subformat=CFG_BIBDOCFILE_DEFAULT_ICON_SUBFORMAT, modification_date=None): """ 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_BIBDOCFILE_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 InvenioBibDocFileError: in case of errors. """ #first check if an icon already exists if not docformat: docformat = decompose_file(filename)[2] if subformat: docformat += ";%s" % subformat self.add_file_new_format(filename, docformat=docformat, modification_date=modification_date) def delete_icon(self, subformat_re=CFG_BIBDOCFILE_ICON_SUBFORMAT_RE): """ @param subformat_re: by default the convention is that L{CFG_BIBDOCFILE_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 change_name(self, recid, newname): """ Renames this document in connection with a given record. @param newname: the new name. @type newname: string @raise InvenioBibDocFileError: if the new name corresponds to a document already attached to the record owning this document or if the name was not changed. """ newname = normalize_docname(newname) res = run_sql("SELECT id_bibdoc FROM bibrec_bibdoc WHERE id_bibrec=%s AND docname=%s", (recid, newname)) if res: raise InvenioBibDocFileError("A bibdoc called %s already exists for recid %s" % (newname, recid)) updated = run_sql("update bibrec_bibdoc set docname=%s where id_bibdoc=%s and id_bibrec=%s", (newname, self.id, recid)) if not updated: raise InvenioBibDocFileError("Docname for bibdoc %s in record %s was not changed" % (self.id, recid)) # docid is known, the document already exists res2 = run_sql("SELECT id_bibrec, type, docname FROM bibrec_bibdoc WHERE id_bibdoc=%s", (self.id,)) ## Refreshing names and types. self.bibrec_types = [(r[0], r[1], r[2]) for r in res2 ] # just in case the result was behaving like tuples but was something else if not res2: # fake attachment self.bibrec_types = [(0, None, "fake_name_for_unattached_document")] self.touch('rename') def set_comment(self, comment, docformat, 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() docformat = normalize_format(docformat) self.more_info.set_comment(comment, docformat, version) self.dirty = True def set_description(self, description, docformat, 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() docformat = normalize_format(docformat) self.more_info.set_description(description, docformat, version) self.dirty = True def set_flag(self, flagname, docformat, 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() docformat = normalize_format(docformat) self.more_info.set_flag(flagname, docformat, version) self.dirty = True def has_flag(self, flagname, docformat, 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() docformat = normalize_format(docformat) return self.more_info.has_flag(flagname, docformat, version) def unset_flag(self, flagname, docformat, 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() docformat = normalize_format(docformat) self.more_info.unset_flag(flagname, docformat, version) self.dirty = True def get_comment(self, docformat, 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() docformat = normalize_format(docformat) return self.more_info.get_comment(docformat, version) def get_description(self, docformat, 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() docformat = normalize_format(docformat) return self.more_info.get_description(docformat, version) def hidden_p(self, docformat, 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', docformat, version) 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_id(self): """ @return: the id of this document. @rtype: integer """ return self.id def get_file(self, docformat, version="", exact_docformat=False): """ 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 @param exact_docformat: if True, consider always the complete docformat (including subformat if any) @type exact_docformat: bool @return: the L{BibDocFile} instance. @rtype: BibDocFile """ if version == "": docfiles = self.list_latest_files() else: version = int(version) docfiles = self.list_version_files(version) docformat = normalize_format(docformat) for docfile in docfiles: if (docfile.get_format() == docformat or not docformat): return docfile ## Let's skip the subformat specification and consider just the ## superformat if not exact_docformat: superformat = get_superformat_from_format(docformat) for docfile in docfiles: if get_superformat_from_format(docfile.get_format()) == superformat: return docfile raise InvenioBibDocFileError("No file for doc %i of format '%s', version '%s'" % (self.id, docformat, 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, recid=None): """ Delete this document. @see: L{undelete} for how to undelete the document. @raise InvenioBibDocFileError: in case of errors. """ try: today = datetime.today() recids = [] if recid: recids = [recid] else: recids = [link["recid"] for link in self.bibrec_links] for rid in recids: brd = BibRecDocs(rid) docname = brd.get_docname(self.id) # if the document is attached to some records brd.change_name(docid=self.id, newname = 'DELETED-%s%s-%s' % (today.strftime('%Y%m%d%H%M%S'), today.microsecond, docname)) run_sql("UPDATE bibdoc SET status='DELETED' WHERE id=%s", (self.id,)) self.status = 'DELETED' except Exception as e: register_exception(alert_admin=True) raise InvenioBibDocFileError, "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='', recid=None): """ 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 InvenioBibDocFileError: in case of any error. """ try: run_sql("UPDATE bibdoc SET status=%s WHERE id=%s AND status='DELETED'", (previous_status, self.id)) except Exception as e: raise InvenioBibDocFileError, "It's impossible to undelete bibdoc %s: %s" % (self.id, e) if recid: bibrecdocs = BibRecDocs(recid) docname = bibrecdocs.get_docname(self.id) if docname.startswith('DELETED-'): try: # Let's remove DELETED-20080214144322- in front of the docname original_name = '-'.join(docname.split('-')[2:]) original_name = bibrecdocs.propose_unique_docname(original_name) bibrecdocs.change_name(docid=self.id, newname=original_name) except Exception as e: raise InvenioBibDocFileError, "It's impossible to restore the previous docname %s. %s kept as docname because: %s" % (original_name, docname, e) else: raise InvenioBibDocFileError, "Strange just undeleted docname isn't called DELETED-somedate-docname but %s" % docname def delete_file(self, docformat, 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(docformat, version) except InvenioBibDocFileError: return try: os.remove(afile.get_full_path()) run_sql("DELETE FROM bibdocfsinfo WHERE id_bibdoc=%s AND version=%s AND format=%s", (self.id, afile.get_version(), afile.get_format())) last_version = run_sql("SELECT max(version) FROM bibdocfsinfo WHERE id_bibdoc=%s", (self.id, ))[0][0] if last_version: ## Updating information about last version run_sql("UPDATE bibdocfsinfo SET last_version=true WHERE id_bibdoc=%s AND version=%s", (self.id, last_version)) run_sql("UPDATE bibdocfsinfo SET last_version=false WHERE id_bibdoc=%s AND version<>%s", (self.id, last_version)) except OSError: pass self.touch('delete') 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, docformat, version, size, checksum, timestamp=''): """Log an action into the bibdoclog table.""" try: if timestamp: run_sql('INSERT INTO hstDOCUMENT(action, id_bibdoc, docname, docformat, docversion, docsize, docchecksum, doctimestamp) VALUES(%s, %s, %s, %s, %s, %s, %s, %s)', (action, docid, docname, docformat, version, size, checksum, timestamp)) else: run_sql('INSERT INTO hstDOCUMENT(action, id_bibdoc, docname, docformat, docversion, docsize, docchecksum, doctimestamp) VALUES(%s, %s, %s, %s, %s, %s, %s, NOW())', (action, docid, docname, docformat, 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 iteritems(old_files): if key in added_files: del added_files[key] else: deleted_files[key] = value return (added_files, deleted_files) if context != ('init', 'init_from_disk'): previous_file_list = list(self._docfiles) res = run_sql("SELECT status, creation_date," "modification_date FROM bibdoc WHERE id=%s", (self.id,)) self.cd = res[0][1] self.md = res[0][2] self.status = res[0][0] self.more_info = BibDocMoreInfo(self.id) self._docfiles = [] if CFG_BIBDOCFILE_ENABLE_BIBDOCFSINFO_CACHE and context == 'init': ## In normal init context we read from DB res = run_sql("SELECT version, format, cd, md, checksum, filesize FROM bibdocfsinfo WHERE id_bibdoc=%s", (self.id, )) for version, docformat, cd, md, checksum, size in res: filepath = self.get_filepath(docformat, version) self._docfiles.append(BibDocFile( filepath, self.bibrec_types, version, docformat, self.id, self.status, checksum, self.more_info, human_readable=self.human_readable, cd=cd, md=md, size=size, bibdoc=self)) else: if os.path.exists(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) dummy, dummy, docformat, fileversion = decompose_file_with_version(filepath) checksum = self.md5s.get_checksum(afile) self._docfiles.append(BibDocFile(filepath, self.bibrec_types, fileversion, docformat, self.id, self.status, checksum, self.more_info, human_readable=self.human_readable, bibdoc=self)) except Exception as e: register_exception() raise InvenioBibDocFileError, e if context in ('init', 'init_from_disk'): 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, docformat, version), (size, checksum, md) in iteritems(added_files): if context == 'rename': md = '' # No modification time log_action(addedstr, self.id, docname, docformat, version, size, checksum, md) for (docname, docformat, version), (size, checksum, md) in iteritems(deleted_files): if context == 'rename': md = '' # No modification time log_action(deletedstr, self.id, docname, docformat, version, size, checksum, md) def _sync_to_db(self): """ Update the content of the bibdocfile table by taking what is available on the filesystem. """ self._build_file_list('init_from_disk') run_sql("DELETE FROM bibdocfsinfo WHERE id_bibdoc=%s", (self.id,)) for afile in self.docfiles: run_sql("INSERT INTO bibdocfsinfo(id_bibdoc, version, format, last_version, cd, md, checksum, filesize, mime) VALUES(%s, %s, %s, false, %s, %s, %s, %s, %s)", (self.id, afile.get_version(), afile.get_format(), afile.cd, afile.md, afile.get_checksum(), afile.get_size(), afile.mime)) run_sql("UPDATE bibdocfsinfo SET last_version=true WHERE id_bibdoc=%s AND version=%s", (self.id, self.get_latest_version())) 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.rel_type,bibdoc.status FROM " "bibdoc_bibdoc AS ln,bibdoc WHERE bibdoc.id=ln.id_bibdoc2 AND " "ln.id_bibdoc1=%s", (str(self.id),)) for row in res: docid = row[0] doctype = row[1] if row[2] != 'DELETED': if doctype not in self.related_files: self.related_files[doctype] = [] cur_doc = BibDoc.create_instance(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, docformat, userid=0, recid=0): """Register the information about a download of a particular file.""" docformat = normalize_format(docformat) if docformat[:1] == '.': docformat = docformat[1:] docformat = docformat.upper() if not version: version = self.get_latest_version() 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())", (recid, self.id, version, docformat, userid, ip_address,)) def get_incoming_relations(self, rel_type=None): """Return all relations in which this BibDoc appears on target position @param rel_type: Type of the relation, to which we want to limit our search. None = any type @type rel_type: string @return: List of BibRelation instances @rtype: list """ return BibRelation.get_relations(rel_type = rel_type, bibdoc2_id = self.id) def get_outgoing_relations(self, rel_type=None): """Return all relations in which this BibDoc appears on target position @param rel_type: Type of the relation, to which we want to limit our search. None = any type @type rel_type: string @return: List of BibRelation instances @rtype: list """ return BibRelation.get_relations(rel_type = rel_type, bibdoc1_id = self.id) def create_outgoing_relation(self, bibdoc2, rel_type): """ Create an outgoing relation between current BibDoc and a different one """ return BibRelation.create(bibdoc1_id = self.id, bibdoc2_id = bibdoc2.id, rel_type = rel_type) def create_incoming_relation(self, bibdoc1, rel_type): """ Create an outgoing relation between a particular version of current BibDoc and a particular version of a different BibDoc """ return BibRelation.create(bibdoc1_id = bibdoc1.id, bibdoc2_id = self.id, rel_type = rel_type) def generic_path2bidocfile(fullpath): """ Returns a BibDocFile objects that wraps the given fullpath. @note: the object will contain the minimum information that can be guessed from the fullpath (e.g. docname, format, subformat, version, md5, creation_date, modification_date). It won't contain for example a comment, a description, a doctype, a restriction. """ fullpath = os.path.abspath(fullpath) try: path, name, docformat, version = decompose_file_with_version(fullpath) except ValueError: ## There is no version version = 0 path, name, docformat = decompose_file(fullpath) md5folder = Md5Folder(path) checksum = md5folder.get_checksum(os.path.basename(fullpath)) return BibDocFile(fullpath=fullpath, recid_doctypes=[(0, None, name)], version=version, docformat=docformat, docid=0, status=None, checksum=checksum, more_info=None) class BibDocFile(object): """This class represents a physical file in the Invenio filesystem. It should never be instantiated directly""" def __init__(self, fullpath, recid_doctypes, version, docformat, docid, status, checksum, more_info=None, human_readable=False, cd=None, md=None, size=None, bibdoc=None): self.fullpath = os.path.abspath(fullpath) self.docid = docid self.recids_doctypes = recid_doctypes self.version = version self.status = status self.checksum = checksum self.human_readable = human_readable self.name = recid_doctypes[0][2] if bibdoc is not None: self.__bibdoc = ref(bibdoc) else: self.__bibdoc = None if more_info: self.description = more_info.get_description(docformat, version) self.comment = more_info.get_comment(docformat, version) self.flags = more_info.get_flags(docformat, version) else: self.description = None self.comment = None self.flags = [] self.format = normalize_format(docformat) self.superformat = get_superformat_from_format(self.format) self.subformat = get_subformat_from_format(self.format) if docformat: self.recids_doctypes = [(a,b,c+self.superformat) for (a,b,c) in self.recids_doctypes] self.mime, self.encoding = _mimes.guess_type(self.recids_doctypes[0][2]) if self.mime is None: self.mime = "application/octet-stream" self.more_info = more_info self.hidden = 'HIDDEN' in self.flags self.size = size or os.path.getsize(fullpath) self.md = md or datetime.fromtimestamp(os.path.getmtime(fullpath)) try: self.cd = cd or datetime.fromtimestamp(os.path.getctime(fullpath)) except OSError: self.cd = self.md self.dir = os.path.dirname(fullpath) + # make filename url safe + url_safe_filename = urllib.quote(self.name) if self.subformat: - self.url = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recids_doctypes[0][0], self.name, self.superformat), {'subformat' : self.subformat}) - self.fullurl = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recids_doctypes[0][0], self.name, self.superformat), {'subformat' : self.subformat, 'version' : self.version}) + self.url = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recids_doctypes[0][0], url_safe_filename, self.superformat), {'subformat' : self.subformat}) + self.fullurl = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recids_doctypes[0][0], url_safe_filename, self.superformat), {'subformat' : self.subformat, 'version' : self.version}) else: - self.url = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recids_doctypes[0][0], self.name, self.superformat), {}) - self.fullurl = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recids_doctypes[0][0], self.name, self.superformat), {'version' : self.version}) + self.url = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recids_doctypes[0][0], url_safe_filename, self.superformat), {}) + self.fullurl = create_url('%s/%s/%s/files/%s%s' % (CFG_SITE_SECURE_URL, CFG_SITE_RECORD, self.recids_doctypes[0][0], url_safe_filename, self.superformat), {'version' : self.version}) self.etag = '"%i%s%i"' % (self.docid, self.format, self.version) self.magic = None @property def bibdoc(self): """ Wrapper around the referenced bibdoc necesseary to avoid memory leaks. """ if self.__bibdoc is None or self.__bibdoc() is None: bibdoc = BibDoc(self.docid) self.__bibdoc = ref(bibdoc) return bibdoc return self.__bibdoc() def __getstate__(self): """Remove weakref so the object can be pickled.""" dict_ = copy.copy(self.__dict__) dict_['_BibDocFile__bibdoc'] = self.bibdoc return dict_ def __setstate__(self, data_dict): """Undo what `__getstate__` did setting back the weakref. :param data_dict: `dict` from `__getstate__` """ for (name, value) in data_dict.iteritems(): setattr(self, name, value) if self.__bibdoc is not None: self.__bibdoc = ref(self.__bibdoc) def __repr__(self): return ('BibDocFile(%s, %i, %s, %s, %i, %i, %s, %s, %s, %s)' % (repr(self.fullpath), self.version, repr(self.name), repr(self.format), self.recids_doctypes[0][0], self.docid, repr(self.status), repr(self.checksum), repr(self.more_info), repr(self.human_readable))) def format_recids(self): if self.bibdoc: return self.bibdoc.format_recids() return "0" def __str__(self): recids = self.format_recids() out = '%s:%s:%s:%s:fullpath=%s\n' % (recids, self.docid, self.version, self.format, self.fullpath) out += '%s:%s:%s:%s:name=%s\n' % (recids, self.docid, self.version, self.format, self.name) out += '%s:%s:%s:%s:subformat=%s\n' % (recids, self.docid, self.version, self.format, get_subformat_from_format(self.format)) out += '%s:%s:%s:%s:status=%s\n' % (recids, self.docid, self.version, self.format, self.status) out += '%s:%s:%s:%s:checksum=%s\n' % (recids, self.docid, self.version, self.format, self.checksum) if self.human_readable: out += '%s:%s:%s:%s:size=%s\n' % (recids, self.docid, self.version, self.format, nice_size(self.size)) else: out += '%s:%s:%s:%s:size=%s\n' % (recids, self.docid, self.version, self.format, self.size) out += '%s:%s:%s:%s:creation time=%s\n' % (recids, self.docid, self.version, self.format, self.cd) out += '%s:%s:%s:%s:modification time=%s\n' % (recids, self.docid, self.version, self.format, self.md) out += '%s:%s:%s:%s:magic=%s\n' % (recids, self.docid, self.version, self.format, self.get_magic()) out += '%s:%s:%s:%s:mime=%s\n' % (recids, self.docid, self.version, self.format, self.mime) out += '%s:%s:%s:%s:encoding=%s\n' % (recids, self.docid, self.version, self.format, self.encoding) out += '%s:%s:%s:%s:url=%s\n' % (recids, self.docid, self.version, self.format, self.url) out += '%s:%s:%s:%s:fullurl=%s\n' % (recids, self.docid, self.version, self.format, self.fullurl) out += '%s:%s:%s:%s:description=%s\n' % (recids, self.docid, self.version, self.format, self.description) out += '%s:%s:%s:%s:comment=%s\n' % (recids, self.docid, self.version, self.format, self.comment) out += '%s:%s:%s:%s:hidden=%s\n' % (recids, self.docid, self.version, self.format, self.hidden) out += '%s:%s:%s:%s:flags=%s\n' % (recids, self.docid, self.version, self.format, self.flags) out += '%s:%s:%s:%s:etag=%s\n' % (recids, self.docid, self.version, self.format, self.etag) return out 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_BIBDOCFILE_ICON_SUBFORMAT_RE): """ @param subformat_re: by default the convention is that L{CFG_BIBDOCFILE_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): """Returns the first type connected with the bibdoc of this file.""" return self.recids_doctypes[0][1] 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): """Returns the first name connected with the bibdoc of this file.""" return self.recids_doctypes[0][2] 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 first recid connected with the bibdoc of this file.""" return self.recids_doctypes[0][0] 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: if CFG_HAS_MAGIC == 1: 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) elif CFG_HAS_MAGIC == 2: magic_result = [] for key in ({'mime': False, 'mime_encoding': False}, {'mime': True, 'mime_encoding': False}, {'mime': False, 'mime_encoding': True}): magic_result.append(_magic_wrapper(self.fullpath, **key)) 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, download=False): """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 InvenioBibDocFileError, "File %s, version %i, is corrupted!" % (self.recids_doctypes[0][2], self.version) stream_file(req, self.fullpath, "%s%s" % (self.name, self.superformat), self.mime, self.encoding, self.etag, self.checksum, self.fullurl, download=download) raise apache.SERVER_RETURN, apache.DONE else: req.status = apache.HTTP_NOT_FOUND raise InvenioBibDocFileError, "%s does not exists!" % self.fullpath _RE_STATUS_PARSER = re.compile(r'^(?Pemail|group|egroup|role|firerole|status):\s*(?P.*)$', 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. """ if not status: return (0, CFG_WEBACCESS_WARNING_MSGS[0]) 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]) # TODO for future reimplementation of stream_file #class StreamFileException(Exception): # def __init__(self, value): # self.value = value _RE_BAD_MSIE = re.compile("MSIE\s+(\d+\.\d+)") def stream_file(req, fullpath, fullname=None, mime=None, encoding=None, etag=None, md5str=None, location=None, download=False): """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. md5str should be passed as an hexadecimal string. """ ## TODO for future reimplementation of stream_file # from flask import send_file # if fullname is None: # fullname = fullpath.split('/')[-1] # response = send_file(fullpath, # attachment_filename=fullname.replace('"', '\\"'), # as_attachment=False) # if not download: # response.headers['Content-Disposition'] = 'inline; filename="%s"' % fullname.replace('"', '\\"') # # raise StreamFileException(response) def normal_streaming(size): req.set_content_length(size) req.send_http_header() if req.method != 'HEAD': 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 req.method != 'HEAD': 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 req.method != 'HEAD': 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 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 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(): """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 iteritems(req.headers_in): 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 headers = get_normalized_headers() g = _RE_BAD_MSIE.search(headers.get('user-agent', "MSIE 6.0")) bad_msie = g and float(g.group(1)) < 9.0 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) if bad_msie: ## IE is confused by quotes req.headers_out["Content-Disposition"] = 'attachment; filename=%s' % fullname.replace('"', '\\"') elif download: req.headers_out["Content-Disposition"] = 'attachment; filename="%s"' % fullname.replace('"', '\\"') else: ## IE is confused by inline req.headers_out["Content-Disposition"] = 'inline; filename="%s"' % fullname.replace('"', '\\"') req.headers_out["X-Sendfile"] = fullpath if mime is None: (mime, encoding) = _mimes.guess_type(fullpath) if mime is None: mime = "application/octet-stream" if not bad_msie: ## IE is confused by not supported mimetypes req.content_type = mime return "" else: raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND 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 if not bad_msie: ## IE is confused by not supported mimetypes 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 md5str is not None: req.headers_out["Content-MD5"] = base64.encodestring(binascii.unhexlify(md5str.upper()))[:-1] if bad_msie: ## IE is confused by quotes req.headers_out["Content-Disposition"] = 'attachment; filename=%s' % fullname.replace('"', '\\"') elif download: req.headers_out["Content-Disposition"] = 'attachment; filename="%s"' % fullname.replace('"', '\\"') else: ## IE is confused by inline 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_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_BIBDOCFILE_FILESYSTEM_BIBDOC_GROUP_LIMIT)) return os.path.join(CFG_BIBDOCFILE_FILEDIR, group, str(docid)) class Md5Folder(object): """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 self.load() 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(0o022) 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 as e: register_exception(alert_admin=True) raise InvenioBibDocFileError("Encountered an exception while storing .md5 for folder '%s': '%s'" % (self.folder, e)) def load(self): """Load .md5 into the md5 dictionary""" self.md5s = {} md5_path = os.path.join(self.folder, ".md5") if os.path.exists(md5_path): for row in open(md5_path, "r"): md5hash = row[:32] filename = row[34:].strip() self.md5s[filename] = md5hash else: self.update() 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 as e: register_exception(alert_admin=True) raise InvenioBibDocFileError("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 as e: register_exception(alert_admin=True) raise InvenioBibDocFileError("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 as e: raise InvenioBibDocFileError("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 as e: register_exception(alert_admin=True) raise InvenioBibDocFileError("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/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/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/CFG_SITE_RECORD/xxx/files/... it returns a BibDocFile object for the corresponding recid/docname/format.""" docformat = decompose_bibdocfile_url(url)[2] return bibdocfile_url_to_bibdoc(url).get_file(docformat) def bibdocfile_url_to_fullpath(url): """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/%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_BIBDOCFILE_FILEDIR/g0/123/bar.pdf;1") returns the docid (e.g. 123).""" if not fullpath.startswith(os.path.join(CFG_BIBDOCFILE_FILEDIR, 'g')): raise InvenioBibDocFileError, "Fullpath %s doesn't correspond to a valid bibdocfile fullpath" % fullpath dirname = decompose_file_with_version(fullpath)[0] try: return int(dirname.split('/')[-1]) except: raise InvenioBibDocFileError, "Fullpath %s doesn't correspond to a valid bibdocfile fullpath" % fullpath def decompose_bibdocfile_fullpath(fullpath): """Given a bibdocfile fullpath (e.g. "CFG_BIBDOCFILE_FILEDIR/g0/123/bar.pdf;1") returns a quadruple (recid, docname, format, version).""" if not fullpath.startswith(os.path.join(CFG_BIBDOCFILE_FILEDIR, 'g')): raise InvenioBibDocFileError, "Fullpath %s doesn't correspond to a valid bibdocfile fullpath" % fullpath dirname, dummy, extension, version = decompose_file_with_version(fullpath) try: docid = int(dirname.split('/')[-1]) return {"doc_id" : docid, "extension": extension, "version": version} except: raise InvenioBibDocFileError, "Fullpath %s doesn't correspond to a valid bibdocfile fullpath" % fullpath _RE_BIBDOCFILE_URL = re.compile("/%s/(?P\d+)/files/(?P.*)" % (re.escape(CFG_SITE_RECORD), )) 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) scheme, netloc, path, query, dummy_fragment = urlsplit(url) if "%s://%s" % (scheme, netloc) not in (CFG_SITE_URL, CFG_SITE_SECURE_URL): raise InvenioBibDocFileError("URL %s doesn't correspond to a valid BibDocFile URL." % url) g = _RE_BIBDOCFILE_URL.match(urllib.unquote(path)) if g: recid = int(g.group('recid')) rest = g.group('rest') dummy, docname, docformat = decompose_file(rest) query = parse_qs(query) if 'subformat' in query: docformat += ";%s" % query['subformat'][0] return recid, docname, docformat else: raise InvenioBibDocFileError, "Url %s doesn't correspond to a valid record inside the system." % url 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/CFG_SITE_RECORD/123/files) it returns the recid.""" g = re_bibdocfile_old_url.search(url) if g: return int(g.group(1)) raise InvenioBibDocFileError('%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 = parse_qs(params) if 'docid' in params: docid = int(params['docid'][0]) bibdoc = BibDoc.create_instance(docid) if bibdoc.bibrec_links: recid = bibdoc.bibrec_links[0]["rec_id"] docname = bibdoc.bibrec_links[0]["doc_name"] else: raise InvenioBibDocFileError("Old style URL pointing to an unattached document") elif 'recid' in params: recid = int(params['recid'][0]) if 'name' in params: docname = params['name'][0] else: docname = '' else: raise InvenioBibDocFileError('%s has not enough params to correspond to a bibdocfile.' % url) docformat = normalize_format(params.get('format', [''])[0]) return (recid, docname, docformat) except Exception as e: raise InvenioBibDocFileError('Problem with %s: %s' % (url, e)) else: raise InvenioBibDocFileError('%s has no params to correspond to a bibdocfile.' % url) else: raise InvenioBibDocFileError('%s is not a valid very old bibdocfile url' % url) def get_docname_from_url(url): """Return a potential docname given a url""" path = 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 = 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.""" if is_url_a_local_file(url): path = 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 = urlsplit(url)[0] return protocol in ('', 'file') def check_valid_url(url): """ Check for validity of a url or a file. @param url: the URL to check @type url: string @raise StandardError: if the URL is not a valid URL. """ try: if is_url_a_local_file(url): path = 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: try: open_url(url) except InvenioBibdocfileUnauthorizedURL as e: raise StandardError, str(e) except Exception as e: raise StandardError, "%s is not a correct url: %s" % (url, e) def safe_mkstemp(suffix, prefix='bibdocfile_'): """Create a temporary filename that don't have any '.' inside a part from the suffix.""" tmpfd, tmppath = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=CFG_TMPDIR) # Close the file and leave the responsability to the client code to # correctly open/close it. os.close(tmpfd) if '.' not in suffix: # Just in case format is empty return tmppath while '.' in os.path.basename(tmppath)[:-len(suffix)]: os.remove(tmppath) tmpfd, tmppath = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=CFG_TMPDIR) os.close(tmpfd) return tmppath def download_local_file(filename, docformat=None): """ Copies a local file to Invenio's temporary directory. @param filename: the name of the file to copy @type filename: string @param format: the format of the file to copy (will be found if not specified) @type format: string @return: the path of the temporary file created @rtype: string @raise StandardError: if something went wrong """ # Make sure the format is OK. if docformat is None: docformat = guess_format_from_url(filename) else: docformat = normalize_format(docformat) tmppath = '' # Now try to copy. try: path = urlsplit(urllib.unquote(filename))[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_WEBSUBMIT_STORAGEDIR]: if path.startswith(allowed_path): tmppath = safe_mkstemp(docformat) shutil.copy(path, tmppath) if os.path.getsize(tmppath) == 0: os.remove(tmppath) raise StandardError, "%s seems to be empty" % filename break else: raise StandardError, "%s is not in one of the allowed paths." % path except Exception as e: raise StandardError, "Impossible to copy the local file '%s': %s" % \ (filename, str(e)) return tmppath def download_external_url(url, docformat=None, progress_callback=None): """ Download a url (if it corresponds to a remote file) and return a local url to it. @param url: the URL to download @type url: string @param format: the format of the file (will be found if not specified) @type format: string @return: the path to the download local file @rtype: string @raise StandardError: if the download failed """ tmppath = None # Make sure the format is OK. if docformat is None: # First try to find a known extension to the URL docformat = decompose_file(url, skip_version=True, only_known_extensions=True)[2] if not docformat: # No correct format could be found. Will try to get it from the # HTTP message headers. docformat = '' else: docformat = normalize_format(docformat) from_file, to_file, tmppath = None, None, '' try: from_file = open_url(url) except InvenioBibdocfileUnauthorizedURL as e: raise StandardError, str(e) except urllib2.URLError as e: raise StandardError, 'URL could not be opened: %s' % str(e) if not docformat: # We could not determine the format from the URL, so let's try # to read it from the HTTP headers. docformat = get_format_from_http_response(from_file) try: tmppath = safe_mkstemp(docformat) if progress_callback: total_size = int(from_file.info().getheader('Content-Length').strip()) progress_size = 0 to_file = open(tmppath, 'w') while True: block = from_file.read(CFG_BIBDOCFILE_BLOCK_SIZE) if not block: break to_file.write(block) if progress_callback: progress_size += CFG_BIBDOCFILE_BLOCK_SIZE progress_callback(progress_size, CFG_BIBDOCFILE_BLOCK_SIZE, total_size) to_file.close() from_file.close() if os.path.getsize(tmppath) == 0: raise StandardError, "%s seems to be empty" % url except Exception as e: # Try to close and remove the temporary file. try: to_file.close() except Exception: pass try: os.remove(tmppath) except Exception: pass raise StandardError, "Error when downloading %s into %s: %s" % \ (url, tmppath, e) return tmppath def get_format_from_http_response(response): """ Tries to retrieve the format of the file from the message headers of the HTTP response. @param response: the HTTP response @type response: file-like object (as returned by urllib.urlopen) @return: the format of the remote resource @rtype: string """ def parse_content_type(text): return text.split(';')[0].strip() def parse_content_disposition(text): for item in text.split(';'): item = item.strip() if item.strip().startswith('filename='): return item[len('filename="'):-len('"')] info = response.info() docformat = '' content_disposition = info.getheader('Content-Disposition') if content_disposition: filename = parse_content_disposition(content_disposition) if filename: docformat = decompose_file(filename, only_known_extensions=False)[2] if docformat: return docformat content_type = info.getheader('Content-Type') if content_type: content_type = parse_content_type(content_type) if content_type not in ('text/plain', 'application/octet-stream'): ## We actually ignore these mimetypes since they are the ## defaults often returned by Apache in case the mimetype ## was not known if content_type in CFG_BIBDOCFILE_PREFERRED_MIMETYPES_MAPPING: docformat = normalize_format(CFG_BIBDOCFILE_PREFERRED_MIMETYPES_MAPPING[content_type]) else: ext = _mimes.guess_extension(content_type) if ext: docformat = normalize_format(ext) return docformat def download_url(url, docformat=None): """ Download a url (if it corresponds to a remote file) and return a local url to it. """ tmppath = None try: if is_url_a_local_file(url): tmppath = download_local_file(url, docformat = docformat) else: tmppath = download_external_url(url, docformat = docformat) except StandardError: raise return tmppath class MoreInfo(object): """This class represents a genering MoreInfo dictionary. MoreInfo object can be attached to bibdoc, bibversion, format or BibRelation. The entity where a particular MoreInfo object is attached has to be specified using the constructor parametes. This class is a thin wrapper around the database table. """ def __init__(self, docid = None, version = None, docformat = None, relation = None, cache_only = False, cache_reads = True, initial_data = None): """ @param cache_only Determines if MoreInfo object should be created in memory only or reflected in the database @type cache_only boolean @param cache_reads Determines if reads should be executed on the in-memory cache or should be redirected to the database. If this is true, cache can be entirely regenerated from the database only upon an explicit request. If the value is not present in the cache, the database is queried @type cache_reads boolean @param initial_data Allows to specify initial content of the cache. This parameter is useful when we create an in-memory instance from serialised value @type initial_data string """ self.docid = docid self.version = version self.format = docformat self.relation = relation self.cache_only = cache_only if initial_data != None: self.cache = initial_data self.dirty = initial_data if not self.cache_only: self._flush_cache() #inserts new entries else: self.cache = {} self.dirty = {} self.cache_reads = cache_reads if not self.cache_only: self.populate_from_database() @staticmethod def create_from_serialised(ser_str, docid = None, version = None, docformat = None, relation = None, cache_only = False, cache_reads = True): """Creates an instance of MoreInfo using serialised data as the cache content""" data = cPickle.loads(base64.b64decode(ser_str)) return MoreInfo(docid = docid, version = version, docformat = docformat, relation = relation, cache_only = cache_only, cache_reads = cache_reads, initial_data = data); def serialise_cache(self): """Returns a serialised representation of the cache""" return base64.b64encode(cPickle.dumps(self.get_cache())) def populate_from_database(self): """Retrieves all values of MoreInfo and places them in the cache""" where_str, where_args = self._generate_where_query_args() query_str = "SELECT namespace, data_key, data_value FROM bibdocmoreinfo WHERE %s" % (where_str, ) res = run_sql(query_str, where_args) if res: for row in res: namespace, data_key, data_value_ser = row data_value = cPickle.loads(data_value_ser) if not namespace in self.cache: self.cache[namespace] = {} self.cache[namespace][data_key] = data_value def _mark_dirty(self, namespace, data_key): """Marks a data key dirty - that should be saved into the database""" if not namespace in self.dirty: self.dirty[namespace] = {} self.dirty[namespace][data_key] = True def _database_get_distinct_string_list(self, column, namespace = None): """A private method reading an unique list of strings from the moreinfo database table""" where_str, where_args = self._generate_where_query_args( namespace = namespace) query_str = "SELECT DISTINCT %s FROM bibdocmoreinfo WHERE %s" % \ ( column, where_str, ) if DBG_LOG_QUERIES: from invenio.legacy.bibsched.bibtask import write_message write_message("Executing query: " + query_str + " ARGS: " + repr(where_args)) print("Executing query: " + query_str + " ARGS: " + repr(where_args)) res = run_sql(query_str, where_args) return (res and [x[0] for x in res]) or [] # after migrating to python 2.6, can be rewritten using x if y else z syntax: return [x[0] for x in res] if res else [] def _database_get_namespaces(self): """Read the database to discover namespaces declared in a given MoreInfo""" return self._database_get_distinct_string_list("namespace") def _database_get_keys(self, namespace): """Returns all keys assigned in a given namespace of a MoreInfo instance""" return self._database_get_distinct_string_list("data_key", namespace=namespace) def _database_contains_key(self, namespace, key): return self._database_read_value(namespace, key) != None def _database_save_value(self, namespace, key, value): """Write changes into the database""" #TODO: this should happen within one transaction serialised_val = cPickle.dumps(value) # on duplicate key will not work here as miltiple null values are permitted by the index if not self._database_contains_key(namespace, key): #insert new value query_parts = [] query_args = [] to_process = [(self.docid, "id_bibdoc"), (self.version, "version"), (self.format, "format"), (self.relation, "id_rel"), (str(namespace), "namespace"), (str(key), "data_key"), (str(serialised_val), "data_value")] for entry in to_process: _val_or_null(entry[0], q_str = query_parts, q_args = query_args) columns_str = ", ".join(map(lambda x: x[1], to_process)) values_str = ", ".join(query_parts) query_str = "INSERT INTO bibdocmoreinfo (%s) VALUES(%s)" % \ (columns_str, values_str) if DBG_LOG_QUERIES: from invenio.legacy.bibsched.bibtask import write_message write_message("Executing query: " + query_str + " ARGS: " + repr(query_args)) print("Executing query: " + query_str + " ARGS: " + repr(query_args)) run_sql(query_str, query_args) else: #Update existing value where_str, where_args = self._generate_where_query_args(namespace, key) query_str = "UPDATE bibdocmoreinfo SET data_value=%s WHERE " + where_str query_args = [str(serialised_val)] + where_args if DBG_LOG_QUERIES: from invenio.legacy.bibsched.bibtask import write_message write_message("Executing query: " + query_str + " ARGS: " + repr(query_args)) print("Executing query: " + query_str + " ARGS: " + repr(query_args)) run_sql(query_str, query_args ) def _database_read_value(self, namespace, key): """Reads a value directly from the database @param namespace - namespace of the data to be read @param key - key of the data to be read """ where_str, where_args = self._generate_where_query_args(namespace = namespace, data_key = key) query_str = "SELECT data_value FROM bibdocmoreinfo WHERE " + where_str res = run_sql(query_str, where_args) if DBG_LOG_QUERIES: from invenio.legacy.bibsched.bibtask import write_message write_message("Executing query: " + query_str + " ARGS: " + repr(where_args) + "WITH THE RESULT: " + str(res)) s_ = "" if res: s_ = cPickle.loads(res[0][0]) print("Executing query: " + query_str + " ARGS: " + repr(where_args) + " WITH THE RESULT: " + str(s_)) if res and res[0][0]: try: return cPickle.loads(res[0][0]) except: raise Exception("Error when deserialising value for %s key=%s retrieved value=%s" % (repr(self), str(key), str(res[0][0]))) return None def _database_remove_value(self, namespace, key): """Removes an entry directly in the database""" where_str, where_args = self._generate_where_query_args(namespace = namespace, data_key = key) query_str = "DELETE FROM bibdocmoreinfo WHERE " + where_str if DBG_LOG_QUERIES: from invenio.legacy.bibsched.bibtask import write_message write_message("Executing query: " + query_str + " ARGS: " + repr(where_args)) print("Executing query: " + query_str + " ARGS: " + repr(where_args)) run_sql(query_str, where_args) return None def _flush_cache(self): """Writes all the dirty cache entries into the database""" for namespace in self.dirty: for data_key in self.dirty[namespace]: if namespace in self.cache and data_key in self.cache[namespace]\ and not self.cache[namespace][data_key] is None: self._database_save_value(namespace, data_key, self.cache[namespace][data_key]) else: # This might happen if a value has been removed from the cache self._database_remove_value(namespace, data_key) self.dirty = {} def _generate_where_query_args(self, namespace = None, data_key = None): """Private method generating WHERE clause of SQL statements""" ns = [] if namespace != None: ns = [(namespace, "namespace")] dk = [] if data_key != None: dk = [(data_key, "data_key")] to_process = [(self.docid, "id_bibdoc"), (self.version, "version"), (self.format, "format"), (self.relation, "id_rel")] + \ ns + dk return _sql_generate_conjunctive_where(to_process) def set_data(self, namespace, key, value): """setting data directly in the database dictionary""" if not namespace in self.cache: self.cache[namespace] = {} self.cache[namespace][key] = value self._mark_dirty(namespace, key) if not self.cache_only: self._flush_cache() def get_data(self, namespace, key): """retrieving data from the database""" if self.cache_reads or self.cache_only: if namespace in self.cache and key in self.cache[namespace]: return self.cache[namespace][key] if not self.cache_only: # we have a permission to read from the database value = self._database_read_value(namespace, key) if value: if not namespace in self.cache: self.cache[namespace] = {} self.cache[namespace][key] = value return value return None def del_key(self, namespace, key): """retrieving data from the database""" if not namespace in self.cache: return None del self.cache[namespace][key] self._mark_dirty(namespace, key) if not self.cache_only: self._flush_cache() def contains_key(self, namespace, key): return self.get_data(namespace, key) != None # the dictionary interface -> updating the default namespace def __setitem__(self, key, value): self.set_data("", key, value) #the default value def __getitem__(self, key): return self.get_data("", key) def __delitem__(self, key): self.del_key("", key) def __contains__(self, key): return self.contains_key("", key) def __repr__(self): return "MoreInfo(docid=%s, version=%s, docformat=%s, relation=%s)" % \ (self.docid, self.version, self.format, self.relation) def delete(self): """Remove all entries associated with this MoreInfo""" self.cache = {} if not self.cache_only: where_str, query_args = self._generate_where_query_args() query_str = "DELETE FROM bibdocmoreinfo WHERE %s" % (where_str, ) if DBG_LOG_QUERIES: from invenio.legacy.bibsched.bibtask import write_message write_message("Executing query: " + query_str + " ARGS: " + repr(query_args)) print("Executing query: " + query_str + " ARGS: " + repr(query_args)) run_sql(query_str, query_args) def get_cache(self): """Returns the content of the cache @return The content of the MoreInfo cache @rtype dictionary {namespace: {key1: value1, ... }, namespace2: {}} """ return self.cache def get_namespaces(self): """Returns a list of namespaces present in the MoreInfo structure. If the object is permitted access to the database, the data should be always read from there. Unlike when reading a particular value, we can not check if value is missing in the cache """ if self.cache_only and self.cache_reads: return self.cache.keys() return self._database_get_namespaces() def get_keys(self, namespace): """Returns a list of keys present in a given namespace""" if self.cache_only and self.cache_reads: res = [] if namespace in self.cache: res = self.cache[namespace].keys() return res else: return self._database_get_keys(namespace) def flush(self): """Flush the content into the database""" self._flush_cache() class BibDocMoreInfo(MoreInfo): """ 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, cache_only = False, initial_data = None): if not (type(docid) in (long, int) and docid > 0): raise ValueError("docid is not a positive integer, but %s." % docid) MoreInfo.__init__(self, docid, cache_only = cache_only, initial_data = initial_data) if 'descriptions' not in self: self['descriptions'] = {} if 'comments' not in self: self['comments'] = {} if 'flags' not in self: self['flags'] = {} if DBG_LOG_QUERIES: from invenio.legacy.bibsched.bibtask import write_message write_message("Creating BibDocMoreInfo :" + repr(self["comments"])) print("Creating BibdocMoreInfo :" + repr(self["comments"])) def __repr__(self): """ @return: the canonical string representation of the C{BibDocMoreInfo}. @rtype: string """ return 'BibDocMoreInfo(%i, %s)' % (self.docid, repr(cPickle.dumps(self))) def set_flag(self, flagname, docformat, 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: flags = self['flags'] if not flagname in flags: flags[flagname] = {} if not version in flags[flagname]: flags[flagname][version] = {} if not docformat in flags[flagname][version]: flags[flagname][version][docformat] = {} flags[flagname][version][docformat] = True self['flags'] = flags else: raise ValueError, "%s is not in %s" % \ (flagname, CFG_BIBDOCFILE_AVAILABLE_FLAGS) def get_comment(self, docformat, 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) docformat = normalize_format(docformat) return self['comments'].get(version, {}).get(docformat) except: register_exception() raise def get_description(self, docformat, 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) docformat = normalize_format(docformat) return self['descriptions'].get(version, {}).get(docformat) except: register_exception() raise def has_flag(self, flagname, docformat, 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['flags'].get(flagname, {}).get(version, {}).get(docformat, False) else: raise ValueError, "%s is not in %s" % (flagname, CFG_BIBDOCFILE_AVAILABLE_FLAGS) def get_flags(self, docformat, 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['flags'] if docformat in self['flags'][flag].get(version, {})] def set_comment(self, comment, docformat, 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) docformat = normalize_format(docformat) if comment == KEEP_OLD_VALUE: comment = self.get_comment(docformat, version) or self.get_comment(docformat, version - 1) if not comment: self.unset_comment(docformat, version) return if not version in self['comments']: comments = self['comments'] comments[version] = {} self['comments'] = comments comments = self['comments'] comments[version][docformat] = comment self['comments'] = comments except: register_exception() raise def set_description(self, description, docformat, 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) docformat = normalize_format(docformat) if description == KEEP_OLD_VALUE: description = self.get_description(docformat, version) or self.get_description(docformat, version - 1) if not description: self.unset_description(docformat, version) return descriptions = self['descriptions'] if not version in descriptions: descriptions[version] = {} descriptions[version][docformat] = description self.set_data("", 'descriptions', descriptions) except: register_exception() raise def unset_comment(self, docformat, 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) comments = self['comments'] del comments[version][docformat] self['comments'] = comments except KeyError: pass except: register_exception() raise def unset_description(self, docformat, 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) descriptions = self['descriptions'] del descriptions[version][docformat] self['descriptions'] = descriptions except KeyError: pass except: register_exception() raise def unset_flag(self, flagname, docformat, 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: flags = self['flags'] del flags[flagname][version][docformat] self['flags'] = flags except KeyError: pass else: raise ValueError, "%s is not in %s" % (flagname, CFG_BIBDOCFILE_AVAILABLE_FLAGS) _bib_relation__any_value = -1 class BibRelation(object): """ A representation of a relation between documents or their particular versions """ def __init__(self, rel_type = None, bibdoc1_id = None, bibdoc2_id = None, bibdoc1_ver = None, bibdoc2_ver = None, bibdoc1_fmt = None, bibdoc2_fmt = None, rel_id = None): """ The constructor of the class representing a relation between two documents. If the more_info parameter is specified, no data is retrieved from the database and the internal dictionary is initialised with the passed value. If the more_info is not provided, the value is read from the database. In the case of non-existing record, an empty dictionary is assigned. If a version of whichever record is not specified, the resulting object desctibes a relation of all version of a given BibDoc. @param bibdoc1 @type bibdoc1 BibDoc @param bibdoc1_ver @type version1_ver int @param bibdoc2 @type bibdoc2 BibDco @param bibdoc2_ver @type bibdoc2_ver int @param bibdoc1_fmt format of the first document @type bibdoc1_fmt string @param bibdoc2_fmt format of the second document @type bibdoc2_fmt string @param rel_type @type rel_type string @param more_info The serialised representation of the more_info @type more_info string @param rel_id allows to specify the identifier of the newly created relation @type rel_ide unsigned int """ self.id = rel_id self.bibdoc1_id = bibdoc1_id self.bibdoc2_id = bibdoc2_id self.bibdoc1_ver = bibdoc1_ver self.bibdoc2_ver = bibdoc2_ver self.bibdoc1_fmt = bibdoc1_fmt self.bibdoc2_fmt = bibdoc2_fmt self.rel_type = rel_type if rel_id == None: self._fill_id_from_data() else: self._fill_data_from_id() self.more_info = MoreInfo(relation = self.id) def _fill_data_from_id(self): """Fill all the relation data from the relation identifier """ query = "SELECT id_bibdoc1, version1, format1, id_bibdoc2, version2, format2, rel_type FROM bibdoc_bibdoc WHERE id=%s" res = run_sql(query, (str(self.id), )) if res != None and res[0] != None: self.bibdoc1_id = res[0][0] self.bibdoc1_ver = res[0][1] self.bibdoc1_fmt = res[0][2] self.bibdoc2_id = res[0][3] self.bibdoc2_ver = res[0][4] self.bibdoc2_fmt = res[0][5] self.rel_type = res[0][6] def _fill_id_from_data(self): """Fill the relation identifier based on the data provided""" where_str, where_args = self._get_where_clauses() query = "SELECT id FROM bibdoc_bibdoc WHERE %s" % (where_str, ) res = run_sql(query, where_args) if res and res[0][0]: self.id = int(res[0][0]) def _get_value_column_mapping(self): """ Returns a list of tuples each tuple consists of a value and a name of a database column where this value should fit """ return [(self.rel_type, "rel_type"), (self.bibdoc1_id, "id_bibdoc1"), (self.bibdoc1_ver, "version1"), (self.bibdoc1_fmt, "format1"), (self.bibdoc2_id, "id_bibdoc2"), (self.bibdoc2_ver, "version2"), (self.bibdoc2_fmt, "format2")] def _get_where_clauses(self): """Private function returning part of the SQL statement identifying current relation @return @rtype tuple """ return _sql_generate_conjunctive_where(self._get_value_column_mapping()) @staticmethod def create(bibdoc1_id = None, bibdoc1_ver = None, bibdoc1_fmt = None, bibdoc2_id = None, bibdoc2_ver = None, bibdoc2_fmt = None, rel_type = ""): """ Create a relation and return instance. Ommiting an argument means that a particular relation concerns any value of the parameter """ # check if there is already entry corresponding to parameters existing = BibRelation.get_relations(rel_type = rel_type, bibdoc1_id = bibdoc1_id, bibdoc2_id = bibdoc2_id, bibdoc1_ver = bibdoc1_ver, bibdoc2_ver = bibdoc2_ver, bibdoc1_fmt = bibdoc1_fmt, bibdoc2_fmt = bibdoc2_fmt) if len(existing) > 0: return existing[0] # build the insert query and execute it to_process = [(rel_type, "rel_type"), (bibdoc1_id, "id_bibdoc1"), (bibdoc1_ver, "version1"), (bibdoc1_fmt, "format1"), (bibdoc2_id, "id_bibdoc2"), (bibdoc2_ver, "version2"), (bibdoc2_fmt, "format2")] values_list = [] args_list = [] columns_list = [] for entry in to_process: columns_list.append(entry[1]) if entry[0] == None: values_list.append("NULL") else: values_list.append("%s") args_list.append(entry[0]) query = "INSERT INTO bibdoc_bibdoc (%s) VALUES (%s)" % (", ".join(columns_list), ", ".join(values_list)) # print "Query: %s Args: %s" % (query, str(args_list)) rel_id = run_sql(query, args_list) return BibRelation(rel_id = rel_id) def delete(self): """ Removes a relation between objects from the database. executing the flush function on the same object will restore the relation """ where_str, where_args = self._get_where_clauses() run_sql("DELETE FROM bibdoc_bibdoc WHERE %s" % (where_str,), where_args) # kwalitee: disable=sql # removing associated MoreInfo self.more_info.delete() def get_more_info(self): return self.more_info @staticmethod def get_relations(rel_type = _bib_relation__any_value, bibdoc1_id = _bib_relation__any_value, bibdoc2_id = _bib_relation__any_value, bibdoc1_ver = _bib_relation__any_value, bibdoc2_ver = _bib_relation__any_value, bibdoc1_fmt = _bib_relation__any_value, bibdoc2_fmt = _bib_relation__any_value): """Retrieves list of relations satisfying condtions. If a parameter is specified, its value has to match exactly. If a parameter is ommited, any of its values will be accepted""" to_process = [(rel_type, "rel_type"), (bibdoc1_id, "id_bibdoc1"), (bibdoc1_ver, "version1"), (bibdoc1_fmt, "format1"), (bibdoc2_id, "id_bibdoc2"), (bibdoc2_ver, "version2"), (bibdoc2_fmt, "format2")] where_str, where_args = _sql_generate_conjunctive_where( filter(lambda x: x[0] != _bib_relation__any_value, to_process)) if where_str: where_str = "WHERE " + where_str # in case of nonempty where, we need a where clause query_str = "SELECT id FROM bibdoc_bibdoc %s" % (where_str, ) # print "running query : %s with arguments %s on the object %s" % (query_str, str(where_args), repr(self)) try: res = run_sql(query_str, where_args) except: raise Exception(query_str + " " + str(where_args)) results = [] if res != None: for res_row in res: results.append(BibRelation(rel_id=res_row[0])) return results # Access to MoreInfo def set_data(self, category, key, value): """assign additional information to this relation""" self.more_info.set_data(category, key, value) def get_data(self, category, key): """read additional information assigned to this relation""" return self.more_info.get_data(category, key) #the dictionary interface allowing to set data bypassing the namespaces def __setitem__(self, key, value): self.more_info[key] = value def __getitem__(self, key): return self.more_info[key] def __contains__(self, key): return self.more_info.__contains__(key) def __repr__(self): return "BibRelation(id_bibdoc1 = %s, version1 = %s, format1 = %s, id_bibdoc2 = %s, version2 = %s, format2 = %s, rel_type = %s)" % \ (self.bibdoc1_id, self.bibdoc1_ver, self.bibdoc1_fmt, self.bibdoc2_id, self.bibdoc2_ver, self.bibdoc2_fmt, self.rel_type) 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 '' class HeadRequest(urllib2.Request): """ A request object to perform a HEAD request. """ def get_method(self): return 'HEAD' def read_cookie(cookiefile): """ Parses a cookie file and returns a string as needed for the urllib2 headers The file should respect the Netscape cookie specifications """ cookie_data = '' cfile = open(cookiefile, 'r') for line in cfile.readlines(): tokens = line.split('\t') if len(tokens) == 7: # we are on a cookie line cookie_data += '%s=%s; ' % (tokens[5], tokens[6].replace('\n', '')) cfile.close() return cookie_data def open_url(url, headers=None, head_request=False): """ Opens a URL. If headers are passed as argument, no check is performed and the URL will be opened. Otherwise checks if the URL is present in CFG_BIBUPLOAD_FFT_ALLOWED_EXTERNAL_URLS and uses the headers specified in the config variable. @param url: the URL to open @type url: string @param headers: the headers to use @type headers: dictionary @param head_request: if True, perform a HEAD request, otherwise a POST request @type head_request: boolean @return: a file-like object as returned by urllib2.urlopen. """ headers_to_use = None if headers is None: for regex, headers in _CFG_BIBUPLOAD_FFT_ALLOWED_EXTERNAL_URLS: if regex.match(url) is not None: headers_to_use = headers break if headers_to_use is None: # URL is not allowed. raise InvenioBibdocfileUnauthorizedURL, "%s is not an authorized " \ "external URL." % url else: headers_to_use = headers request_obj = head_request and HeadRequest or urllib2.Request request = request_obj(url) request.add_header('User-Agent', make_user_agent_string('bibdocfile')) for key, value in headers_to_use.items(): try: value = globals()[value['fnc']](**value['args']) except (KeyError, TypeError): pass request.add_header(key, value) return urllib2.urlopen(request) def update_modification_date_of_file(filepath, modification_date): """Update the modification time and date of the file with the modification_date @param filepath: the full path of the file that needs to be updated @type filepath: string @param modification_date: the new modification date and time @type modification_date: datetime.datetime object """ try: modif_date_in_seconds = time.mktime(modification_date.timetuple()) # try to get the time in seconds except (AttributeError, TypeError): modif_date_in_seconds = 0 if modif_date_in_seconds: statinfo = os.stat(filepath) # we need to keep the same access time os.utime(filepath, (statinfo.st_atime, modif_date_in_seconds)) #update the modification time diff --git a/invenio/legacy/search_engine/__init__.py b/invenio/legacy/search_engine/__init__.py index 72579e379..a0d7c10d8 100644 --- a/invenio/legacy/search_engine/__init__.py +++ b/invenio/legacy/search_engine/__init__.py @@ -1,7192 +1,7192 @@ # -*- coding: utf-8 -*- # # This file is part of Invenio. # Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 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,W0703 """Invenio Search Engine in mod_python.""" __lastupdated__ = """$Date$""" __revision__ = "$Id$" # import general modules: import cgi import cStringIO import copy import os import re import time import string import urllib import urlparse import zlib import sys try: ## import optional module: import numpy CFG_NUMPY_IMPORTABLE = True except ImportError: CFG_NUMPY_IMPORTABLE = False if sys.hexversion < 0x2040000: # pylint: disable=W0622 from sets import Set as set # pylint: enable=W0622 from six import iteritems, string_types # import Invenio stuff: from invenio.base.globals import cfg from invenio.config import \ CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \ CFG_BASE_URL, \ CFG_BIBFORMAT_HIDDEN_TAGS, \ CFG_BIBINDEX_CHARS_PUNCTUATION, \ CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS, \ CFG_BIBSORT_BUCKETS, \ CFG_BIBSORT_ENABLED, \ CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG, \ CFG_BIBUPLOAD_SERIALIZE_RECORD_STRUCTURE, \ CFG_CERN_SITE, \ CFG_INSPIRE_SITE, \ CFG_LOGDIR, \ CFG_OAI_ID_FIELD, \ CFG_SCOAP3_SITE, \ CFG_SITE_LANG, \ CFG_SITE_NAME, \ CFG_SITE_RECORD, \ CFG_SITE_URL, \ CFG_SOLR_URL, \ CFG_WEBCOMMENT_ALLOW_REVIEWS, \ CFG_WEBSEARCH_CALL_BIBFORMAT, \ CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX, \ CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS, \ CFG_WEBSEARCH_DETAILED_META_FORMAT, \ CFG_WEBSEARCH_DISPLAY_NEAREST_TERMS, \ CFG_WEBSEARCH_FIELDS_CONVERT, \ CFG_WEBSEARCH_FULLTEXT_SNIPPETS, \ CFG_WEBSEARCH_IDXPAIRS_EXACT_SEARCH, \ CFG_WEBSEARCH_IDXPAIRS_EXACT_SEARCH, \ CFG_WEBSEARCH_IDXPAIRS_FIELDS,\ CFG_WEBSEARCH_IDXPAIRS_FIELDS,\ CFG_WEBSEARCH_MAX_RECORDS_CITEDBY, \ CFG_WEBSEARCH_MAX_RECORDS_REFERSTO, \ CFG_WEBSEARCH_NB_RECORDS_TO_SORT, \ CFG_WEBSEARCH_PREV_NEXT_HIT_LIMIT, \ CFG_WEBSEARCH_SEARCH_CACHE_SIZE, \ CFG_WEBSEARCH_SEARCH_CACHE_TIMEOUT, \ CFG_WEBSEARCH_SYNONYM_KBRS, \ CFG_WEBSEARCH_USE_ALEPH_SYSNOS, \ CFG_WEBSEARCH_USE_MATHJAX_FOR_FORMATS, \ CFG_WEBSEARCH_VIEWRESTRCOLL_POLICY, \ CFG_WEBSEARCH_WILDCARD_LIMIT, \ CFG_XAPIAN_ENABLED try: from invenio.config import CFG_BIBSORT_DEFAULT_FIELD, \ CFG_BIBSORT_DEFAULT_FIELD_ORDER except ImportError: CFG_BIBSORT_DEFAULT_FIELD = 'latest first' CFG_BIBSORT_DEFAULT_FIELD_ORDER = 'd' from invenio.modules.search.errors import \ InvenioWebSearchUnknownCollectionError, \ InvenioWebSearchWildcardLimitError, \ InvenioWebSearchReferstoLimitError, \ InvenioWebSearchCitedbyLimitError from invenio.legacy.bibrecord import (get_fieldvalues, get_fieldvalues_alephseq_like) from .utils import record_exists from invenio.legacy.bibrecord import create_record, record_xml_output from invenio.legacy.bibrank.record_sorter import ( get_bibrank_methods, is_method_valid, rank_records as rank_records_bibrank, rank_by_citations) from invenio.legacy.bibrank.downloads_similarity import register_page_view_event, calculate_reading_similarity_list from invenio.legacy.bibindex.engine_stemmer import stem from invenio.modules.indexer.tokenizers.BibIndexDefaultTokenizer import BibIndexDefaultTokenizer from invenio.modules.indexer.tokenizers.BibIndexCJKTokenizer import BibIndexCJKTokenizer, is_there_any_CJK_character_in_text from invenio.legacy.bibindex.engine_utils import author_name_requires_phrase_search, \ get_field_tags from invenio.legacy.bibindex.engine_washer import wash_index_term, lower_index_term, wash_author_name from invenio.legacy.bibindex.engine_config import CFG_BIBINDEX_SYNONYM_MATCH_TYPE from invenio.legacy.bibindex.adminlib import get_idx_indexer from invenio.modules.formatter import format_record, format_records, get_output_format_content_type, create_excel from invenio.legacy.bibrank.downloads_grapher import create_download_history_graph_and_box from invenio.modules.knowledge.api import get_kbr_values from invenio.legacy.miscutil.data_cacher import DataCacher from invenio.legacy.websearch_external_collections import print_external_results_overview, perform_external_collection_search from invenio.modules.access.control import acc_get_action_id from invenio.modules.access.local_config import VIEWRESTRCOLL, \ CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS, \ CFG_ACC_GRANT_VIEWER_RIGHTS_TO_EMAILS_IN_TAGS from invenio.legacy.websearch.adminlib import get_detailed_page_tabs, get_detailed_page_tabs_counts from intbitset import intbitset from invenio.legacy.dbquery import DatabaseError, deserialize_via_marshal, InvenioDbQueryWildcardLimitError from invenio.modules.access.engine import acc_authorize_action from invenio.ext.logging import register_exception from invenio.ext.cache import cache from invenio.utils.text import encode_for_xml, wash_for_utf8, strip_accents from invenio.utils.html import get_mathjax_header from invenio.utils.html import nmtoken_from_string from invenio.legacy import bibrecord import invenio.legacy.template webstyle_templates = invenio.legacy.template.load('webstyle') webcomment_templates = invenio.legacy.template.load('webcomment') websearch_templates = invenio.legacy.template.load('websearch') from invenio.legacy.bibrank.citation_searcher import calculate_cited_by_list, \ calculate_co_cited_with_list, get_records_with_num_cites, \ get_refersto_hitset, get_citedby_hitset, get_cited_by_list, \ get_refers_to_list, get_citers_log from invenio.legacy.bibrank.citation_grapher import create_citation_history_graph_and_box from invenio.legacy.bibrank.selfcites_searcher import get_self_cited_by_list, \ get_self_cited_by, \ get_self_refers_to_list from invenio.legacy.dbquery import run_sql, run_sql_with_limit, \ wash_table_column_name, get_table_update_time from invenio.legacy.webuser import getUid, collect_user_info, session_param_set from invenio.legacy.webpage import pageheaderonly, pagefooteronly, create_error_box, write_warning from invenio.base.i18n import gettext_set_language from invenio.legacy.search_engine.query_parser import SearchQueryParenthesisedParser, \ SpiresToInvenioSyntaxConverter from invenio.utils import apache from invenio.legacy.miscutil.solrutils_bibindex_searcher import solr_get_bitset from invenio.legacy.miscutil.xapianutils_bibindex_searcher import xapian_get_bitset from invenio.modules.search import services from invenio.legacy.websearch_external_collections import calculate_hosted_collections_results, do_calculate_hosted_collections_results from invenio.legacy.websearch_external_collections.config import CFG_HOSTED_COLLECTION_TIMEOUT_ANTE_SEARCH from invenio.legacy.websearch_external_collections.config import CFG_HOSTED_COLLECTION_TIMEOUT_POST_SEARCH from invenio.legacy.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(r'[\s]') re_quotes = re.compile('[\'\"]') re_doublequote = re.compile('\"') re_logical_and = re.compile(r'\sand\s', re.I) re_logical_or = re.compile(r'\sor\s', re.I) re_logical_not = re.compile(r'\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_parens_quotes = re.compile(r'[\'\"]{1}[^\'\"]*(\([^\'\"]*\))[^\'\"]*[\'\"]{1}') re_pattern_regexp_quotes = re.compile(r"\/(.*?)\/") 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(r"\$TODAY\$") re_pattern_parens = re.compile(r'\([^\)]+\s+[^\)]+\)') re_punctuation_followed_by_space = re.compile(CFG_BIBINDEX_CHARS_PUNCTUATION + r'\s') # em possible values EM_REPOSITORY={"body" : "B", "header" : "H", "footer" : "F", "search_box" : "S", "see_also_box" : "L", "basket" : "K", "alert" : "A", "search_info" : "I", "overview" : "O", "all_portalboxes" : "P", "te_portalbox" : "Pte", "tp_portalbox" : "Ptp", "np_portalbox" : "Pnp", "ne_portalbox" : "Pne", "lt_portalbox" : "Plt", "rt_portalbox" : "Prt", "search_services": "SER"}; class RestrictedCollectionDataCacher(DataCacher): def __init__(self): def cache_filler(): ret = [] 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,), run_on_slave=True) 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 NameError: 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 = [] auths = acc_authorize_action( user_info, 'viewrestrcoll', batch_args=True, collection=restricted_collection_cache.cache ) for collection, auth in zip(restricted_collection_cache.cache, auths): if auth[0] == 0: ret.append(collection) return ret def get_all_restricted_recids(): """ Return the set of all the restricted recids, i.e. the ids of those records which belong to at least one restricted collection. """ ret = intbitset() for collection in restricted_collection_cache.cache: ret |= get_collection_reclist(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 if CFG_CERN_SITE: #the egroup might be in the form egroup@cern.ch if email_or_group.replace('@cern.ch', ' [CERN]') in user_info['group']: return True return False ###FIXME: This method needs to be refactorized def is_user_viewer_of_record(user_info, recid): """ Check if the user is allow to view the record based in the marc tags inside CFG_ACC_GRANT_VIEWER_RIGHTS_TO_EMAILS_IN_TAGS i.e. his email is inside the 506__m tag or he is inside an e-group listed in the 506__m tag @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 'allow to view' the record; False otherwise @rtype: bool """ authorized_emails_or_group = [] for tag in CFG_ACC_GRANT_VIEWER_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) """ policy = CFG_WEBSEARCH_VIEWRESTRCOLL_POLICY.strip().upper() if isinstance(recid, str): recid = int(recid) ## 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, '') if is_user_viewer_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 not restricted_collections and record_public_p(recid): ## The record is public and not part of any restricted collection return (0, '') if restricted_collections: ## If there are restricted collections the user must be authorized to all/any of them (depending on the policy) auth_code, auth_msg = 0, '' for collection in restricted_collections: (auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=collection) if auth_code and policy != 'ANY': ## Ouch! the user is not authorized to this collection return (auth_code, auth_msg) elif auth_code == 0 and policy == 'ANY': ## Good! At least one collection is authorized return (0, '') ## Depending on the policy, the user will be either authorized or not return auth_code, auth_msg 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 FieldTokenizerDataCacher(DataCacher): """ Provides cache for tokenizer information for fields corresponding to indexes. This class is not to be used directly; use function get_field_tokenizer_type() instead. """ def __init__(self): def cache_filler(): try: res = run_sql("""SELECT fld.code, ind.tokenizer FROM idxINDEX AS ind, field AS fld, idxINDEX_field AS indfld WHERE ind.id = indfld.id_idxINDEX AND indfld.id_field = fld.id""") 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: field_tokenizer_cache.is_ok_p except Exception: field_tokenizer_cache = FieldTokenizerDataCacher() def get_field_tokenizer_type(field_name, recreate_cache_if_needed=True): """Return tokenizer type for given field corresponding to an index if applicable.""" if recreate_cache_if_needed: field_tokenizer_cache.recreate_cache_if_needed() tokenizer = None try: tokenizer = field_tokenizer_cache.cache[field_name] except KeyError: return None return tokenizer 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 = {} res = run_sql("SELECT name FROM collection") for name in res: ret[name[0]] = 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 coll not in collection_reclist_cache.cache: return intbitset() # collection does not exist; return empty set if not collection_reclist_cache.cache[coll]: # collection's reclist not in the cache yet, so calculate it # and fill the cache: reclist = intbitset() query = "SELECT nbrecs,reclist FROM collection WHERE name=%s" res = run_sql(query, (coll, ), 1) if res and res[0][1]: reclist = intbitset(res[0][1]) collection_reclist_cache.cache[coll] = reclist # finally, return reclist: return collection_reclist_cache.cache[coll] def get_available_output_formats(visible_only=False): """ Return the list of available output formats. When visible_only is True, returns only those output formats that have visibility flag set to 1. """ formats = [] query = "SELECT code,name FROM format" if visible_only: query += " WHERE visibility='1'" query += " 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" }) return formats # Flask cache for search results. from invenio.modules.search.cache import search_results_cache, get_search_results_cache_key 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 c not in ret: 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 f not in ret: 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 name FROM collection ORDER BY name ASC") for c_name in res: c_name = c_name[0] # 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 ASC""", (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 not 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, removing any trailing punctuation-like signs from words in pattern. """ words = {} # clean trailing punctuation signs inside pattern pattern = re_punctuation_followed_by_space.sub(' ', pattern) for word in pattern.split(): if word not in words: 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"): write_warning("Matching type '%s' is not implemented yet." % cgi.escape(m), "Warning", req=req) 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', 'exactfirstauthor', 'authorityauthor') 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 p.find(',') >= 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: "'"+x.group(1).replace(' ', '__SPACE__')+"'", p) p = re_pattern_double_quotes.sub(lambda x: "\""+x.group(1).replace(' ', '__SPACE__')+"\"", p) p = re_pattern_regexp_quotes.sub(lambda x: "/"+x.group(1).replace(' ', '__SPACE__')+"/", p) # and spaces after colon as well: p = re_pattern_spaces_after_colon.sub(lambda x: x.group(1).replace(' ', '__SPACE__'), p) # wash argument: 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 p.split(): # 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 pi.find(":") > 0: fi, pi = pi.split(":", 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 = pi.replace('"', '') # remove quote signs opfts.append([oi, pi, fi, 'a']) elif pi[0] == "'" and pi[-1] == "'": pi = pi.replace("'", "") # 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 len(fi) > 1 and str(fi[0]).isdigit() and str(fi[1]).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"): write_warning("Ignoring standalone wildcard word.", "Warning", req=req) del opfts[i] if pi == '' or pi == ' ': fi = opfts[i][2] if fi: if of.startswith("h"): write_warning("Ignoring empty %s search term." % fi, "Warning", req=req) 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='', em=''): """ Start page according to given output format. @param title_message: title of the page, not escaped for HTML @param description: description of the page, not escaped for HTML @param keywords: keywords of the page, not escaped for HTML """ _ = 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("""\n""") else: # we are doing XML output: req.content_type = get_output_format_content_type(of, 'text/xml') req.send_http_header() req.write("""\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 == "intbitset": req.content_type = "application/octet-stream" req.send_http_header() elif of == "recjson": req.content_type = "application/json" 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(req.is_https()) else: metaheaderadd = '' # Add metadata in meta tags for Google scholar-esque harvesting... # only if we have a detailed meta format and we are looking at a # single record if recID != -1 and CFG_WEBSEARCH_DETAILED_META_FORMAT and \ record_exists(recID) == 1: metaheaderadd += format_record(recID, CFG_WEBSEARCH_DETAILED_META_FORMAT, ln=ln) ## generate navtrail: navtrail = create_navtrail_links(cc, aas, ln) if navtrail != '': navtrail += ' > ' 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 += ' %s' % \ (CFG_BASE_URL, CFG_SITE_RECORD, recID, cgi.escape(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 += ' > ' + format_name else: # Discussion, citations, etc. tabs tab_label = get_detailed_page_tabs(cc, ln=ln)[tab]['label'] navtrail += ' > ' + _(tab_label) else: navtrail += cgi.escape(title_message) if p: # we are serving search/browse results pages, so insert pattern: navtrail += ": " + cgi.escape(p) title_message = p + " - " + title_message body_css_classes = [] if cc: # we know the collection, lets allow page styles based on cc #collection names may not satisfy rules for css classes which #are something like: -?[_a-zA-Z]+[_a-zA-Z0-9-]* #however it isn't clear what we should do about cases with #numbers, so we leave them to fail. Everything else becomes "_" css = nmtoken_from_string(cc).replace('.', '_').replace('-', '_').replace(':', '_') body_css_classes.append(css) ## finally, print page header: if em == '' or EM_REPOSITORY["header"] in em: 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, body_css_classes=body_css_classes)) req.write(websearch_templates.tmpl_search_pagestart(ln=ln)) else: req.content_type = content_type req.send_http_header() def page_end(req, of="hb", ln=CFG_SITE_LANG, em=""): "End page according to given output format: e.g. close XML tags, add HTML footer, etc." if of == "id": return [] # empty recID list if of == "intbitset": return intbitset() if not req: return # we were called from CLI if of.startswith('h'): req.write(websearch_templates.tmpl_search_pageend(ln = ln)) # pagebody end if em == "" or EM_REPOSITORY["footer"] in em: req.write(pagefooteronly(lastupdated=__lastupdated__, language=ln, req=req)) return def create_add_to_search_pattern(p, p1, f1, m1, op1): """Create the search pattern """ if not p1: return p init_search_pattern = p # operation: AND, OR, AND NOT if op1 == 'a' and p: # we don't want '+' at the begining of the query op = ' +' elif op1 == 'o': op = ' |' elif op1 == 'n': op = ' -' else: op = '' # field field = '' if f1: field = f1 + ':' # type of search pattern = p1 start = '(' end = ')' if m1 == 'e': start = end = '"' elif m1 == 'p': start = end = "'" elif m1 == 'r': start = end = '/' else: # m1 == 'o' or m1 =='a' words = p1.strip().split(' ') if len(words) == 1: start = end = '' pattern = field + words[0] elif m1 == 'o': pattern = ' |'.join([field + word for word in words]) else: pattern = ' '.join([field + word for word in words]) #avoid having field:(word1 word2) since this is not currently correctly working return init_search_pattern + op + start + pattern + end if not pattern: return '' #avoid having field:(word1 word2) since this is not currently correctly working return init_search_pattern + op + field + start + pattern + end def create_page_title_search_pattern_info(p, p1, p2, p3): """Create the search pattern bit for the page 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") # trailing space in May distinguishes short/long form of the month name 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.strip()) 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="", em=""): """Create search box for 'search again in the results page' functionality.""" if em != "" and EM_REPOSITORY["search_box"] not in em: if EM_REPOSITORY["body"] in em and cc != CFG_SITE_NAME: return ''' <h1 class="headline">%(ccname)s</h1>''' % {'ccname' : cgi.escape(cc), } else: return "" # 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 ***' % (CFG_SCOAP3_SITE and _("any publisher or journal") or _("any public collection")) }) # this field is used to remove the current collection from the ones to be searched. temp.append({'value': '', 'text': '*** %s ***' % (CFG_SCOAP3_SITE and _("remove this publisher or journal") or _("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(r"^[\s\-]*", "", val['value'])) }) coll_selects.append(temp) coll_selects.append([{'value': '', 'text' : '*** %s ***' % (CFG_SCOAP3_SITE and _("add another publisher or journal") or _("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 ***' % (CFG_SCOAP3_SITE and _("any publisher or journal") or _("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 = get_available_output_formats(visible_only=True) # 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 if CFG_INSPIRE_SITE: 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 and (em=="" or EM_REPOSITORY["body"] in em) ) def create_exact_author_browse_help_link(p=None, p1=None, p2=None, p3=None, f=None, f1=None, f2=None, f3=None, rm=None, cc=None, ln=None, jrec=None, rg=None, aas=0, action=""): """Creates a link to help switch from author to exact author while browsing""" if action == 'browse': search_fields = (f, f1, f2, f3) if 'author' in search_fields or 'firstauthor' in search_fields: def add_exact(field): if field == 'author' or field == 'firstauthor': return 'exact' + field return field fe, f1e, f2e, f3e = [add_exact(field) for field in search_fields] link_name = f or f1 link_name = (link_name == 'firstauthor' and 'exact first author') or 'exact author' return websearch_templates.tmpl_exact_author_browse_help_link(p=p, p1=p1, p2=p2, p3=p3, f=fe, f1=f1e, f2=f2e, f3=f3e, rm=rm, cc=cc, ln=ln, jrec=jrec, rg=rg, aas=aas, action=action, link_name=link_name) return "" 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': _(CFG_BIBSORT_DEFAULT_FIELD) }] 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 ci in collection_reclist_cache.cache: # yes this collection is real, so use it: cc = ci break else: # check once if cc is real: if cc not in collection_reclist_cache.cache: 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 coll in collection_reclist_cache.cache: 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)) #remove duplicates while preserving the order set_out = set() colls_out_for_display = [coll for coll in colls_out_for_display if coll not in set_out and not set_out.add(coll)] 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)) #remove duplicates while preserving the order set_out = set() colls_out = [coll for coll in colls_out if coll not in set_out and not set_out.add(coll)] 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 get_synonym_terms(term, kbr_name, match_type, use_memoise=False): """ 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 @param use_memoise: can we memoise while doing lookups? @type use_memoise: bool @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 == CFG_BIBINDEX_SYNONYM_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 == CFG_BIBINDEX_SYNONYM_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', use_memoise=use_memoise): for kbr_value in kbr_values: dterms[kbr_value + term_remainder] = 1 ## return list of term synonyms: return dterms.keys() def wash_output_format(ouput_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(ouput_format[0:3]).isdigit() and len(ouput_format) != 6: # asked to print MARC tags, but not enough digits, # so let's switch back to HTML brief default return 'hb' else: return ouput_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: "'"+x.group(1).replace(' ', '__SPACE__')+"'", p) p = re_pattern_double_quotes.sub(lambda x: "\""+x.group(1).replace(' ', '__SPACE__')+"\"", p) p = re_pattern_regexp_quotes.sub(lambda x: "/"+x.group(1).replace(' ', '__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 = p.strip() # 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, )) if not res or not res[0][0]: return False try: return res[0][0].startswith("hostedcollection:") except IndexError: 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.""" res = run_sql("SELECT name FROM collection WHERE name=%s", (c,)) if res: return res[0][0] else: 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, coll_type='r', public_only=1): """Return a list of sons (first-level descendants) of type 'coll_type' for collection 'coll'. If coll_type = '*', both regular and virtual collections will be returned. If public_only, then return only non-restricted son collections. """ coll_sons = [] if coll_type == '*': coll_type_query = " IN ('r', 'v')" query_params = (coll, ) else: coll_type_query = "=%s" query_params = (coll_type, coll) 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" % coll_type_query query += " ORDER BY cc.score ASC" res = run_sql(query, query_params) for name in res: if not public_only or not collection_restricted_p(name[0]): coll_sons.append(name[0]) return coll_sons class CollectionAllChildrenDataCacher(DataCacher): """Cache for all children of a collection (regular & virtual, public & private)""" def __init__(self): def cache_filler(): def get_all_children(coll, coll_type='r', public_only=1, d_internal_coll_sons=None): """Return a list of all children of type 'coll_type' for collection 'coll'. If public_only, then return only non-restricted child collections. If coll_type='*', then return both regular and virtual collections. d_internal_coll_sons is an internal dictionary used in recursion for minimizing the number of database calls and should not be used outside this scope. """ if not d_internal_coll_sons: d_internal_coll_sons = {} children = [] if coll not in d_internal_coll_sons: d_internal_coll_sons[coll] = get_coll_sons(coll, coll_type, public_only) for child in d_internal_coll_sons[coll]: children.append(child) children.extend(get_all_children(child, coll_type, public_only, d_internal_coll_sons)[0]) return children, d_internal_coll_sons ret = {} d_internal_coll_sons = None collections = collection_reclist_cache.cache.keys() for collection in collections: ret[collection], d_internal_coll_sons = get_all_children(collection, '*', public_only=0, d_internal_coll_sons=d_internal_coll_sons) return ret def timestamp_verifier(): return max(get_table_update_time('collection'), get_table_update_time('collection_collection')) DataCacher.__init__(self, cache_filler, timestamp_verifier) try: if not collection_allchildren_cache.is_ok_p: raise Exception except Exception: collection_allchildren_cache = CollectionAllChildrenDataCacher() def get_collection_allchildren(coll, recreate_cache_if_needed=True): """Returns the list of all children of a collection.""" if recreate_cache_if_needed: collection_allchildren_cache.recreate_cache_if_needed() if coll not in collection_allchildren_cache.cache: return [] # collection does not exist; return empty list return collection_allchildren_cache.cache[coll] def get_coll_real_descendants(coll, 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 ASC""", (coll, 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_phrases(req, colls, p, f, rg, ln=CFG_SITE_LANG): """Returns either biliographic phrases or words indexes.""" ## is p enclosed in quotes? (coming from exact search) if p.startswith('"') and p.endswith('"'): p = p[1:-1] ## okay, "real browse" follows: ## FIXME: the maths in the get_nearest_terms_in_bibxxx is just a test if not f and p.find(":") > 0: # does 'p' contain ':'? f, p = p.split(":", 1) ## do we search in words indexes? # FIXME uncomment this #if not f: # return browse_in_bibwords(req, p, f) coll_hitset = intbitset() for coll_name in colls: coll_hitset |= get_collection_reclist(coll_name) index_id = get_index_id_from_field(f) if index_id != 0: browsed_phrases_in_colls = get_nearest_terms_in_idxphrase_with_collection(p, index_id, rg/2, rg/2, coll_hitset) 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: register_exception(req=req, alert_admin=True) # 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 = intbitset() 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 != []: #write_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: nbhits = get_nbhits_in_bibxxx(phrase, f, coll_hitset) if nbhits > 0: browsed_phrases_in_colls.append([phrase, nbhits]) return browsed_phrases_in_colls def browse_pattern(req, colls, p, f, rg, ln=CFG_SITE_LANG): """Displays either biliographic phrases or words indexes.""" # load the right message language _ = gettext_set_language(ln) browsed_phrases_in_colls = browse_pattern_phrases(req, colls, p, f, rg, ln) if len(browsed_phrases_in_colls) == 0: req.write(_("No values found.")) return ## 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.) 'ap' is also internally used for allowing hidden tag search (for requests coming from webcoll, for example). In this case ap=-9 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 = intbitset() # sanity check: if not p: hitset_full = intbitset(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] write_warning("Search stage 1: basic search units are: %s" % cgi.escape(repr(basic_search_units)), req=req) write_warning("Search stage 1: execution took %.2f seconds." % (t2 - t1), req=req) # 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['CFG_BIBFORMAT_HIDDEN_TAGS'] can_see_hidden = False if req: user_info = collect_user_info(req) can_see_hidden = user_info.get('precached_canseehiddenmarctags', False) if not req and ap == -9: # special request, coming from webcoll can_see_hidden = True 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 dummy_o, p, f, m in basic_search_units] if 'fulltext' in fields_to_be_searched: write_warning(_("Full-text search is currently available for all arXiv papers, many theses, a few report series and some journal articles"), req=req) elif 'caption' in fields_to_be_searched: write_warning(_("Warning: figure caption search is only available for a subset of papers mostly from %(x_range_from_year)s-%(x_range_to_year)s.") % {'x_range_from_year': '2008', 'x_range_to_year': '2012'}, req=req) for idx_unit in xrange(len(basic_search_units)): bsu_o, bsu_p, bsu_f, bsu_m = basic_search_units[idx_unit] if bsu_f and len(bsu_f) < 2: if of.startswith("h"): write_warning(_("There is no index %(x_name)s. Searching for %(x_text)s in all fields.", x_name=bsu_f, x_text=bsu_p), req=req) bsu_f = '' bsu_m = 'w' if of.startswith("h") and verbose: write_warning(_('Instead searching %(x_name)s.', x_name=str([bsu_o, bsu_p, bsu_f, bsu_m])), req=req) try: basic_search_unit_hitset = search_unit(bsu_p, bsu_f, bsu_m, wl) except InvenioWebSearchWildcardLimitError as excp: basic_search_unit_hitset = excp.res if of.startswith("h"): write_warning(_("Search term too generic, displaying only partial results..."), req=req) except InvenioWebSearchReferstoLimitError, excp: basic_search_unit_hitset = excp.res if of.startswith("h"): write_warning(_("Search term after reference operator too generic, displaying only partial results..."), req=req) except InvenioWebSearchCitedbyLimitError, excp: basic_search_unit_hitset = excp.res if of.startswith("h"): write_warning(_("Search term after citedby operator too generic, displaying only partial results..."), req=req) # 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: write_warning(_("No phrase index available for fulltext yet, looking for word combination..."), req=req) #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 = intbitset() if verbose >= 9 and of.startswith("h"): write_warning("Pattern %s hitlist omitted since \ it queries in a hidden tag %s" % (cgi.escape(repr(bsu_p)), repr(myhiddens)), req=req) display_nearest_terms_box = False #..and stop spying, too. if verbose >= 9 and of.startswith("h"): write_warning("Search stage 1: pattern %s gave hitlist %s" % (cgi.escape(bsu_p), basic_search_unit_hitset), req=req) if len(basic_search_unit_hitset) > 0 or \ ap<1 or \ bsu_o in ("|", "-") 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: write_warning("Trying (%s,%s,%s)" % (cgi.escape(bsu_pn), cgi.escape(bsu_f), cgi.escape(bsu_m)), req=req) 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'): write_warning(_("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>"}, req=req) 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": write_warning(_("Requested record does not seem to exist."), req=req) else: write_warning(create_nearest_terms_box(req.argd, bsu_p, bsu_f, bsu_m, ln=ln), req=req) 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": write_warning(_("Requested record does not seem to exist."), req=req) else: write_warning(create_nearest_terms_box(req.argd, bsu_p, bsu_f, bsu_m, ln=ln), req=req) return hitset_empty if verbose and of.startswith("h"): t2 = os.times()[4] for idx_unit in range(0, len(basic_search_units)): write_warning("Search stage 2: basic search unit %s gave %d hits." % (basic_search_units[idx_unit][1:], len(basic_search_units_hitsets[idx_unit])), req=req) write_warning("Search stage 2: execution took %.2f seconds." % (t2 - t1), req=req) # 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 = intbitset(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"): write_warning("Invalid set operation %s." % cgi.escape(this_unit_operation), "Error", req=req) 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) write_warning(text, req=req) if verbose and of.startswith("h"): t2 = os.times()[4] write_warning("Search stage 3: boolean query gave %d hits." % len(hitset_in_any_collection), req=req) write_warning("Search stage 3: execution took %.2f seconds." % (t2 - t1), req=req) 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) but still call it for searches like ('U(1)' | 'U(2)'): if not re_pattern_parens.search(re_pattern_parens_quotes.sub('_', 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 = intbitset(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"): write_warning("Search stage 1: search_pattern_parenthesised() searched %s." % repr(p), req=req) write_warning("Search stage 1: search_pattern_parenthesised() returned %s." % repr(parsing_result), req=req) # 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: write_warning(_("Search syntax misunderstood. Ignoring all parentheses in the query. If this doesn't help, please check your search and try again."), req=req) # 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, ignore_synonyms=None): """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. Parameter 'ignore_synonyms' is a list of terms for which we should not try to further find a synonym. This function is suitable as a low-level API. """ ## create empty output results set: hitset = intbitset() if not p: # sanity checking return hitset tokenizer = get_field_tokenizer_type(f) hitset_cjk = intbitset() if tokenizer == "BibIndexCJKTokenizer": if is_there_any_CJK_character_in_text(p): cjk_tok = BibIndexCJKTokenizer() chars = cjk_tok.tokenize_for_words(p) for char in chars: hitset_cjk |= search_unit_in_bibwords(char, f, wl) ## eventually look up runtime synonyms: hitset_synonyms = intbitset() if CFG_WEBSEARCH_SYNONYM_KBRS.has_key(f or 'anyfield'): if ignore_synonyms is None: ignore_synonyms = [] ignore_synonyms.append(p) for p_synonym in get_synonym_terms(p, CFG_WEBSEARCH_SYNONYM_KBRS[f or 'anyfield'][0], CFG_WEBSEARCH_SYNONYM_KBRS[f or 'anyfield'][1]): if p_synonym != p and \ not p_synonym in ignore_synonyms: hitset_synonyms |= search_unit(p_synonym, f, m, wl, ignore_synonyms) ## look up hits: if f == 'fulltext' and get_idx_indexer('fulltext') == 'SOLR' and CFG_SOLR_URL: # redirect to Solr try: return search_unit_in_solr(p, f, m) except: # There were troubles with getting full-text search # results from Solr. Let us alert the admin of these # problems and let us simply return empty results to the # end user. register_exception() return hitset elif f == 'fulltext' and get_idx_indexer('fulltext') == 'XAPIAN' and CFG_XAPIAN_ENABLED: # redirect to Xapian try: return search_unit_in_xapian(p, f, m) except: # There were troubles with getting full-text search # results from Xapian. Let us alert the admin of these # problems and let us simply return empty results to the # end user. register_exception() return hitset 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 == 'referstoexcludingselfcites': # we are doing search by the citation count hitset = search_unit_refersto_excluding_selfcites(p) elif f == 'cataloguer': # we are doing search by the cataloguer nickname hitset = search_unit_in_record_history(p) elif f == 'rawref': from invenio.legacy.refextract.api import search_from_reference field, pattern = search_from_reference(p) return search_unit(pattern, field) elif f == 'citedby': # we are doing search by the citation count hitset = search_unit_citedby(p) elif f == 'collection': # we are doing search by the collection name or MARC field hitset = search_unit_collection(p, m, wl=wl) elif f == 'tag': module_found = False try: from invenio.modules.tags.search_units import search_unit_in_tags module_found = True except: # WebTag module is disabled, so ignore 'tag' selector pass if module_found: return search_unit_in_tags(p) elif f == 'citedbyexcludingselfcites': # we are doing search by the citation count hitset = search_unit_citedby_excluding_selfcites(p) - elif m == 'a' or m == 'r' or f == 'subject': + 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: if m == 'a' and index_id in get_idxpair_field_ids(): #for exact match on the admin configured fields we are searching in the pair tables hitset = search_unit_in_idxpairs(p, f, m, wl) else: hitset = search_unit_in_idxphrases(p, f, m, wl) else: hitset = search_unit_in_bibxxx(p, f, m, wl) # if not hitset and m == 'a' and (p[0] != '%' and p[-1] != '%'): # #if we have no results by doing exact matching, do partial matching # #for removing the distinction between simple and double quotes # 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:]) elif p.startswith("citedexcludingselfcites:"): # we are doing search by the citation count hitset = search_unit_by_times_cited(p[6:], exclude_selfcites=True) else: # we are doing bibwords search by default hitset = search_unit_in_bibwords(p, f, wl=wl) ## merge synonym results and return total: hitset |= hitset_synonyms hitset |= hitset_cjk return hitset def get_idxpair_field_ids(): """Returns the list of ids for the fields that idxPAIRS should be used on""" index_dict = dict(run_sql("SELECT name, id FROM idxINDEX")) return [index_dict[field] for field in index_dict if field in cfg['CFG_WEBSEARCH_IDXPAIRS_FIELDS']] def search_unit_in_bibwords(word, f, decompress=zlib.decompress, wl=0): """Searches for 'word' inside bibwordsX table for field 'f' and returns hitset of recIDs.""" hitset = intbitset() # 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 # if no field is specified, search in the global index. f = f or 'anyfield' 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 intbitset() # word index f does not exist # wash 'word' argument and run query: if f.endswith('count') and word.endswith('+'): # field count query of the form N+ so transform N+ to N->99999: word = word[:-1] + '->99999' word = word.replace('*', '%') # we now use '*' as the truncation character words = word.split("->", 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) # We remove trailing truncation character before stemming if word0.endswith('%'): word0 = stem(word0[:-1], stemming_language) + '%' else: word0 = stem(word0, stemming_language) if word1.endswith('%'): word1 = stem(word1[:-1], stemming_language) + '%' else: word1 = stem(word1, stemming_language) word0_washed = wash_index_term(word0) word1_washed = wash_index_term(word1) if f.endswith('count'): # field count query; convert to integers in order # to have numerical behaviour for 'BETWEEN n1 AND n2' query try: word0_washed = int(word0_washed) word1_washed = int(word1_washed) except ValueError: pass try: res = run_sql_with_limit("SELECT term,hitlist FROM %s WHERE term BETWEEN %%s AND %%s" % bibwordsX, (word0_washed, word1_washed), wildcard_limit=wl) except InvenioDbQueryWildcardLimitError as 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) # We remove trailing truncation character before stemming if word.endswith('%'): word = stem(word[:-1], stemming_language) + '%' else: word = stem(word, stemming_language) if word.find('%') >= 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 as 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 = intbitset(hitlist) # add the results: if set_used: hitset.union_update(hitset_bibwrd) else: hitset = 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(hitset) # okay, return result set: return hitset def search_unit_in_idxpairs(p, f, search_type, wl=0): """Searches for pair 'p' inside idxPAIR table for field 'f' and returns hitset of recIDs found.""" limit_reached = 0 # flag for knowing if the query limit has been reached do_exact_search = True # flag to know when it makes sense to try to do exact matching result_set = intbitset() #determine the idxPAIR table to read from index_id = get_index_id_from_field(f) if not index_id: return intbitset() stemming_language = get_index_stemming_language(index_id) pairs_tokenizer = BibIndexDefaultTokenizer(stemming_language) idxpair_table_washed = wash_table_column_name("idxPAIR%02dF" % index_id) if p.startswith("%") and p.endswith("%"): p = p[1:-1] original_pattern = p p = string.replace(p, '*', '%') # we now use '*' as the truncation character queries_releated_vars = [] # contains tuples of (query_addons, query_params, use_query_limit) #is it a span query? ps = p.split("->", 1) if len(ps) == 2 and not (ps[0].endswith(' ') or ps[1].startswith(' ')): #so we are dealing with a span query pairs_left = pairs_tokenizer.tokenize_for_pairs(ps[0]) pairs_right = pairs_tokenizer.tokenize_for_pairs(ps[1]) if not pairs_left or not pairs_right: # we are not actually dealing with pairs but with words return search_unit_in_bibwords(original_pattern, f, wl=wl) elif len(pairs_left) != len(pairs_right): # it is kind of hard to know what the user actually wanted # we have to do: foo bar baz -> qux xyz, so let's swith to phrase return search_unit_in_idxphrases(original_pattern, f, search_type, wl) elif len(pairs_left) > 1 and \ len(pairs_right) > 1 and \ pairs_left[:-1] != pairs_right[:-1]: # again we have something like: foo bar baz -> abc xyz qux # so we'd better switch to phrase return search_unit_in_idxphrases(original_pattern, f, search_type, wl) else: # finally, we can treat the search using idxPairs # at this step we have either: foo bar -> abc xyz # or foo bar abc -> foo bar xyz queries_releated_vars = [("BETWEEN %s AND %s", (pairs_left[-1], pairs_right[-1]), True)] for pair in pairs_left[:-1]:# which should be equal with pairs_right[:-1] queries_releated_vars.append(("= %s", (pair, ), False)) do_exact_search = False # no exact search for span queries elif p.find('%') > -1: #tokenizing p will remove the '%', so we have to make sure it stays replacement = 'xxxxxxxxxx' #hopefuly this will not clash with anything in the future p = string.replace(p, '%', replacement) pairs = pairs_tokenizer.tokenize_for_pairs(p) if not pairs: # we are not actually dealing with pairs but with words return search_unit_in_bibwords(original_pattern, f, wl=wl) queries_releated_vars = [] for pair in pairs: if string.find(pair, replacement) > -1: pair = string.replace(pair, replacement, '%') #we replace back the % sign queries_releated_vars.append(("LIKE %s", (pair, ), True)) else: queries_releated_vars.append(("= %s", (pair, ), False)) do_exact_search = False else: #normal query pairs = pairs_tokenizer.tokenize_for_pairs(p) if not pairs: # we are not actually dealing with pairs but with words return search_unit_in_bibwords(original_pattern, f, wl=wl) queries_releated_vars = [] for pair in pairs: queries_releated_vars.append(("= %s", (pair, ), False)) first_results = 1 # flag to know if it's the first set of results or not for query_var in queries_releated_vars: query_addons = query_var[0] query_params = query_var[1] use_query_limit = query_var[2] if use_query_limit: try: res = run_sql_with_limit("SELECT term, hitlist FROM %s WHERE term %s" % (idxpair_table_washed, query_addons), query_params, wildcard_limit=wl) #kwalitee:disable=sql except InvenioDbQueryWildcardLimitError as 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" % (idxpair_table_washed, query_addons), query_params) #kwalitee:disable=sql if not res: return intbitset() for pair, hitlist in res: hitset_idxpairs = intbitset(hitlist) if first_results: result_set = hitset_idxpairs first_results = 0 else: result_set.intersection_update(hitset_idxpairs) #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(result_set) # check if we need to eliminate the false positives if cfg['CFG_WEBSEARCH_IDXPAIRS_EXACT_SEARCH'] and do_exact_search: # we need to eliminate the false positives idxphrase_table_washed = wash_table_column_name("idxPHRASE%02dR" % index_id) not_exact_search = intbitset() for recid in result_set: res = run_sql("SELECT termlist FROM %s WHERE id_bibrec %s" %(idxphrase_table_washed, '=%s'), (recid, )) #kwalitee:disable=sql if res: termlist = deserialize_via_marshal(res[0][0]) if not [term for term in termlist if term.lower().find(p.lower()) > -1]: not_exact_search.add(recid) else: not_exact_search.add(recid) # remove the recs that are false positives from the final result result_set.difference_update(not_exact_search) return result_set def search_unit_in_idxphrases(p, f, search_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).""" # call word search method in some cases: if f.endswith('count'): return search_unit_in_bibwords(p, f, wl=wl) hitset = intbitset() # 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 intbitset() # phrase index f does not exist # detect query type (exact phrase, partial phrase, regexp): if search_type == 'r': query_addons = "REGEXP %s" query_params = (p,) use_query_limit = True else: p = p.replace('*', '%') # we now use '*' as the truncation character ps = p.split("->", 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 p.find('%') > -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', 'exactfirstauthor', 'authorityauthor'): 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 as 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 dummy_word, hitlist in res: hitset_bibphrase = intbitset(hitlist) # add the results: if set_used: hitset.union_update(hitset_bibphrase) else: hitset = 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(hitset) # okay, return result set: return hitset 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).""" # call word search method in some cases: if f == 'journal' or f.endswith('count'): return search_unit_in_bibwords(p, f, wl=wl) 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 len(f) >= 2 and 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 intbitset() 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 as 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_and_tag = query_params + (t + '%',) else: # exact query for 't': query += " AND bx.tag=%s" query_params_and_tag = query_params + (t,) if use_query_limit: try: res = run_sql_with_limit(query, query_params_and_tag, wildcard_limit=wl) except InvenioDbQueryWildcardLimitError as excp: res = excp.res limit_reached = 1 # set the limit reached flag to true else: res = run_sql(query, query_params_and_tag) # 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: hitset = intbitset(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(hitset) return hitset def search_unit_in_solr(p, f=None, m=None): """ Query a Solr 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(f, p) def search_unit_in_xapian(p, f=None, m=None): """ Query a Xapian 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 xapian_get_bitset(f, p) def search_unit_in_bibrec(datetext1, datetext2, search_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. """ hitset = intbitset() if search_type and search_type.startswith("m"): search_type = "modification_date" else: search_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" % (search_type,), (datetext1 + '%',)) else: res = run_sql("SELECT id FROM bibrec WHERE %s>=%%s AND %s<=%%s" % (search_type, search_type), (datetext1, datetext2)) for row in res: hitset += row[0] return hitset def search_unit_by_times_cited(p, exclude_selfcites=False): """ 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 = intbitset(run_sql("SELECT id FROM bibrec")) return get_records_with_num_cites(numstr, allrecs, exclude_selfcites=exclude_selfcites) 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) res = get_refersto_hitset(ahitset, record_limit=CFG_WEBSEARCH_MAX_RECORDS_REFERSTO) if len(ahitset) >= CFG_WEBSEARCH_MAX_RECORDS_REFERSTO: raise InvenioWebSearchReferstoLimitError(res) return res else: return intbitset([]) def search_unit_refersto_excluding_selfcites(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) citers = intbitset() citations = get_cited_by_list(ahitset, record_limit=CFG_WEBSEARCH_MAX_RECORDS_REFERSTO) selfcitations = get_self_cited_by_list(ahitset, record_limit=CFG_WEBSEARCH_MAX_RECORDS_REFERSTO) for cites, selfcites in zip(citations, selfcitations): # cites is in the form [(citee, citers), ...] citers += cites[1] - selfcites[1] if len(ahitset) >= CFG_WEBSEARCH_MAX_RECORDS_REFERSTO: raise InvenioWebSearchReferstoLimitError(citers) return citers else: return intbitset([]) def search_unit_in_record_history(query): """ Return hitset of recIDs that were modified by the given cataloguer """ if query: try: cataloguer_name, modification_date = query.split(":") except ValueError: cataloguer_name = query modification_date = "" if modification_date: spires_syntax_converter = SpiresToInvenioSyntaxConverter() modification_date = spires_syntax_converter.convert_date(modification_date) parts = modification_date.split('->', 1) if len(parts) > 1: start_date, end_date = parts res = run_sql("SELECT id_bibrec FROM hstRECORD WHERE job_person=%s AND job_date>=%s AND job_date<=%s", (cataloguer_name, start_date, end_date)) else: res = run_sql("SELECT id_bibrec FROM hstRECORD WHERE job_person=%s AND job_date LIKE %s", (cataloguer_name, modification_date + '%',)) return intbitset(res) else: sql = "SELECT id_bibrec FROM hstRECORD WHERE job_person=%s" res = intbitset(run_sql(sql, (cataloguer_name,))) return res else: return intbitset([]) 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: res = get_citedby_hitset(ahitset, record_limit=CFG_WEBSEARCH_MAX_RECORDS_CITEDBY) if len(ahitset) >= CFG_WEBSEARCH_MAX_RECORDS_CITEDBY: raise InvenioWebSearchCitedbyLimitError(res) return res else: return intbitset([]) else: return intbitset([]) def search_unit_collection(query, m, wl=None): """ Search for records satisfying the query (e.g. collection:"BOOK" or collection:"Books") and return list of records in the collection. """ if len(query): ahitset = get_collection_reclist(query) if not ahitset: return search_unit_in_bibwords(query, 'collection', m, wl=wl) return ahitset else: return intbitset([]) def search_unit_citedby_excluding_selfcites(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) citees = intbitset() references = get_refers_to_list(ahitset, record_limit=CFG_WEBSEARCH_MAX_RECORDS_CITEDBY) selfreferences = get_self_refers_to_list(ahitset, record_limit=CFG_WEBSEARCH_MAX_RECORDS_CITEDBY) for refs, selfrefs in zip(references, selfreferences): # refs is in the form [(citer, citees), ...] citees += refs[1] - selfrefs[1] if len(ahitset) >= CFG_WEBSEARCH_MAX_RECORDS_CITEDBY: raise InvenioWebSearchCitedbyLimitError(citees) return citees else: return intbitset([]) def get_records_that_can_be_displayed(user_info, hitset_in_any_collection, current_coll=CFG_SITE_NAME, colls=None, permitted_restricted_collections=None): """ Return records that can be displayed. """ records_that_can_be_displayed = intbitset() if colls is None: colls = [current_coll] # let's get the restricted collections the user has rights to view if permitted_restricted_collections is None: if user_info['guest'] == '1': ## For guest users that are actually authorized to some restricted ## collection (by virtue of the IP address in a FireRole rule) ## we explicitly build the list of permitted_restricted_collections permitted_restricted_collections = get_permitted_restricted_collections(user_info) else: permitted_restricted_collections = user_info.get('precached_permitted_restricted_collections', []) policy = CFG_WEBSEARCH_VIEWRESTRCOLL_POLICY.strip().upper() current_coll_children = get_collection_allchildren(current_coll) # real & virtual # add all restricted collections, that the user has access to, and are under the current collection # do not use set here, in order to maintain a specific order: # children of 'cc' (real, virtual, restricted), rest of 'c' that are not cc's children colls_to_be_displayed = [coll for coll in current_coll_children if coll in colls or coll in permitted_restricted_collections] colls_to_be_displayed.extend([coll for coll in colls if coll not in colls_to_be_displayed]) if policy == 'ANY':# the user needs to have access to at least one collection that restricts the records #we need this to be able to remove records that are both in a public and restricted collection permitted_recids = intbitset() notpermitted_recids = intbitset() for collection in restricted_collection_cache.cache: if collection in permitted_restricted_collections: permitted_recids |= get_collection_reclist(collection) else: notpermitted_recids |= get_collection_reclist(collection) records_that_can_be_displayed = hitset_in_any_collection - (notpermitted_recids - permitted_recids) else:# the user needs to have access to all collections that restrict a records notpermitted_recids = intbitset() for collection in restricted_collection_cache.cache: if collection not in permitted_restricted_collections: notpermitted_recids |= get_collection_reclist(collection) records_that_can_be_displayed = hitset_in_any_collection - notpermitted_recids if records_that_can_be_displayed.is_infinite(): # We should not return infinite results for user. records_that_can_be_displayed = intbitset() for coll in colls_to_be_displayed: records_that_can_be_displayed |= get_collection_reclist(coll) return records_that_can_be_displayed def intersect_results_with_collrecs(req, hitset_in_any_collection, colls, 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 = {} # all final results results_nbhits = 0 # calculate the list of recids (restricted or not) that the user has rights to access and we should display (only those) if not req or isinstance(req, cStringIO.OutputType): # called from CLI user_info = {} for coll in colls: results[coll] = hitset_in_any_collection & get_collection_reclist(coll) results_nbhits += len(results[coll]) records_that_can_be_displayed = hitset_in_any_collection permitted_restricted_collections = [] else: user_info = collect_user_info(req) # let's get the restricted collections the user has rights to view if user_info['guest'] == '1': ## For guest users that are actually authorized to some restricted ## collection (by virtue of the IP address in a FireRole rule) ## we explicitly build the list of permitted_restricted_collections permitted_restricted_collections = get_permitted_restricted_collections(user_info) else: permitted_restricted_collections = user_info.get('precached_permitted_restricted_collections', []) # let's build the list of the both public and restricted # child collections of the collection from which the user # started his/her search. This list of children colls will be # used in the warning proposing a search in that collections try: current_coll = req.argd['cc'] # current_coll: coll from which user started his/her search except: from flask import request current_coll = request.args.get('cc', CFG_SITE_NAME) # current_coll: coll from which user started his/her search current_coll_children = get_collection_allchildren(current_coll) # real & virtual # add all restricted collections, that the user has access to, and are under the current collection # do not use set here, in order to maintain a specific order: # children of 'cc' (real, virtual, restricted), rest of 'c' that are not cc's children colls_to_be_displayed = [coll for coll in current_coll_children if coll in colls or coll in permitted_restricted_collections] colls_to_be_displayed.extend([coll for coll in colls if coll not in colls_to_be_displayed]) records_that_can_be_displayed = get_records_that_can_be_displayed( user_info, hitset_in_any_collection, current_coll, colls, permitted_restricted_collections) for coll in colls_to_be_displayed: results[coll] = results.get(coll, intbitset()) | (records_that_can_be_displayed & get_collection_reclist(coll)) results_nbhits += len(results[coll]) if results_nbhits == 0: # no hits found, try to search in Home and restricted and/or hidden collections: results = {} results_in_Home = records_that_can_be_displayed & get_collection_reclist(CFG_SITE_NAME) results_in_restricted_collections = intbitset() results_in_hidden_collections = intbitset() for coll in permitted_restricted_collections: if not get_coll_ancestors(coll): # hidden collection results_in_hidden_collections.union_update(records_that_can_be_displayed & get_collection_reclist(coll)) else: results_in_restricted_collections.union_update(records_that_can_be_displayed & get_collection_reclist(coll)) # in this way, we do not count twice, records that are both in Home collection and in a restricted collection total_results = len(results_in_Home.union(results_in_restricted_collections)) if total_results > 0: # some hits found in Home and/or restricted collections, 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=[]) len_colls_to_display = len(colls_to_be_displayed) # trim the list of collections to first two, since it might get very large write_warning(_("No match found in collection %(x_collection)s. Other 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_to_be_displayed[:2]], ', ') + (len_colls_to_display > 2 and ' et al' or '') + '</em>', 'x_url_open': '<a class="nearestterms" href="%s">' % (url), 'x_nb_hits': total_results, 'x_url_close': '</a>'}, req=req) # display the hole list of collections in a comment if len_colls_to_display > 2: write_warning("<!--No match found in collection <em>%(x_collection)s</em>.-->" % {'x_collection': string.join([get_coll_i18nname(coll, ln, False) for coll in colls_to_be_displayed], ', ')}, req=req) else: # no hits found, either user is looking for a document and he/she has not rights # or user is looking for a hidden document: if of.startswith("h") and display_nearest_terms_box: if len(results_in_hidden_collections) > 0: write_warning(_("No public collection matched your query. " "If you were looking for a hidden document, please type " "the correct URL for this record."), req=req) else: write_warning(_("No public collection matched your query. " "If you were looking for a non-public document, please choose " "the desired restricted collection first."), req=req) if verbose and of.startswith("h"): t2 = os.times()[4] write_warning("Search stage 4: intersecting with collection universe gave %d hits." % results_nbhits, req=req) write_warning("Search stage 4: execution took %.2f seconds." % (t2 - t1), req=req) 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 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 final_results = {} for coll in results.keys(): final_results[coll] = results[coll].intersection(hitset) nb_total += len(final_results[coll]) if nb_total == 0: if of.startswith("h"): write_warning(aptext, req=req) final_results = results_ap return final_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) 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) if not CFG_WEBSEARCH_DISPLAY_NEAREST_TERMS: return _("Your search did not match any records. Please try again.") 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' or f == 'referstoexcludingselfcites': return _("There are no records referring to %(x_rec)s.", x_rec=cgi.escape(p)) if f == 'cataloguer': return _("There are no records modified by %(x_rec)s.", x_rec=cgi.escape(p)) if f == 'citedby' or f == 'citedbyexcludingselfcites': return _("There are no records cited by %(x_rec)s.", x_rec=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 %(x_name)s.", x_name=('<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 %(x_name)s.", x_name=('<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, dummy_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) #argd[px] = string.replace(argd_px, p, term, 1) #we need something similar, but case insensitive pattern_index = string.find(argd_px.lower(), p.lower()) if pattern_index > -1: argd[px] = argd_px[:pattern_index] + term + argd_px[pattern_index+len(p):] break #this is doing exactly the same as: #argd[px] = re.sub('(?i)' + re.escape(p), term, argd_px, 1) #but is ~4x faster (2us vs. 8.25us) 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 %(x_name)s did not match any record. Nearest terms in any collection are:", x_name=("<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 = [x[0] for x in 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 = [x[0] for x in 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 (intbitset). 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, intbitset(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, intbitset(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) # 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 ValueError: 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(intbitset(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(intbitset(hitlist[0])) return out def get_nbhits_in_bibxxx(p, f, in_hitset=None): """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 if in_hitset is None: nbhits = len(recIDs) else: nbhits = len(intbitset(recIDs.keys()).intersection(in_hitset)) return nbhits 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") for dbcollid in dbcollids: variants = ("collection:" + dbcollid, 'collection:"' + dbcollid + '"', "980__a:" + dbcollid, '980__a:"' + dbcollid + '"', '980:' + dbcollid , '980:"' + dbcollid + '"') res = run_sql("SELECT name FROM collection WHERE dbquery IN (%s,%s,%s,%s,%s,%s)", variants) if res: out = res[0][0] break if CFG_CERN_SITE: recID = int(recID) # 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): return alternative_collection # dirty hack for FP FP_collections = {'DO': ['Current Price Enquiries', 'Archived Price Enquiries'], 'IT': ['Current Invitation for Tenders', 'Archived Invitation for Tenders'], 'MS': ['Current Market Surveys', 'Archived Market Surveys']} fp_coll_ids = [coll for coll in dbcollids if coll in FP_collections] for coll in fp_coll_ids: for coll_name in FP_collections[coll]: if recID in get_collection_reclist(coll_name): return coll_name 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', []): name = get_coll_normalised_name(name) if name and 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_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 get_merged_recid(recID): """ Return the record ID of the record with which the given record has been merged. @param recID: deleted record recID @type recID: int @return: merged record recID @rtype: int or None """ merged_recid = None for val in get_fieldvalues(recID, "970__d"): try: merged_recid = int(val) break except ValueError: pass return merged_recid 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 """ return bibrecord.record_empty(get_record(recID)) 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_search_info(p, f, sf, so, sp, rm, of, ot, collection=CFG_SITE_NAME, nb_found=-1, jrec=1, rg=CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS, 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, em=""): """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.""" if em != '' and EM_REPOSITORY["search_info"] not in em: return "" # 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=CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS, 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, em=""): """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.""" if em != '' and EM_REPOSITORY["search_info"] not in em: return "" # 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, em=""): """Prints results overview box with links to particular collections below.""" if em != "" and EM_REPOSITORY["overview"] not in em: return "" 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, em = ""): """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, display_body = em == "" or EM_REPOSITORY["body"] in em, display_add_to_basket = em == "" or EM_REPOSITORY["basket"] in em) class BibSortDataCacher(DataCacher): """ Cache holding all structures created by bibsort ( _data, data_dict). """ def __init__(self, method_name): self.method_name = method_name self.method_id = 0 res = run_sql("""SELECT id from bsrMETHOD where name = %s""", (self.method_name,)) if res and res[0]: self.method_id = res[0][0] else: self.method_id = 0 def cache_filler(): method_id = self.method_id alldicts = {} if self.method_id == 0: return {} try: res_data = run_sql("""SELECT data_dict_ordered from bsrMETHODDATA \ where id_bsrMETHOD = %s""", (method_id,)) res_buckets = run_sql("""SELECT bucket_no, bucket_data from bsrMETHODDATABUCKET\ where id_bsrMETHOD = %s""", (method_id,)) except Exception: # database problems, return empty cache return {} try: data_dict_ordered = deserialize_via_marshal(res_data[0][0]) except IndexError: data_dict_ordered = {} alldicts['data_dict_ordered'] = data_dict_ordered # recid: weight if not res_buckets: alldicts['bucket_data'] = {} return alldicts for row in res_buckets: bucket_no = row[0] try: bucket_data = intbitset(row[1]) except IndexError: bucket_data = intbitset([]) alldicts.setdefault('bucket_data', {})[bucket_no] = bucket_data return alldicts def timestamp_verifier(): method_id = self.method_id res = run_sql("""SELECT last_updated from bsrMETHODDATA where id_bsrMETHOD = %s""", (method_id,)) try: update_time_methoddata = str(res[0][0]) except IndexError: update_time_methoddata = '1970-01-01 00:00:00' res = run_sql("""SELECT max(last_updated) from bsrMETHODDATABUCKET where id_bsrMETHOD = %s""", (method_id,)) try: update_time_buckets = str(res[0][0]) except IndexError: update_time_buckets = '1970-01-01 00:00:00' return max(update_time_methoddata, update_time_buckets) DataCacher.__init__(self, cache_filler, timestamp_verifier) def get_sorting_methods(): res = run_sql("""SELECT m.name, m.definition FROM bsrMETHOD m, bsrMETHODDATA md WHERE m.id = md.id_bsrMETHOD""") return dict(res) SORTING_METHODS = get_sorting_methods() CACHE_SORTED_DATA = {} for sorting_method in SORTING_METHODS: try: CACHE_SORTED_DATA[sorting_method].is_ok_p except KeyError: CACHE_SORTED_DATA[sorting_method] = BibSortDataCacher(sorting_method) def get_tags_from_sort_fields(sort_fields): """Given a list of sort_fields, return the tags associated with it and also the name of the field that has no tags associated, to be able to display a message to the user.""" tags = [] if not sort_fields: return [], '' for sort_field in sort_fields: if sort_field and (len(sort_field) > 1 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 field_tags = get_field_tags(sort_field) if field_tags: tags.extend(field_tags) else: return [], sort_field return tags, '' def rank_records(req, rank_method_code, rank_limit_relevance, hitset_global, pattern=None, verbose=0, sort_order='d', of='hb', ln=CFG_SITE_LANG, rg=None, jrec=None, field='', sorting_methods=SORTING_METHODS): """Initial entry point for ranking records, acts like a dispatcher. (i) rank_method_code is in bsrMETHOD, bibsort buckets can be used; (ii)rank_method_code is not in bsrMETHOD, use bibrank; """ # Special case: sorting by citations is fast because we store the # ranking dictionary in memory, so we do not use bibsort buckets. if CFG_BIBSORT_ENABLED and sorting_methods and rank_method_code != 'citation': for sort_method in sorting_methods: definition = sorting_methods[sort_method] if definition.startswith('RNK') and \ definition.replace('RNK:', '').strip().lower() == rank_method_code.lower(): solution_recs, solution_scores = \ sort_records_bibsort(req, hitset_global, sort_method, '', sort_order, verbose, of, ln, rg, jrec, 'r') comment = '' if verbose > 0: comment = 'find_citations retlist %s' % [[solution_recs[i], solution_scores[i]] for i in range(len(solution_recs))] return solution_recs, solution_scores, '(', ')', comment if rank_method_code.lower() == 'citation': related_to = [] else: related_to = pattern solution_recs, solution_scores, prefix, suffix, comment = \ rank_records_bibrank(rank_method_code=rank_method_code, rank_limit_relevance=rank_limit_relevance, hitset=hitset_global, verbose=verbose, field=field, related_to=related_to, rg=rg, jrec=jrec) # Solution recs can be None, in case of error or other cases # which should be all be changed to return an empty list. if solution_recs and sort_order == 'd': solution_recs.reverse() solution_scores.reverse() return solution_recs, solution_scores, prefix, suffix, comment def sort_records_latest(recIDs, jrec, rg, sort_order): if sort_order == 'd': recIDs.reverse() return slice_records(recIDs, jrec, rg) def sort_or_rank_records(req, recIDs, rm, sf, so, sp, p, verbose=0, of='hb', ln=CFG_SITE_LANG, rg=None, jrec=None, field='', sorting_methods=SORTING_METHODS): """Sort or rank records. Entry point for deciding to either sort or rank records.""" if rm: ranking_result = rank_records(req, rm, 0, recIDs, p, verbose, so, of, ln, rg, jrec, field, sorting_methods=sorting_methods) if ranking_result[0]: return ranking_result[0] # ranked recids elif sf or (CFG_BIBSORT_ENABLED and SORTING_METHODS): return sort_records(req, recIDs, sf, so, sp, verbose, of, ln, rg, jrec) return recIDs.tolist() def sort_records(req, recIDs, sort_field='', sort_order='a', sort_pattern='', verbose=0, of='hb', ln=CFG_SITE_LANG, rg=None, jrec=None, sorting_methods=SORTING_METHODS): """Initial entry point for sorting records, acts like a dispatcher. (i) sort_field is in the bsrMETHOD, and thus, the BibSort has sorted the data for this field, so we can use the cache; (ii)sort_field is not in bsrMETHOD, and thus, the cache does not contain any information regarding this sorting method""" _ = gettext_set_language(ln) # bibsort does not handle sort_pattern for now, use bibxxx if sort_pattern: return sort_records_bibxxx(req, recIDs, None, sort_field, sort_order, sort_pattern, verbose, of, ln, rg, jrec) # ignore the use of buckets, use old fashion sorting use_sorting_buckets = CFG_BIBSORT_ENABLED and sorting_methods # Default sorting if not sort_field: if use_sorting_buckets: return sort_records_bibsort(req, recIDs, CFG_BIBSORT_DEFAULT_FIELD, sort_field, CFG_BIBSORT_DEFAULT_FIELD_ORDER, verbose, of, ln, rg, jrec) else: return sort_records_latest(recIDs, jrec, rg, sort_order) sort_fields = sort_field.split(",") if len(sort_fields) == 1: # we have only one sorting_field, check if it is treated by BibSort for sort_method in sorting_methods: definition = sorting_methods[sort_method] if use_sorting_buckets and \ ((definition.startswith('FIELD') and definition.replace('FIELD:', '').strip().lower() == sort_fields[0].lower()) or sort_method == sort_fields[0]): #use BibSort return sort_records_bibsort(req, recIDs, sort_method, sort_field, sort_order, verbose, of, ln, rg, jrec) #deduce sorting MARC tag out of the 'sort_field' argument: tags, error_field = get_tags_from_sort_fields(sort_fields) if error_field: if use_sorting_buckets: return sort_records_bibsort(req, recIDs, CFG_BIBSORT_DEFAULT_FIELD, sort_field, sort_order, verbose, of, ln, rg, jrec) else: if of.startswith('h'): write_warning(_("Sorry, %(x_option)s does not seem to be a valid sort option. The records will not be sorted.", x_option=cgi.escape(error_field)), "Error", req=req) return slice_records(recIDs, jrec, rg) elif tags: for sort_method in sorting_methods: definition = sorting_methods[sort_method] if definition.startswith('MARC') \ and definition.replace('MARC:', '').strip().split(',') == tags \ and use_sorting_buckets: #this list of tags have a designated method in BibSort, so use it return sort_records_bibsort(req, recIDs, sort_method, sort_field, sort_order, verbose, of, ln, rg, jrec) #we do not have this sort_field in BibSort tables -> do the old fashion sorting return sort_records_bibxxx(req, recIDs, tags, sort_field, sort_order, sort_pattern, verbose, of, ln, rg, jrec) else: return slice_records(recIDs, jrec, rg) def sort_records_bibsort(req, recIDs, sort_method, sort_field='', sort_order='d', verbose=0, of='hb', ln=CFG_SITE_LANG, rg=None, jrec=1, sort_or_rank='s', sorting_methods=SORTING_METHODS): """This function orders the recIDs list, based on a sorting method(sort_field) using the BibSortDataCacher for speed""" _ = gettext_set_language(ln) if not jrec: jrec = 1 #sanity check if sort_method not in sorting_methods: if sort_or_rank == 'r': return rank_records_bibrank(rank_method_code=sort_method, rank_limit_relevance=0, hitset=recIDs, verbose=verbose) else: return sort_records_bibxxx(req, recIDs, None, sort_field, sort_order, '', verbose, of, ln, rg, jrec) if verbose >= 3 and of.startswith('h'): write_warning("Sorting (using BibSort cache) by method %s (definition %s)." % (cgi.escape(repr(sort_method)), cgi.escape(repr(sorting_methods[sort_method]))), req=req) #we should return sorted records up to irec_max(exclusive) dummy, irec_max = get_interval_for_records_to_sort(len(recIDs), jrec, rg) solution = intbitset() input_recids = intbitset(recIDs) CACHE_SORTED_DATA[sort_method].recreate_cache_if_needed() sort_cache = CACHE_SORTED_DATA[sort_method].cache bucket_numbers = sort_cache['bucket_data'].keys() #check if all buckets have been constructed if len(bucket_numbers) != CFG_BIBSORT_BUCKETS: if verbose > 3 and of.startswith('h'): write_warning("Not all buckets have been constructed.. switching to old fashion sorting.", req=req) if sort_or_rank == 'r': return rank_records_bibrank(rank_method_code=sort_method, rank_limit_relevance=0, hitset=recIDs, verbose=verbose) else: return sort_records_bibxxx(req, recIDs, None, sort_field, sort_order, '', verbose, of, ln, rg, jrec) if sort_order == 'd': bucket_numbers.reverse() for bucket_no in bucket_numbers: solution.union_update( input_recids & sort_cache['bucket_data'][bucket_no] ) if len(solution) >= irec_max: break dict_solution = {} missing_records = intbitset() for recid in solution: try: dict_solution[recid] = sort_cache['data_dict_ordered'][recid] except KeyError: # recid is in buckets, but not in the bsrMETHODDATA, # maybe because the value has been deleted, but the change has not # yet been propagated to the buckets missing_records.add(recid) # check if there are recids that are not in any bucket -> to be added at # the end/top, ordered by insertion date if len(solution) < irec_max: #some records have not been yet inserted in the bibsort structures #or, some records have no value for the sort_method missing_records += input_recids - solution reverse = sort_order == 'd' if sort_method.strip().lower() == CFG_BIBSORT_DEFAULT_FIELD and reverse: # If we want to sort the records on their insertion date, add the # missing records at the top. solution = sorted(missing_records, reverse=True) + \ sorted(dict_solution, key=dict_solution.__getitem__, reverse=True) else: solution = sorted(dict_solution, key=dict_solution.__getitem__, reverse=reverse) + sorted(missing_records) # Only keep records, we are going to display solution = slice_records(solution, jrec, rg) if sort_or_rank == 'r': # We need the recids, with their ranking score return solution, [dict_solution.get(record, 0) for record in solution] else: return solution def slice_records(recIDs, jrec, rg): if not jrec: jrec = 1 if rg: recIDs = recIDs[jrec-1:jrec-1+rg] else: recIDs = recIDs[jrec-1:] return recIDs def sort_records_bibxxx(req, recIDs, tags, sort_field='', sort_order='d', sort_pattern='', verbose=0, of='hb', ln=CFG_SITE_LANG, rg=None, jrec=None): """OLD FASHION SORTING WITH NO CACHE, for sort fields that are not run in BibSort 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 slice_records(recIDs, jrec, rg) if len(recIDs) > CFG_WEBSEARCH_NB_RECORDS_TO_SORT: if of.startswith('h'): write_warning(_("Sorry, sorting is allowed on sets of up to %(x_name)d records only. Using default sort order.", x_name=CFG_WEBSEARCH_NB_RECORDS_TO_SORT), "Warning", req=req) return slice_records(recIDs, jrec, rg) recIDs_dict = {} recIDs_out = [] if not tags: # tags have not been camputed yet sort_fields = sort_field.split(',') tags, error_field = get_tags_from_sort_fields(sort_fields) if error_field: if of.startswith('h'): write_warning(_("Sorry, %(x_name)s does not seem to be a valid sort option. The records will not be sorted.", x_name=cgi.escape(error_field)), "Error", req=req) return slice_records(recIDs, jrec, rg) if verbose >= 3 and of.startswith('h'): write_warning("Sorting by tags %s." % cgi.escape(repr(tags)), req=req) if sort_pattern: write_warning("Sorting preferentially by %s." % cgi.escape(sort_pattern), req=req) ## 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 + " " + ''.join(vals) else: # no sort pattern defined, so join them all together val = ''.join(vals) val = strip_accents(val.lower()) # sort values regardless of accents and case if val in recIDs_dict: recIDs_dict[val].append(recID) else: recIDs_dict[val] = [recID] # create output array: for k in sorted(recIDs_dict.keys()): recIDs_out.extend(recIDs_dict[k]) # ascending or descending? if sort_order == 'd': recIDs_out.reverse() recIDs = recIDs_out # return only up to the maximum that we need return slice_records(recIDs, jrec, rg) def get_interval_for_records_to_sort(nb_found, jrec=None, rg=None): """calculates in which interval should the sorted records be a value of 'rg=-9999' means to print all records: to be used with care.""" if not jrec: jrec = 1 if not rg: #return all return jrec-1, nb_found 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 sort records from irec_min to irec_max excluded irec_min = jrec - 1 irec_max = irec_min + rg if irec_min < 0: irec_min = 0 if irec_max > nb_found: irec_max = nb_found return irec_min, irec_max def print_records(req, recIDs, jrec=1, rg=CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS, 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='', em='', nb_found=-1): """ 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. """ if em != "" and EM_REPOSITORY["body"] not in em: return # 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 nb_found == -1: nb_found = len(recIDs) if nb_found: if not rg or 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 len(recIDs) > rg and rg != -9999: recIDs = slice_records(recIDs, jrec, rg) if format.startswith('x'): # print header if needed if print_records_prologue_p: print_records_prologue(req, format) if ot: # asked to print some filtered fields only, so call print_record() on the fly: for recid in recIDs: x = print_record(recid, format, ot=ot, ln=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') else: format_records(recIDs, 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 recid in recIDs: x = print_record(recid, 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.startswith('recjson'): # we are doing recjson output: req.write('[') for idx, recid in enumerate(recIDs): if idx > 0: req.write(',') req.write(print_record(recid, format, ot, ln, search_pattern=search_pattern, user_info=user_info, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm)) req.write(']') elif format == 'excel': create_excel(recIDs=recIDs, req=req, ot=ot, user_info=user_info) else: # we are doing HTML output: if format == 'hp' or format.startswith("hb_") or format.startswith("hd_"): # portfolio and on-the-fly formats: for recid in recIDs: req.write(print_record(recid, format, ot=ot, ln=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 if em != "" and EM_REPOSITORY["basket"] not in em: display_add_to_basket = False req.write(websearch_templates.tmpl_record_format_htmlbrief_header(ln=ln)) for irec, recid in enumerate(recIDs): row_number = jrec+irec if relevances and relevances[irec]: relevance = relevances[irec] else: relevance = '' record = print_record(recid, format, ot=ot, ln=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: referer = user_info.get('referer', '') for recid in recIDs: if record_exists(recid) == -1: write_warning(_("The record has been deleted."), req=req) merged_recid = get_merged_recid(recid) if merged_recid: write_warning(_("The record %(x_rec)d replaces it.", x_rec=merged_recid), req=req) continue unordered_tabs = get_detailed_page_tabs(get_colID(guess_collection_of_a_record(recid, referer, False)), recid, ln=ln) ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in iteritems(unordered_tabs)] 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_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 tabs = [(unordered_tabs[tab_id]['label'], '%s/%s/%s/%s%s' % (CFG_BASE_URL, CFG_SITE_RECORD, recid_to_display, tab_id, link_ln), tab_id == tab, unordered_tabs[tab_id]['enabled']) for (tab_id, dummy_order) in ordered_tabs_id if unordered_tabs[tab_id]['visible'] is True] tabs_counts = get_detailed_page_tabs_counts(recid) citedbynum = tabs_counts['Citations'] references = tabs_counts['References'] discussions = tabs_counts['Discussions'] # load content if tab == 'usage': req.write(webstyle_templates.detailed_record_container_top(recid, tabs, ln, citationnum=citedbynum, referencenum=references, discussionnum=discussions)) r = calculate_reading_similarity_list(recid, "downloads") downloadsimilarity = None downloadhistory = None #if r: # downloadsimilarity = r if CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS: downloadhistory = create_download_history_graph_and_box(recid, ln) r = calculate_reading_similarity_list(recid, "pageviews") viewsimilarity = None if r: viewsimilarity = r content = websearch_templates.tmpl_detailed_record_statistics(recid, ln, downloadsimilarity=downloadsimilarity, downloadhistory=downloadhistory, viewsimilarity=viewsimilarity) req.write(content) req.write(webstyle_templates.detailed_record_container_bottom(recid, tabs, ln)) elif tab == 'citations': req.write(webstyle_templates.detailed_record_container_top(recid, tabs, ln, citationnum=citedbynum, referencenum=references, discussionnum=discussions)) 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) selfcited = rank_by_citations(get_self_cited_by(recid), verbose=verbose) selfcited = reversed(selfcited[0]) selfcited = [recid for recid, dummy in selfcited] 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: write_warning("Citation graph debug: " + str(len(citationhistory)), req=req) req.write(websearch_templates.tmpl_detailed_record_citations_citation_history(ln, citationhistory)) # Citation log entries = get_citers_log(recid) req.write(websearch_templates.tmpl_detailed_record_citations_citation_log(ln, entries)) 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(recid, tabs, ln, citationnum=citedbynum, referencenum=references, discussionnum=discussions)) req.write(format_record(recid, 'HDREF', ln=ln, user_info=user_info, verbose=verbose, force_2nd_pass=True)) req.write(webstyle_templates.detailed_record_container_bottom(recid, tabs, ln)) elif tab == 'keywords': from invenio.legacy.bibclassify.webinterface import main_page main_page(req, recid, tabs, ln, webstyle_templates) elif tab == 'plots': req.write(webstyle_templates.detailed_record_container_top(recid, tabs, ln)) content = websearch_templates.tmpl_record_plots(recID=recid, ln=ln) req.write(content) req.write(webstyle_templates.detailed_record_container_bottom(recid, tabs, ln)) elif tab == 'hepdata': req.write(webstyle_templates.detailed_record_container_top(recid, tabs, ln, include_jquery=True, include_mathjax=True)) from invenio.utils import hepdata as hepdatautils from invenio.utils.hepdata import display as hepdatadisplayutils data = hepdatautils.retrieve_data_for_record(recid) if data: content = websearch_templates.tmpl_record_hepdata(data, recid, True) else: content = websearch_templates.tmpl_record_no_hepdata() req.write(content) req.write(webstyle_templates.detailed_record_container_bottom(recid, tabs, ln)) else: # Metadata tab req.write(webstyle_templates.detailed_record_container_top( recid, tabs, ln, show_short_rec_p=False, citationnum=citedbynum, referencenum=references, discussionnum=discussions)) creationdate = None modificationdate = None if record_exists(recid) == 1: creationdate = get_creation_date(recid) modificationdate = get_modification_date(recid) content = print_record(recid, 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=recid, ln=ln, format=format, creationdate=creationdate, modificationdate=modificationdate, content=content) # display of the next-hit/previous-hit/back-to-search links # on the detailed record pages content += websearch_templates.tmpl_display_back_to_search(req, recid, ln) req.write(content) req.write(webstyle_templates.detailed_record_container_bottom(recid, 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.modules.comments.api import get_mini_reviews reviews = get_mini_reviews(recid=recid, ln=ln) else: reviews = '' actions = format_record(recid, 'HDACT', ln=ln, user_info=user_info, verbose=verbose) files = format_record(recid, 'HDFILE', ln=ln, user_info=user_info, verbose=verbose) req.write(webstyle_templates.detailed_record_mini_panel(recid, ln, format, files=files, reviews=reviews, actions=actions)) else: # Other formats for recid in recIDs: req.write(print_record(recid, format, ot, ln, search_pattern=search_pattern, user_info=user_info, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm)) else: write_warning(_("Use different search terms."), req=req) def print_records_prologue(req, format, cc=None): """ 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(cc=cc) elif format.startswith('xe8x'): prologue = websearch_templates.tmpl_xml_endnote_8x_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(cc=cc) 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('xe8x'): epilogue = websearch_templates.tmpl_xml_endnote_8x_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: val = value[0][0] except IndexError: ### In case it does not exist, let's build it! pass else: return deserialize_via_marshal(val) 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='', brief_links=True): """ 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) #check from user information if the user has the right to see hidden fields/tags in the #records as well can_see_hidden = False if user_info: can_see_hidden = user_info.get('precached_canseehiddenmarctags', False) if format == 'recjson': import json from invenio.modules.records.api import get_record as get_recjson ot = ot if ot and len(ot) else None return json.dumps(get_recjson(recID).dumps( keywords=ot, filter_hidden=not can_see_hidden)) _ = gettext_set_language(ln) # The 'attribute this paper' link is shown only if the session states it should and # the record is included in the collections to which bibauthorid is limited. if user_info: display_claim_this_paper = (user_info.get("precached_viewclaimlink", False) and recID in intbitset.union(*[get_collection_reclist(x) for x in BIBAUTHORID_LIMIT_TO_COLLECTIONS])) else: display_claim_this_paper = False can_edit_record = False if check_user_can_edit_record(user_info, recID): can_edit_record = True out = "" # sanity check: record_exist_p = record_exists(recID) if record_exist_p == 0: # doesn't exist return out # We must still check some special formats, but these # should disappear when BibFormat improves. if not (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.") # was record deleted-but-merged ? merged_recid = get_merged_recid(recID) if merged_recid: out += ' ' + _("The record %(x_rec)d replaces it.", x_rec=merged_recid) 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 brief_links and 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, display_edit_link=can_edit_record) return out 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 and not ot: # record 'recID' is formatted in 'format', and we are not # asking for field-filtered output; so print it: out += "%s" % decompress(res[0][0]) elif ot: # field-filtered output was asked for; print only some fields record = get_record(recID) if not can_see_hidden: for tag in cfg['CFG_BIBFORMAT_HIDDEN_TAGS']: del record[tag] ot = list(set(ot) - set(cfg['CFG_BIBFORMAT_HIDDEN_TAGS'])) out += record_xml_output(record, ot) else: # record 'recID' is not formatted in 'format' or we ask # for field-filtered output -- 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['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\n" % encode_for_xml(f) for f in get_fieldvalues(recID, "65017a"): out += " %s\n" % encode_for_xml(f) for f in get_fieldvalues(recID, "8564_u"): if f.split('.') == 'png': continue out += " %s\n" % encode_for_xml(f) for f in get_fieldvalues(recID, "520__a"): out += " %s\n" % encode_for_xml(f) out += " %s\n" % get_creation_date(recID) out += " \n" elif len(format) == 6 and str(format[0:3]).isdigit(): # user has asked to print some fields only if format == "001": out += "%s\n" % (format, recID, format) else: vals = get_fieldvalues(recID, format) for val in vals: out += "%s\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
" + cgi.escape(get_fieldvalues_alephseq_like(recID, ["001", CFG_OAI_ID_FIELD, "980"], can_see_hidden)) + "
" else: out += "\n
" + cgi.escape(get_fieldvalues_alephseq_like(recID, ot, can_see_hidden)) + "
" elif format.startswith("h") and ot: ## user directly asked for some tags to be displayed only if record_exist_p == -1: out += "\n
" + get_fieldvalues_alephseq_like(recID, ["001", CFG_OAI_ID_FIELD, "980"], can_see_hidden) + "
" else: out += "\n
" + get_fieldvalues_alephseq_like(recID, ot, can_see_hidden) + "
" 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 += '' % websearch_templates.build_search_url(recid=recID, ln=ln) # firstly, title: titles = get_fieldvalues(recID, "245__a") if titles: for title in titles: out += "%s" % title else: # usual title not found, try conference title: titles = get_fieldvalues(recID, "111__a") if titles: for title in titles: out += "%s" % title else: # just print record ID: out += "%s %d" % (get_field_i18nname("record ID", ln, False), recID) out += "" # secondly, authors: authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a") if authors: out += " - %s" % authors[0] if len(authors) > 1: out += " et al" # 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, display_edit_link=can_edit_record) # print record closing tags, if needed: if format == "marcxml" or format == "oai_dc": out += " \n" out += " \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.modules.formatter.utils import get_pdf_snippets keywords = [] if search_pattern is not None: for unit in create_basic_search_units(None, str(search_pattern), None): bsu_o, bsu_p, bsu_f, bsu_m = unit[0], unit[1], unit[2], unit[3] if (bsu_o != '-' and bsu_f in [None, 'fulltext']): if bsu_m == 'a' and bsu_p.startswith('%') and bsu_p.endswith('%'): # remove leading and training `%' representing partial phrase search keywords.append(bsu_p[1:-1]) else: keywords.append(bsu_p) 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'].lower(): # check snippets only if URL contains fulltext # FIXME: make it work for CLI too, via new function arg if keywords: snippets = '' try: snippets = get_pdf_snippets(recID, keywords, user_info) except: register_exception() 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 IndexError: 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 def clean_dictionary(dictionary, list_of_items): """Returns a copy of the dictionary with all the items in the list_of_items as empty strings""" out_dictionary = dictionary.copy() out_dictionary.update((item, '') for item in list_of_items) return out_dictionary ### CALLABLES def perform_request_search(req=None, cc=CFG_SITE_NAME, c=None, p="", f="", rg=None, sf="", so="a", 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=0, em=""): """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. (Note that `rg' is ignored in case of `of=id'.) 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, "intbitset" means to return an intbitset representation of the recIDs found (no sorting or ranking will be performed). (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. em - output only part of the page. 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. (Note that `jrec' is ignored in case of `of=id'.) 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 """ kwargs = prs_wash_arguments(req=req, cc=cc, c=c, p=p, f=f, rg=rg, sf=sf, so=so, sp=sp, rm=rm, of=of, ot=ot, aas=aas, p1=p1, f1=f1, m1=m1, op1=op1, p2=p2, f2=f2, m2=m2, op2=op2, p3=p3, f3=f3, m3=m3, sc=sc, jrec=jrec, recid=recid, recidb=recidb, sysno=sysno, id=id, idb=idb, sysnb=sysnb, action=action, d1=d1, d1y=d1y, d1m=d1m, d1d=d1d, d2=d2, d2y=d2y, d2m=d2m, d2d=d2d, dt=dt, verbose=verbose, ap=ap, ln=ln, ec=ec, tab=tab, wl=wl, em=em) return prs_perform_search(kwargs=kwargs, **kwargs) def prs_perform_search(kwargs=None, **dummy): """Internal call which does the search, it is calling standard Invenio; Unless you know what you are doing, don't use this call as an API """ # separately because we can call it independently out = prs_wash_arguments_colls(kwargs=kwargs, **kwargs) if not out: return out return prs_search(kwargs=kwargs, **kwargs) def prs_wash_arguments_colls(kwargs=None, of=None, req=None, cc=None, c=None, sc=None, verbose=None, aas=None, ln=None, em="", **dummy): """ Check and wash collection list argument before we start searching. If there are troubles, e.g. a collection is not defined, print warning to the browser. @return: True if collection list is OK, and various False values (empty string, empty list) if there was an error. """ # 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() 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? kwargs['colls_to_display'] = colls_to_display kwargs['colls_to_search'] = colls_to_search kwargs['hosted_colls'] = hosted_colls kwargs['wash_colls_debug'] = wash_colls_debug except InvenioWebSearchUnknownCollectionError as 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)) page_end(req, of, ln, em) return '' elif of == "id": return [] elif of == "intbitset": return intbitset() elif of == "recjson": return [] elif of.startswith("x"): # Print empty, but valid XML print_records_prologue(req, of) print_records_epilogue(req, of) page_end(req, of, ln, em) return '' else: page_end(req, of, ln, em) return '' return True def prs_wash_arguments(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="", uid=None, wl=0, em="", **dummy): """ Sets the (default) values and checks others for the PRS call """ # wash output format: of = wash_output_format(of) # wash all arguments requiring special care 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) (d1y, d1m, d1d, d2y, d2m, d2d) = map(int, (d1y, d1m, d1d, d2y, d2m, d2d)) datetext1, datetext2 = wash_dates(d1, d1y, d1m, d1d, d2, d2y, d2m, d2d) # wash ranking method: if not is_method_valid(None, rm): rm = "" # 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, dict)) \ and getattr(req, 'args', None): # 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 fieldcode in fieldargs: for val in fieldargs[fieldcode]: pl += "+%s:\"%s\" " % (fieldcode, val) pl_in_url += "&%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): if uid is None: try: uid = getUid(req) except: uid = 0 _ = gettext_set_language(ln) if aas == 2: #add-to-search interface p = create_add_to_search_pattern(p, p1, f1, m1, op1) default_addtosearch_args = websearch_templates.restore_search_args_to_default(['p1', 'f1', 'm1', 'op1']) if req: req.argd.update(default_addtosearch_args) req.argd['p'] = p kwargs = {'req': req, 'cc': cc, 'c': c, 'p': p, 'f': f, 'rg': rg, 'sf': sf, 'so': so, 'sp': sp, 'rm': rm, 'of': of, 'ot': ot, 'aas': aas, 'p1': p1, 'f1': f1, 'm1': m1, 'op1': op1, 'p2': p2, 'f2': f2, 'm2': m2, 'op2': op2, 'p3': p3, 'f3': f3, 'm3': m3, 'sc': sc, 'jrec': jrec, 'recid': recid, 'recidb': recidb, 'sysno': sysno, 'id': id, 'idb': idb, 'sysnb': sysnb, 'action': action, 'd1': d1, 'd1y': d1y, 'd1m': d1m, 'd1d': d1d, 'd2': d2, 'd2y': d2y, 'd2m': d2m, 'd2d': d2d, 'dt': dt, 'verbose': verbose, 'ap': ap, 'ln': ln, 'ec': ec, 'tab': tab, 'wl': wl, 'em': em, 'datetext1': datetext1, 'datetext2': datetext2, 'uid': uid, 'pl': pl, 'pl_in_url': pl_in_url, '_': _, 'selected_external_collections_infos': None, } kwargs.update(**dummy) return kwargs def prs_search(kwargs=None, recid=0, req=None, cc=None, p=None, p1=None, p2=None, p3=None, f=None, ec=None, verbose=None, ln=None, selected_external_collections_infos=None, action=None, rm=None, of=None, em=None, **dummy): """ This function write various bits into the req object as the search proceeds (so that pieces of a page are rendered even before the search ended) """ ## 0 - start output if recid >= 0: # recid can be 0 if deduced from sysno and if such sysno does not exist output = prs_detailed_record(kwargs=kwargs, **kwargs) if output is not None: return output elif action == "browse": ## 2 - browse needed of = 'hb' output = prs_browse(kwargs=kwargs, **kwargs) if output is not None: return output elif rm and p.startswith("recid:"): ## 3-ter - similarity search (or old-style citation search) needed output = prs_search_similar_records(kwargs=kwargs, **kwargs) if output is not None: return output elif p.startswith("cocitedwith:"): #WAS EXPERIMENTAL ## 3-terter - cited by search needed output = prs_search_cocitedwith(kwargs=kwargs, **kwargs) if output is not None: return output else: ## 3 - common search needed output = prs_search_common(kwargs=kwargs, **kwargs) if output is not None: return output # External searches if of.startswith("h"): if not of in ['hcs', 'hcs2']: perform_external_collection_search_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) return page_end(req, of, ln, em) def prs_detailed_record(kwargs=None, req=None, of=None, cc=None, aas=None, ln=None, uid=None, recid=None, recidb=None, p=None, verbose=None, tab=None, sf=None, so=None, sp=None, rm=None, ot=None, _=None, em=None, **dummy): """Formats and prints one record""" ## 1 - detailed record display title, description, keywords = \ websearch_templates.tmpl_record_page_header_content(req, recid, ln) if req is not None and req.method != 'HEAD': page_start(req, of, cc, aas, ln, uid, title, description, keywords, recid, tab, em) # 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 in ["id", "intbitset"]: result = [recidx for recidx in range(recid, recidb) if record_exists(recidx)] if of == "intbitset": return intbitset(result) else: return result 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, em=em, nb_found=len(range(recid, recidb))) 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 == "intbitset": return intbitset() elif of == "recjson": 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: write_warning(_("Requested record does not seem to exist."), req=req) def prs_browse(kwargs=None, req=None, of=None, cc=None, aas=None, ln=None, uid=None, _=None, p=None, p1=None, p2=None, p3=None, colls_to_display=None, f=None, rg=None, sf=None, so=None, sp=None, rm=None, ot=None, f1=None, m1=None, op1=None, f2=None, m2=None, op2=None, f3=None, m3=None, sc=None, pl=None, d1y=None, d1m=None, d1d=None, d2y=None, d2m=None, d2d=None, dt=None, jrec=None, ec=None, action=None, colls_to_search=None, verbose=None, em=None, **dummy): page_start(req, of, cc, aas, ln, uid, _("Browse"), p=create_page_title_search_pattern_info(p, p1, p2, p3), em=em) 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, em )) write_warning(create_exact_author_browse_help_link(p, p1, p2, p3, f, f1, f2, f3, rm, cc, ln, jrec, rg, aas, action), req=req) 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 KeyboardInterrupt: # This happens usually from the command line # The error handling we want is different raise 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, em) def prs_search_similar_records(kwargs=None, req=None, of=None, cc=None, pl_in_url=None, ln=None, uid=None, _=None, p=None, p1=None, p2=None, p3=None, colls_to_display=None, f=None, rg=None, sf=None, so=None, sp=None, rm=None, ot=None, aas=None, f1=None, m1=None, op1=None, f2=None, m2=None, op2=None, f3=None, m3=None, sc=None, pl=None, d1y=None, d1m=None, d1d=None, d2y=None, d2m=None, d2d=None, dt=None, jrec=None, ec=None, action=None, em=None, verbose=None, **dummy): if req and req.method != 'HEAD': page_start(req, of, cc, aas, ln, uid, _("Search Results"), p=create_page_title_search_pattern_info(p, p1, p2, p3), em=em) 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, em )) recid = p[6:] if record_exists(recid) != 1: # record does not exist if of.startswith("h"): if req.header_only: raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) else: write_warning(_("Requested record does not seem to exist."), req=req) if of == "id": return [] if of == "intbitset": return intbitset() elif of == "recjson": 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_bibrank(rank_method_code=rm, rank_limit_relevance=0, hitset=get_collection_reclist(cc), related_to=[p], verbose=verbose, field=f, rg=rg, jrec=jrec) 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, em=em)) write_warning(results_similar_comments, req=req) 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, em=em, nb_found=len(results_similar_recIDs)) elif of == "id": return results_similar_recIDs elif of == "intbitset": return intbitset(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, em=em, nb_found=len(results_similar_recIDs)) else: # rank_records failed and returned some error message to display: if of.startswith("h"): write_warning(results_similar_relevances_prologue, req=req) write_warning(results_similar_relevances_epilogue, req=req) write_warning(results_similar_comments, req=req) if of == "id": return [] elif of == "intbitset": return intbitset() elif of == "recjson": return [] elif of.startswith("x"): # Print empty, but valid XML print_records_prologue(req, of) print_records_epilogue(req, of) def prs_search_cocitedwith(kwargs=None, req=None, of=None, cc=None, pl_in_url=None, ln=None, uid=None, _=None, p=None, p1=None, p2=None, p3=None, colls_to_display=None, f=None, rg=None, sf=None, so=None, sp=None, rm=None, ot=None, aas=None, f1=None, m1=None, op1=None, f2=None, m2=None, op2=None, f3=None, m3=None, sc=None, pl=None, d1y=None, d1m=None, d1d=None, d2y=None, d2m=None, d2d=None, dt=None, jrec=None, ec=None, action=None, verbose=None, em=None, **dummy): page_start(req, of, cc, aas, ln, uid, _("Search Results"), p=create_page_title_search_pattern_info(p, p1, p2, p3), em=em) 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, em )) recID = p[12:] if record_exists(recID) != 1: # record does not exist if of.startswith("h"): write_warning(_("Requested record does not seem to exist."), req=req) if of == "id": return [] elif of == "intbitset": return intbitset() elif of == "recjson": 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 = [x[0] for x in 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, em=em)) print_records(req, results_cocited_recIDs, jrec, rg, of, ot, ln, search_pattern=p, verbose=verbose, sf=sf, so=so, sp=sp, rm=rm, em=em, nb_found=len(results_cocited_recIDs)) elif of == "id": return results_cocited_recIDs elif of == "intbitset": return intbitset(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, em=em, nb_found=len(results_cocited_recIDs)) else: # cited rank_records failed and returned some error message to display: if of.startswith("h"): write_warning("nothing found", req=req) if of == "id": return [] elif of == "intbitset": return intbitset() elif of == "recjson": return [] elif of.startswith("x"): # Print empty, but valid XML print_records_prologue(req, of) print_records_epilogue(req, of) def prs_search_hosted_collections(kwargs=None, req=None, of=None, ln=None, _=None, p=None, p1=None, p2=None, p3=None, hosted_colls=None, f=None, colls_to_search=None, hosted_colls_actual_or_potential_results_p=None, verbose=None, **dummy): hosted_colls_results = hosted_colls_timeouts = hosted_colls_true_results = None # 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] is None or result[1] is False: # these are the searches the returned no or zero results if verbose: write_warning("Hosted collections (perform_search_request): %s returned no results" % result[0][1].name, req=req) else: # these are the searches that actually returned results on time hosted_colls_true_results.append(result) if verbose: write_warning("Hosted collections (perform_search_request): %s returned %s results in %s seconds" % (result[0][1].name, result[1], result[2]), req=req) else: if verbose: write_warning("Hosted collections (perform_search_request): there were no hosted collections results to be printed at this time", req=req) if hosted_colls_timeouts: if verbose: for timeout in hosted_colls_timeouts: write_warning("Hosted collections (perform_search_request): %s timed out and will be searched again later" % timeout[0][1].name, req=req) # 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: write_warning("Hosted collections (perform_search_request): there were no hosted collections to be searched", req=req) ## let's define some useful boolean variables: # True means there are actual or potential hosted collections results to be printed kwargs['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) kwargs['hosted_colls_potential_results_p'] = not (not hosted_colls or not hosted_colls_timeouts) # True means we only have hosted collections to deal with kwargs['only_hosted_colls_actual_or_potential_results_p'] = not colls_to_search and hosted_colls_actual_or_potential_results_p kwargs['hosted_colls_results'] = hosted_colls_results kwargs['hosted_colls_timeouts'] = hosted_colls_timeouts kwargs['hosted_colls_true_results'] = hosted_colls_true_results def prs_advanced_search(results_in_any_collection, kwargs=None, req=None, of=None, cc=None, ln=None, _=None, p=None, p1=None, p2=None, p3=None, f=None, f1=None, m1=None, op1=None, f2=None, m2=None, op2=None, f3=None, m3=None, ap=None, ec=None, selected_external_collections_infos=None, verbose=None, wl=None, em=None, **dummy): len_results_p1 = 0 len_results_p2 = 0 len_results_p3 = 0 try: results_in_any_collection.union_update(search_pattern_parenthesised(req, p1, f1, m1, ap=ap, of=of, verbose=verbose, ln=ln, wl=wl)) len_results_p1 = len(results_in_any_collection) if len_results_p1 == 0: if of.startswith("h"): perform_external_collection_search_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) 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, em) if p2: results_tmp = search_pattern_parenthesised(req, p2, f2, m2, ap=ap, of=of, verbose=verbose, ln=ln, wl=wl) len_results_p2 = len(results_tmp) 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"): write_warning("Invalid set operation %s." % cgi.escape(op1), "Error", req=req) if len(results_in_any_collection) == 0: if of.startswith("h"): if len_results_p2: #each individual query returned results, but the boolean operation did not nearestterms = [] nearest_search_args = req.argd.copy() if p1: nearestterms.append((p1, len_results_p1, clean_dictionary(nearest_search_args, ['p2', 'f2', 'm2', 'p3', 'f3', 'm3']))) nearestterms.append((p2, len_results_p2, clean_dictionary(nearest_search_args, ['p1', 'f1', 'm1', 'p3', 'f3', 'm3']))) write_warning(websearch_templates.tmpl_search_no_boolean_hits(ln=ln, nearestterms=nearestterms), req=req) perform_external_collection_search_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) elif of.startswith("x"): # Print empty, but valid XML print_records_prologue(req, of) print_records_epilogue(req, of) if p3: results_tmp = search_pattern_parenthesised(req, p3, f3, m3, ap=ap, of=of, verbose=verbose, ln=ln, wl=wl) len_results_p3 = len(results_tmp) 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"): write_warning("Invalid set operation %s." % cgi.escape(op2), "Error", req=req) if len(results_in_any_collection) == 0 and len_results_p3 and of.startswith("h"): #each individual query returned results but the boolean operation did not nearestterms = [] nearest_search_args = req.argd.copy() if p1: nearestterms.append((p1, len_results_p1, clean_dictionary(nearest_search_args, ['p2', 'f2', 'm2', 'p3', 'f3', 'm3']))) if p2: nearestterms.append((p2, len_results_p2, clean_dictionary(nearest_search_args, ['p1', 'f1', 'm1', 'p3', 'f3', 'm3']))) nearestterms.append((p3, len_results_p3, clean_dictionary(nearest_search_args, ['p1', 'f1', 'm1', 'p2', 'f2', 'm2']))) write_warning(websearch_templates.tmpl_search_no_boolean_hits(ln=ln, nearestterms=nearestterms), req=req) except KeyboardInterrupt: # This happens usually from the command line # The error handling we want is different raise 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_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) 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, em) def prs_simple_search(results_in_any_collection, kwargs=None, req=None, of=None, cc=None, ln=None, p=None, f=None, p1=None, p2=None, p3=None, ec=None, verbose=None, selected_external_collections_infos=None, only_hosted_colls_actual_or_potential_results_p=None, query_representation_in_cache=None, ap=None, hosted_colls_actual_or_potential_results_p=None, wl=None, em=None, **dummy): try: results_in_cache = intbitset().fastload( search_results_cache.get(query_representation_in_cache)) except: results_in_cache = None if results_in_cache is not None: # query is not in the cache already, so reuse it: results_in_any_collection.union_update(results_in_cache) if verbose and of.startswith("h"): write_warning("Search stage 0: query found in cache, reusing cached results.", req=req) 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.union_update(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_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) return page_end(req, of, ln, em) def prs_intersect_results_with_collrecs(results_final, results_in_any_collection, kwargs=None, colls_to_search=None, req=None, of=None, ln=None, cc=None, p=None, p1=None, p2=None, p3=None, f=None, ec=None, verbose=None, selected_external_collections_infos=None, em=None, **dummy): display_nearest_terms_box=not kwargs['hosted_colls_actual_or_potential_results_p'] 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.update(intersect_results_with_collrecs(req, results_in_any_collection, colls_to_search, of, verbose, ln, display_nearest_terms_box=display_nearest_terms_box)) 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_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) return page_end(req, of, ln, em) def prs_store_results_in_cache(query_representation_in_cache, results_in_any_collection, req=None, verbose=None, of=None, **dummy): if CFG_WEBSEARCH_SEARCH_CACHE_SIZE > 0: search_results_cache.set(query_representation_in_cache, results_in_any_collection.fastdump(), timeout=CFG_WEBSEARCH_SEARCH_CACHE_TIMEOUT) search_results_cache.set(query_representation_in_cache + '::cc', dummy.get('cc', CFG_SITE_NAME), timeout=CFG_WEBSEARCH_SEARCH_CACHE_TIMEOUT) if req: from flask import request req = request search_results_cache.set(query_representation_in_cache + '::p', req.values.get('p', ''), timeout=CFG_WEBSEARCH_SEARCH_CACHE_TIMEOUT) if verbose and of.startswith("h"): write_warning(req, "Search stage 3: storing query results in cache.", req=req) def prs_apply_search_limits(results_final, kwargs=None, req=None, of=None, cc=None, ln=None, _=None, p=None, p1=None, p2=None, p3=None, f=None, pl=None, ap=None, dt=None, ec=None, selected_external_collections_infos=None, hosted_colls_actual_or_potential_results_p=None, datetext1=None, datetext2=None, verbose=None, wl=None, em=None, **dummy): if datetext1 != "" and results_final != {}: if verbose and of.startswith("h"): write_warning("Search stage 5: applying time etc limits, from %s until %s..." % (datetext1, datetext2), req=req) try: results_temp = 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) if results_temp: results_final.update(results_temp) else: results_final.clear() 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_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) return page_end(req, of, ln, em) if results_final == {} and not hosted_colls_actual_or_potential_results_p: if of.startswith("h"): perform_external_collection_search_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) #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, em) if pl and results_final != {}: pl = wash_pattern(pl) if verbose and of.startswith("h"): write_warning("Search stage 5: applying search pattern limit %s..." % cgi.escape(pl), req=req) try: results_temp = 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) if results_temp: results_final.update(results_temp) else: results_final.clear() 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_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) return page_end(req, of, ln, em) if results_final == {} and not hosted_colls_actual_or_potential_results_p: if of.startswith("h"): perform_external_collection_search_with_em(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos, em=em) 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, em) def prs_split_into_collections(kwargs=None, results_final=None, colls_to_search=None, hosted_colls_results=None, cpu_time=0, results_final_nb_total=None, hosted_colls_actual_or_potential_results_p=None, hosted_colls_true_results=None, hosted_colls_timeouts=None, **dummy): 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 = intbitset() 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 kwargs['results_final_nb'] = results_final_nb kwargs['results_final_nb_total'] = results_final_nb_total kwargs['results_final_for_all_selected_colls'] = results_final_for_all_selected_colls kwargs['cpu_time'] = cpu_time #rca TODO: check where the cpu_time is used, this line was missing return (results_final_nb, results_final_nb_total, results_final_for_all_selected_colls) def prs_summarize_records(kwargs=None, req=None, p=None, f=None, aas=None, p1=None, p2=None, p3=None, f1=None, f2=None, f3=None, op1=None, op2=None, ln=None, results_final_for_all_selected_colls=None, of='hcs', **dummy): # feed the current search to be summarized: from invenio.legacy.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, of, ln, search_p, search_f, req) def prs_print_records(kwargs=None, results_final=None, req=None, of=None, cc=None, pl_in_url=None, ln=None, _=None, p=None, p1=None, p2=None, p3=None, f=None, rg=None, sf=None, so=None, sp=None, rm=None, ot=None, aas=None, f1=None, m1=None, op1=None, f2=None, m2=None, op2=None, f3=None, m3=None, sc=None, d1y=None, d1m=None, d1d=None, d2y=None, d2m=None, d2d=None, dt=None, jrec=None, colls_to_search=None, hosted_colls_actual_or_potential_results_p=None, hosted_colls_results=None, hosted_colls_true_results=None, hosted_colls_timeouts=None, results_final_nb=None, cpu_time=None, verbose=None, em=None, **dummy): 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, cc=cc) results_final_colls = [] wlqh_results_overlimit = 0 for coll in colls_to_search: if coll in results_final 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, em=em)) results_final_recIDs = list(results_final[coll]) results_final_nb_found = len(results_final_recIDs) results_final_relevances = [] results_final_relevances_prologue = "" results_final_relevances_epilogue = "" if 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(req, rm, 0, results_final[coll], string.split(p) + string.split(p1) + string.split(p2) + string.split(p3), verbose, so, of, ln, rg, jrec, kwargs['f']) if of.startswith("h"): write_warning(results_final_comments, req=req) if results_final_recIDs_ranked: results_final_recIDs = results_final_recIDs_ranked else: # rank_records failed and returned some error message to display: write_warning(results_final_relevances_prologue, req=req) write_warning(results_final_relevances_epilogue, req=req) else: results_final_recIDs = sort_records(req, results_final_recIDs, sf, so, sp, verbose, of, ln, rg, jrec) if len(results_final_recIDs) < CFG_WEBSEARCH_PREV_NEXT_HIT_LIMIT: results_final_colls.append(results_final_recIDs) else: wlqh_results_overlimit = 1 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, em=em, nb_found=results_final_nb_found) 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, em=em)) if req and not isinstance(req, cStringIO.OutputType): # store the last search results page session_param_set(req, 'websearch-last-query', req.unparsed_uri) if wlqh_results_overlimit: results_final_colls = None # store list of results if user wants to display hits # in a single list, or store list of collections of records # if user displays hits split by collections: session_param_set(req, 'websearch-last-query-hits', results_final_colls) #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, em=em)) req.write(print_hosted_results(url_and_engine=result[0], ln=ln, of=of, req=req, limit=rg, em=em)) 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] is None or result[1] is 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, em=em)) 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, em=em)) 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, em=em)) 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)) def prs_log_query(kwargs=None, req=None, uid=None, of=None, ln=None, p=None, f=None, colls_to_search=None, results_final_nb_total=None, em=None, **dummy): # FIXME move query logging to signal receiver # log query: try: from flask_login import current_user if req: from flask import request req = request id_query = log_query(req.host, '&'.join(map(lambda (k,v): k+'='+v, request.values.iteritems(multi=True))), uid) #id_query = log_query(req.remote_host, req.args, uid) #of = request.values.get('of', 'hb') if of.startswith("h") and id_query and (em == '' or EM_REPOSITORY["alert"] in em): if not of in ['hcs', 'hcs2']: # display alert/RSS teaser for non-summary formats: display_email_alert_part = True if current_user: if current_user['email'] == 'guest': if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS > 4: display_email_alert_part = False else: if not current_user['precached_usealerts']: display_email_alert_part = False from flask import flash flash(websearch_templates.tmpl_alert_rss_teaser_box_for_query(id_query, \ ln=ln, display_email_alert_part=display_email_alert_part), 'search-results-after') 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) def prs_search_common(kwargs=None, req=None, of=None, cc=None, ln=None, uid=None, _=None, p=None, p1=None, p2=None, p3=None, colls_to_display=None, f=None, rg=None, sf=None, so=None, sp=None, rm=None, ot=None, aas=None, f1=None, m1=None, op1=None, f2=None, m2=None, op2=None, f3=None, m3=None, sc=None, pl=None, d1y=None, d1m=None, d1d=None, d2y=None, d2m=None, d2d=None, dt=None, jrec=None, ec=None, action=None, colls_to_search=None, wash_colls_debug=None, verbose=None, wl=None, em=None, **dummy): query_representation_in_cache = get_search_results_cache_key(**kwargs) page_start(req, of, cc, aas, ln, uid, p=create_page_title_search_pattern_info(p, p1, p2, p3), em=em) if of.startswith("h") and verbose and wash_colls_debug: write_warning("wash_colls debugging info : %s" % wash_colls_debug, req=req) prs_search_hosted_collections(kwargs=kwargs, **kwargs) 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, em )) # WebSearch services if jrec <= 1 and \ (em == "" and True or (EM_REPOSITORY["search_services"] in em)): user_info = collect_user_info(req) # display only on first search page, and only if wanted # when 'em' param set. for answer_relevance, answer_html in services.get_answers( req, user_info, of, cc, colls_to_search, p, f, ln): req.write('
') req.write(answer_html) if verbose > 8: write_warning("Service relevance: %i" % answer_relevance, req=req) req.write('
') t1 = os.times()[4] results_in_any_collection = intbitset() if aas == 2 and not (p2 or p3): ## 3A add-to-search output = prs_simple_search(results_in_any_collection, kwargs=kwargs, **kwargs) if output is not None: return output elif aas == 1 or (p1 or p2 or p3): ## 3B - advanced search output = prs_advanced_search(results_in_any_collection, kwargs=kwargs, **kwargs) if output is not None: return output else: ## 3C - simple search output = prs_simple_search(results_in_any_collection, kwargs=kwargs, **kwargs) if output is not None: return output if len(results_in_any_collection) == 0 and not kwargs['hosted_colls_actual_or_potential_results_p']: if of.startswith("x"): # Print empty, but valid XML print_records_prologue(req, of) print_records_epilogue(req, of) return None # store this search query results into search results cache if needed: prs_store_results_in_cache(query_representation_in_cache, results_in_any_collection, **kwargs) # search stage 4 and 5: intersection with collection universe and sorting/limiting try: output = prs_intersect_with_colls_and_apply_search_limits(results_in_any_collection, kwargs=kwargs, **kwargs) if output is not None: return output except KeyboardInterrupt: # This happens usually from the command line # The error handling we want is different raise except: # no results to display return None t2 = os.times()[4] cpu_time = t2 - t1 kwargs['cpu_time'] = cpu_time ## search stage 6: display results: return prs_display_results(kwargs=kwargs, **kwargs) def prs_intersect_with_colls_and_apply_search_limits(results_in_any_collection, kwargs=None, req=None, of=None, **dummy): # search stage 4: intersection with collection universe: results_final = {} output = prs_intersect_results_with_collrecs(results_final, results_in_any_collection, kwargs, **kwargs) if output is not None: return output # another external search if we still don't have something if results_final == {} and not kwargs['hosted_colls_actual_or_potential_results_p']: if of.startswith("x"): # Print empty, but valid XML print_records_prologue(req, of) print_records_epilogue(req, of) kwargs['results_final'] = results_final raise Exception # search stage 5: apply search option limits and restrictions: output = prs_apply_search_limits(results_final, kwargs=kwargs, **kwargs) kwargs['results_final'] = results_final if output is not None: return output def prs_display_results(kwargs=None, results_final=None, req=None, of=None, sf=None, so=None, sp=None, verbose=None, p=None, p1=None, p2=None, p3=None, cc=None, ln=None, _=None, ec=None, colls_to_search=None, rm=None, cpu_time=None, f=None, em=None, jrec=None, rg=None, **dummy ): ## search stage 6: display results: # split result set into collections (results_final_nb, results_final_nb_total, results_final_for_all_selected_colls) = prs_split_into_collections(kwargs=kwargs, **kwargs) # 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 kwargs['hosted_colls_potential_results_p']: if of.startswith("h"): write_warning("No match found, please enter different search terms.", req=req) elif of.startswith("x"): # Print empty, but valid XML print_records_prologue(req, of) print_records_epilogue(req, of) else: prs_log_query(kwargs=kwargs, **kwargs) # 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 == "intbitset": #return the result as an intbitset return results_final_for_all_selected_colls elif of == "id": # we have been asked to return list of recIDs recIDs = list(results_final_for_all_selected_colls) if rm: # do we have to rank? results_final_for_all_colls_rank_records_output = rank_records(req, rm, 0, results_final_for_all_selected_colls, p.split() + p1.split() + p2.split() + p3.split(), verbose, so, of, ln, kwargs['rg'], kwargs['jrec'], kwargs['f']) if results_final_for_all_colls_rank_records_output[0]: recIDs = results_final_for_all_colls_rank_records_output[0] elif sf or (CFG_BIBSORT_ENABLED and SORTING_METHODS): # do we have to sort? recIDs = sort_records(req, recIDs, sf, so, sp, verbose, of, ln) return slice_records(recIDs, jrec, rg) elif of.startswith("h"): if of not in ['hcs', 'hcs2', 'hcv', 'htcv', 'tlcv']: # 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=kwargs['hosted_colls_potential_results_p'], em=em)) kwargs['selected_external_collections_infos'] = print_external_results_overview(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, print_overview=em == "" or EM_REPOSITORY["overview"] in em) # print number of hits found for XML outputs: if of.startswith("x") or of == 'mobb': req.write("\n" % kwargs['results_final_nb_total']) # print records: if of in ['hcs', 'hcs2']: prs_summarize_records(kwargs=kwargs, **kwargs) elif of in ['hcv', 'htcv', 'tlcv'] and CFG_INSPIRE_SITE: from invenio.legacy.search_engine.cvifier import cvify_records cvify_records(results_final_for_all_selected_colls, of, req, so) else: prs_print_records(kwargs=kwargs, **kwargs) # this is a copy of the prs_display_results with output parts removed, needed for external modules def prs_rank_results(kwargs=None, results_final=None, req=None, colls_to_search=None, sf=None, so=None, sp=None, of=None, rm=None, p=None, p1=None, p2=None, p3=None, verbose=None, **dummy ): ## search stage 6: display results: # split result set into collections dummy_results_final_nb, dummy_results_final_nb_total, results_final_for_all_selected_colls = prs_split_into_collections(kwargs=kwargs, **kwargs) # 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) # we have been asked to return list of recIDs recIDs = list(results_final_for_all_selected_colls) if rm: # do we have to rank? results_final_for_all_colls_rank_records_output = rank_records(req, rm, 0, results_final_for_all_selected_colls, p.split() + p1.split() + p2.split() + p3.split(), verbose, so, of, field=kwargs['f']) if results_final_for_all_colls_rank_records_output[0]: recIDs = results_final_for_all_colls_rank_records_output[0] elif sf or (CFG_BIBSORT_ENABLED and SORTING_METHODS): # do we have to sort? recIDs = sort_records(req, recIDs, sf, so, sp, verbose, of) return recIDs def perform_request_cache(req, action="show"): """Manipulates the search engine cache.""" req.content_type = "text/html" req.send_http_header() req.write("") out = "" out += "

Search Cache

" req.write(out) # show collection reclist cache: out = "

Collection reclist cache

" out += "- collection table last updated: %s" % get_table_update_time('collection') out += "
- reclist cache timestamp: %s" % collection_reclist_cache.timestamp out += "
- reclist cache contents:" out += "
" for coll in collection_reclist_cache.cache.keys(): if collection_reclist_cache.cache[coll]: out += "%s (%d)
" % (coll, len(collection_reclist_cache.cache[coll])) out += "
" req.write(out) # show field i18nname cache: out = "

Field I18N names cache

" out += "- fieldname table last updated: %s" % get_table_update_time('fieldname') out += "
- i18nname cache timestamp: %s" % field_i18nname_cache.timestamp out += "
- i18nname cache contents:" out += "
" for field in field_i18nname_cache.cache.keys(): for ln in field_i18nname_cache.cache[field].keys(): out += "%s, %s = %s
" % (field, ln, field_i18nname_cache.cache[field][ln]) out += "
" req.write(out) # show collection i18nname cache: out = "

Collection I18N names cache

" out += "- collectionname table last updated: %s" % get_table_update_time('collectionname') out += "
- i18nname cache timestamp: %s" % collection_i18nname_cache.timestamp out += "
- i18nname cache contents:" out += "
" for coll in collection_i18nname_cache.cache.keys(): for ln in collection_i18nname_cache.cache[coll].keys(): out += "%s, %s = %s
" % (coll, ln, collection_i18nname_cache.cache[coll][ln]) out += "
" req.write(out) req.write("") 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("") req.write("

Search Log

") if date: # case A: display stats for a day yyyymmdd = string.atoi(date) req.write("

Date: %d

" % yyyymmdd) req.write("""""") req.write("" % ("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 = line.split("#") i += 1 req.write("" % (i, datetime[8:10], datetime[10:12], datetime[12:], p, f, c, nbhits)) except: pass # ignore eventual wrong log lines req.write("
%s%s%s%s%s%s
#%d%s:%s:%s%s%s%s%s
") 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("""""") req.write("" % ("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("""""" % (day, CFG_SITE_URL, day, line)) p.close() req.write("
%s%s
%s%s
") req.write("") return "\n" def get_all_field_values(tag): """ Return all existing values stored for a given tag. @param tag: the full tag, e.g. 909C0b @type tag: string @return: the list of values @rtype: list of strings """ table = 'bib%02dx' % int(tag[:2]) return [row[0] for row in run_sql("SELECT DISTINCT(value) FROM %s WHERE tag=%%s" % table, (tag, ))] def get_most_popular_field_values(recids, tags, exclude_values=None, count_repetitive_values=True, split_by=0): """ 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.) @return: list of tuples containing tag and its frequency 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, string_types): 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, sort=False, split_by=split_by)) 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 val in valuefreqdict: valuefreqdict[val] += 1 else: valuefreqdict[val] = 1 ## sort by descending frequency of values: if not CFG_NUMPY_IMPORTABLE: ## original version out = [] vals = valuefreqdict.keys() vals.sort(_get_most_popular_field_values_helper_sorter) for val in vals: tmpdisplv = '' if val in displaytmp: tmpdisplv = displaytmp[val] else: tmpdisplv = val out.append((tmpdisplv, valuefreqdict[val])) return out else: f = [] # frequencies n = [] # original names ln = [] # lowercased names ## build lists within one iteration for (val, freq) in iteritems(valuefreqdict): f.append(-1 * freq) if val in displaytmp: n.append(displaytmp[val]) else: n.append(val) ln.append(val.lower()) ## sort by frequency (desc) and then by lowercased name. return [(n[i], -1 * f[i]) for i in numpy.lexsort([ln, f])] def profile(p="", f="", c=CFG_SITE_NAME): """Profile search time.""" import profile as pyprofile import pstats pyprofile.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 def perform_external_collection_search_with_em(req, current_collection, pattern_list, field, external_collection, verbosity_level=0, lang=CFG_SITE_LANG, selected_external_collections_infos=None, em=""): perform_external_collection_search(req, current_collection, pattern_list, field, external_collection, verbosity_level, lang, selected_external_collections_infos, print_overview=em == "" or EM_REPOSITORY["overview"] in em, print_search_info=em == "" or EM_REPOSITORY["search_info"] in em, print_see_also_box=em == "" or EM_REPOSITORY["see_also_box"] in em, print_body=em == "" or EM_REPOSITORY["body"] in em) @cache.memoize(timeout=5) def get_fulltext_terms_from_search_pattern(search_pattern): keywords = [] if search_pattern is not None: for unit in create_basic_search_units(None, search_pattern.encode('utf-8'), None): bsu_o, bsu_p, bsu_f, bsu_m = unit[0], unit[1], unit[2], unit[3] if (bsu_o != '-' and bsu_f in [None, 'fulltext']): if bsu_m == 'a' and bsu_p.startswith('%') and bsu_p.endswith('%'): # remove leading and training `%' representing partial phrase search keywords.append(bsu_p[1:-1]) else: keywords.append(bsu_p) return keywords def check_user_can_edit_record(req, recid): """ Check if user has authorization to modify a collection the recid belongs to """ record_collections = get_all_collections_of_a_record(recid) if not record_collections: # Check if user has access to all collections auth_code, auth_message = acc_authorize_action(req, 'runbibedit', collection='') if auth_code == 0: return True else: for collection in record_collections: auth_code, auth_message = acc_authorize_action(req, 'runbibedit', collection=collection) if auth_code == 0: return True return False diff --git a/invenio/legacy/websession/session.py b/invenio/legacy/websession/session.py index 90497dbf7..ccc42b201 100644 --- a/invenio/legacy/websession/session.py +++ b/invenio/legacy/websession/session.py @@ -1,552 +1,551 @@ # -*- coding: utf-8 -*- # This file is part of Invenio. -# Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2014 CERN. +# Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2014, 2015 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. """ Session management adapted from mod_python Session class. Just use L{get_session} to obtain a session object (with a dictionary interface, which will let you store permanent information). """ from invenio.legacy.wsgi.utils import add_cookies, Cookie, get_cookie import random import zlib from six.moves import cPickle import re import sys import os import time from datetime import datetime, timedelta from uuid import uuid4 from invenio.utils.date import convert_datestruct_to_datetext from invenio.legacy.dbquery import run_sql, blob_to_string from invenio.config import (CFG_WEBSESSION_EXPIRY_LIMIT_REMEMBER, CFG_WEBSESSION_EXPIRY_LIMIT_DEFAULT, CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_WEBSESSION_IPADDR_CHECK_SKIP_BITS, CFG_WEBSEARCH_PREV_NEXT_HIT_FOR_GUESTS, CFG_WEBSESSION_STORAGE) from invenio.legacy.websession.websession_config import (CFG_WEBSESSION_COOKIE_NAME, CFG_WEBSESSION_ONE_DAY, CFG_WEBSESSION_CLEANUP_CHANCE) from invenio.utils.redis import get_redis from invenio.utils.hash import md5 CFG_FULL_HTTPS = CFG_SITE_URL.lower().startswith("https://") if CFG_WEBSEARCH_PREV_NEXT_HIT_FOR_GUESTS: _CFG_SESSION_NON_USEFUL_KEYS = ('uid', 'user_info') else: _CFG_SESSION_NON_USEFUL_KEYS = ('uid', 'user_info', 'websearch-last-query', 'websearch-last-query-hits') def get_session(req, sid=None): """ Obtain a session. If the session has already been created for the current request, returns the already existing session. @param req: the mod_python request object. @type req: mod_python request object @param sid: the session identifier of an already existing session. @type sid: 32 hexadecimal string @return: the session. @rtype: InvenioSession @raise ValueError: if C{sid} is provided and it doesn't correspond to a valid session. """ from flask import session if sid is not None: req._session = session return req._session if not hasattr(req, '_session'): req._session = session return req._session class InvenioSessionBase(dict): """ This class implements a Session handling based on MySQL. @param req: the mod_python request object. @type req: mod_python request object @param sid: the session identifier if already known @type sid: 32 hexadecimal string @ivar _remember_me: if the session cookie should last one day or until the browser is closed. @type _remember_me: bool @note: The code is heavily based on ModPython 3.3.1 DBMSession implementation. @note: This class implements IP verification to prevent basic cookie stealing. @raise ValueError: if C{sid} is provided and correspond to a broken session. """ def __init__(self, req, sid=None): self._remember_me = False self._req, self._sid, self._secret = req, sid, None self._lock = CFG_WEBSESSION_ENABLE_LOCKING self._new = 1 self._locked = 0 self._invalid = 0 self._dirty = False self._http_ip = None self._https_ip = None self.__need_https = False self._cleanup_function = None dict.__init__(self) if not self._sid: # check to see if cookie exists cookie = get_cookie(req, CFG_WEBSESSION_COOKIE_NAME) if cookie: self._sid = cookie.value else: stub_cookie = get_cookie(req, CFG_WEBSESSION_COOKIE_NAME + 'stub') self.__need_https = stub_cookie and stub_cookie.value == 'HTTPS' if self._sid: if not _check_sid(self._sid): if sid: # Supplied explicitly by user of the class, # raise an exception and make the user code # deal with it. raise ValueError("Invalid Session ID: sid=%s" % sid) else: # Derived from the cookie sent by browser, # wipe it out so it gets replaced with a # correct value. self._sid = None if self._sid: # attempt to load ourselves self.lock() if self.load(): self._new = 0 if self._new: # make a new session if self._sid: self.unlock() # unlock old sid self._sid = _new_sid(self._req) self.lock() # lock new sid remote_ip = self._req.remote_ip if self._req.is_https(): self._https_ip = remote_ip else: self._http_ip = remote_ip # need cleanup? if random.randint(1, CFG_WEBSESSION_CLEANUP_CHANCE) == 1: self.cleanup() def get_dirty(self): """ Is this session dirty? """ return self._dirty def set_dirty(self, dummy=True): """ Flag this session as dirty. It takes a parameter, just in order to be used within a property """ self._dirty = True dirty = property(get_dirty, set_dirty) def __setitem__(self, key, value): if self.get(key) != value: dict.__setitem__(self, key, value) self._dirty = True def __delitem__(self, key): if key in self: dict.__delitem__(self, key) self._dirty = True def set_remember_me(self, remember_me=True): """ Set/Unset the L{_remember_me} flag. @param remember_me: True if the session cookie should last one day or until the browser is closed. @type remember_me: bool """ self._remember_me = remember_me self['_permanent'] = remember_me add_cookies(self._req, self.make_cookies()) def load(self): """ Load the session from the database. @return: 1 in case of success, 0 otherwise. @rtype: integer """ session_dict = None invalid = False res = self.load_from_storage(self._sid) if res: session_dict = cPickle.loads(blob_to_string(res)) remote_ip = self._req.remote_ip if self._req.is_https(): if session_dict['_https_ip'] is not None: if ':' in remote_ip: ## IPV6 address, we don't skip bits if session_dict['_https_ip'] != remote_ip: invalid = True else: if _mkip(session_dict['_https_ip']) >> \ CFG_WEBSESSION_IPADDR_CHECK_SKIP_BITS != \ _mkip(remote_ip) >> \ CFG_WEBSESSION_IPADDR_CHECK_SKIP_BITS: invalid = True else: session_dict['_https_ip'] = remote_ip else: if session_dict['_http_ip'] is not None: if ':' in remote_ip: ## IPV6 address, we don't skip bits if session_dict['_http_ip'] != remote_ip: invalid = True else: if _mkip(session_dict['_http_ip']) >> \ CFG_WEBSESSION_IPADDR_CHECK_SKIP_BITS != \ _mkip(remote_ip) >> \ CFG_WEBSESSION_IPADDR_CHECK_SKIP_BITS: invalid = True else: session_dict['_http_ip'] = remote_ip if session_dict is None: return 0 if invalid: return 0 self.update(session_dict) self._remember_me = session_dict.get("_permanent", False) return 1 def is_useful(self): """ Return True if the session contains some key considered useful (i.e. that deserve being preserved) """ for key in self: if key not in _CFG_SESSION_NON_USEFUL_KEYS: return True return False def save(self): """ Save the session to the database. """ uid = self.get('uid', -1) if not self._invalid and self._sid and self._dirty and (uid > 0 or self.is_useful()): ## We store something only for real users or useful sessions. session_dict = {"_data" : self.copy(), "_created" : self._created, "_accessed": self._accessed, "_timeout" : self._timeout, "_http_ip" : self._http_ip, "_https_ip" : self._https_ip, "_remember_me" : self._remember_me } session_object = cPickle.dumps(session_dict, -1) self.save_in_storage(self._sid, session_object, self._timeout, uid) for cookie in self.make_cookies(): self._req.set_cookie(cookie) ## No more dirty :-) self._dirty = False def delete(self): """ Delete the session. """ self.delete_from_storage(self._sid) self.clear() def invalidate(self): """ Declare the session as invalid. """ cookies = self.make_cookies() for cookie in cookies: cookie.expires = 0 add_cookies(self._req, cookies) self.delete() self._invalid = 1 if hasattr(self._req, '_session'): delattr(self._req, '_session') def make_cookies(self): """ Create the necessary cookies to implement secure session handling (possibly over HTTPS). @return: a list of cookies. """ cookies = [] uid = self.get('_uid', -1) if uid > 0 and CFG_SITE_SECURE_URL.startswith("https://"): - stub_cookie = Cookie(CFG_WEBSESSION_COOKIE_NAME + 'stub', 'HTTPS') + stub_cookie = Cookie(CFG_WEBSESSION_COOKIE_NAME + 'stub', 'HTTPS', HttpOnly=True) else: - stub_cookie = Cookie(CFG_WEBSESSION_COOKIE_NAME + 'stub', 'NO') + stub_cookie = Cookie(CFG_WEBSESSION_COOKIE_NAME + 'stub', 'NO', HttpOnly=True) cookies.append(stub_cookie) if self._req.is_https() or not CFG_SITE_SECURE_URL.startswith("https://") or uid <= 0: - cookie = Cookie(CFG_WEBSESSION_COOKIE_NAME, self._sid) + cookie = Cookie(CFG_WEBSESSION_COOKIE_NAME, self._sid, HttpOnly=True) if CFG_SITE_SECURE_URL.startswith("https://") and uid > 0: cookie.secure = True - cookie.httponly = True cookies.append(cookie) for cookie in cookies: cookie.path = '/' if self._remember_me: cookie.expires = time.time() + CFG_WEBSESSION_ONE_DAY * CFG_WEBSESSION_EXPIRY_LIMIT_REMEMBER cookie.max_age = CFG_WEBSESSION_ONE_DAY * CFG_WEBSESSION_EXPIRY_LIMIT_REMEMBER return cookies def initial_http_ip(self): """ @return: the initial ip addressed for the HTTP protocol for which this session was issued. @rtype: string @note: it returns None if this session has always been used through HTTPS requests. """ return self._http_ip def initial_https_ip(self): """ @return: the initial ip addressed for the HTTPS protocol for which this session was issued. @rtype: string @note: it returns None if this session has always been used through HTTP requests. """ return self._https_ip def lock(self): """ Lock the session. """ if self._lock: self._locked = 1 def unlock(self): """ Unlock the session. """ if self._lock and self._locked: self._locked = 0 def is_new(self): """ @return: True if the session has just been created. @rtype: bool """ return not not self._new def sid(self): """ @return: the session identifier. @rtype: 32 hexadecimal string """ return self._sid def cleanup(self): """ Perform the database session cleanup. """ if self._cleanup_function: self._req.register_cleanup(self._cleanup_function) self._req.log_error("InvenioSession: registered database cleanup.") def __del__(self): self.save() self.unlock() def get_need_https(self): return self.__need_https ## This property will be True if the connection need to be set to HTTPS ## in order for the session to be successfully read. This can actually ## be checked by not having a cookie, but just having the stub_cookie. ## The default cookie is only sent via HTTPS, while the stub_cookie ## is also sent via HTTP and contains the uid, of the user. So if there ## is actually a stub cookie and its value is different than -1 this ## property will be True, meaning the server should redirect the client ## to an HTTPS connection if she really wants to access authenticated ## resources. need_https = property(get_need_https) def _unlock_session_cleanup(session): """ Auxliary function to unlock a session. """ session.unlock() _RE_VALIDATE_SID = re.compile('[0-9a-f]{32}$') def _check_sid(sid): """ Check the validity of the session identifier. The sid must be 32 characters long, and consisting of the characters 0-9 and a-f. The sid may be passed in a cookie from the client and as such should not be trusted. This is particularly important in FileSession, where the session filename is derived from the sid. A sid containing '/' or '.' characters could result in a directory traversal attack @param sid: the session identifier. @type sid: string @return: True if the session identifier is valid. @rtype: bool """ return not not _RE_VALIDATE_SID.match(sid) def _new_sid(req): """ Make a number based on current time, pid, remote ip and two random ints, then hash with md5. This should be fairly unique and very difficult to guess. @param req: the mod_python request object. @type req: mod_python request object. @return: the session identifier. @rtype: 32 hexadecimal string @warning: The current implementation of _new_sid returns an md5 hexdigest string. To avoid a possible directory traversal attack in FileSession the sid is validated using the _check_sid() method and the compiled regex validate_sid_re. The sid will be accepted only if len(sid) == 32 and it only contains the characters 0-9 and a-f. If you change this implementation of _new_sid, make sure to also change the validation scheme, as well as the test_Session_illegal_sid() unit test in test/test.py. """ return uuid4().hex the_time = long(time.time()*10000) pid = os.getpid() random_generator = _get_generator() rnd1 = random_generator.randint(0, 999999999) rnd2 = random_generator.randint(0, 999999999) remote_ip = req.remote_ip return md5("%d%d%d%d%s" % ( the_time, pid, rnd1, rnd2, remote_ip) ).hexdigest() def _mkip(ip): """ Compute a numerical value for a dotted IP """ num = 0L for i in ip.split('.'): num = (num << 8) + int(i) return num class InvenioSessionMySQL(InvenioSessionBase): def __init__(self, req, sid=None): def cb_session_cleanup(data=None): """ Session cleanup procedure which to be executed at the end of the request handling. """ run_sql("""DELETE LOW_PRIORITY FROM session WHERE session_expiry <= UTC_TIMESTAMP()""") self.cleanup_function = cb_session_cleanup super(InvenioSessionMySQL, self).__init__(req, sid) def load_from_storage(self, sid): ret = run_sql("""SELECT session_object FROM session WHERE session_key = %s""", [sid]) if ret: return ret[0][0] def delete_from_storage(self, sid): return run_sql("""DELETE LOW_PRIORITY FROM session WHERE session_key=%s""", [sid]) def save_in_storage(self, sid, session_object, timeout, uid): session_key = sid session_expiry = time.time() + timeout + CFG_WEBSESSION_ONE_DAY session_expiry = convert_datestruct_to_datetext(time.gmtime(session_expiry)) run_sql("""INSERT INTO session( session_key, session_expiry, session_object, uid ) VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE session_expiry=%s, session_object=%s, uid=%s """, (session_key, session_expiry, session_object, uid, session_expiry, session_object, uid)) class InvenioSessionRedis(InvenioSessionBase): def generate_key(self, sid): return 'session_%s' % sid def load_from_storage(self, sid): return get_redis().get(self.generate_key(sid)) def delete_from_storage(self, sid): return get_redis().delete(self.generate_key(sid)) def save_in_storage(self, sid, session_object, timeout, uid): # pylint: disable=W0613 return get_redis().setex(self.generate_key(sid), session_object, timeout) if CFG_WEBSESSION_STORAGE == 'mysql': InvenioSession = InvenioSessionMySQL elif CFG_WEBSESSION_STORAGE == 'redis': InvenioSession = InvenioSessionRedis diff --git a/invenio/legacy/wsgi/utils.py b/invenio/legacy/wsgi/utils.py index fec098bc3..9392fffbc 100644 --- a/invenio/legacy/wsgi/utils.py +++ b/invenio/legacy/wsgi/utils.py @@ -1,881 +1,883 @@ # -*- coding: utf-8 -*- # # This file is part of Invenio. -# Copyright (C) 2009, 2010, 2011, 2014 CERN. +# Copyright (C) 2009, 2010, 2011, 2014, 2015 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. """ mod_python->WSGI Framework utilities This code has been taken from mod_python original source code and rearranged here to easying the migration from mod_python to wsgi. The code taken from mod_python is under the following License. """ # Copyright 2004 Apache Software Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you # may not use this file except in compliance with the License. You # may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. See the License for the specific language governing # permissions and limitations under the License. # # Originally developed by Gregory Trubetskoy. # # $Id: apache.py 468216 2006-10-27 00:54:12Z grahamd $ from wsgiref.headers import Headers import time import re import os import cgi import cStringIO import tempfile from types import TypeType, ClassType, BuiltinFunctionType, MethodType, ListType from invenio.config import CFG_TMPDIR, CFG_TMPSHAREDDIR from invenio.utils.apache import \ SERVER_RETURN, \ HTTP_LENGTH_REQUIRED, \ HTTP_BAD_REQUEST, \ InvenioWebInterfaceWSGIContentLenghtError, \ InvenioWebInterfaceWSGIContentTypeError, \ InvenioWebInterfaceWSGIContentMD5Error class table(Headers): add = Headers.add_header iteritems = Headers.items def __getitem__(self, name): ret = Headers.__getitem__(self, name) if ret is None: return '' else: return str(ret) # Some functions made public exists_config_define = lambda dummy: True # Some constants class metaCookie(type): def __new__(cls, clsname, bases, clsdict): _valid_attr = ( "version", "path", "domain", "secure", "comment", "expires", "max_age", # RFC 2965 "commentURL", "discard", "port", # Microsoft Extension "httponly" ) # _valid_attr + property values # (note __slots__ is a new Python feature, it # prevents any other attribute from being set) __slots__ = _valid_attr + ("name", "value", "_value", "_expires", "__data__") clsdict["_valid_attr"] = _valid_attr clsdict["__slots__"] = __slots__ def set_expires(self, value): if type(value) == type(""): # if it's a string, it should be # valid format as per Netscape spec try: t = time.strptime(value, "%a, %d-%b-%Y %H:%M:%S GMT") except ValueError: raise ValueError, "Invalid expires time: %s" % value t = time.mktime(t) else: # otherwise assume it's a number # representing time as from time.time() t = value value = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", time.gmtime(t)) self._expires = "%s" % value def get_expires(self): return self._expires clsdict["expires"] = property(fget=get_expires, fset=set_expires) return type.__new__(cls, clsname, bases, clsdict) class Cookie(object): """ This class implements the basic Cookie functionality. Note that unlike the Python Standard Library Cookie class, this class represents a single cookie (not a list of Morsels). """ __metaclass__ = metaCookie DOWNGRADE = 0 IGNORE = 1 EXCEPTION = 3 def parse(Class, str, **kw): """ Parse a Cookie or Set-Cookie header value, and return a dict of Cookies. Note: the string should NOT include the header name, only the value. """ dict = _parse_cookie(str, Class, **kw) return dict parse = classmethod(parse) def __init__(self, name, value, **kw): """ This constructor takes at least a name and value as the arguments, as well as optionally any of allowed cookie attributes as defined in the existing cookie standards. """ self.name, self.value = name, value for k in kw: setattr(self, k.lower(), kw[k]) # subclasses can use this for internal stuff self.__data__ = {} def __str__(self): """ Provides the string representation of the Cookie suitable for sending to the browser. Note that the actual header name will not be part of the string. This method makes no attempt to automatically double-quote strings that contain special characters, even though the RFC's dictate this. This is because doing so seems to confuse most browsers out there. """ result = ["%s=%s" % (self.name, self.value)] # pylint: disable=E1101 # The attribute _valid_attr is provided by the metaclass 'metaCookie'. for name in self._valid_attr: if hasattr(self, name): - if name in ("secure", "discard", "httponly"): + if name in ("secure", "discard"): result.append(name) + elif name == "httponly": + result.append("HttpOnly") else: result.append("%s=%s" % (name, getattr(self, name))) # pylint: enable=E1101 return "; ".join(result) def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, str(self)) # This is a simplified and in some places corrected # (at least I think it is) pattern from standard lib Cookie.py _cookiePattern = re.compile( r"(?x)" # Verbose pattern r"[,\ ]*" # space/comma (RFC2616 4.2) before attr-val is eaten r"(?P" # Start of group 'key' r"[^;\ =]+" # anything but ';', ' ' or '=' r")" # End of group 'key' r"\ *(=\ *)?" # a space, then may be "=", more space r"(?P" # Start of group 'val' r'"(?:[^\\"]|\\.)*"' # a doublequoted string r"|" # or r"[^;]*" # any word or empty string r")" # End of group 'val' r"\s*;?" # probably ending in a semi-colon ) def _parse_cookie(str, Class, names=None): # XXX problem is we should allow duplicate # strings result = {} matchIter = _cookiePattern.finditer(str) for match in matchIter: key, val = match.group("key"), match.group("val") # We just ditch the cookies names which start with a dollar sign since # those are in fact RFC2965 cookies attributes. See bug [#MODPYTHON-3]. if key[0] != '$' and names is None or key in names: result[key] = Class(key, val) return result _RE_BAD_MSIE = re.compile("MSIE\s+(\d+\.\d+)") def add_cookies(req, cookies): """ Sets one or more cookie in outgoing headers and adds a cache directive so that caches don't cache the cookie. """ if "Set-Cookie" not in req.headers_out: g = _RE_BAD_MSIE.search(req.headers_in.get('User-Agent', "MSIE 6.0")) bad_msie = g and float(g.group(1)) < 9.0 if not (bad_msie and req.is_https()): req.headers_out.add("Cache-Control", 'no-cache="set-cookie"') for cookie in cookies: req.headers_out.add("Set-Cookie", str(cookie)) def get_cookies(req, cls=Cookie, **kw): """ A shorthand for retrieveing and parsing cookies given a Cookie class. The class must be one of the classes from this module. """ if "cookie" not in getattr(req, "headers_in", {}): return {} cookies = req.headers_in["cookie"] if isinstance(cookies, list): cookies = '; '.join(cookies) return cls.parse(cookies, **kw) def get_cookie(req, name, cls=Cookie, **kw): cookies = get_cookies(req, cls, names=[name], **kw) if name in cookies: return cookies[name] parse_qs = cgi.parse_qs parse_qsl = cgi.parse_qsl # Maximum line length for reading. (64KB) # Fixes memory error when upload large files such as 700+MB ISOs. readBlockSize = 65368 """ The classes below are a (almost) a drop-in replacement for the standard cgi.py FieldStorage class. They should have pretty much the same functionality. These classes differ in that unlike cgi.FieldStorage, they are not recursive. The class FieldStorage contains a list of instances of Field class. Field class is incapable of storing anything in it. These objects should be considerably faster than the ones in cgi.py because they do not expect CGI environment, and are optimized specifically for Apache and mod_python. """ class Field: def __init__(self, name, *args, **kwargs): self.name = name # Some third party packages such as Trac create # instances of the Field object and insert it # directly into the list of form fields. To # maintain backward compatibility check for # where more than just a field name is supplied # and invoke an additional initialisation step # to process the arguments. Ideally, third party # code should use the add_field() method of the # form, but if they need to maintain backward # compatibility with older versions of mod_python # they will not have a choice but to use old # way of doing things and thus we need this code # for the forseeable future to cope with that. if args or kwargs: self.__bc_init__(*args, **kwargs) def __bc_init__(self, file, ctype, type_options, disp, disp_options, headers = {}): self.file = file self.type = ctype self.type_options = type_options self.disposition = disp self.disposition_options = disp_options if "filename" in disp_options: self.filename = disp_options["filename"] else: self.filename = None self.headers = headers def __repr__(self): """Return printable representation.""" return "Field(%s, %s)" % (`self.name`, `self.value`) def __getattr__(self, name): if name != 'value': raise AttributeError, name if self.file: self.file.seek(0) value = self.file.read() self.file.seek(0) else: value = None return value def __del__(self): self.file.close() class StringField(str): """ This class is basically a string with added attributes for compatibility with std lib cgi.py. Basically, this works the opposite of Field, as it stores its data in a string, but creates a file on demand. Field creates a value on demand and stores data in a file. """ filename = None headers = {} ctype = "text/plain" type_options = {} disposition = None disp_options = None def __new__(cls, value): '''Create StringField instance. You'll have to set name yourself.''' obj = str.__new__(cls, value) obj.value = value return obj def __str__(self): return str.__str__(self) def __getattr__(self, name): if name != 'file': raise AttributeError, name self.file = cStringIO.StringIO(self.value) return self.file def __repr__(self): """Return printable representation (to pass unit tests).""" return "Field(%s, %s)" % (`self.name`, `self.value`) class FieldList(list): def __init__(self): self.__table = None list.__init__(self) def table(self): if self.__table is None: self.__table = {} for item in self: if item.name in self.__table: self.__table[item.name].append(item) else: self.__table[item.name] = [item] return self.__table def __delitem__(self, *args): self.__table = None return list.__delitem__(self, *args) def __delslice__(self, *args): self.__table = None return list.__delslice__(self, *args) def __iadd__(self, *args): self.__table = None return list.__iadd__(self, *args) def __imul__(self, *args): self.__table = None return list.__imul__(self, *args) def __setitem__(self, *args): self.__table = None return list.__setitem__(self, *args) def __setslice__(self, *args): self.__table = None return list.__setslice__(self, *args) def append(self, *args): self.__table = None return list.append(self, *args) def extend(self, *args): self.__table = None return list.extend(self, *args) def insert(self, *args): self.__table = None return list.insert(self, *args) def pop(self, *args): self.__table = None return list.pop(self, *args) def remove(self, *args): self.__table = None return list.remove(self, *args) class FieldStorage: def __init__(self, req, keep_blank_values=0, strict_parsing=0, file_callback=None, field_callback=None, to_tmp_shared=False): # # Whenever readline is called ALWAYS use the max size EVEN when # not expecting a long line. - this helps protect against # malformed content from exhausting memory. # self.list = FieldList() self.wsgi_input_consumed = False # always process GET-style parameters if req.args: pairs = parse_qsl(req.args, keep_blank_values) for pair in pairs: self.add_field(pair[0], pair[1]) if req.method != "POST": return try: clen = int(req.headers_in["content-length"]) except (KeyError, ValueError): # absent content-length is not acceptable raise SERVER_RETURN, HTTP_LENGTH_REQUIRED self.clen = clen self.count = 0 if "content-type" not in req.headers_in: ctype = "application/x-www-form-urlencoded" else: ctype = req.headers_in["content-type"] if ctype.startswith("application/x-www-form-urlencoded"): pairs = parse_qsl(req.read(clen), keep_blank_values) self.wsgi_input_consumed = True for pair in pairs: self.add_field(pair[0], pair[1]) return elif not ctype.startswith("multipart/"): # we don't understand this content-type return self.wsgi_input_consumed = True # figure out boundary try: i = ctype.lower().rindex("boundary=") boundary = ctype[i+9:] if len(boundary) >= 2 and boundary[0] == boundary[-1] == '"': boundary = boundary[1:-1] boundary = re.compile("--" + re.escape(boundary) + "(--)?\r?\n") except ValueError: raise SERVER_RETURN, HTTP_BAD_REQUEST # read until boundary self.read_to_boundary(req, boundary, None) end_of_stream = False while not end_of_stream and not self.eof(): # jjj JIM BEGIN WHILE ## parse headers ctype, type_options = "text/plain", {} disp, disp_options = None, {} headers = table([]) line = req.readline(readBlockSize) self.count += len(line) if self.eof(): end_of_stream = True match = boundary.match(line) if (not line) or match: # we stop if we reached the end of the stream or a stop # boundary (which means '--' after the boundary) we # continue to the next part if we reached a simple # boundary in either case this would mean the entity is # malformed, but we're tolerating it anyway. end_of_stream = (not line) or (match.group(1) is not None) continue skip_this_part = False while line not in ('\r','\r\n'): nextline = req.readline(readBlockSize) self.count += len(nextline) if self.eof(): end_of_stream = True while nextline and nextline[0] in [ ' ', '\t']: line = line + nextline nextline = req.readline(readBlockSize) self.count += len(nextline) if self.eof(): end_of_stream = True # we read the headers until we reach an empty line # NOTE : a single \n would mean the entity is malformed, but # we're tolerating it anyway h, v = line.split(":", 1) headers.add(h, v) h = h.lower() if h == "content-disposition": disp, disp_options = parse_header(v) elif h == "content-type": ctype, type_options = parse_header(v) # # NOTE: FIX up binary rubbish sent as content type # from Microsoft IE 6.0 when sending a file which # does not have a suffix. # if ctype.find('/') == -1: ctype = 'application/octet-stream' line = nextline match = boundary.match(line) if (not line) or match: # we stop if we reached the end of the stream or a # stop boundary (which means '--' after the # boundary) we continue to the next part if we # reached a simple boundary in either case this # would mean the entity is malformed, but we're # tolerating it anyway. skip_this_part = True end_of_stream = (not line) or (match.group(1) is not None) break if skip_this_part: continue if "name" in disp_options: name = disp_options["name"] else: name = None # create a file object # is this a file? if "filename" in disp_options: if file_callback and callable(file_callback): file = file_callback(disp_options["filename"]) else: if to_tmp_shared: file = tempfile.NamedTemporaryFile(dir=CFG_TMPSHAREDDIR) else: file = tempfile.NamedTemporaryFile(dir=CFG_TMPDIR) else: if field_callback and callable(field_callback): file = field_callback() else: file = cStringIO.StringIO() # read it in self.read_to_boundary(req, boundary, file) if self.eof(): end_of_stream = True file.seek(0) # make a Field if "filename" in disp_options: field = Field(name) field.filename = disp_options["filename"] else: field = StringField(file.read()) field.name = name field.file = file field.type = ctype field.type_options = type_options field.disposition = disp field.disposition_options = disp_options field.headers = headers self.list.append(field) def add_field(self, key, value): """Insert a field as key/value pair""" item = StringField(value) item.name = key self.list.append(item) def __setitem__(self, key, value): table = self.list.table() if key in table: items = table[key] for item in items: self.list.remove(item) item = StringField(value) item.name = key self.list.append(item) def read_to_boundary(self, req, boundary, file): previous_delimiter = None while not self.eof(): line = req.readline(readBlockSize) self.count += len(line) if not line: # end of stream if file is not None and previous_delimiter is not None: file.write(previous_delimiter) return True match = boundary.match(line) if match: # the line is the boundary, so we bail out # if the two last chars are '--' it is the end of the entity return match.group(1) is not None if line[-2:] == '\r\n': # the line ends with a \r\n, which COULD be part # of the next boundary. We write the previous line delimiter # then we write the line without \r\n and save it for the next # iteration if it was not part of the boundary if file is not None: if previous_delimiter is not None: file.write(previous_delimiter) file.write(line[:-2]) previous_delimiter = '\r\n' elif line[-1:] == '\r': # the line ends with \r, which is only possible if # readBlockSize bytes have been read. In that case the # \r COULD be part of the next boundary, so we save it # for the next iteration assert len(line) == readBlockSize if file is not None: if previous_delimiter is not None: file.write(previous_delimiter) file.write(line[:-1]) previous_delimiter = '\r' elif line == '\n' and previous_delimiter == '\r': # the line us a single \n and we were in the middle of a \r\n, # so we complete the delimiter previous_delimiter = '\r\n' else: if file is not None: if previous_delimiter is not None: file.write(previous_delimiter) file.write(line) previous_delimiter = None def eof(self): return self.clen <= self.count def __getitem__(self, key): """Dictionary style indexing.""" found = self.list.table()[key] if len(found) == 1: return found[0] else: return found def get(self, key, default): try: return self.__getitem__(key) except (TypeError, KeyError): return default def keys(self): """Dictionary style keys() method.""" return self.list.table().keys() def __iter__(self): return iter(self.keys()) def __repr__(self): return repr(self.list.table()) def has_key(self, key): """Dictionary style has_key() method.""" return (key in self.list.table()) __contains__ = has_key def __len__(self): """Dictionary style len(x) support.""" return len(self.list.table()) def getfirst(self, key, default=None): """ return the first value received """ try: return self.list.table()[key][0] except KeyError: return default def getlist(self, key): """ return a list of received values """ try: return self.list.table()[key] except KeyError: return [] def items(self): """Dictionary-style items(), except that items are returned in the same order as they were supplied in the form.""" return [(item.name, item) for item in self.list] def __delitem__(self, key): table = self.list.table() values = table[key] for value in values: self.list.remove(value) def clear(self): self.list = FieldList() def parse_header(line): """Parse a Content-type like header. Return the main content-type and a dictionary of options. """ plist = map(lambda a: a.strip(), line.split(';')) key = plist[0].lower() del plist[0] pdict = {} for p in plist: i = p.find('=') if i >= 0: name = p[:i].strip().lower() value = p[i+1:].strip() if len(value) >= 2 and value[0] == value[-1] == '"': value = value[1:-1] pdict[name] = value return key, pdict def apply_fs_data(object, fs, **args): """ Apply FieldStorage data to an object - the object must be callable. Examine the args, and match then with fs data, then call the object, return the result. """ # we need to weed out unexpected keyword arguments # and for that we need to get a list of them. There # are a few options for callable objects here: fc = None expected = [] if hasattr(object, "func_code"): # function fc = object.func_code expected = fc.co_varnames[0:fc.co_argcount] elif hasattr(object, 'im_func'): # method fc = object.im_func.func_code expected = fc.co_varnames[1:fc.co_argcount] elif type(object) in (TypeType,ClassType): # class fc = object.__init__.im_func.func_code expected = fc.co_varnames[1:fc.co_argcount] elif type(object) is BuiltinFunctionType: # builtin fc = None expected = [] elif hasattr(object, '__call__'): # callable object if type(object.__call__) is MethodType: fc = object.__call__.im_func.func_code expected = fc.co_varnames[1:fc.co_argcount] else: # abuse of objects to create hierarchy return apply_fs_data(object.__call__, fs, **args) # add form data to args for field in fs.list: if field.filename: val = field else: val = field.value args.setdefault(field.name, []).append(val) # replace lists with single values for arg in args: if ((type(args[arg]) is ListType) and (len(args[arg]) == 1)): args[arg] = args[arg][0] # remove unexpected args unless co_flags & 0x08, # meaning function accepts **kw syntax if fc is None: args = {} elif not (fc.co_flags & 0x08): for name in args.keys(): if name not in expected: del args[name] return object(**args) RE_CDISPOSITION_FILENAME = re.compile(r'filename=(?P[\w\.]*)') def handle_file_post(req, allowed_mimetypes=None): """ Handle the POST of a file. @return: the a tuple with the full path to the file saved on disk, and it's mimetype as provided by the request. @rtype: (string, string) """ from invenio.legacy.bibdocfile.api import decompose_file, md5 ## We retrieve the length clen = req.headers_in["Content-Length"] if clen is None: raise InvenioWebInterfaceWSGIContentLenghtError("Content-Length header is missing") try: clen = int(clen) assert (clen > 1) except (ValueError, AssertionError): raise InvenioWebInterfaceWSGIContentLenghtError("Content-Length header should contain a positive integer") ## Let's take the content type ctype = req.headers_in["Content-Type"] if allowed_mimetypes and ctype not in allowed_mimetypes: raise InvenioWebInterfaceWSGIContentTypeError("Content-Type not in allowed list of content types: %s" % allowed_mimetypes) ## Let's optionally accept a suggested filename suffix = prefix = '' g = RE_CDISPOSITION_FILENAME.search(req.headers_in.get("Content-Disposition", "")) if g: dummy, prefix, suffix = decompose_file(g.group("filename")) ## Let's optionally accept an MD5 hash (and use it later for comparison) cmd5 = req.headers_in.get("Content-MD5") if cmd5: the_md5 = md5() ## Ok. We can initialize the file fd, path = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=CFG_TMPDIR) the_file = os.fdopen(fd, 'w') ## Let's read the file while True: chunk = req.read(min(10240, clen)) if len(chunk) < min(10240, clen): ## We expected to read at least clen (which is different than 0) ## but chunk was shorter! Gosh! Error! Panic! the_file.close() os.close(fd) os.remove(path) raise InvenioWebInterfaceWSGIContentLenghtError("File shorter than what specified in Content-Length") if cmd5: ## MD5 was in the header let's compute it the_md5.update(chunk) ## And let's definitively write the content to disk :-) the_file.write(chunk) clen -= len(chunk) if clen == 0: ## That's it. Everything was read. break if cmd5 and the_md5.hexdigest().lower() != cmd5.strip().lower(): ## Let's check the MD5 the_file.close() os.close(fd) os.remove(path) raise InvenioWebInterfaceWSGIContentMD5Error("MD5 checksum does not match") ## Let's clean everything up the_file.close() return (path, ctype)