Page MenuHomec4science

No OneTemporary

File Metadata

Created
Thu, May 9, 09:19
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc b/modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc
index b6cc466a8..2096214b9 100644
--- a/modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc
+++ b/modules/bibclassify/doc/admin/bibclassify-admin-guide.webdoc
@@ -1,253 +1,253 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibClassify Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.1 <a href="#1.1">Thesaurus</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.2 <a href="#1.2">Keyword extraction</a><br />
<strong>2. <a href="#2">Running BibClassify</a></strong><br />
<a name="1"></a><h2>1. Overview</h2>
<p>BibClassify automatically extracts keywords from fulltext documents.
The automatic assignment of keywords to textual documents has clear
benefits in the digital library environment as it aids
catalogization, classification and retrieval of documents.</p>
<a name="1.1"></a><h3>1.1 Thesaurus</h3>
<p> BibClassify performs an extraction of keywords based on the
recurrence of specific terms, taken from a controlled vocabulary. A
controlled vocabulary is a thesaurus of all the terms that are
relevant in a specific context. When a context is defined by a
discipline or branch of knowledge then the vocabulary is said to be a
<em>subject thesaurus</em>. Various existing subject thesauri can be found <a href="http://www.fbi.fh-koeln.de/institut/labor/Bir/thesauri_new/thesen.htm">here</a>.</p>
<p> A subject thesarus can be expressed in several different
formats. Different institutions/disciplines have developed different
ways of representing their vocabulary systems. BibClassify accepts thesauri in two formats:</p>
<ul>
<li> <b>Simple text</b>. <br />
This is a simple list of allowed keywords. One keyword per line. E.g. <br />
<blockquote>
<pre>
asymmetry
asymptotic behavior
ATLAS
atmosphere
</pre>
</blockquote>
</li><li> <b> RDF SKOS taxonomy </b>. <br />
This is a richer and more complex structure to describe concepts. E.g. <br />
<blockquote>
<pre>
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#asymmetry"&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.yieldasymmetry"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.timeasymmetry"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.timereversalasymmetry"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.supernovaasymmetry"/&gt;
&lt;prefLabel xml:lang="en"&gt;asymmetry&lt;/prefLabel&gt;
&lt;hiddenLabel xml:lang="en"&gt;/asymmetr\w*/&lt;/hiddenLabel&gt;
&lt;hiddenLabel xml:lang="en"&gt;/nonsymmetric\w*/&lt;/hiddenLabel&gt;
&lt;/Concept&gt;
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#asymptoticbehavior"&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.transformationasymptoticbehavior"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.totalcrosssectionasymptoticbehavior"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.space-timeasymptoticbehavior"/&gt;
&lt;prefLabel xml:lang="en"&gt;asymptotic behavior&lt;/prefLabel&gt;
&lt;altLabel xml:lang="en"&gt;asymptotic behaviour&lt;/altLabel&gt;
&lt;/Concept&gt;
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#ATLAS"&gt;
&lt;prefLabel xml:lang="en"&gt;ATLAS&lt;/prefLabel&gt;
&lt;/Concept&gt;
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#atmosphere"&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.nucleusatmosphere"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.neutrinoatmosphere"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.muonatmosphere"/&gt;
&lt;prefLabel xml:lang="en"&gt;atmosphere&lt;/prefLabel&gt;
&lt;hiddenLabel xml:lang="en"&gt;/atmospher\w*/&lt;/hiddenLabel&gt;
&lt;/Concept&gt;
</pre>
</blockquote>
</li></ul>
In RDF SKOS, every keyword is wrapped around a <em>concept</em> which
encapsulates the full semantics and hierarchical status of a term -
including synonyms, alternative forms, broader concepts, notes and so
on - rather than just a plain keyword.
<p> The specification of the SKOS language and <a href="http://www.w3.org/TR/2005/WD-swbp-thesaurus-pubguide-20050517/">various manuals</a> that
aid the building of a semantic thesaurus can be found at the <a href="http://www.w3.org/TR/2005/WD-swbp-skos-core-guide-20051102/">SKOS W3C
website</a>. Furthermore, BibClassify can function on top
of an extended version of SKOS, which includes special elements such
as keychains, composite keywords and special annotations. The
-extension of the SKOS language is documented in the <a href="<WEBURL>/help/hacking/bibclassify/">hacking guide</a>.</p>
+extension of the SKOS language is documented in the <a href="<CFG_SITE_URL>/help/hacking/bibclassify/">hacking guide</a>.</p>
<a name="1.2"></a><h3>1.1 Keyword extraction</h3>
<p>BibClassify computes the keywords of a fulltext document based on
the frequency of thesaurus terms in it. In other words, it calculates
how many times a thesaurus keyword (and its alternative and hidden
labels, defined in the taxonomy) appear in a text and it ranks the
results. Unlike other similar systems, BibClassify does not use any
machine learning or AI methodologies - just plain phrase matching
using <a href="http://en.wikipedia.org/wiki/Regex">regular expressions</a>: it exploits the conformation and richness
of the thesaurus to produce accurate results. It is then clear that
BibClassify performs best on top of rich, well-structured, subject
thesauri expressed in the RDF SKOS language. The simple text mode,
described in 1.1, has been retained only for historical and
demonstrative reasons.</p>
<p> A detailed account of the phrase matching mechanisms used by
-BibClassify is included in the <a href="<WEBURL>/help/hacking/bibclassify/">hacking guide</a>.</p>
+BibClassify is included in the <a href="<CFG_SITE_URL>/help/hacking/bibclassify/">hacking guide</a>.</p>
<a name="2"></a><h2>2. Running BibClassify</h2>
<p><span class="adminbox">&nbsp;<b>Dependencies.</b> BibClassify
requires Python <a href="http://rdflib.net/">RDFLib</a> in
order to process the RDF SKOS taxonomy.</span></p>
<p>In order to extract relevant keywords from a document
<code>fulltext.pdf</code> based on a controlled vocabulary
<code>thesaurus.rdf</code>, you would run BibClassify as follows:</p>
<blockquote>
<pre>
$ bibclassify -t fulltext.pdf -K thesaurus.rdf
</pre>
</blockquote>
<p>Here, the following basic parameters were used:</p>
<ul>
<li><code>-f, --file=FILENAME</code>.<br />
The path to the PDF document that you would like to extract keywords from. If you
would like to analyse a text (ASCII) file, rather than a PDF, you can use the <code>-t, --textfile=FILENAME</code>
parameter instead.</li>
<li><code>-K, --taxonomy=FILENAME</code>.<br />
The path to the RDF SKOS thesaurus. Make sure that the RDF file is
well-formed and validated. If you would like to use a simple text
thesaurus (one keyword per line), you can use the paramater <code>-k,
--thesaurus=FILENAME</code> instead (NB. this mode is deprecated). </li>
</ul>
<p>In addition, you can tweak the output and performance of BibClassify by setting the following parameters:</p>
<ul>
<li><code>-o, --output=HTML|TEXT</code>.<br />
Sets the desired output to html or text (default is text). The html
output generates an html page containing a tag cloud representation of
the most recurrent keywords in the fulltext. A sample html output is shown below.</li>
<li><code>-m, --mode=PARTIAL|FULL</code>.<br />
Sets the desired processing mode, partial or full (default is full). The
partial mode will run BibClassify on the initial portion of the document
(abstract) and a small set of selected random pages. The full mode runs on the whole
of the document. Although both modes yield similar results, the partial
mode is only reccommended when performance times are an issue or when
dealing with extremely large documents (over 150 pages).</li>
<li><code>-q, --spires</code>.<br />
When set, the generated keywords (composite keywords) are output in
the traditional <a href="http://www.slac.stanford.edu/spires/">SPIRES</a> format, i.e.
<code>keyword1, keyword2</code> rather than the standard
BibClassify format <code>keyword1: keyword2</code></li>
<li><code>-n, --nkeywords=NUMBER</code>.<br />
This parameter sets the number of output (single) keywords that will be output (default is 25).</li>
<li><code>-l, --limit=NUMBER</code>.<br />
This parameter sets the maximum number of single keywords that will make part of
the pool of composite candidates, i.e. the single keywords that occur in the vicinity creating sequences of keywords.
(default is 70). Tweaking with this value can have drastic effects on
-the generated results. Please check the <a href="<WEBURL>/help/hacking/bibclassify/">hacking guide</a> to find out more.</li>
+the generated results. Please check the <a href="<CFG_SITE_URL>/help/hacking/bibclassify/">hacking guide</a> to find out more.</li>
</ul>
<p><span class="adminbox">&nbsp;<b>NB.</b> BibClassify can run as a CDS Invenio
module or as a standalone program. If you already run a server with a CDS Invenio installation,
you can simply run <em>/opt/cds-invenio/bin/bibclassify</em>. Otherwise, run <em>python bibclassifylib.py</em> and you might need to set
a couple of variables manually (location of pdftotext binary and
temporary directory)</li></span></p>
<p>As an example, running BibClassify on document <a
href="http://cdsweb.cern.ch/record/977446">hep-ph/0608096</a>
using the high-energy physics SKOS taxonomy (<code>HEP.rdf</code>) would yield the following results (text output):
<pre><code>
<b>Composite keywords:</b>
22 inflaton: decay [38, 82]
20 energy: density [26, 21]
9 field theory: scalar [0, 0]
8 effect: nonperturbative [23, 42]
8 baryon: asymmetry [11, 18]
6 supersymmetry: flat direction [29, 144]
4 operator: nonrenormalizable [9, 5]
4 interaction: nonlinear [21, 4]
4 decay: time [82, 30]
3 potential: scalar [27, 16]
2 time: conformal [30, 4]
2 symmetry: U(1) [7, 5]
2 supersymmetry: potential [29, 27]
2 inflaton: oscillation [38, 22]
2 coupling: minimal [18, 6]
2 coupling: conformal [18, 4]
1 temperature: reheating [6, 10]
1 supersymmetry: minimal [29, 6]
1 resonance: effect [15, 23]
1 operator: scalar [9, 16]
1 n: decay [5, 82]
1 inflaton: potential [38, 27]
1 fundamental constant: fine structure [0, 0]
1 entropy: density [3, 21]
1 coupling: gauge [18, 18]
<b>Single keywords:</b>
80 preheating
58 mass
29 rotation
14 inflation
12 magnetic moment
11 fluctuation
9 gravitation
8 Hubble constant
7 scaling
7 mixing
7 longitudinal
5 supergravity
4 gauge boson
4 boundary condition
3 matter
3 grand unified theory
</pre></code>
or, the following keyword-cloud HTML visualization:<br />
<br />
-<img src="<WEBURL>/img/admin/bibclassify-admin-guide-cloud.jpeg" alt="tag-cloud for document hep-ph/0608096" border="0" />
+<img src="<CFG_SITE_URL>/img/admin/bibclassify-admin-guide-cloud.jpeg" alt="tag-cloud for document hep-ph/0608096" border="0" />
</p>
diff --git a/modules/bibclassify/doc/hacking/bibclassify-extraction-algorithm.webdoc b/modules/bibclassify/doc/hacking/bibclassify-extraction-algorithm.webdoc
index 0533cdab6..f622346ac 100644
--- a/modules/bibclassify/doc/hacking/bibclassify-extraction-algorithm.webdoc
+++ b/modules/bibclassify/doc/hacking/bibclassify-extraction-algorithm.webdoc
@@ -1,278 +1,278 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: The code behind BibClassify: the extraction algorithm -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibclassify-internals"/>BibClassify Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibclassify-internals"/>BibClassify Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
<strong>2. <a href="#2">Preprocessing</a></strong><br />
<strong>3. <a href="#3">Single Keyword (mkw) processing</a></strong><br />
<strong>4. <a href="#4">Composite keyword (ckw)processing</a></strong><br />
<strong>5. <a href="#5">Postprocessing</a></strong><br />
<a name="1"></a><h2>1. Overview</h2>
<p> This section provides a detailed account of the phrase matching
techniques used by BibClassify to automatically extract significant terms
from fulltext documents. While reading this guide, you are advised to refer to the original
BibClassify code, mostly contained in <code>lib/python/invenio/bibclassifylib.py</code>.
This guide refers to version 2006/09/15.</p>
<p>The bulk of the extraction mechanism takes place inside the function
<code>generate_keywords_rdf</code>. This function is triggered when
BibClassify is launched with parameter <code>-K,
--taxonomy=FILENAME</code>. Let's have a look at what happens inside this function, step-by-step.
<p><span class="adminbox">&nbsp;<b>NB.</b> BibClassify can also run on top of simple text thesauri
(parameter <code>-k, --thesaurus=FILENAME</code>, function
<code>generate_keywords</code>), however this mode is now deprecated
and no longer maintained.</span></p>
<a name="2"></a><h2>2. Preprocessing</h2>
<p> At the beginning of the function, various local variables are
declared. Among these,</p>
<ul>
<li><code>namespace</code>: This variable
points to the main rdf:Namespace used in the taxonomy. In the case of
RDF SKOS, this is
<code>http://www.w3.org/2004/02/skos/core#</code>. If you need to use namespaces other than this one, please modify this variable
accordingly.</li>
<li><code>delimiter</code>: This variable represents the delimiter
symbol used to separate composite keywords. For example, current HEP
taxonomy adopts ":" (e.g. <em>baryon: asymmetry</em>) whereas the SPIRES standard
adopts "," (e.g. <em>baryon, asymmetry</em>). If you intend to set up and use
composite keywords in your taxonomy, please set this variable to the desired format.
</ul>
<p>The taxonomy (<code>dictfile</code>) is stored and parsed into memory via <a href="http://rdflib.net/">RDFlib</a> by the following two lines of code:
<blockquote>
<code>
store = rdflib.Graph()<br />
store.parse(dictfile)
</code>
</blockquote></p>
<p><span class="adminbox">&nbsp;<b>NB.</b> RDFLib provides very handy
libraries for RDF manipulation, however when dealing with large RDF
files, loading and parsing are by far the principal factor affecting
the performance of BibClassify. For example, when loading the HEP
taxonomy (in <code>/opt/cds-invenio/etc/bibclassify/HEP.rdf</code>:
7.4 MB, 16000 Concepts) on an Intel(R) Xeon(TM) CPU 3.06GHz the two
lines of code above take a total of <b>26 seconds</b> to complete
(over two thirds of the total execution time - 36 seconds). If
performance is your main concern, consider a faster library or working
on a pre-loaded RDF store. </span>
</p>
<p>At this point, the fulltext of the document is converted from text
to PDF (using standard linux command <code>pdftotext</code>) and stored into a
string <code>text_string</code>. This string will contain the full
document if running in slow mode or an arbitrary excerpt of the
document (about 40%) if running in fast mode. Moreover, the very
beginning of the string (10%) is stored in a variable called
<code>abstract</code>. This is done base on the assumption that
manuscripts generally contain crucial information such as title and
abstract in the very first portion of the document. This portion can
be then treated to be more relevant than the remainder of the document. Please
bear this in mind if running BibClassify on documents with different
structures or when running on heterogeneous collections.
</p>
<p> In many manuscripts, the author includes a list of pre-assigned
terms that describe the topic of the article. The last step before
keyword extraction begins is to locate these author-assigned
keywords. We try to isolate these by searching for the key phrase
<em>Keywords</em> followed by a list of terms. When found, the string
is stored into variable <code>safe_keys</code> and used later to match
BibClassify output against author assigned keywords (these are marked
in the output with an asterisk, e.g. <code>13* Hubble constant</code>)
</p>
<a name="3"></a><h2>3. Single Keyword (mkw) processing</h2>
<p> The bulk of the phrase matching operations - the extraction of the single
keywords from the fulltext - is contained in a big <code>for</code> loop. In this
loop, every RDF <code>Concept</code> is parsed, one at a time, and its
components (such as <code>prefLabel</code> and <code>altLabel</code>) matched inside the document.
</p>
<p><span class="adminbox">&nbsp;<b>NB.</b> For a detailed explanation
of the RDF SKOS syntax, please refer to the
<a href="http://www.w3.org/TR/2005/WD-swbp-skos-core-guide-20051102/">SKOS W3C
website</a>. <br />
The rationale behind single and composite keywords is covered in the <a
href="bibclassify-hep-taxonomy">HEP taxonomy hacking
section</a>.</span>
</p>
<p> For every concept in the taxonomy that has a <code>prefLabel</code>, we store
its searchable labels (<code>altLabel</code> and <code>hiddenLabel</code>) in a list
(<code>candidates</code>). At this point we also check whether the
concept has been flagged with a <code>nostandalone</code> note,
i.e. it will be used for the computation of composite keywords, but it
does not count as a single keyword on its own (this is the case of many
short particle names that if considered on their own would generate a lot of noise
output).
</p>
<p> For every <code>candidate</code> term in the
<code>candidates</code> list, a regex is compiled around the term. In
doing this, it is important to consider carefully the anatomy of the
term:
</p>
<ul>
<li> When the candidate term contains a hyphen/minus sign (<b>-</b>): need
to understand whether it is a minus sign or a hyphen by splitting the
string around it.
<ul>
<li>If it is a minus sign, retain it and perform a
standard search. </li>
<li>If it is a hyphen, search for it and every possible alternative form too
(e.g. <em>word-word</em> will also try to match <em>wordword</em> and
<em>word word</em>).</li></ul></li>
<li> When the candidate term is an uppercase word
(e.g. <em>ATLAS</em>) or it is a very short string (e.g. <em>B+</em>):
perform a case sensitive search.</li>
<li> In all other cases, perform a case-insensitive search.
</ul>
<p>
<code>hiddenLabel</code> containing wildcards (e.g. <em>gauge theor*</em>) are
processed separately at the beginning. This is done to avoid double
matching, i.e. if a <code>hiddenLabel</code>'s regex matches any other label, the
latter will be excluded from the occurrence calculation.
</p>
<p>The assembling and compilation of the regex is performed for each
candidate in the function <code>makePattern</code>. The function wraps
a regex around the candidate term according to the type of term
detected, as explained above. When compiling the regular expressions around
the candidate terms, the basic rule for making patterns is:
<blockquote><b>(?:[^A-Za-z0-9\+-])(</b> + candidate term
+ <b>)(?=[^A-Za-z0-9\+-])</b></blockquote> The word separator (in bold) differs from the
standard regex for non-whitespace character (<em>\s</em>) as it
includes plus and minus signs (that in the case of HEP
thesaurus terms cannot be regarded as whitespace). The compiled
pattern is then matched inside the <code>text_string</code> via a
<code>sre.findall</code> and the resulting keyword occurrences stored
in a list of single keywords(<code>keylist</code>). This list is then
sorted by term occurrence and can be used for the final processing
task: the computation of the combined keywords.
</p>
<p><span class="adminbox">&nbsp;<b>NB.</b> The compilation of
candidate terms into regex patterns also affects negatively performance times.
However, this is not as influent as the storing and parsing of the RDF taxonomy, demonstrated above.
When compiling the 16000 Concepts of the <a href="http://cdswebdev.cern.ch/bibclassify/HEP.rdf">HEP taxonomy</a> on an Intel(R) Xeon(TM) CPU 3.06GHz
this step takes a total of <b>9 seconds</b> (one fourth of the total
execution time - 36 seconds). It is clear that such performance can be
greatly improved by working on a pre-compiled set of regex patterns,
to be re-generated only whenever the taxonomy is modified.
</span>
</p>
<a name="4"></a><h2>4. Composite keyword (ckw) processing</h2>
<p>This is the final processing step of the extraction
mechanism. Although this step takes place only if composite keywords
are present in the taxonomy, it is the one step that - in the case of high energy physics - produces the most
accurate results - especially thanks to the richness and level of
detail reached by the current HEP thesaurus.
</p>
<p>In this part, BibClassify looks for possible keyword combinations
and key pairs by analysing the list of most recurrent single keywords
(<code>keylist</code>). The total number of mkws that are possible ckw
candidates is defined by the parameter <code>-l,
--limit</code>. The higher this value (default is 70), the higher
the number of mkws that make it to the pool of ckw candidates. In
order to compute possible combinations, BibClassify loops around
<code>l</code> single keywords in <code>keylist</code>. For each entry,
it creates key : value pairs in two dictionaries:
</p>
<ul> <li>
<code>compositesIDX</code>, an index dictionary to aid retrieval of components in
<code>keylist</code> (key:value pair of the form <em>URI : keylist entry</em>)
</li> <li><code>composites</code>, a dictionary to keep track of possible
combinations between mkws (key: value pair of the form
<em>ckwURI : [mkw1URI, mkw2URI]</em>)</li> </ul>
<p>
For example, if the mkws <em>zinc</em>
and <em>tungsten</em> both appear in the <code>keylist</code>, they make
a possible composite keyword as the ckw <em>zinc, tungsten</em> exist
in the taxonomy. Bibclassify would then create entries in the
<code>composites</code> dictionary as follows:
<blockquote>
<em> http://cern.ch/thesauri/HEP.rdf#Composite.zinctungsten : [http://cern.ch/thesauri/HEP.rdf#zinc, http://cern.ch/thesauri/HEP.rdf#tungsten] </em>
</blockquote>
which would be a valid ckw candidate.
</p>
<p><span class="adminbox">&nbsp;<b>NB.</b> One could add at this point
the possibility of having combinations of more than two mkws, e.g.
<em>ckwURI : [mkw1URI, mkw2URI, mkw3URI]</em>. This is feasible, but
was not implemented at this stage, because of the performance overhead that would
be generated by the phrase matching of more complex regular expressions.</span>
</p>
<p>Once the <code>composites</code> dictionary is completed, its keys
that point to lists of two values (like the example above) are ckw
candidates. We now need to check whether they actually appear one next
to the other in the text. This is done in function
<code>makeCompPattern</code> by compiling a pair of regular
expressions: one for <em>mkw1</em> followed by <em>mkw2</em> and one
for the inverse situation, <em>mkw2</em> followed by <em>mkw1</em>. Once again,
when compiling the regex pattern we have to take extra care to treat
special cases (hyphens, short names, wildcards) accordingly. The sum
of the incidence of the two patterns in the <code>text_string</code> is
stored in a list (<code>compositesOUT</code>) that is then sorted (by
occurrence) and output.
</p>
<a name="5"></a><h2>5. Postprocessing</h2>
<p> Before presenting the results to the user, some extra filtering
occurs, primarily to refine the output keywords. The main postprocessing actions performed on the results are:</p>
<ul>
<li> Ensure that the order of the occurrence counter ([n1,n2]) for composite keywords (e.g. <em>baryon: asymmetry [7, 12]</em>) is correct.</li>
<li> Ensure that "stray" wildcard labels (e.g. hiddenLabels of composite keywords) do not cause double phrase matching.</li>
<li> Produce the desired output using the chosen ckw delimiter: the BibClassify standard (:) or the SPIRES one (,).</li>
<li> Filter out single keywords that match one into each other, e.g. if <em>magnetic</em> and <em>magnetic field</em> appear among mkws, subtract the occurrence of <em>magnetic</em> from <em>magnetic field</em>.
<p><span class="adminbox">&nbsp;<b>NB.</b> One could also perform this
last post-processing step at the composite keyword level, e.g. <em>energy:
density</em> to be overridden by <em>dark energy: density</em>. This has
not been yet implemented for security reasons (the incidence of altLabels
on the ckw computation).</li></ul>
<p> The final results that are produced to the user consist of the
first <code>n</code> entries of <code>keylist</code> (the single
keywords) and the entries in <code>compositesOUT</code> (the composite
keywords). The results may be presented in text or html format,
according to the output mode chosen at the command line. Sample output (both text and html) can be found in the
<a href="bibclassify-admin-guide">BibClassify Admin Guide</a>.
</p>
diff --git a/modules/bibclassify/doc/hacking/bibclassify-hep-taxonomy.webdoc b/modules/bibclassify/doc/hacking/bibclassify-hep-taxonomy.webdoc
index 8febc78f0..23a4e6b61 100644
--- a/modules/bibclassify/doc/hacking/bibclassify-hep-taxonomy.webdoc
+++ b/modules/bibclassify/doc/hacking/bibclassify-hep-taxonomy.webdoc
@@ -1,118 +1,118 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: The HEP taxonomy: rationale and extensions -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibclassify-internals"/>BibClassify Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibclassify-internals"/>BibClassify Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p> The DESY Library has been responsible for maintaining a thesaurus
of high energy physics (HEP) terms for a long time. The thesaurus is
currently used as a subject controlled vocabulary by HEP institutes
worldwide. The need to convert the HEP <a
href="http://www-library.desy.de/schlagw2.html">text
thesaurus</a> to a more complex taxonomy (in <code>/opt/cds-invenio/etc/bibclassify/HEP.rdf</code>), with richer structure
and semantics, was mainly driven by the needs of the BibClassify system.
The current taxonomy oh high energy physics is expressed in the <a href="http://www.w3.org/2004/02/skos/">SKOS</a>
syntax. SKOS is a dialect of RDF and it is especially intended for the
representation of knowledge organization systems, such as thesauri,
taxonomies and basic ontologies.
<p><span class="adminbox">&nbsp;<b>NB.</b> The reasons behind the adoption of SKOS, instead of other similar
knowledge organization formats - notably <a href="http://www.w3.org/TR/owl-features/">OWL</a> - are to do with the simplicity, yet completeness, of SKOS.
The SKOS language contains all the basic properties that were needed to express the taxonomy and it allows straightforward conversion from text to RDF.
If you are interested in a more detailed discussion on this matter, please check the <a
href="https://savannah.cern.ch/cookbook/?func=detailitem&item_id=112">CERN-DESY email correspondence</a>.</span>
</p>
<p> In order to satisfy the needs of typical HEP classification
schemes and practices, the SKOS language had to be extended to include
additional properties. HEP keywords are often expressed as a
combination - a pair - of keywords. An example of this is:
<blockquote> <code>Born-Infeld model: monopole</code> </blockquote>
Here both <code>Born-Infeld model</code> and <code>monopole</code> are
standard HEP keywords (<em>single keywords</em>). However, they also
combine together to express a collective concept. We call it a
<em>composite keyword</em> and have extended the SKOS language in
order to include such new paradigm. The two property extensions
created for this purpose are:</p>
<ul>
<li> <code>composite</code>:
to express a relationship of <em>combination</em> with another
single keyword by pointing to the target composite keyword. It is a subProperty of the SKOS property <code>narrower</code></li>
<li> <code>compositeOf</code>: to
express the relationship of dependence of a composite keyword from two single concepts.
It is a subProperty of the SKOS property <code>broader</code></li>
</ul>
<p>
These two extensions are probably best described by an example.
The single keyword <code>Born-Infeld model</code> is expressed in the HEP taxonomy as:
<blockquote><pre>
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#Born-Infeldmodel"&gt;
&lt;prefLabel xml:lang="en"&gt;Born-Infeld model&lt;/prefLabel&gt;
&lt;hiddenLabel xml:lang="en"&gt;Born-Infeld&lt;/hiddenLabel&gt;
&lt;altLabel xml:lang="en"&gt;DBI&lt;/altLabel&gt;
&lt;broader rdf:resource="http://cern.ch/thesauri/HEP.rdf#fieldtheoreticalmodel"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.Born-Infeldmodelrelativistic"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.Born-Infeldmodelnonlinear"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.Born-Infeldmodelnonabelian"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.Born-Infeldmodelmonopole"/&gt;
&lt;composite rdf:resource="http://cern.ch/thesauri/HEP.rdf#Composite.Born-Infeldmodelchiral"/&gt;
&lt;/Concept&gt;
</pre></blockquote>
The concept contains all the usual SKOS tags to express the relations
and denominations of a concept (<code>prefLabel</code>,
<code>broader</code>, etc.). In addition, it contains five
<code>composite</code> tags: these link to five different combinations
of this keyword with fellow single keywords. For example one of these points to the
composite keyword <code>Born-Infeld model: monopole</code>, whose
entry is:
<blockquote><pre>
&lt;Concept rdf:about="http://cern.ch/thesauri/HEP.rdf#Composite.Born-Infeldmodelmonopole"&gt;
&lt;prefLabel xml:lang="en"&gt;Born-Infeld model: monopole&lt;/prefLabel&gt;
&lt;compositeOf rdf:resource="http://cern.ch/thesauri/HEP.rdf#Born-Infeldmodel"/&gt;
&lt;compositeOf rdf:resource="http://cern.ch/thesauri/HEP.rdf#monopole"/&gt;
&lt;/Concept&gt;
</pre></blockquote>
</p>
<p> The structure of single and composite keywords, as well as their
associations expressed by properties <code>composite</code> and
<code>compositeOf</code> are self-evident. By using such a model, we
are able to efficiently extract keyword pairs from fulltext, as
explained in the <a href="bibclassify-extraction-algorithm">BibClassify extraction guide</a>.
</p>
<p> Finally, it is worth pointing out a couple of other syntax
practices that might be specific only to the HEP taxonomy:
</p>
<ul> <li>
<code>hiddenLabel</code>: this lexical label (currently in unstable
state) is used primarily to define alternative diplays of a keyword
that are meant to be hidden from the user, but are still accessible to
text parsing operations. In the HEP taxonomy, these include
mispellings and wildcards (stricly expressed as legal regular
expressions).</li> <li> <code>note</code>: this label is currently
reserved to describe a <code>nostandalone</code> condition - a
property of those single keywords that can only appear as part of
composite keywords (their occurrence as standalones is discarded).
</li></ul>
diff --git a/modules/bibclassify/doc/hacking/bibclassify-internals.webdoc b/modules/bibclassify/doc/hacking/bibclassify-internals.webdoc
index 7873e1fb6..e96a15cf8 100644
--- a/modules/bibclassify/doc/hacking/bibclassify-internals.webdoc
+++ b/modules/bibclassify/doc/hacking/bibclassify-internals.webdoc
@@ -1,41 +1,41 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibClassify Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>This page collects some information relative to the development of the
BibClassify module and the high energy physics (HEP) taxonomy.</p>
<blockquote> <dl>
<dt><a href="bibclassify-hep-taxonomy">The HEP taxonomy: rationale and extensions</a>
<dd>This guide presents the general architecture of the HEP taxonomy
and the RDF SKOS extensions adopted. </dd></dt>
<dt><a href="bibclassify-extraction-algorithm">The code behind BibClassify: the extraction algorithm</a>
<dd>This guide illustrates in detail the <em>modus
operandi</em> of BibClassify.</dd></dt>
</dl> </blockquote>
<span class="warning">WARNING. This guides are intended mainly for
developers, wishing to find out about specific details of the
<br />.<br /> If you are looking for a general usage guide, please refer to the <a
-href="<WEBURL>/help/admin/bibclassify-admin-guide">BibClassify Admin Guide</a>.</span>
+href="<CFG_SITE_URL>/help/admin/bibclassify-admin-guide">BibClassify Admin Guide</a>.</span>
diff --git a/modules/bibclassify/lib/bibclassify_regression_tests.py b/modules/bibclassify/lib/bibclassify_regression_tests.py
index 9187f68f4..c8395082b 100644
--- a/modules/bibclassify/lib/bibclassify_regression_tests.py
+++ b/modules/bibclassify/lib/bibclassify_regression_tests.py
@@ -1,63 +1,63 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibClassify Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages, \
test_web_page_existence
class BibClassifyWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibClassify web pages whether they are up or not."""
def test_availability_bibclassify_admin_guide(self):
"""bibclassify - availability of BibClassify Admin Guide page"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/admin/bibclassify-admin-guide',
+ test_web_page_content(CFG_SITE_URL + '/help/admin/bibclassify-admin-guide',
expected_text="BibClassify Admin Guide"))
return
def test_availability_bibclassify_admin_guide_images(self):
"""bibclassify - availability of BibClassify Admin Guide images"""
- test_web_page_existence(weburl + '/img/admin/bibclassify-admin-guide-cloud.jpeg')
+ test_web_page_existence(CFG_SITE_URL + '/img/admin/bibclassify-admin-guide-cloud.jpeg')
def test_availability_bibclassify_hacking_pages(self):
"""bibclassify - availability of BibClassify Hacking Guide pages"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/hacking/bibclassify-internals',
+ test_web_page_content(CFG_SITE_URL + '/help/hacking/bibclassify-internals',
expected_text="BibClassify Internals"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/hacking/bibclassify-hep-taxonomy',
+ test_web_page_content(CFG_SITE_URL + '/help/hacking/bibclassify-hep-taxonomy',
expected_text="The HEP taxonomy: rationale and extensions"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/hacking/bibclassify-extraction-algorithm',
+ test_web_page_content(CFG_SITE_URL + '/help/hacking/bibclassify-extraction-algorithm',
expected_text="The code behind BibClassify: the extraction algorithm"))
return
test_suite = make_test_suite(BibClassifyWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibconvert/doc/admin/bibconvert-admin-guide.webdoc b/modules/bibconvert/doc/admin/bibconvert-admin-guide.webdoc
index d545dd166..4dde42bdb 100644
--- a/modules/bibconvert/doc/admin/bibconvert-admin-guide.webdoc
+++ b/modules/bibconvert/doc/admin/bibconvert-admin-guide.webdoc
@@ -1,1055 +1,1055 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibConvert Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>A. <a href="#A">Overview</a></strong><br />
<strong>B. <a href="#B">XML-oriented mode</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>1 <a href="#B.1">Configuration File Examples</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>2 <a href="#B.2">Running BibConvert</a></strong><br />
<strong>C. <a href="#C">Plain text-oriented mode</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>1 <a href="#C.1">Configuration File Examples</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>2 <a href="#C.2">Running BibConvert</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>3 <a href="#C.3">BibConvert Configuration Guide</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="#G">Conventions</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1 <a href="#C.3.1">Step 1 Definition of Source Record</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2 <a href="#C.3.2">Step 2 Definition of Source Fields</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3 <a href="#C.3.3">Step 3 Definition of Target Record</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4 <a href="#C.3.4">Formatting in BibConvert</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4.1 <a href="#C.3.4.1">Definition of Formatting Functions</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4.2 <a href="#C.3.4.2">Generated Values</a><br />
<a name="A"></a><h2>A. Overview</h2>
<p>BibConvert utility enables you to convert metadata records from
various metadata formats into another metadata format supported by the
CDS Invenio local database. It is designed to process XML harvested
metadata records, converting them into MARC21 before they are uploaded
into the database. However, BibConvert is flexible enough to deal also
with other structured metadata according to your needs, and offers a
way to actually insert what you want into the database.
<p>BibConvert is suitable for tasks such as conversion of records
received from multiple data sources, or conversion of records from
another system that may support a different metadata format.</p>
<p>In order to cover a wider range of possible conversions, BibConvert
has 2 different modes, each dealing with different types of data, and
each using different configuration files.<p>
<dl>
<dt><b>Plain text-oriented mode:</b></dt>
<dd>Deals with source data being typically structured with line breaks,
and character-based separators. You can use this mode when you need
to process line-based data, such as comma/tab separated values.
Still, this mode is powerful enough to convert
complex structures, at the cost of a more complex configuration.</dd>
<dt><b>XML-oriented mode</b></dt>
<dd>Convert source data being encoded in XML. Provided you have installed
a supported XSLT processor on your machine, BibConvert can make use of standard
XSLT to interpret your XML data.
</dd>
</dl>
<p>In addition to XSLT, we provide a home-made solution for converting
XML source data. It uses our own BFX language as transformation language,
extended with XPath for node selections.<br/>
You should consider using this solution only in the case where you have not installed (or do not
want to install) an XSLT processor on your machine.
</p>
<a name="B"></a><h2>B XML-Oriented Mode</h2>
<a name="B.1"></a><h3>1 Configuration File Examples</h3>
<h4>Using XSLT</h4>
You can use standard XSL stylesheets to transform your source data. Have a look
at the provided samples in <code>etc/bibconvert/config</code> directory
of your CDS Invenio installation.<br/>
<h4>Using BFX</h4>
BFX (BibFormat for XML) uses a syntax similar to XSLT. Roughly they only
differ in the name of the tags.<br/>
More documentation about it is to be added soon (FIXME).<br/>
Have a look
at the provided samples in <code>etc/bibconvert/config</code> directory
of your CDS Invenio installation to learn more about it.
<a name="B.2"></a><h3>2. Running BibConvert</h3>
BibConvert in XML-oriented mode has only 1 parameter: <code>-c</code>.
It is used to specify which transformation stylesheet to apply to the piped XML.
<blockquote>
<pre>
$ bibconvert -coaidc2marcxml.xsl < sample.xml > /tmp/record.xml
</pre>
</blockquote>
If the stylesheet you want to use is installed in the <code>etc/bibconvert/config</code>
directory of your CDS Invenio installation, then you can just refer to it by its filename.
Otherwise use the full path to the file.
<a name="C"></a><h2>C Plain Text-Oriented Mode</h2>
<a name="C.1"></a><h3>1 Configuration File Examples</h3>
<p>OAI DublinCore into MARC21 and OAI MARC into MARC21 configurations
will be provided as default configuration, ensuring the standard
uploading sequence
(incl. <a href="bibharvest-admin-guide">BibHarvest</a>
and <a href="bibupload-admin-guide">BibUpload</a> utilities). Other
configurations can be created according to your needs. The
configuration file that has to be created for each data source is a
text file with following structure:
<protect>
<blockquote>
<pre>
&nbsp; ### the configuration starts here
&nbsp; ### Configuration of bibconvert templates
&nbsp; ### source data : <source_data_name>
&nbsp;
&nbsp; === data extraction configuration template ===
&nbsp; ### here comes the data extraction configuration template
&nbsp; # entry example:
&nbsp;
&nbsp; AU---%A---MAX---;---
&nbsp;
&nbsp; # extracts maximum available data by field from metadata record
&nbsp; # the values are found between specified tags
&nbsp; # in this case between the '%A' tag and other tags defined
&nbsp; # repetitive values are recognized by a semicolon separator
&nbsp; # resp. by multiple presence of '%A' tag
&nbsp;
&nbsp; === data source configuration template ===
&nbsp; ### here comes the data source configuration template
&nbsp; # entry example:
&nbsp;
&nbsp; AU---<:FIRSTNAME:>-<:SURNAME:>
&nbsp;
&nbsp; # describes the contents of extracted source data fields
&nbsp; # in this case, the field AU is described as having two distinct subfields
&nbsp;
&nbsp; === data target configuration template ===
&nbsp; ### here comes the data target configuration template
&nbsp; # entry example:
&nbsp;
&nbsp; AU::CONF(AU,,0)---&lt;datafield id="700" ind1="" ind2="">&lt;subfield code="a">&lt;:AU*::SURNAME::CAP():>, &lt;AU*::FIRSTNAME::ABR():>&lt;/subfield>&lt;/datafield>
&nbsp;
&nbsp; # This section concerns rather the desired output, while previous two were focused on the data source structures.
&nbsp; # Each line equals to one output line, composed of given literals and values from extracted source data fields.
&nbsp; # In this example, the XML Marc21 output line is defined,
&nbsp; # containing re-formatted values of source fields SURNAME and FIRSTNAME
&nbsp;
&nbsp; ### the configuration ends here
</pre>
</blockquote>
</protect>
<p>Having prepared a configuration, the BibConvert will convert the
source data file according to it in a batch mode. The BibConvert is
fully compatible with the Uploader1.x configuration language. For more
information, have a look at the <a href="#C">BibConvert Configuration
Guide</a> section below.
<a name="C.2"></a><h3>2. Running BibConvert</h3>
<p>For a fully functional demo, consider the following sample input data:
<blockquote>
-<a href="<WEBURL>/admin/bibconvert/sample.dat">sample.dat</a> -- sample bibliographic data to be converted and inputted into CDS Invenio
-<br /><a href="<WEBURL>/admin/bibconvert/sample.cfg">sample.cfg</a> -- sample configuration file, featuring knowledge base demo
+<a href="<CFG_SITE_URL>/admin/bibconvert/sample.dat">sample.dat</a> -- sample bibliographic data to be converted and inputted into CDS Invenio
+<br /><a href="<CFG_SITE_URL>/admin/bibconvert/sample.cfg">sample.cfg</a> -- sample configuration file, featuring knowledge base demo
</blockquote>
<p>To convert the above data into XML MARC, use the following command:
<blockquote>
<pre>
$ bibconvert -b'&lt;collection>' -csample.cfg -e'&lt;/collection>' < sample.dat > /tmp/sample.xml
</pre>
</blockquote>
and see the XML MARC output file. You would then continue the upload procedure by calling <a
href="bibupload-admin-guide">BibUpload</a>.
<p>Other useful BibConvert configuration examples:
<blockquote>
-<a href="<WEBURL>/admin/bibconvert/dcq.cfg">dcq.cfg</a> -- Qualified Dublin Core in SGML to XML MARC example
-<br /><a href="<WEBURL>/admin/bibconvert/dcq.dat">dcq.dat</a> -- corresponding data file, featuring collection identifiers demo
+<a href="<CFG_SITE_URL>/admin/bibconvert/dcq.cfg">dcq.cfg</a> -- Qualified Dublin Core in SGML to XML MARC example
+<br /><a href="<CFG_SITE_URL>/admin/bibconvert/dcq.dat">dcq.dat</a> -- corresponding data file, featuring collection identifiers demo
</blockquote>
<blockquote>
-<a href="<WEBURL>/admin/bibconvert/dcxml-to-marcxml.cfg">dcxml-to-marcxml.cfg</a> -- OAI XML Dublin Core to XML MARC example
+<a href="<CFG_SITE_URL>/admin/bibconvert/dcxml-to-marcxml.cfg">dcxml-to-marcxml.cfg</a> -- OAI XML Dublin Core to XML MARC example
</blockquote>
<blockquote>
-<a href="<WEBURL>/admin/bibconvert/bibtex.cfg">bibtex.cfg</a> -- BibTeX to XML MARC example
+<a href="<CFG_SITE_URL>/admin/bibconvert/bibtex.cfg">bibtex.cfg</a> -- BibTeX to XML MARC example
</blockquote>
<a name="C.3"></a><h3>3 BibConvert Configuration Guide</h3>
<h4><a name="G"></a>Conventions</h4>
<br/>- comment line starts with '#' sign in the first column
<br/>- each section is declared by a line starting with '===' (further characters on the line are ignored)
<br/>- values are separated by '---'
<h4><a name="C.3.1"></a>3.1 Step 1 Definition of Source record</h4>
<p>- Create/edit "data extraction configuration template" section of the configuration file.
<br />- Each line of this section stands for a definition of one source field:
<p><strong>name---keyword---terminating string---separator---</strong>
<p>- Choose a (valid) name allowed by the system
<br />- Enter <strong>keyword</strong> and <strong>terminating string</strong>, which are boundary tags for
the wanted value extraction
<br />- In case the field is repetitive, enter the value <strong>separator</strong>
<br />- "<strong>---</strong>"is mandatory separator between all values, even zero-length
<br />- <strong>MAX</strong>/<strong>MIN</strong> keywords can be used instead of terminating string
<br />&nbsp;
<p>Example of a definition of author(repetitive) and title (non-repetitive)
fields:
<br />
<pre>
&nbsp; === data extraction configuration template ===
&nbsp; ### here comes the data extraction configuration template
&nbsp;
&nbsp; AU---AU_---MAX---;---
&nbsp; TI---TI_---EOL------
</pre>
<h4><a name="C.3.2"></a>3.2 Step 2 Definition of Source fields</h4>
<I>Each field extracted from the source according to the definition done
in the first step can have an internal structure, which is described in
this section.</I>
<p>- Create/edit "data source configuration template" section of the configuration file.
<br />- Each line of this section stands for a definition of one source field
<br />- <name> corresponds to the name defined in the step 1
<p>name---{CONST&lt;:SUBFIELD:>[CONST]}}
<p>- Enter only constants that appear systematically.
<br />- Between two discrete subfields has to be defined a constant of a non zero
length
<br />- "---"is a mandatory separator between the name and the source
field definition
<p>Example of a definition of author(repetitive) and title (non-repetitive)
fields:
<pre>
=== data source configuration template ===
TI---&lt;:TI:>
AU---&lt;:FIRSTNAME:>-&lt;:SURNAME:>
</pre>
<h4><a name="C.3.3"></a>3.3 Step 3 Definition of target record</h4>
<I>This definition describes the layout of the target record that is created by the conversion,
together with the corresponcence to the source fields defined in step 2.</I>
<p>- Create/edit "data target configuration template" section of the configuration file.
<br />- Each line of this section stands for an output line created by the conversion.
<br />- &lt;name> corresponds to the name defined in the steps 1 and 2
<p>CODE---CONST&lt;:name::SUBFIELD::FUNCT():>CONST&lt;:GENERATED_VALUE:>
<p>- <strong>CODE</strong> stands for a tag for readability (optional)
<br />- "<strong>::</strong>"is a mandatory separator between the name and the subfield
definition
<br />- optionally, you can apply the appropriate <a href="#C.3.4.1">formatting function(s)</a>
and <a href="#C.3.4.2">generated values</a>
<br />- "<strong>::</strong>"is a mandatory separator between the subfield definition and the function(s)
<br />- "<strong>---</strong>"is a mandatory separator between the tag and the output code definition
<br />- mark repetitive source fields with an asterisk (*)
<p>Example of a definition of author (repetitive) and title (non-repetitive) codes:
<protect>
<pre>
<br/>AU::CONF(AU,,0)---&lt;datafield id="700" ind1="" ind2="">&lt;subfield code="a">&lt;:AU*::AU:>&lt;/subfield>&lt;/datafield>
<br/>TI::CONF(TI,,0)---&lt;datafield id="245" ind1="" ind2="">&lt;subfield code="a">&lt;:TI::TI::SUP(SPACE, ):>&lt;/subfield>&lt;/datafield>
</pre>
</protect>
<br />- preserve newlines in a source field for later use by formatting
functions by marking them with "^"
<p>Example of a definition of a book editors field in which the newlines are preserved
so that they can be processed by the JOINMULTILINES formatting function:
<protect>
<pre>
<br/>AU---&lt;datafield id="773" ind1=" " ind2=" ">&lt;:BOOKEDITOR^::BOOKEDITOR::JOINMULTILINES(&lt;subfield code="a">,&lt;/subfield>):>&lt;/datafield>
<br /><br />
With a value such as:<br />
Test
Case, A
<br />
The results may be:<br /><br />
&lt;datafield tag="773" ind1="" ind2="">&lt;subfield code="a">Test&lt;/subfield>&lt;subfield code="a">Case, A&lt;/subfield>&lt;/datafield>
</pre>
</protect>
<h4><a name="C.3.4"></a>3.4 Formatting in BibConvert</h4>
<h5>&nbsp;<a name="C.3.4.1"></a>3.4.1 Definition of formatting functions</h5>
<blockquote>Every field can be processed with a variety of functions that
partially or entirely change the original value.
<br />There are three types of functions available that take as element either
single characters, words or the entire value of processed field.
<br />&nbsp;
<p>Every function requires a certain number of parameters to be entered
in brackets. If an&nbsp; insufficient number of parameters is present,
the function uses default values. Default values are constructed with attempt to keep the original value.
<p>The configuration of templates is case sensitive.
<p>The following functions are available:
<p><a href="#ADD">ADD(prefix,suffix) - add prefix/suffix</a>
<br /><a href="#KB">KB(kb_file,[0-9]) -lookup in kb_file and replace value</a>
<br /><a href="#ABR">ABR(x,suffix)/ABRW(x,suffix) - abbreviation with suffix addition</a>
<br /><a href="#ABRX">ABRX() - abbreviate exclusively words longer</a>
<br /><a href="#CUT">CUT(prefix,postfix) - remove substring from side</a>
<br /><a href="#REP">REP(x,y) - replacement of characters</a>
<br /><a href="#SUP">SUP(type) - suppression of characters of specified type</a>
<br /><a href="#LIM">LIM(n,L/R)/LIMW(str,L/R) - restriction to n letters</a>
<br /><a href="#WORDS">WORDS(n,side) - restriction to n words from L/R</a>
<br /><a href="#MINL">MINL(n)/MAXL(n) - replacement of words shorter/greater
than n</a>
<br /><a href="#MINLW">MINLW(n) - replacement of short values</a>
<br /><a href="#EXPW">EXP(str,1|0)/EXPW(type) - replacement of words from
value if containing spec. type/string</a>
<br /><a href="#IF">IF(value,valueT,valueF) - replace T/F value</a>
<br /><a href="#UP">UP/DOWN/CAP/SHAPE/NUM - lower case and upper case, shape</a>
<br /><a href="#SPLIT">SPLIT(n,h,str,from)/SPLITW(sep,h,str,from) - split
into more lines</a>
<br /><a href="#CONF">CONF(field,value,1/0)/CONFL(value,1/0) - confirm validity
of a field</a>
<br /><a href="#RANGE">RANGE(from,to) - confirm only entries in the specified
range</a>
<br /><a href="#DEFP">DEFP() - default print</a>
<br /><a href="#IFDEFP">IFDEFP(field,value,1/0) - IF condition is met, default print</a>
<br /><a href="#JOINMULTILINES">JOINMULTILINES(prefix,suffix) - Join a multiline string into a single line
with each segment having prefix and suffix</a>
<br />&nbsp;</blockquote>
<h4>
<a name="ADD"></a>ADD(prefix,postfix)</h4>
<blockquote>default: ADD(,)&nbsp;&nbsp;&nbsp; no addition
<p>Adds prefix/postfix to the value, we can use this function to add the proper
field name as a prefix of the value itself:
<p>ADD(WAU=,)&nbsp;&nbsp;&nbsp; prefix for the first author (which may
have been taken from the field AU2)
<br />&nbsp;</blockquote>
<h4>
<a name="KB"></a>KB(kb_file)&nbsp;&nbsp;&nbsp; -&nbsp;&nbsp;&nbsp; kb_file search</h4>
<blockquote>default: KB(kb_file,1/0/R)
<p>The input value is compared to a kb_file and may be replaced
by another value. In the case that the input value is not recognized, it is by default kept
without any modification. This default can be overridden by <strong>_DEFAULT_---default value</strong> entry in the kb_file
<p>The file specified in the parameter is a text file representing a table
of values that correspond to each other:
<p>{<strong>input_value---output_value</strong>}
<p>KB(file,1) searches the exact value passed.
<br />KB(file,0) searches the KB code inside the value passed.
<br />KB(file,2) as 0 but not case sensitive
<br />KB(file,R) replacements are applied on substrings/characters only.
<br/>
<br/> bibconvert look-up value in KB_file in one of following modes:
<br/> ===========================================================
<br/> 1 - case sensitive / match (default)
<br/> 2 - not case sensitive / search
<br/> 3 - case sensitive / search
<br/> 4 - not case sensitive / match
<br/> 5 - case sensitive / search (in KB)
<br/> 6 - not case sensitive / search (in KB)
<br/> 7 - case sensitive / search (reciprocal)
<br/> 8 - not case sensitive / search (reciprocal)
<br/> 9 - replace by _DEFAULT_ only
<br/> R - not case sensitive / search (reciprocal) replace
<br/>
<br/>
<p>Edge spaces are not considered.
Output value is not further formated.</blockquote>
<h4>
<a name="ABR"></a>ABR(x,trm),ABRW(x,trm)&nbsp; - abbreviate term to x places
with(out) postfix</h4>
<blockquote>default: ABR(1,.)
<br />default: ABRW(1,.)
<p>The words in the input value are shortened according to the parameters
specified. By default, only the initial character is kept and the output
value is terminated by a dot.
<br />ABRW takes entire value as one word.
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>ABR()</td>
<td>firstname_surname</td>
<td>f._s.</td>
</tr>
<tr>
<td>ABR(1,)</td>
<td>firstname_surname</td>
<td>f_s</td>
</tr>
<tr>
<td>ABR(10,COMMA)</td>
<td>firstname_surname</td>
<td>firstname,_surname,</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
<a name="ABRX"></a>ABRX() - abbreviate exclusively words longer than given limit</h4>
<blockquote>default: ABRX(1,.)
<p>Exclusively words that reach the specified length limit in the input value are abbreviated.
No suffix is appended to the words shorter than specified limit.
</blockquote>
<h4>
<a name="CUT"></a>CUT(prefix,postfix) - remove substring from side</h4>
<blockquote>default: CUT(,)
<p>Remove string from the value (reverse function to the "ADD")
</blockquote>
<h4>
<a name="REP"></a>REP(x,y)&nbsp;&nbsp; - replace x with y</h4>
<blockquote>default: REP(,)&nbsp;&nbsp;&nbsp; no replacement
<p>The input value is searched for the string specified in the first parameter.
All such strings are replaced with the string specified in the second parameter.
</blockquote>
<h4>
<a name="SUP"></a>SUP(type,string)&nbsp;&nbsp; - suppress chars of certain
type</h4>
<blockquote>default: SUP(,)&nbsp;&nbsp;&nbsp; type not recognized
<p>All groups of characters belonging to the type specified in the first
parameter are suppressed or replaced with a string specified in the second
parameter.
<p>Recognized types:
<p>SPACE .. invisible chars incl. NEWLINE
<br />ALPHA .. alphabetic
<br />NALPHA .. not alphabetic
<br />NUM .. numeric
<br />NNUM&nbsp;&nbsp;&nbsp; .. not numeric
<br />ALNUM&nbsp; .. alphanumeric
<br />NALNUM&nbsp; .. non alphanumeric
<br />LOWER&nbsp; .. lower case
<br />UPPER&nbsp; .. upper case
<br />PUNCT&nbsp; .. punctuation
<br />NPUNCT&nbsp; .. not punctuation
<br />&nbsp;
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>SUP(SPACE,-)</td>
<td>sep_1999</td>
<td>sep-1999</td>
</tr>
<tr>
<td>SUP(NNUM)</td>
<td>sep_1999</td>
<td>1999</td>
</tr>
<tr>
<td>SUP(NUM)</td>
<td>sep_1999</td>
<td>sep_</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
<a name="LIM"></a>LIM(n,side)/LIMW(str,side)&nbsp;&nbsp; - limit to n letters
from L/R</h4>
<blockquote>default: LIM(0,)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
no change
<br />default: LIMW(,R)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; no change
<p>Limits the value in order to get the required number of characters by
cutting excess characters from either side.
<br />LIMW removes the Left/Right side to the (str) string.
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>LIM(4,L)</td>
<td>sep_1999</td>
<td>1999</td>
</tr>
<tr>
<td>LIM(4,R)</td>
<td>sep_1999</td>
<td>sep_</td>
</tr>
<tr>
<td>LIMW(_,R)</td>
<td>sep_1999</td>
<td>sep_</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
<a name="WORDS"></a>WORDS(n,side)&nbsp; - limit to n words from L/R</h4>
<blockquote>default: WORDS(0,R)
<p>Keeps the number of words specified in the first parameter from either
side.
<br />&nbsp;
<br />&nbsp;
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>WORDS(1)</td>
<td>sep_1999</td>
<td>1999</td>
</tr>
<tr>
<td>WORDS(1,L)</td>
<td>sep_1999</td>
<td>sep_</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
<a name="MINL"></a>MINL(n)&nbsp;&nbsp; - exp. words shorter than n</h4>
<blockquote>default: MINL(1)
<p>All words shorter than the limit specified in the parameter are replaced
fro mthe sentence.
<br />The words with length exactly n are kept.
<br />&nbsp;
<br />&nbsp;
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>MINL(2)</td>
<td>History of Physics</td>
<td>History of Physics</td>
</tr>
<tr>
<td>MINL(3)</td>
<td>History of Physics</td>
<td>History Physics</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
MAXL(n)&nbsp;&nbsp; - exp. words longer than n</h4>
<blockquote>default: MAXL(0)
<p>All words greater in number of characters than the limit specified in
the parameter are replaced. Words with length exactly n are kept.
<br />&nbsp;
<br />&nbsp;
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>MAXL(2)</td>
<td>History of Physics</td>
<td>of</td>
</tr>
<tr>
<td>MAXL(3)</td>
<td>History of Physics</td>
<td>of</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
<a name="MINLW"></a>MINLW(n) - replacement of short values</h4>
<blockquote>default: MINLW(1) (no change)
<p>The entire value is deleted if shorter than the specified limit.
<br />This is used for the validation of created records, where we have 20
characters in the header.
<br />The default validation is MINLW(21), i.e. the record entry will not
be consided as valid, unless it contains at least 21 characters including
the header. This default setting can be overriden by the -l command line option.
<p>In order to increase the necessary length of the output line in the configuration
itself, apply the function on the total value:
<p>AU::MINLW(25)---CER &lt;:SYSNO:> AU&nbsp;&nbsp;&nbsp; L &lt;:SURNAME:>,
&lt;:NAME:>
<br />&nbsp;
<br />&nbsp;</blockquote>
<h4>
<a name="EXPW"></a>EXP(str,1|0) - exp./aprove word containing specified
string</h4>
<blockquote>default: EXP&nbsp;&nbsp; (,0)&nbsp;&nbsp;&nbsp;&nbsp; leave
all value
<p>The record is shortened by replacing words containing the specified
string.
<br />The second parameter states whether the string approves the word (0)
or disables it (1).
<p>for example, to get the email address from the value, use the following
<br />&nbsp;
<br />&nbsp;
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>EXP(@,0)</td>
<td>mail to: libdesk@cern.ch</td>
<td>libdesk@cern.ch</td>
</tr>
<tr>
<td>EXP(:,1)</td>
<td>mail to: libdesk@cern.ch</td>
<td>mail libdesk@cern.ch</td>
</tr>
<tr>
<td>EXP(@)</td>
<td>mail to: libdesk@cern.ch</td>
<td>libdesk@cern.ch</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
EXPW(type)&nbsp;&nbsp; - exp. word from value if containing spec. type</h4>
<blockquote>default: EXPW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type
not recognized
<br />&nbsp;
<p>The sentence is shortened by replacing words containing specified type
of character.
<p>Types supported in EXPW function:
<p>ALPHA .. alphabetic
<br />NALPHA .. not alphabetic
<br />NUM .. numeric
<br />NNUM&nbsp;&nbsp;&nbsp; .. not numeric
<br />ALNUM&nbsp; .. alphanumeric
<br />NALNUM&nbsp; .. non alphanumeric
<br />LOWER&nbsp; .. lower case
<br />UPPER&nbsp; .. upper case
<br />PUNCT&nbsp; .. punctuation
<br />NPUNCT&nbsp; .. non punctuation
<p>Note: SPACE is not handled as a keyword, since all space characters
are considered as word separators.
<br />&nbsp;
<br />&nbsp;
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>EXPW(NNUM)</td>
<td>sep_1999</td>
<td>1999</td>
</tr>
<tr>
<td>EXPW(NUM)</td>
<td>sep_1999</td>
<td>sep</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
<a name="IF"></a>IF(value,valueT,valueF) - replace T/F value</h4>
<blockquote>default: IF(,,)
<p>Compares the value with the first parameter. In case the result is TRUE,
the input value is replaced with the second parameter, otherwise the input
value is replaced with the third parameter.
<br />In case the input value has to be kept, whatever it is, the keyword
ORIG can be used (usually in the place of the third parameter)
<br />&nbsp;
<br />&nbsp;
<blockquote>&nbsp;
<table border width="50%" >
<tr>
<td>example</td>
<td>input</td>
<td>output</td>
</tr>
<tr>
<td>IF(sep_1999,sep)</td>
<td>sep_1999</td>
<td>sep</td>
</tr>
<tr>
<td>IF(oct_1999,oct)</td>
<td>sep_1999</td>
<td></td>
</tr>
<tr>
<td>IF(oct_1999,oct,ORIG)</td>
<td>sep_1999</td>
<td>oct_1999</td>
</tr>
</table>
</blockquote>
</blockquote>
<h4>
<a name="UP"></a>UP&nbsp;&nbsp;&nbsp; - upper case</h4>
<blockquote>Convert all characters to upper case</blockquote>
<h4>
DOWN&nbsp;&nbsp; - lower case</h4>
<blockquote>Convert all characters to lower case</blockquote>
<h4>
CAP&nbsp;&nbsp;&nbsp; - make capitals</h4>
<blockquote>Convert the initial character of each word to upper case
and the rest of characters to lower case</blockquote>
<h4>
SHAPE&nbsp;&nbsp;&nbsp; - format string</h4>
<blockquote>Supresses all invalid spaces</blockquote>
<h4>
<B>NUM&nbsp;&nbsp;&nbsp; - number</B></h4>
<blockquote>If it contains at least one digit, convert it into a
number by suppressing other characters. Leading zeroes are deleted.</blockquote>
<h4>
<a name="SPLIT"></a>SPLIT(n,h,str,from)</h4>
<blockquote>Splits the input value into more lines, where each line contains
at most (n+h+length of str) characters, (n) being the number of characters
following the number of characters in the header, specified in (h). The
header repeats at the beginning of each line. An additional string can
be inserted as a separator between the header and the following value.
This string is specified by the third parameter (str). It is possible to
restrict the application of (str) so it does not appear on the first line
by entering "2" for (from)</blockquote>
<h4>
SPLITW(sep,h,str,from)</h4>
<blockquote>Splits the input value into more lines by replacing the line
separator stated in (sep) with CR/LFs. Also, as in the case of the SPLIT
function, the first (h) characters are taken as a header and repeat at
the beginning of each line.&nbsp; An additional string can be inserted
as a separator between the header and the following value. This string
is specified by the third parameter (str). It is possible to restrict the
application of (str) so it does not appear on the first line by entering
"2" for (from)</blockquote>
<h4>
<a name="CONF"></a>CONF(field,value,1/0)&nbsp; - confirm validity of a
field</h4>
<blockquote>The input value is taken as it is, or refused depending on
the value of some other field. In case the other (field) contains&nbsp;
the string specified in (value), then the input value is confirmed (1)
or refused (0).</blockquote>
<h4>
CONFL(str,1|0) - confirm validity of a field</h4>
<blockquote>The input value is confirmed if it contains (<B>1</B>)/misses(<B>0</B>)
the specified string (<B>str</B>)</blockquote>
<h4>
<a name="RANGE"></a>RANGE(from,to) - confirm only entries in the specified
range</h4>
<blockquote>Left side function of target template configuration section to select the desired
entries from the repetitive field.
<br />The range can only be continuous.
<p>The entry is confirmed in case its input falls into the range from-to
specified in the parameter, border values included. As an upper limit it
is possibe to use the keyword MAX.
<p>This is useful in case of AU code, where the first entry has a different
definition from other entries:
<p>AU::RANGE(1,1)---CER &lt;:SYSNO:> AU2&nbsp;&nbsp;&nbsp; L &lt;:AU::SURNAME:>,
&lt;:AU::NAME:>&nbsp;&nbsp;&nbsp; ... takes the first name from the defined
AU field
<br />AU::RANGE(2,MAX)---CER &lt;:SYSNO:> AU&nbsp;&nbsp;&nbsp;&nbsp; L &lt;:AU::SURNAME:>
, &lt;:AU::NAME:>&nbsp;&nbsp;&nbsp; ... takes the the rest of namesfrom
the AU field
<br />&nbsp;</blockquote>
<h4><a name="DEFP"></a>DEFP() - default print</h4>
<blockquote>The value is printed by default even if it does not contain any variable input from the source file.</blockquote>
<h4>
<a name="IFDEFP"></a>IFDEFP(field,value,1/0)&nbsp;- IF condition is met, default print</h4>
<blockquote>The line is printed by default (even if it does not contain any variable input from the source file) IF
a condition is met that depends on the value of some other field. The condition is basically either that "field"
contains "value" (in which case the 3rd parameter should be set to 1), or that "field" does NOT contain "value"
(in which case the 3rd parameter should be set to 0).
<br />
<br />
For example, given the following line:<br /><br />
690C::REP(EOL,)::IFDEFP(comboYEL,BOOK,1)---&lt;datafield tag="690" ind1="C" ind2=" ">&lt;subfield code="a">BOOK&lt;/subfield>&lt;/datafield><br />
<br />
We want to print the line if the (field) "comboYEL" contains the (value) "BOOK", otherwise we don't want to print it.
Therefore, the 3rd parameter is set to "1". However, in the following line:<br /><br />
690C::REP(EOL,)::IFDEFP(comboYEL,BOOK,0)---&lt;datafield tag="690" ind1="C" ind2=" ">&lt;subfield code="a">OTHER&lt;/subfield>&lt;/datafield><br />
<br />
We want to print the line if the (field) "comboYEL" does NOT contain the (value) "BOOK", otherwise we don't want to
print it. Therefore, the 3rd parameter is set to "0".<br />
<br />
This is achieved by using "IFDEFP". If the line had contained variables, the "CONF" function would have been used
instead.
</blockquote>
<h4>
<a name="JOINMULTILINES"></a>JOINMULTILINES(prefix,suffix) - Join a multiline string into a single line
with each segment having prefix and suffix</h4>
<blockquote>
Given a field-value with newlines in it, split the field on the new lines (\n),
separating them with prefix, then suffix. E.g.:<br />
For the field XX with the value:<br /><br />
&nbsp;&nbsp;Test<br />
&nbsp;&nbsp;Case, A<br />
<br />And the function call:<br /><br />
&nbsp;&nbsp;&lt;:XX^::XX::JOINMULTILINES(&lt;subfield code="a">,&lt;/subfield>):><br />
<br />The results would be:<br />
<br />&nbsp;&nbsp;&lt;subfield code="a">Test&lt;/subfield>&lt;subfield code="a">Case, A&lt;/subfield><br />
<br />One note on this: &lt;:XX^::XX:<br />
Without the ^ the newlines will be lost as bibconvert will remove them, so
you'll never see an effect from this function.
</blockquote>
<h4>
<a name="C.3.4.2"></a>3.4.2 Generated values</h4>
<blockquote>In the template configurations, values can be either taken from the source
or generated in the process itself. This is mainly useful for evaluating constant values.
<p>Currently, the following date values are generated:
<br />&nbsp;</blockquote>
<h4>
DATE(format,n)</h4>
<blockquote>default: DATE(,10)
<p>where n is the number of digits required.
<p>Generates the current date in the form given as a parameter. The format
has to be given according to the ANSI C notation, i.e. the string is composed
out of following components:
<p><a name="date"></a>&nbsp; %a&nbsp;&nbsp;&nbsp; abbreviated weekday name
<br />&nbsp; %A&nbsp;&nbsp;&nbsp; full weekday name
<br />&nbsp; %b&nbsp;&nbsp;&nbsp; abbreviated month name
<br />&nbsp; %B&nbsp;&nbsp;&nbsp; full month name
<br />&nbsp; %c&nbsp;&nbsp;&nbsp; date and time representation
<br />&nbsp; %d&nbsp;&nbsp;&nbsp; decimal day of month number (01-31)
<br />&nbsp; %H&nbsp;&nbsp;&nbsp; hour (00-23)(12 hour format)
<br />&nbsp; %I&nbsp;&nbsp;&nbsp; hour (01-12)(12 hour format)
<br />&nbsp; %j&nbsp;&nbsp;&nbsp; day of year(001-366)
<br />&nbsp; %m&nbsp;&nbsp;&nbsp; month (01-12)
<br />&nbsp; %M&nbsp;&nbsp;&nbsp; minute (00-59)
<br />&nbsp; %p&nbsp;&nbsp;&nbsp; local equivalent of a.m. or p.m.
<br />&nbsp; %S&nbsp;&nbsp;&nbsp; second (00-59)
<br />&nbsp; %U&nbsp;&nbsp;&nbsp; week number in year (00-53)(starting with
Sunday)
<br />&nbsp; %V&nbsp;&nbsp;&nbsp; week number in year
<br />&nbsp; %w&nbsp;&nbsp;&nbsp; weekday (0-6)(starting with Sunday)
<br />&nbsp; %W&nbsp;&nbsp;&nbsp; week number in year (00-53)(starting with
Monday)
<br />&nbsp; %x&nbsp;&nbsp;&nbsp; local date representation
<br />&nbsp; %X&nbsp;&nbsp;&nbsp; local time representation
<br />&nbsp; %y&nbsp;&nbsp;&nbsp; year (no century prefix)
<br />&nbsp; %Y&nbsp;&nbsp;&nbsp; year (with century prefix)
<br />&nbsp; %Z&nbsp;&nbsp;&nbsp; time zone name
<br />&nbsp; %%&nbsp;&nbsp;&nbsp; %
<br />&nbsp;</blockquote>
<h4>
WEEK(diff)</h4>
<blockquote>Enters the two-digit number of the current week (%V) increased
by specified difference.
<br />If the resulting number is negative, the returned value is zero (00).
<br />Values are kept up to 99, three digit values are shortened from the
left.
<p>WEEK(-4)&nbsp;&nbsp;&nbsp; returns 48, if current week is 52
<br />WEEK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; current
week
<br />&nbsp;</blockquote>
<h4>
SYSNO</h4>
<blockquote>&nbsp;
<br />Works the same as DATE, however the format of the resulting value is
fixed so it complies with the requirements of further record handling.
The format is 'whhmmss', where:
<p>w&nbsp;&nbsp;&nbsp;&nbsp; current weekday
<br />hh&nbsp;&nbsp;&nbsp; current hour
<br />mm&nbsp;&nbsp;&nbsp; current minute
<br />ss&nbsp;&nbsp;&nbsp; current second
<p>The system number, if generated like this, contains a variable value
changing every second. For the system number is an identifier of the record,
it is needed to ensure it will be unique for the entire record processed.
Unlike the function DATE, which simply generates the value of format given,
SYSNO keeps the value persistent throughout the entire record and excludes collision
with other records that are generated in period of one week with one second granularity.
<p>It is not possible to use the DATE function for generating a system number instead.
<p>The system number is unique in range of one week only, according to
the current definition.
<br />&nbsp;
<br />&nbsp;</blockquote><h4>
OAI</h4>
<blockquote>
<p/>Inserts OAI identifier incremented by one for earch record
Starting value that is used in the first record in the batch job can be specified on the command line using the -o&lt;starting_value> option.
</blockquote>
diff --git a/modules/bibconvert/doc/hacking/bibconvert-api.webdoc b/modules/bibconvert/doc/hacking/bibconvert-api.webdoc
index 122f7e3f6..eb5494645 100644
--- a/modules/bibconvert/doc/hacking/bibconvert-api.webdoc
+++ b/modules/bibconvert/doc/hacking/bibconvert-api.webdoc
@@ -1,64 +1,64 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibConvert API -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibconvert-internals">BibConvert Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibconvert-internals">BibConvert Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<protect>
<pre>
CDS Invenio BibConvert can be called from within your Python
programs via a high-level API.
BibConvert High-level API (subject to change)
Description:
The high-level access to the BibConvert formatting and value generation
allows to perform basic text formatting basde on BibConvert's metadata
conversion configuration language. There are two main functions accessible
via the BibConvert API: (i) The BibConvert formatter and (ii) The BibConvert
value generator. For detailed description of formatting functions and
- generated values please have a look at the <a href=<WEBURL>/help/admin/bibconvert-admin-guide>BibConvert Admin Guide</a>.
+ generated values please have a look at the <a href=<CFG_SITE_URL>/help/admin/bibconvert-admin-guide>BibConvert Admin Guide</a>.
Signature:
def format_field(value_raw, "function(parameters)"):
"""
value_raw - text to be formatted
function - BibConvert formatting function
parameters - Comma separated parameters
output - formated text
def generate():
"""
"""
Examples:
>>> # import the function:
>>> from invenio.bibconvert import format_field, generate
>>> # format field
>>> out = format_field(value_raw,"")
>>> # generate value in requested format
>>> out = generate("DATE(%Y-%m-%dT%H:%M:%SZ)")
</pre>
</protect>
diff --git a/modules/bibconvert/doc/hacking/bibconvert-internals.webdoc b/modules/bibconvert/doc/hacking/bibconvert-internals.webdoc
index f591542e6..bad1b627b 100644
--- a/modules/bibconvert/doc/hacking/bibconvert-internals.webdoc
+++ b/modules/bibconvert/doc/hacking/bibconvert-internals.webdoc
@@ -1,34 +1,34 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibConvert Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<p>This page summarizes all the information suitable to dig inside
BibConvert and it's different methods.</p>
<blockquote>
<dl>
<dt><a href="bibconvert-api">BibConvert API</a>
<dd>How to call the BibConvert library.</dd>
</dt>
</dl>
</blockquote>
diff --git a/modules/bibconvert/lib/bibconvert_regression_tests.py b/modules/bibconvert/lib/bibconvert_regression_tests.py
index bdce804be..05a22a22e 100644
--- a/modules/bibconvert/lib/bibconvert_regression_tests.py
+++ b/modules/bibconvert/lib/bibconvert_regression_tests.py
@@ -1,68 +1,68 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibConvert Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages, \
test_web_page_existence
class BibConvertWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibConvert web pages whether they are up or not."""
def test_availability_bibconvert_admin_guide(self):
"""bibconvert - availability of BibConvert Admin Guide page"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/admin/bibconvert-admin-guide',
+ test_web_page_content(CFG_SITE_URL + '/help/admin/bibconvert-admin-guide',
expected_text="BibConvert Admin Guide"))
return
def test_availability_bibconvert_admin_guide_parts(self):
"""bibconvert - availability of BibConvert Admin Guide parts"""
- test_web_page_existence(weburl + '/admin/bibconvert/bibtex.cfg')
- test_web_page_existence(weburl + '/admin/bibconvert/dcq.cfg')
- test_web_page_existence(weburl + '/admin/bibconvert/dcq.dat')
- test_web_page_existence(weburl + '/admin/bibconvert/dcxml-to-marcxml.cfg')
- test_web_page_existence(weburl + '/admin/bibconvert/example_oaimarc2xm.xsl')
- test_web_page_existence(weburl + '/admin/bibconvert/example_oaimarc2xm_collID.kb')
- test_web_page_existence(weburl + '/admin/bibconvert/sample.cfg')
- test_web_page_existence(weburl + '/admin/bibconvert/sample.dat')
- test_web_page_existence(weburl + '/admin/bibconvert/sample.kb')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/bibtex.cfg')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/dcq.cfg')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/dcq.dat')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/dcxml-to-marcxml.cfg')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/example_oaimarc2xm.xsl')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/example_oaimarc2xm_collID.kb')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/sample.cfg')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/sample.dat')
+ test_web_page_existence(CFG_SITE_URL + '/admin/bibconvert/sample.kb')
def test_availability_bibconvert_hacking_pages(self):
"""bibconvert - availability of BibConvert Hacking Guide pages"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/hacking/bibconvert-internals',
+ test_web_page_content(CFG_SITE_URL + '/help/hacking/bibconvert-internals',
expected_text="BibConvert Internals"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/hacking/bibconvert-api',
+ test_web_page_content(CFG_SITE_URL + '/help/hacking/bibconvert-api',
expected_text="BibConvert API"))
return
test_suite = make_test_suite(BibConvertWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibconvert/lib/bibconvert_xslt_engine.py b/modules/bibconvert/lib/bibconvert_xslt_engine.py
index d38c8b9e7..a196b410d 100644
--- a/modules/bibconvert/lib/bibconvert_xslt_engine.py
+++ b/modules/bibconvert/lib/bibconvert_xslt_engine.py
@@ -1,263 +1,263 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
bibconvert_xslt_engine - Wrapper for an XSLT engine.
Customized to support BibConvert functions through the
use of XPath 'format' function.
Dependencies: Need one of the following XSLT processors:
- libxml2 & libxslt
- 4suite
Used by: bibconvert.in
FIXME: - Find better namespace for functions
- Find less bogus URI (given as param to processor)
for source and template
- Implement command-line options
- Think about better handling of 'value' parameter
in bibconvert_function_*
"""
__revision__ = "$Id$"
import sys
import os
from invenio.config import \
CFG_ETCDIR, \
- weburl
+ CFG_SITE_URL
from invenio.bibconvert import FormatField
# The namespace used for BibConvert functions
CFG_BIBCONVERT_FUNCTION_NS = "http://cdsweb.cern.ch/bibconvert/fn"
# Import one XSLT processor
#
# processor_type:
# -1 : No processor found
# 0 : libxslt
# 1 : 4suite
processor_type = -1
try:
# libxml2 & libxslt
import libxml2
import libxslt
processor_type = 0
except ImportError:
pass
if processor_type == -1:
try:
# 4suite
from Ft.Xml.Xslt import Processor
from Ft.Xml import InputSource
from xml.dom import Node
processor_type = 1
except ImportError:
pass
CFG_BIBCONVERT_XSL_PATH = "%s%sbibconvert%sconfig" % (CFG_ETCDIR, os.sep, os.sep)
def bibconvert_function_libxslt(ctx, value, func):
"""
libxslt extension function:
Bridge between BibConvert formatting functions and XSL stylesheets.
Can be used in that way in XSL stylesheet
(provided xmlns:fn="http://cdsweb.cern.ch/bibconvert/fn" has been declared):
<xsl:value-of select="fn:format(., 'ADD(mypref,mysuff)')"/>
(Adds strings 'mypref' and 'mysuff' as prefix/suffix to current node value,
using BibConvert ADD function)
if value is int, value is converted to string
if value is Node (PyCObj), first child node (text node) is taken as value
"""
try:
if isinstance(value, str):
string_value = value
elif isinstance(value, int):
string_value = str(value)
else:
string_value = libxml2.xmlNode(_obj=value[0]).children.content
return FormatField(string_value, func).rstrip('\n')
except Exception, err:
sys.stderr.write("Error during formatting function evaluation: " + \
str(err) + \
'\n')
return ''
def bibconvert_function_4suite(ctx, value, func):
"""
4suite extension function:
Bridge between BibConvert formatting functions and XSL stylesheets.
Can be used in that way in XSL stylesheet
(provided xmlns:fn="http://cdsweb.cern.ch/bibconvert/fn" has been declared):
<xsl:value-of select="fn:format(., 'ADD(mypref,mysuff)')"/>
(Adds strings 'mypref' and 'mysuff' as prefix/suffix to current node value,
using BibConvert ADD function)
if value is int, value is converted to string
if value is Node, first child node (text node) is taken as value
"""
try:
if len(value) > 0 and isinstance(value[0], Node):
string_value = value[0].firstChild.nodeValue
if string_value is None:
string_value = ''
else:
string_value = str(value)
return FormatField(string_value, func).rstrip('\n')
except Exception, err:
sys.stderr.write("Error during formatting function evaluation: " + \
str(err) + \
'\n')
return ''
def convert(xmltext, template_filename=None, template_source=None):
"""
Processes an XML text according to a template, and returns the result.
The template can be given either by name (or by path) or by source.
If source is given, name is ignored.
bibconvert_xslt_engine will look for template_filename in standard directories
for templates. If not found, template_filename will be assumed to be a path to
a template. If none can be found, return None.
Raises an exception if cannot find an appropriate XSLT processor.
@param xmltext The string representation of the XML to process
@param template_filename The name of the template to use for the processing
@param template_source The configuration describing the processing.
@return the transformed XML text.
"""
if processor_type == -1:
# No XSLT processor found
raise "No XSLT processor could be found"
# Retrieve template and read it
if template_source:
template = template_source
elif template_filename:
try:
path_to_templates = (CFG_BIBCONVERT_XSL_PATH + os.sep +
template_filename)
if os.path.exists(path_to_templates):
template = file(path_to_templates).read()
elif os.path.exists(template_filename):
template = file(template_filename).read()
else:
sys.stderr.write(template_filename +' does not exist.')
return None
except IOError:
sys.stderr.write(template_filename +' could not be read.')
return None
else:
sys.stderr.write(template_filename +' was not given.')
return None
result = ""
if processor_type == 0:
# libxml2 & libxslt
# Register BibConvert functions for use in XSL
libxslt.registerExtModuleFunction("format",
CFG_BIBCONVERT_FUNCTION_NS,
bibconvert_function_libxslt)
# Load template and source
template_xml = libxml2.parseDoc(template)
processor = libxslt.parseStylesheetDoc(template_xml)
source = libxml2.parseDoc(xmltext)
# Transform
result_object = processor.applyStylesheet(source, None)
result = processor.saveResultToString(result_object)
# Deallocate
processor.freeStylesheet()
source.freeDoc()
result_object.freeDoc()
elif processor_type == 1:
# 4suite
# Init
processor = Processor.Processor()
# Register BibConvert functions for use in XSL
processor.registerExtensionFunction(CFG_BIBCONVERT_FUNCTION_NS,
"format",
bibconvert_function_4suite)
# Load template and source
transform = InputSource.DefaultFactory.fromString(template,
- uri=weburl)
+ uri=CFG_SITE_URL)
source = InputSource.DefaultFactory.fromString(xmltext,
- uri=weburl)
+ uri=CFG_SITE_URL)
processor.appendStylesheet(transform)
# Transform
result = processor.run(source)
else:
sys.stderr.write("No XSLT processor could be found")
return result
## def bc_profile():
## """
## Runs a benchmark
## """
## global xmltext
## convert(xmltext, 'oaidc2marcxml.xsl')
## return
## def benchmark():
## """
## Benchmark the module, using profile and pstats
## """
## import profile
## import pstats
## from invenio.bibformat import record_get_xml
## global xmltext
## xmltext = record_get_xml(10, 'oai_dc')
## profile.run('bc_profile()', "bibconvert_xslt_profile")
## p = pstats.Stats("bibconvert_xslt_profile")
## p.strip_dirs().sort_stats("cumulative").print_stats()
if __name__ == "__main__":
pass
diff --git a/modules/bibedit/doc/admin/bibedit-admin-guide.webdoc b/modules/bibedit/doc/admin/bibedit-admin-guide.webdoc
index affb7d58e..a489061a0 100644
--- a/modules/bibedit/doc/admin/bibedit-admin-guide.webdoc
+++ b/modules/bibedit/doc/admin/bibedit-admin-guide.webdoc
@@ -1,200 +1,200 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibEdit Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
<strong>2. <a href="#2">Edit records via Web interface</a></strong><br />
<strong>3. <a href="#3">Edit records via command line</a></strong><br />
<strong>4. <a href="#4">Delete records via command line</a></strong><br />
<strong>5. <a href="#5">Delete all records</a></strong><br />
<a name="1"></a><h2>1. Overview</h2>
<p>BibEdit enables you to directly manipulate bibliographic data, edit a
single record, do global replacements, and other cataloguing tasks.</p>
<a name="2"></a><h2>2. Edit records via Web interface</h2>
<p>To edit records via the web interface, please go to <a
-href="<WEBURL>/admin/bibedit/bibeditadmin.py">BibEdit Admin
+href="<CFG_SITE_URL>/admin/bibedit/bibeditadmin.py">BibEdit Admin
Interface</a>. This interface will let you to add, change or
delete fields in a record.</p>
<p>If you want to change several records at once, please use the batch
command-line techniques describe below.</p>
<a name="3"></a><h2>3. Edit records via command line</h2>
<p>The idea is to download record in XML MARC format, edit it by using
any editor, and upload the changes back. Note that you can edit any
number of records at the same time: for example, you can download all
records written by <code>Qllis, J</code>, open the file in your
favourite text editor, and change globally the author name to the
proper form <code>Ellis, J</code>.</p>
<p>You therefore continue as follows:</p>
<ol>
<li> Download the record in XML MARC. For example, download record ID 1234:
<pre>
$ wget -O z.xml 'http://your.site/record/1234?of=xm'
</pre>
or download latest 5,000 public documents written by <code>Qllis, J</code>:
<pre>
$ wget -O z.xml 'http://your.site/search?p=Qllis%2C+J&f=author&of=xm&rg=5000'
</pre>
<li> Edit the metadata as necessary:
<pre>
$ emacs z.xml
</pre>
<li> Upload changes back:
<pre>
$ bibupload -r z.xml
</pre>
<li> See the progress of the treatment of the file via BibSched:
<pre>
$ bibsched
</pre>
If you do not want to wait for the next wake-up time of indexing
and formatting daemons, launch them manually now:
<pre>
$ bibindex
$ bibreformat
$ webcoll
</pre>
and watch the progress via <code>bibsched</code>.
</ol>
<p>After which the record(s) should be fully modified and formatted and
all indexes and collections updated, as necessary.</p>
<a name="4"></a><h2>4. Delete records via command line</h2>
<p>Once a record has been uploaded, we prefer not to *destroy* it fully
anymore (i.e. to wipe it out and to reuse its record ID for another
record) for a variety of reasons. For example, some users may have
put this record already into their baskets in the meantime, or the
record might have already been announced by alert emails to the
external world, or the OAI harvestors might have harvested it already,
etc. We usually prefer only to *mark* records as deleted, so that our
record IDs are ensured to stay permanent.</p>
<p>Thus said, the canonical way to delete the record #1234 in CDS Invenio
v0.1.x development branch is to download its XML MARC:
<pre>
$ wget -O z.xml 'http://your.site/record/1234?of=xm'
</pre>
and to mark it as deleted by adding the indicator ``DELETED'' into the
MARC 980 $$c tag:
<pre>
$ emacs z.xml
[...]
&lt;datafield tag="980" ind1=" " ind2=" "&gt;
&lt;subfield code="a"&gt;PREPRINT&lt;/subfield&gt;
&lt;subfield code="c"&gt;DELETED&lt;/subfield&gt;
&lt;/datafield&gt;
[...]
</pre>
and upload thusly modified record in the `replace' mode:
<pre>
$ bibupload -r z.xml
</pre>
and watch the progress via <code>bibsched</code>, as mentioned in the
<a href="#3">section 3</a>.
</p>
<p>This procedure will remove the record from the collection cache so
that the record won't be findable anymore. In addition, if the users
try to access this record via direct URL such as distributed by the
alert engine (record/1234) or via their baskets, they will
see a message ``This record has been deleted''. Please note though
that the original MARCXML of the record stays kept in the database,
for example you can access it by:
<pre>
$ python -c "from zlib import decompress; \\
from invenio.dbquery import run_sql; \\
print decompress(run_sql('SELECT value FROM bibfmt \\
WHERE id_bibrec=1234 AND format=\'xm\'')[0][0])"
</pre>
<p>In some cases you may want to hide the record from the searches,
but to leave it accessible via direct URLs or via baskets. In this
case the best it to alter its collection tag (980) to some
non-existent collection, for example:
<pre>
$ wget -O z.xml 'http:://localhost/record/1234?of=xm'
$ perl -pi -e 's,<subfield code="a">ARTICLE</subfield>,<subfield code="a">HIDDENARTICLE</subfield>,g' z.xml
$ bibupload -r z.xml
</pre>
This will make the record non-existent as far as the search engine is
concerned, because it won't belong to any existing collection, but the
record will exist ``on its own'' and the users knowing its recID will
be able to access it.
</p>
<p>P.S. Note that the ``bibXXx'' tables will keep having entries for the
deleted records. These entries are to be cleaned from time to
time by the BibEdit garbage collector. This GC isn't part of
CDS Invenio yet; moreover in the future we plan to abolish all the
bibXXx tables, so that this won't be necessary anymore.
<a name="5"></a><h2>5. Delete all records</h2>
<p>If you want to wipe out all the existing bibliographic content of
your site, for example to start uploading the documents from
scratch again, you can launch:
<pre>
$ /opt/cds-invenio/bin/dbexec &lt; /opt/cds-invenio/src/cds-invenio-0.90/modules/miscutil/sql/tabbibclean.sql
$ rm -rf /opt/cds-invenio/var/data/files/*
$ /opt/cds-invenio/bin/webcoll
$ /opt/cds-invenio/bin/bibindex --reindex
</pre>
Note that you may also want to delete the fulltext files and the
submission counters in <code>/opt/cds-invenio/var/data</code>
subdirectories, if you use WebSubmit.
</p>
diff --git a/modules/bibedit/lib/bibedit_regression_tests.py b/modules/bibedit/lib/bibedit_regression_tests.py
index 5b273d7bc..1ea128bb5 100644
--- a/modules/bibedit/lib/bibedit_regression_tests.py
+++ b/modules/bibedit/lib/bibedit_regression_tests.py
@@ -1,69 +1,69 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibEdit Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class BibEditWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibEdit web pages whether they are up or not."""
def test_bibedit_admin_interface_availability(self):
"""bibedit - availability of BibEdit Admin interface pages"""
- baseurl = weburl + '/admin/bibedit/bibeditadmin.py/'
+ baseurl = CFG_SITE_URL + '/admin/bibedit/bibeditadmin.py/'
_exports = ['', 'index', 'edit', 'submit']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_bibedit_admin_guide_availability(self):
"""bibedit - availability of BibEdit Admin guide pages"""
- url = weburl + '/help/admin/bibedit-admin-guide'
+ url = CFG_SITE_URL + '/help/admin/bibedit-admin-guide'
error_messages = test_web_page_content(url,
expected_text="BibEdit Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(BibEditWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibedit/lib/bibedit_templates.py b/modules/bibedit/lib/bibedit_templates.py
index 78c59dace..f9381a6d0 100644
--- a/modules/bibedit/lib/bibedit_templates.py
+++ b/modules/bibedit/lib/bibedit_templates.py
@@ -1,562 +1,562 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
from invenio.bibedit_dblayer import *
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.messages import gettext_set_language
## Link of edit and delete button:
-btn_delete_url = weburl + "/img/iconcross.gif"
-btn_edit_url = weburl + "/img/iconpen.gif"
-weburl_bibedit = "%s/admin/bibedit/bibeditadmin.py" % weburl
+btn_delete_url = CFG_SITE_URL + "/img/iconcross.gif"
+btn_edit_url = CFG_SITE_URL + "/img/iconpen.gif"
+bibediturl = "%s/admin/bibedit/bibeditadmin.py" % CFG_SITE_URL
class Template:
def tmpl_table_header(self, ln, type_table, recid, temp="false", format_tag='marc',
tag='', num_field=None, add=0):
""" Return the Header of table. """
_ = gettext_set_language(ln)
(tag, ind1, ind2, junk) = marc_to_split_tag(tag)
tag = tag + ind1 + ind2 + "%"
if type_table != "record":
if add == 1:
print_input_add_form = self.tmpl_input('hidden', 2, 'add')
else:
print_input_add_form = ''
print_action_add_subfield = print_input_add_form + self.tmpl_input('hidden', str(num_field), 'num_field')
print_action_edit_100 = print_input_add_form + self.tmpl_input('hidden', str(num_field), 'num_field')
if add != 1:
- link_add_subfields = self.tmpl_link(ln, _("Add Subfield"), weburl_bibedit, 'edit',
+ link_add_subfields = self.tmpl_link(ln, _("Add Subfield"), bibediturl, 'edit',
{'recid' : str(recid),
'tag' : tag[:3],
'num_field' : str(num_field),
'format_tag' : format_tag,
'temp' : 'true',
'add' : 1})
link_edit_100 = ""
print_action_edit_100 = ""
#tag[:3] == 100x is never true, of course. This functionality TBD
#and will be used in enrichment editing
if str(tag[:3]) == "100x": #FIXME
link_edit_100 = self.tmpl_link(
- ln, _("Edit institute"), weburl_bibedit, 'edit',
+ ln, _("Edit institute"), bibediturl, 'edit',
{'recid' : str(recid),
'tag' : tag[:3],
'num_field' : str(num_field),
'format_tag' : format_tag,
'temp' : 'true',
'add' : 1})
print_action_edit_100 = """ %(field)s:
%(link_edit_100)s
""" % {'field' : _("Field"),
'link_edit_100' : link_edit_100}
print_action_add_subfield = """ %(field)s:
%(link_add_subfields)s
""" % {'field' : _("Field"),
'link_add_subfields' : link_add_subfields}
if add == 1:
link_form = "edit"
else:
link_form = "index"
- result = """ <form action="%(weburl_bibedit)s/%(link_form)s" method="POST">
+ result = """ <form action="%(bibediturl)s/%(link_form)s" method="POST">
%(input_recid)s
%(input_temp)s
%(input_ln)s
<div class="bibEditCellRight" style="font-weight: normal;">
%(print_action_add_subfield)s
%(print_action_edit_100)s
</div>
- """ % {'weburl_bibedit' : weburl_bibedit,
+ """ % {'bibediturl' : bibediturl,
'input_recid' : self.tmpl_input('hidden', recid, 'recid'),
'input_temp' : self.tmpl_input('hidden', 'true', 'temp'),
'input_ln' : self.tmpl_input('hidden', ln, 'ln'),
'link_form' : link_form,
'print_action_add_subfield' : print_action_add_subfield,
'print_action_edit_100' : print_action_edit_100}
else:
link_submit = ''
- link_add_field = self.tmpl_link(ln, _("Add Field"), weburl_bibedit, 'index',
+ link_add_field = self.tmpl_link(ln, _("Add Field"), bibediturl, 'index',
{'recid' : str(recid),
'tag' : tag[:3],
'format_tag' : format_tag,
'temp' : 'true',
'add' : 3}, 'add') + " | "
- link_diplay_verbose = self.tmpl_link(ln, _("Verbose"), weburl_bibedit, 'index',
+ link_diplay_verbose = self.tmpl_link(ln, _("Verbose"), bibediturl, 'index',
{'recid' : str(recid),
'format_tag' : 's',
'temp' : temp})
- link_diplay_marc = self.tmpl_link(ln, "MARC", weburl_bibedit, 'index',
+ link_diplay_marc = self.tmpl_link(ln, "MARC", bibediturl, 'index',
{'recid' : str(recid),
'format_tag' : 'marc',
'temp' : temp})
if temp != "false" and add != 3:
- link_submit = self.tmpl_link(ln, _("Submit"), weburl_bibedit, 'submit', {'recid' : str(recid)}) + " | "
+ link_submit = self.tmpl_link(ln, _("Submit"), bibediturl, 'submit', {'recid' : str(recid)}) + " | "
if add == 3:
link_add_field = ''
result = ''
else:
- link_cancel = self.tmpl_link(ln, _("Cancel"), weburl_bibedit, 'index', {'cancel' : str(recid)})
- link_delete = self.tmpl_link(ln, _("Delete"), weburl_bibedit, 'index', {'delete' : str(recid),
+ link_cancel = self.tmpl_link(ln, _("Cancel"), bibediturl, 'index', {'cancel' : str(recid)})
+ link_delete = self.tmpl_link(ln, _("Delete"), bibediturl, 'index', {'delete' : str(recid),
'confirm_delete' :1,
'temp' : temp})
result = """ <div class="bibEditCellRight" style="font-weight: normal">
&nbsp;%(action)s: %(link_submit)s%(link_cancel)s
&nbsp;%(record)s: %(link_add_field)s%(link_delete)s
&nbsp;%(display)s: %(link_diplay_verbose)s | %(link_diplay_marc)s
</div> """ % {'action' : _("Action"),
'record' : _("Record"),
'display' : _("Display"),
'link_submit' : link_submit,
'link_add_field' : link_add_field,
'link_diplay_verbose' : link_diplay_verbose,
'link_diplay_marc' : link_diplay_marc,
'link_cancel' : link_cancel,
'link_delete' : link_delete}
return """ <table class="bibEditTable">
<tr>
<th colspan="6">
%(record)s #%(recid)s
%(result)s
%(num_field)s
</th>
</tr> """ % {'record' : _("Record"),
'recid' : str(recid),
'result' : result,
'num_field': self.tmpl_input('hidden', str(num_field), 'num_field')}
def tmpl_table_value(self, ln, recid, tag, field, format_tag, type_table, add, form_add=0):
""" Return a field to print in table. """
if form_add == 0:
subfields = field[0]
num_field = field[4]
tag_field = split_tag_to_marc(tag, field[1], field[2])
if format_tag != 's':
print_tag_field = tag_field[:-1]
else:
tag_field = tag_field[:-1] + '%'
if get_name_tag(tag_field) != tag_field:
print_tag_field = get_name_tag(tag_field)
else:
print_tag_field = tag_field[:-1]
len_subfields = len(subfields)
if type_table == "record" and add != 3:
print_link_function = self.tmpl_link_function(ln, len_subfields, recid, tag, num_field, format_tag)
print_tag_form = ''
else:
print_link_function = ''
if add == 1:
print_tag_form = self.tmpl_input('hidden', get_tag_name(print_tag_field), 'tag')
else:
print_tag_form = self.tmpl_input('hidden', get_tag_name(print_tag_field), 'edit_tag')
if add == 1:
len_subfields += 1
type_table = "record"
try:
result = """<td rowspan="%(len_subfields)s" class="bibEditCellTag">
%(print_tag_field)s
%(print_tag_form)s
</td>
%(subfield)s
%(print_link_function)s
""" % {'len_subfields' : len_subfields,
'print_tag_field' : print_tag_field,
'print_tag_form' : print_tag_form,
'subfield' : self.tmpl_subfields(ln, recid,
subfields[0][0], subfields[0][1],
tag_field, format_tag, type_table,
0, num_field, len_subfields),
'print_link_function' : print_link_function}
except IndexError:
raise "FIXME: BibEdit does not seem to be able to edit records with controlfields."
if len_subfields != 1:
num_value = -1
for subfield in subfields:
num_value += 1
if num_value != 0:
result += """ <tr>
%s
</tr> """ % self.tmpl_subfields(ln, recid, subfield[0], subfield[1],
tag_field, format_tag,
type_table, num_value,
num_field, len_subfields)
if add == 1:
result += """ <tr>
%s
</tr> """ % self.tmpl_subfields(ln, add=add)
else:
#click on "add field" link on the top of index page.
result = """ <td class="bibEditCellTag">
- <form action="%(weburl_bibedit)s/index" method="POST">
+ <form action="%(bibediturl)s/index" method="POST">
%(input_recid)s
%(input_temp)s
%(input_ln)s
%(input_add)s
<b>
<a name="add">Tag</a>:
<input type="text" name="add_tag" maxlength="3" size="3" />
ind1:
<input type="text" name="add_ind1" maxlength="1" size="1" />
ind2:
<input type="text" name="add_ind2" maxlength="1" size="1" />
</b>
- </td> """ % {'weburl_bibedit' : weburl_bibedit,
+ </td> """ % {'bibediturl' : bibediturl,
'input_recid' : self.tmpl_input('hidden', recid, 'recid'),
'input_temp' : self.tmpl_input('hidden', 'true', 'temp'),
'input_add' : self.tmpl_input('hidden', 4, 'add'),
'input_ln' : self.tmpl_input('hidden', ln, 'ln'),
'recid' : str(recid),
'ln' : ln}
result += "%s" % self.tmpl_subfields(ln, add=add)
return """ <tr><td></td></tr>
<tr>
%s
</tr> """ % result
def tmpl_table_footer(self, ln, type_table, add=0):
""" Return a footer of table. """
_ = gettext_set_language(ln)
button = _("Done")
if type_table != "record" or add == 3:
form = self.tmpl_input('submit', button, class_css='formbutton') + "</form>"
else:
form = ''
return """ <tr>
<td align="right" colspan="6">
%(form)s
</td>
</tr>
</table> """ % {'form' : form}
def tmpl_subfields(self, ln, recid='', tag_subfield='', value='', tag_field='', format_tag='marc',
type_table='', num_value='', num_field='', len_subfields='', add=0):
""" This function return the content of subfield. """
_ = gettext_set_language(ln)
if add == 1 or add == 3:
print_tag_subfield = " $$ " + self.tmpl_input('text', '', 'add_subcode', 1, 1)
else:
if type_table != "record":
print_tag_subfield = " $$ " + self.tmpl_input('text', tag_subfield,
'subcode%s' % str(num_value),
1, 1)
elif format_tag != 's':
print_tag_subfield = "$$%s" % tag_subfield
else:
print_tag_subfield = "%s%s" % (tag_field[:-1], tag_subfield)
if get_name_tag(print_tag_subfield) != print_tag_subfield:
print_tag_subfield = get_name_tag(print_tag_subfield)
else:
print_tag_subfield = "$$%s" % tag_subfield
value = self.tmpl_clean_value(value, "record")
print_value = ''
print_old_value = ''
print_btn = ''
print_bgcolor = ''
print_old_subcode = ''
if type_table != "record" or add == 3:
if add == 1 or add == 3:
print_value = self.tmpl_input('text', '', 'add_value', size="115%c" % '%')
else:
print_old_subcode = self.tmpl_input('hidden', tag_subfield, 'old_subcode%s' % str(num_value))
print_old_value = self.tmpl_input('hidden', value, 'old_value%s' % str(num_value))
if len(value) < 75:
print_value = self.tmpl_input('text', value, 'value%s' % str(num_value),
style="width:100%;")
else:
print_value = '<textarea name="value%(num_value)s" cols="70" rows="5" style="width:100%%;">%(value)s</textarea>'
print_value %= {'num_value' : str(num_value),
'value' : value}
if len_subfields > 1:
print_btn = "<td>%s</td>" \
% self.tmpl_link(ln, '<img border="0" src="%s" alt="%s" />' % (btn_delete_url, _("Delete")),
- weburl_bibedit, 'edit',
+ bibediturl, 'edit',
{'recid' : str(recid),
'tag' : tag_field[:-1]+ tag_subfield,
'num_field' : num_field,
'format_tag' : format_tag,
'temp' : 'true',
'del_subfield' : 1,
'num_subfield' : num_value})
else:
print_value = value
print_bgcolor = " background: #FFF;"
return """ <td style="background: #F5F5F5" class="admintdright">
%(print_tag_subfield)s
%(print_old_subcode)s
</td>
<td style="padding: 0px 5px 0px 5px; %(print_bgcolor)s width:700px">
<span style="font-size:small;">
%(print_value)s
</span>
%(print_old_value)s
</td>
%(print_btn)s
<td></td> """ % {'print_tag_subfield' : print_tag_subfield,
'print_old_subcode' : print_old_subcode,
'print_bgcolor' : print_bgcolor,
'print_value' : print_value,
'print_old_value' : print_old_value,
'print_btn' : print_btn}
def tmpl_link_function(self, ln, len_subfields, recid, tag, num_field, format_tag):
""" Print button function to edit and delete information. """
_ = gettext_set_language(ln)
btn_edit = self.tmpl_link(ln, '<img style="border:0px" src="%s" alt="%s" />' % (btn_edit_url, _("Edit")),
- weburl_bibedit, 'edit',
+ bibediturl, 'edit',
{'recid' : str(recid),
'tag' : tag,
'num_field' : num_field,
'format_tag' : format_tag,
'temp' : 'true'})
btn_delete = self.tmpl_link(ln, '<img style="border: 0px" src="%s" alt="%s" />' % (btn_delete_url, _("Delete")),
- weburl_bibedit, 'index',
+ bibediturl, 'index',
{'recid' : str(recid),
'delete_tag' : tag,
'num_field' : num_field,
'format_tag' : format_tag,
'temp' : 'true'})
return """ <td rowspan="%(len_subfields)s" style="text-align:center;vertical-align:top;">
%(btn_edit)s
</td>
<td rowspan="%(len_subfields)s" style="text-align:center;vertical-align:top;">
%(btn_delete)s
</td>
""" % {'len_subfields' : len_subfields,
'btn_edit' : btn_edit,
'btn_delete' : btn_delete}
def tmpl_clean_value(self, value, format):
""" This function clean value for HTML interface and inverse. """
if format != "html":
value = value.replace('"', '&quot;')
value = value.replace('<', '&lt;')
value = value.replace('>', '&gt;')
else:
value = value.replace('&quot;', '"')
value = value.replace('&lt;', '<')
value = value.replace('&gt;', '>')
return value
def tmpl_warning_temp_file(self, ln):
""" Return a warning message for user who use a temp file. """
_ = gettext_set_language(ln)
return """ <span style="color: #000;
background: #fcc;
padding: 1px;
font-weight: bold;
border-spacing: 3px;
border: 2px solid #900;">
%(message1)s %(message2)s
</span><br/><br/>
""" % {'message1' : _("Your changes are TEMPORARY."),
'message2' : _("To save this record, please click on submit.")}
def tmpl_record_choice_box(self, ln, message):
""" Return a little for; for choice a record to edit. """
_ = gettext_set_language(ln)
if message == 1:
result = """ <span class="errorbox">
<b>
%(message1)s %(message2)s
</b>
</span><br/><br/>
""" % {'message1' : _("This record does not exist."),
'message2' : _("Please try another record ID.")}
elif message == 2:
result = """ <span class="errorbox">
<b>
%(message1)s %(message2)s
</b>
</span><br/><br/>
""" % {'message1' : _("This record is currently being edited by another user."),
'message2' : _("Please try again later.")}
elif message == 3:
result = """ <span class="errorbox">
<b>
%(message1)s
</b>
</span><br/><br/>
""" % {'message1' : _("Cannot edit deleted record.")}
else:
result = ''
- result += """ <form action="%(weburl_bibedit)s/index" method="POST">
+ result += """ <form action="%(bibediturl)s/index" method="POST">
%(input_ln)s
<span style="background-color: #ddd; padding: 5px;">
%(message)s: %(input_recid)s %(input_button)s
</span>
- </form> """ % {'weburl_bibedit' : weburl_bibedit,
+ </form> """ % {'bibediturl' : bibediturl,
'message' : _("Please enter the ID of the record you want to edit"),
'input_ln' : self.tmpl_input('hidden', ln, 'ln'),
'input_recid' : self.tmpl_input('text' , '', 'recid'),
'input_button' : self.tmpl_input('submit', _("Edit"),
class_css='formbutton')}
return result
def tmpl_submit(self, ln):
""" Return a end message of Bibedit. """
_ = gettext_set_language(ln)
out = _("Your modifications have now been submitted. They will be processed as soon as the task queue is empty.") + '<br /><br />'
out += '<h2>' + _("Edit another record") + '</h2>'
out += self.tmpl_record_choice_box(ln=ln, message=0)
return out
def tmpl_deleted(self, ln, message='', recid='', temp='', format_tag=''):
""" Return a deleted message of Bibedit. """
_ = gettext_set_language(ln)
if message == 1:
return """ %(message)s
<div style="float:left;">
- <form action="%(weburl_bibedit)s/index?delete=%(recid)s" method="POST">
+ <form action="%(bibediturl)s/index?delete=%(recid)s" method="POST">
%(input_ln)s
%(input_button_yes)s
</form>
</div>
<div style="float:left;">
- <form action="%(weburl_bibedit)s/index?recid=%(recid)s&temp=%(temp)s&format_tag=%(format_tag)s" method="POST">
+ <form action="%(bibediturl)s/index?recid=%(recid)s&temp=%(temp)s&format_tag=%(format_tag)s" method="POST">
%(input_ln)s
%(input_button_no)s
</form>
</div>
""" % {'message' : _("Do you really want to delete this record?"),
- 'weburl_bibedit' : weburl_bibedit,
+ 'bibediturl' : bibediturl,
'recid' : str(recid),
'input_ln' : self.tmpl_input('hidden', ln, 'ln'),
'input_button_yes' : self.tmpl_input('submit', _("Yes"), class_css='formbutton'),
'input_button_no' : self.tmpl_input('submit', _("No"), class_css='formbutton'),
'temp' : temp,
'format_tag' : format_tag}
else:
out = _("The record will be deleted as soon as the task queue is empty.") + '<br /><br />'
out += '<h2>' + _("Edit another record") + '</h2>'
out += self.tmpl_record_choice_box(ln=ln, message=0)
return out
def tmpl_link(self, ln, text, url, dest, dict_args='', ancre=''):
""" Return a link. """
if dict_args == '':
link_args = ''
else:
link_args = '?'
list_args = dict_args.items()
for arg in list_args:
link_args += "%(name)s=%(value)s&amp;" % {'name' : str(arg[0]),
'value' : str(arg[1])}
link_args += "ln=%s" % ln
if ancre != '':
ancre = '#' + ancre
return '<a href="%(url)s/%(dest)s%(args)s%(ancre)s">%(text)s</a>' % {'text' : text,
'url' : url,
'dest' : dest,
'args' : link_args,
'ancre' : ancre}
def tmpl_input(self, type_input, value='', name='', maxlength='', size='', class_css='', style=''):
""" Return a input form. """
if value != '':
value = 'value="%s"' % str(value)
if name != '':
name = 'name="%s"' % str(name)
if maxlength != '':
maxlength = 'maxlength="%s"' % str(maxlength)
if size != '':
size = 'size="%s"' % str(size)
if class_css != '':
class_css = 'class="%s"' % str(class_css)
if style != '':
style = 'style="%s"' % str(style)
out = '<input type="%(type)s" %(name)s %(value)s %(size)s %(maxlength)s %(class_css)s %(style)s />'
out %= {'type' : type_input,
'value' : value,
'name' : name,
'maxlength' : maxlength,
'size' : size,
'class_css' : class_css,
'style' : style}
return out
diff --git a/modules/bibedit/web/admin/bibeditadmin.py b/modules/bibedit/web/admin/bibeditadmin.py
index 404d489a7..5fb5fe2c1 100644
--- a/modules/bibedit/web/admin/bibeditadmin.py
+++ b/modules/bibedit/web/admin/bibeditadmin.py
@@ -1,136 +1,136 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio BibEdit Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
-from invenio.config import CFG_SITE_LANG, weburl
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL
from invenio.webpage import page
from invenio.webuser import getUid, page_not_authorized
from invenio.bibedit_engine import perform_request_index, perform_request_edit, perform_request_submit
from invenio.search_engine import record_exists
from invenio.access_control_engine import acc_authorize_action
from invenio.messages import gettext_set_language, wash_language
from invenio.urlutils import wash_url_argument, redirect_to_url
-navtrail = """ <a class="navtrail" href=\"%s/help/admin\">Admin Area</a> """ % (weburl,)
+navtrail = """ <a class="navtrail" href=\"%s/help/admin\">Admin Area</a> """ % (CFG_SITE_URL,)
def index(req, ln=CFG_SITE_LANG, recid=None, temp="false", format_tag='marc',
edit_tag=None, delete_tag=None, num_field=None, add=0, cancel=0,
delete=0 ,confirm_delete=0, **args):
""" BibEdit Admin interface. """
ln = wash_language(ln)
_ = gettext_set_language(ln)
uid = getUid(req)
recid = wash_url_argument(recid, "int")
add = wash_url_argument(add, "int")
cancel = wash_url_argument(cancel, "int")
delete = wash_url_argument(delete, "int")
confirm_delete = wash_url_argument(confirm_delete, "int")
(auth_code, auth_message) = acc_authorize_action(req,'runbibedit')
if auth_code == 0:
(body, errors, warnings) = perform_request_index(ln, recid, cancel, delete, confirm_delete, uid, temp, format_tag,
edit_tag, delete_tag, num_field, add, args)
else:
return page_not_authorized(req=req, text=auth_message, navtrail=navtrail)
if recid != 0:
title = _("Record") + " #" + str(recid)
if add == 3:
title = _("Record %s - Add a field") % ('#' + str(recid))
else:
title = _("BibEdit Admin Interface")
return page(title = title,
body = body,
errors = errors,
warnings = warnings,
uid = getUid(req),
language = ln,
navtrail = navtrail,
lastupdated = __lastupdated__,
req = req)
def edit(req, recid=None, tag=None, num_field='0', num_subfield=0, format_tag='marc',
del_subfield=None, temp="false", add=0, ln=CFG_SITE_LANG, **args):
""" Edit Field page. """
ln = wash_language(ln)
_ = gettext_set_language(ln)
uid = getUid(req)
recid = wash_url_argument(recid, "int")
num_field = wash_url_argument(num_field, "int")
add = wash_url_argument(add, "int")
num_subfield = wash_url_argument(num_subfield, "int")
(auth_code, auth_message) = acc_authorize_action(req,'runbibedit')
if (auth_code == 0):
if (recid and tag and (record_exists(recid)>0)):
(body, errors, warnings) = perform_request_edit(ln, recid, uid, tag, num_field, num_subfield,
format_tag, temp, del_subfield, add, args)
else:
redirect_to_url(req, 'index?ln=' + ln)
else:
return page_not_authorized(req=req, text=auth_message, navtrail=navtrail)
title = _("Edit record %(x_recid)s, field %(x_field)s") % {'x_recid': '#' + str(recid),
'x_field': '#' + str(tag[:3])}
if add == 1:
title = _("Edit record %(x_recid)s, field %(x_field)s - Add a subfield") % {'x_recid': '#' + str(recid),
'x_field': '#' + str(tag[:3])}
return page(title = title,
body = body,
errors = errors,
warnings = warnings,
uid = getUid(req),
language = ln,
navtrail = navtrail,
lastupdated = __lastupdated__,
req = req)
def submit(req, recid='', ln=CFG_SITE_LANG):
""" Submit temp_record on database. """
ln = wash_language(ln)
_ = gettext_set_language(ln)
uid = getUid(req)
recid = wash_url_argument(recid, "int")
(auth_code, auth_message) = acc_authorize_action(req,'runbibedit')
if auth_code == 0:
if (recid and (record_exists(recid)>0)):
(body, errors, warnings) = perform_request_submit(ln, recid)
else:
redirect_to_url(req, 'index?ln=' + ln)
else:
return page_not_authorized(req=req, text=auth_message, navtrail=navtrail)
return page(title = _("Submit and save record %s") % ('#' + str(recid)),
body = body,
errors = errors,
warnings = warnings,
uid = getUid(req),
language = ln,
navtrail = navtrail,
lastupdated = __lastupdated__,
req = req)
diff --git a/modules/bibformat/doc/admin/bibformat-admin-guide.webdoc b/modules/bibformat/doc/admin/bibformat-admin-guide.webdoc
index 0571473d1..dcf1b4980 100644
--- a/modules/bibformat/doc/admin/bibformat-admin-guide.webdoc
+++ b/modules/bibformat/doc/admin/bibformat-admin-guide.webdoc
@@ -1,1214 +1,1214 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title:BibFormat Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<ul style="list-style-type:None">
<li><strong>1. <a href="#shortIntro">Overview</a></strong>
<ul style="list-style-type:None">
<li>1.1&nbsp;&nbsp;<a href="#philosophy">How BibFormat works</a></li>
<li>1.2&nbsp;&nbsp;<a href="#tutorial">Short Tutorial</a></li>
<li>1.3&nbsp;&nbsp;<a href="#administerWebFile">Administer Through the Web Interface or Through the Configuration files</a></li>
</ul>
</li>
<li><strong>2. <a href="#outputFormats">Configure Output Formats</a></strong>
<ul style="list-style-type:None">
<li>2.1&nbsp;&nbsp;<a href="#addOutputFormat">Add an Output Format</a></li>
<li>2.2&nbsp;&nbsp;<a href="#removeOutputFormat">Remove an Output Format</a></li>
<li>2.3&nbsp;&nbsp;<a href="#rulesOutputFormat">Edit the Rules of an Output Format</a></li>
<li>2.4&nbsp;&nbsp;<a href="#attrsOutputFormat">Edit the Attributes of an Output Format</a></li>
<li>2.5&nbsp;&nbsp;<a href="#dependenciesOutputFormat">Check the Dependencies an Output Format</a></li>
<li>2.6&nbsp;&nbsp;<a href="#validityOutputFormat">Check the Validity an Output Format</a></li>
</ul>
</li>
<li><strong>3. <a href="#formatTemplates">Configure Format Templates</a></strong>
<ul style="list-style-type:None">
<li>3.1&nbsp;&nbsp;<a href="#addFormatTemplate">Add a Format Template</a></li>
<li>3.2&nbsp;&nbsp;<a href="#removeFormatTemplate">Remove a Format Template</a></li>
<li>3.3&nbsp;&nbsp;<a href="#codeFormatTemplate">Edit the Code of a Format Template</a>
<li>3.4&nbsp;&nbsp;<a href="#editFormatTemplate">Basic Editing</a></li>
<li>3.5&nbsp;&nbsp;<a href="#elementsInFormatTemplate">Use Format Elements</a></li>
<li>3.6&nbsp;&nbsp;<a href="#previewFormatTemplate">Preview a Format Template</a></li>
<li>3.7&nbsp;&nbsp;<a href="#internationalizationTemplate">Internationalization (i18n)</a></li>
<li>3.8&nbsp;&nbsp;<a href="#escapeFormatTemplate">Escaping special HTML/XML characters</a></li>
<li>3.9&nbsp;&nbsp;<a href="#attrsFormatTemplate">Edit the Attributes of a Format Template</a></li>
<li>3.10&nbsp;<a href="#dependenciesFormatTemplate">Check the Dependencies of a Format Template</a></li>
<li>3.11&nbsp;<a href="#validityFormatTemplate">Check the Validity a Format Template</a></li>
<li>3.12&nbsp;<a href="#xslFormatTemplate">XSL Format Templates</a></li>
</ul>
</li>
<li><strong>4. <a href="#FormatElements">Configure Format Elements</a></strong>
<ul style="list-style-type:None">
<li>4.1&nbsp;&nbsp;<a href="#addFormatElement">Add a Format Element</a></li>
<li>4.2&nbsp;&nbsp;<a href="#removeFormatElement">Remove a Format Element</a></li>
<li>4.3&nbsp;&nbsp;<a href="#codeFormatElement">Edit the Code of a Format Element</a></li>
<li>4.4&nbsp;&nbsp;<a href="#previewFormatElement">Preview a Format Element</a></li>
<li>4.5&nbsp;&nbsp;<a href="#internationalizationFormatElement">Internationalization (i18n)</a></li>
<li>4.6&nbsp;&nbsp;<a href="#escapeFormatElement">Escaping special HTML/XML characters</a></li>
<li>4.7&nbsp;&nbsp;<a href="#attrsFormatElement">Edit the Attributes of a Format Element</a></li>
<li>4.8&nbsp;&nbsp;<a href="#dependenciesFormatElement">Check the Dependencies of a Format Element</a></li>
<li>4.9&nbsp;&nbsp;<a href="#validityFormatElement">Check the Validity of a Format Element</a></li>
<li>4.10&nbsp;<a href="#browseDocFormatElement">Browse the Format Elements Documentation</a></li>
</ul>
</li>
<li><strong>5. <a href="#KBs">Configure Knowledge Bases</a></strong>
<ul style="list-style-type:None">
<li>5.1&nbsp;&nbsp;<a href="#addKB">Add a Knowledge Base</a></li>
<li>5.2&nbsp;&nbsp;<a href="#removeKB">Remove a Knowledge Base</a></li>
<li>5.3&nbsp;&nbsp;<a href="#addMappingKB">Add a Mapping</a></li>
<li>5.4&nbsp;&nbsp;<a href="#removeMappingKB">Remove a Mapping</a></li>
<li>5.5&nbsp;&nbsp;<a href="#editMappingKB">Edit a Mapping</a></li>
<li>5.6&nbsp;&nbsp;<a href="#attrsKB">Edit the Attributes of a Knowledge Base</a></li>
<li>5.7&nbsp;&nbsp;<a href="#validityKB">Check the Dependencies a Knowledge Base</a></li>
</ul style="list-style-type:None">
</li>
<li><strong>6. <a href="#BibReformat">Run BibReformat</a></strong>
<ul style="list-style-type:None">
<li>6.1&nbsp;&nbsp;<a href="#runBibReformat">Run BibReformat</a></li>
</ul>
</li>
<li><strong>7. <a href="#Appendix">Appendix</a></strong>
<ul style="list-style-type:None">
<li>7.1&nbsp;&nbsp;<a href="#marcNotation">MARC Notation in Formats</a></li>
<li>7.2&nbsp;&nbsp;<a href="#migration">Migrating from Previous BibFormat</a></li>
<li>7.3&nbsp;&nbsp;<a href="#integrationDreamweaver">Integrating BibFormat into Dreamweaver MX</a></li>
<li>7.4&nbsp;&nbsp;<a href="#faq">FAQ</a></li>
</ul>
</li>
</ul>
<style type="text/css">
<!--
.outputFormatBox {
font-weight: bold;
color: #990000;
background-color: #F4CBCC;
border: 2px dotted #FF0000;
}
.outputFormatBoxMain {
font-weight: bold;
color: #990000;
background-color: #F4CBCC;
border: 1px solid #FF0000;
padding: 4px;
}
.formatTemplateBox {
font-weight: bold;
color: #003300;
background-color: #DEFDBB;
border: 2px dotted #009900;
}
.formatTemplateBoxMain {
font-weight: bold;
color: #003300;
background-color: #DEFDBB;
border: 1px solid #009900;
padding: 5px;
}
.formatElementBox {
font-weight: bold;
color: #000066;
background-color: #CCFFFF;
border: 2px dotted #0066CC;
}
.formatElementBoxMain {
font-weight: bold;
color: #000066;
background-color: #CCFFFF;
border: 1px solid #0066CC;
padding: 5px;
}
-->
</style>
<h2><a name="shortIntro">1. Overview</a></h2>
<h3><a name="philosophy">1.1 How BibFormat Works</a></h3>
<p>BibFormat is in charge of formatting the bibliographic records that
are displayed to your users. It is called by the search engine when it has to
format a record.</p>
<p>As you might need different kind of formatting depending
on the type of record, but potentially have a huge amount of records in your database, you cannot specify
for each of them how they should look. Instead BibFormat uses a rule-based decision process
to decide how to format a record.<br/>
The best way to understand how BibFormat works is to have a look at
a typical workflow:</p>
<table width="90%" border="0" cellspacing="5" cellpadding="0">
<tr>
<td colspan="2"><span style="white-space: nowrap;">Step 1:</span></td>
</tr>
<tr valign="top">
<td>
- <img src="<WEBURL>/img/admin/bibformat-guide-url_bar.png" alt="http://cdsweb.cern.ch/search?recid=946417&amp;ln=en&amp;of=hd"/>
+ <img src="<CFG_SITE_URL>/img/admin/bibformat-guide-url_bar.png" alt="http://cdsweb.cern.ch/search?recid=946417&amp;ln=en&amp;of=hd"/>
</td>
<td width="50%">When CDS Invenio has to display a record, it
asks BibFormat to format the record with the given output format
and language. For example here the requested output format is
<span class="outputFormatBox">hd</span>, which is a short code
for &quot;HTML Detailed&quot;. This means that somehow a user arrived on
the page of the record and asked for a detailed view of the
record.</td>
</tr>
<tr>
<td colspan="2"><hr/><span style="white-space: nowrap;">Step 2:</span></td>
</tr><tr valign="top">
- <td><img src="<WEBURL>/img/admin/bibformat-guide-bfo_hd_rules.png" alt="1. Use Template [Picture HTML Detailed] if tag [980__a] is equal to [PICTURE] 2. Use Template [Thesis HTML detailed] if tag [980__a] is equal to [THESIS] 3. By default use [Default HTML Detailed]"/>
+ <td><img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_hd_rules.png" alt="1. Use Template [Picture HTML Detailed] if tag [980__a] is equal to [PICTURE] 2. Use Template [Thesis HTML detailed] if tag [980__a] is equal to [THESIS] 3. By default use [Default HTML Detailed]"/>
<td>
Beside is a screenshot of the "hd" or "HTML Detailed" output format.
You can see that the output format does not specify how to format the record, but
contains a set of rules which define which template must be used.<br/>
The rules are evaluated from top to bottom.
Each rule defines a condition on a field of the record, and a format template to use to
format the record if the condition matches.
Let's say that the field 980__a of the record is equal to
&quot;Picture&quot;. Then first rules matches, and format template
<span class="formatTemplateBox">Picture HTML Detailed</span> is
used for formatting by BibFormat.<br/>
You can add, remove or edit output formats <a href="bibformatadmin.py/output_formats_manage">here</a></td>
</tr>
<tr>
<td colspan="2"><hr/><span style="white-space: nowrap;">Step 3:</span></td>
</tr>
<tr valign="top">
<td>
<div class="formatTemplateBoxMain"><code>
&lt;h1 align=&quot;center&quot;&gt;&lt;BFE_MAIN_TITLE/&gt;&lt;/h1&gt;<br />
&lt;p align=&quot;center&quot;&gt;<br />
<span class="formatElementBox">&lt;BFE_AUTHORS separator=&quot;; &quot;
link=&quot;yes&quot;/&gt;</span>&lt;br/&gt;<br />
&lt;BFE_DATE format=&quot;%d %B %Y&quot;&gt; .- &lt;BFE_NB_PAGES suffix=&quot;p&quot;&gt;<br />
&lt;/p&gt;</code></div></td>
<td>We see an extract of the Picture HTML Detailed format on the right,
as it is shown in the template editor. As you can see it
is mainly written using HTML. There are however some tags that
are not part of standard HTML. Those tags that starts with
<em>&lt;BFE_</em> are placeholders for the record values. For
example &lt;BFE_MAIN_TITLE/&gt; tells BibFormat to write the title
of the record. We call these tags &quot;elements&quot;. Some
elements have parameters. This is the case of the <span
class="formatElementBox">&lt;BFE_AUTHORS&gt; </span> element,
which can take <em>separator</em> and <em>link</em> as
parameters. The value of separator will be used to separate
authors' names and the link parameter tells if links to authors'
websites have to be created.
All elements are described in the <a href="bibformatadmin.py/format_elements_doc">elements documentation</a>.<br/>
You can add, remove or edit format templates <a href="bibformatadmin.py/format_templates_manage">here</a>.
<p>
In addition to this modified HTML language, BibFormat also supports XSL stylesheets as format templates.
Read the <a href="#xslFormatTemplate">XSL Format Templates</a> section to learn more about XSLT support for your format templates.
</p>
</td>
</tr>
<tr><td colspan="2"><hr/><span style="white-space: nowrap;">Step 4:</span></td></tr>
<tr valign="top">
<td><div class="formatElementBoxMain">
<code>def format(bfo, separator='; ', link='no'):<br />
&nbsp;&nbsp;&nbsp;&quot;&quot;&quot;<br />
&nbsp;&nbsp;&nbsp;Prints the list of authors for the record<br /><br />
&nbsp;&nbsp;&nbsp;@param separator a character to separate the authors<br />
&nbsp;&nbsp;&nbsp;@param link if 'yes' print HTML links to authors<br />
&nbsp;&nbsp;&nbsp;&quot;&quot;&quot;<br />
&nbsp;&nbsp;&nbsp;authors = bfo.fields(&quot;100__a&quot;)<br />
&nbsp;&nbsp;&nbsp;if link == 'yes':<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;authors = map(lambda x: '&lt;a href="'+weburl+'/search?f=author&p='\<br/>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;authors = map(lambda x: '&lt;a href="'+CFG_SITE_URL+'/search?f=author&p='\<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ quote(x) +'"&gt;'+x+'&lt;/a&gt;', authors)<br />
&nbsp;&nbsp;&nbsp;return authors.split(separator)</code>
</div></td>
<td>A format element is written in Python. It acts as a bridge
between the record in the database and the format
template. Typically you will not have to write or read format
elements, just call them from the templates. Each element outputs
some text that is written in the template where it is called.<br/>
Developers can add new elements by creating a new file, naming it
with the name of element, and write a Python <code>format</code>
function that takes as parameters the parameters of the elements
plus a special one <code>bfo</code>. Regular Python code can be
used, including import of other modules.</td>
</tr>
</table>
<br />
<p>In summary BibFormat is called by specifying a record and an output
format, which relies on different templates to do the formatting, and
which themselves rely on different format elements. Only developers need to modify
the format elements layer.<p>
<table width="50%" border="0" align="center" cellpadding="0" cellspacing="2">
<tr align="center">
<td colspan="4"><div class="outputFormatBoxMain">Output Format</div></td>
</tr>
<tr align="center">
<td colspan="2"><div class="formatTemplateBoxMain">Template</div></td>
<td colspan="2"><div class="formatTemplateBoxMain">Template</div></td>
</tr>
<tr align="center">
<td><div class="formatElementBoxMain">Format Element</div></td>
<td><div class="formatElementBoxMain">Format Element</div></td>
<td><div class="formatElementBoxMain">Format Element</div></td>
<td><div class="formatElementBoxMain">Format Element</div></td>
</tr>
</table>
<p>You should now understand the philosophy behind BibFormat. </p>
<h3><a name="tutorial">1.2 Short Tutorial</a></h3>
<p> Let's try to create our own format.
This format will just print the title of a record.
</p>
<p>First go to the main <a href="bibformatadmin.py">BibFormat admin page</a>.
Then click on the "Manage Ouput Format" links. You will see the list of all output formats:</p>
-<img src="<WEBURL>/img/admin/bibformat-guide-bfo_manage.png" alt="Output formats management page"/>
+<img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_manage.png" alt="Output formats management page"/>
<p>This is were you can delete, create or check output formats.
The menu at the top of the page let you go to other admininistration pages.<br/>
Click on the "Add New Output Format" button at the bottom of the page. You can then fill in some attributes
for the output format. Choose "title" as code, "Only Title" as name and "Prints only title" as description:</p>
-<img src="<WEBURL>/img/admin/bibformat-guide-bfo_attributes.png" alt="Screenshot of the Update Output Format Attributes page" />
+<img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_attributes.png" alt="Screenshot of the Update Output Format Attributes page" />
<p>Leave other fields blank, and click on the button "Update Output format Attributes".<br/> You are then
redirected to the rules editor. Notice the menu at the top which let you close the editor, change the attributes again
and check the output format. However do not click on these links before saving your modification of rules!</p>
-<img src="<WEBURL>/img/admin/bibformat-guide-bfo_rules.png" alt="Output format menu" />
+<img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_rules.png" alt="Output format menu" />
<p>As our format does not need to have a different behaviour depending on the record, we do not need to add new rules to the format. You just need to select a format template in the "By default use" list. However we first have to create our special format template that only print titles. So close the editor using the menu at the top of the page, and in the menu that just appeared instead, click on "Manage Format Templates". In a similar way to output formats, you see the list of format templates.</p>
-<img src="<WEBURL>/img/admin/bibformat-guide-bft_manage.png" alt="Format template management page" />
+<img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bft_manage.png" alt="Format template management page" />
<p>
Click on the "Add New Format Template" button at the bottom of the page. As for the output format, fill in the attributes of the template with name "Title" and any relevant description.
</p>
-<img src="<WEBURL>/img/admin/bibformat-guide-bft_attributes.png" alt="update format template attributes"/>
+<img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bft_attributes.png" alt="update format template attributes"/>
<p>Click on the "Update Output Format Attributes" button. You are redirected to the template editor. The editor is divided in three parts. The upper left part contains the code of the template. The bottom part is a preview of the template. The part on the right side is a short remainder of the format elements you can use in you template. You can hide this documentation by clicking on "Hide Documentation".</p>
-<img src="<WEBURL>/img/admin/bibformat-guide-bft_editor2.png" alt="Format template editor" />
+<img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bft_editor2.png" alt="Format template editor" />
<p>The above screenshot shows the template code already filled in. It calls the <code>BFE_TITLE</code> element. If you do not know the name of the element you want to call, you can search for it using the embedded documentation search. You can try to add other elements into your template, or write some HTML formatting.</p>
<p>When you are satisfied with your template, click on the save button, close the editor and go back to the "Only titles" output format rules editor. There select the template you have just created in the "Use by default" menu and save the ouput format and you are done. </p>
<p>This tutorial does not cover all aspects of the management of formats (For example "Knowledge bases" or internationalization). It also does not show all the power of output formats, as the one we have created simply call a template. However you have seen enough to configure BibFormat trough the web interface. Read the sections below to learn more about it.</p>
<h3><a name="administerWebFile">1.3 Administer Through the Web Interface or Through the Configuration files</a></h3>
<p>BibFormat can be administered in two ways. The first way is to use the provided web interface. It should be the most
convenient way of doing for most users. The web interface is simple to use and provides great tools to manage your formats. Its only limitation concerns the format elements, which cannot be modified using it (But the web interface provide a dynamically generated documentation of your elements). <br/>
The other way to administer BibFormat is to directly modify the configuration files using your preferred text editor. This way of doing can bring much power to advanced users, but requires an access to the server's files. It also requires that the user double-check his modifications, or use the web interface to ensure the validity and correctness of his formats.</p>
<p>In this manual we will show both ways of doing. For each explication we show first how to do it through the web interface, then how to do it by manipulating the configuration files. Non-power users can stop reading as soon as they encounter the text "For developers and adventurers only".</p>
<p>We generally recommend to use the web interface, excepted for writing
format elements.</p>
<h2><a name="outputFormats">2. Output Formats</a></h2>
<p>As you potentially have a huge amount of
bibliographic records, you cannot specify manually for each of them
how it should be formatted. This is why you can define rules that will
allow BibFormat to understand which kind of formatting to apply to a given
record. You define this set of rules in what is called an "output
format".</p>
<p>You can have different output formats, each with its own characteristics.
For example you certainly want that when multiple bibliographic records are
displayed at the same time (as it happens in search results), only
short versions are shown to the user , while a detailed record is
preferable when a single record is displayed, whatever the type of the record.<br/>
You might also want to
let your users decide which kind of output they want. For example you
might need to display HTML for regular web browsing, but would also
give a BibTeX version of the bibliographic reference for direct
inclusion in a LaTeX document.</p>
<p>To summarize, an output format groups similar kind of formats, specifying which kind
of formatting has to be done, but not how it has to be done.</p>
<h3><a name="addOutputFormat">2.1 Add an Output Format</a></h3>
<p>To add a new output format, go to the <a href="bibformatadmin.py/output_formats_manage">Manage Output Formats</a> page and click on the "Add New Output Format" button at the bottom of the page. The format has been created. You can then specify the attributes of the output format. See <a href="#attrsOutputFormat">Edit the Attributes of an Output Format</a> to learn more about it.<p>
<strong>For developers and adventurers only:</strong>
<p>Alternatively you can directly add a new output format file into the
/etc/bibformat/outputs/ directory of your CDS Invenio installation, if you have
access to the server's files. Use the format extension .bfo for your file.</p>
<p>You should also check that user <code>www-data</code> has read/write access to the file,
if you want to be able to modify the rules through the web interface.</p>
<h3><a name="removeOutputFormat">2.2 Remove an Output Format</a></h3>
<p>To remove an output format, go to the <a href="bibformatadmin.py/output_formats_manage">Manage Output Formats</a> page and click on the "Delete" button facing the output format you want to delete. If you cannot click on the button (the button is not enabled), this means that you do not have sufficent priviledge to do so (Format is protected. Contact the administrator of the system).</p>
<strong>For developers and adventurers only:</strong>
<p>You can directly remove an output format from the /etc/bibformat/outputs/ directory of your CDS Invenio installation.
However you must make sure that it is removed from the tables <code>format</code> and <code>formatname</code> in the database, so that other modules know that it is not longer available.</p>
<h3><a name="rulesOutputFormat">2.3 Edit the Rules of an Output Format</a></h3>
<p>When you create a new output format, you can at first only specify the default template,
that is the one which is used when all rules fail. In the case of a basic output format,
this is enough. You can however add other rules, by clicking on the "Add New Rule" button.<br/>
Once you have added a rule, you can fill it with a condition, and a template that should be used
if the condition is true. For example the rule</p>
- <img src="<WEBURL>/img/admin/bibformat-guide-bfo_edit_rule.png" alt="Rule: Use template [Picture HTML Detailed] if field [980__a] is equal to [PICTURE]"/>
+ <img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_edit_rule.png" alt="Rule: Use template [Picture HTML Detailed] if field [980__a] is equal to [PICTURE]"/>
<p> will use template named "Picture HTML Detailed" if the field <code>980__a</code> of the record to format is equal to "Picture".
Note that text "PICTURE" will match any letter case like "picture" or "Picture".
Leading and trailing spaces are ignored too (" Picture " will match "PICTURE").
<br/><b>Tips:</b> you can use a regular expression as text. For example "PICT.*" will match "pictures"
and "PICTURE".</p>
- <p><div style="float:right"><img src="<WEBURL>/img/admin/bibformat-guide-bfo_edit_rule2.png" alt="Reorder rules using arrows"/></div>
+ <p><div style="float:right"><img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_edit_rule2.png" alt="Reorder rules using arrows"/></div>
The above configuration will use format template "Default HTML Detailed" if all above rules fail (in that case
if field 980__a is different from "PICTURE"). If you have more rules, you decide in which order the conditions are evaluated. You can reorder rules by clicking on the small arrows on the left of the rules.
</p>
<p>Note that when you are migrating your output formats from the old PHP BibFormat, you might not have translated all the formats to which your output formats refers. In that case you should use <code>defined in old BibFormat</code> option in the format templates menu, to make BibFormat understand that a match for this rule must trigger a call to the <i>Behaviour</i> of the old BibFormat. See section on <a href="#runSideBySide">Run old and new formats side by side</a> for more details on this.</p>
<strong>For developers and adventurers only:</strong>
<p>To write an output format, use the following syntax:<br/>
First you
define which field code you put as the conditon for the rule.
You suffix it with a column. Then on next lines, define the values of
the condition, followed by --- and then the filename of the template
to use:</p>
<pre>
tag 980__a:
PICTURE --- PICTURE_HTML_BRIEF.bft
PREPRINT --- PREPRINT_HTML_BRIEF.bft
PUBLICATION --- PUBLICATION_HTML_BRIEF.bft
</pre>
<p>
This means that if value of field 980__a is equal to PICTURE, then we
will use format template PICTURE_HTML_BRIEF.bft. Note that you must
use the filename of the template, not the name. Also note that spaces
at the end or beginning are not considered. On the following lines,
you can either put other conditions on tag 980__a, or add another tag on
which you want to put conditions.</p>
<p>At the end you can add a default condition:</p>
<pre>
default: PREPRINT_HTML_BRIEF.bft
</pre>
<p>which means that if no condition is matched, a format suitable for
Preprints will be used to format the current record.<p>
<p>The output format file could then look like this:</p>
<pre>
tag 980__a:
PICTURE --- PICTURE_HTML_BRIEF.bft
PREPRINT --- PREPRINT_HTML_BRIEF.bft
PUBLICATION --- PUBLICATION_HTML_BRIEF.bft
tag 8560_f:
.*@cern.ch --- SPECIAL_MEMBER_FORMATTING.bft
default: PREPRINT_HTML_BRIEF.bft
</pre>
<p> You can add as many rules as you want. Keep in mind that they are read
in the order they are defined, and that only first rule that
matches will be used.
Notice the condition on tag 8560_f: it uses a regular expression to
match any email address that ends with @cern.ch (the regular
expression must be understandable by Python)
</p>
<h3><a name="attrsOutputFormat">2.4 Edit the Attributes of an Output Format</a></h3>
<p>An output format has the following attributes:
<ul>
<li><code>code</code>: a short identifier that is used to identify the output format. It must be unique and contain a maximum of 6 letters. Note that the <b>code is not case sensitive</b> ("HB" is equal to "hb").</li>
<li><code>content type</code>: this is the content type of the format, specified in Mime. For example if you were to produce an Excel output, you could use <code>application/ms-excel</code> as content type. If a content type is specified, CDS Invenio will not print the usual header and footerfor the page, but will trigger a download in the client's browser when viewing the page (Unless the browser handles this content type).</li>
<li><code>name</code>: a generic name to display in the interface for this output format.</li>
<li>(*) <code>name</code>: internationalized names for the output format, used for displaying localized name in the search interface.</li>
<li><code>description</code>: an optional description for the output format.</li>
</ul></p>
<p><b>Please read this information regarding output format codes:</b>
There are some reserved codes that you should not use, or at least be aware of when choosing a code for your
output format. The table below summarizes these special words:
</p>
<p align="center">
<small>
<table width="40%" border="1">
<tr>
<th>Code</th><th>Purpose</th>
</tr><tr>
<td>HB</td><td>Used for displaying list of results of a search.</td>
</tr><tr>
<td>HD</td><td>Used when no format is specified when viewing a record.</td>
</tr><tr>
<td>HM</td><td>Used for Marc output. The format is special in the sense that it filters
fields to display according to the 'ot' GET parameter of the HTTP request.</td>
</tr><tr>
<td>Starting with letter 't'</td><td>Used for displaying the value of the field specified by the 'ot' GET parameter of the HTTP request.</td>
</tr><tr>
<td>Starting with 3 digits</td><td>Used for displaying the value of the field specified by the digits.</td>
</tr>
</table>
</small>
</p>
<strong>For developers and adventurers only:</strong>
<p>Excepted for the code, output format attributes cannot be changed in the output format file. These
attributes are saved in the database. As for the <code>code</code>, it is the name of the output format file,
without its <code>.bfo</code> extension. If you change this name, do not forget to propagate the modification in the database.</p>
<h3><a name="dependenciesOutputFormat">2.5 Check the Dependencies an Output Format</a></h3>
<p>To check the dependencies of an output format on format templates, format elements and tags,
go to the <a href="bibformatadmin.py/output_formats_manage">Manage Output Formats</a> page, click on
the output format you want to check, and then in the menu click on "Check Dependencies".</p>
- <img src="<WEBURL>/img/admin/bibformat-guide-bfo_check_deps.png" alt="Check Dependencies menu"/>
+ <img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_check_deps.png" alt="Check Dependencies menu"/>
<p>The next page shows you:
<ul> <li>the format templates which might be called by the rules of the output format</li>
<li>the elements used in each of these templates</li>
<li>the Marc tags involved in these elements</li>
</ul>
Note that some Marc tags might be omitted.</p>
<h3><a name="validityOutputFormat">2.6 Check the Validity an Output Format</a></h3>
<p>To check the validity of an output format, simply go to the <a href="bibformatadmin.py/output_formats_manage">Manage Output Formats</a> page, and look at the column 'status' for the output format you want to check. If message "Ok" is there,
then no problem was found with the output format. If message 'Not Ok' is in the column, click on it to see
the problems that have been found for the output format.</p>
<h2><a name="formatTemplates">3. Format Templates</a></h2>
<p>A format template defines how a record should be formatted. For example it specifies which fields of the record are to be displayed, in which order and with which visual attributes. Basically the format template is written in HTML, so that it is easy for anyone to edit it. BibFormat also has support for XSLT for formatting. Read more <a href="#xslFormatTemplate">about XSL format templates here</a>.</p>
<h3><a name="addFormatTemplate">3.1 Add a Format Template</a></h3>
<p>To add a new format template, go to the <a href="bibformatadmin.py/format_templates_manage">Manage Format Templates</a> page and click on the "Add New Format Template" button at the bottom of the page. The format has been created. You can then specify the attributes of the format template, or ask to make a copy of an existing format.
See <a href="#attrsFormatTemplate">Edit the Attributes of a Format Template</a> to learn more about editing the attributes.<p>
<strong>For developers and adventurers only:</strong>
<p>Alternatively you can directly add a new format template file into the
/etc/bibformat/format_templates/ directory of your CDS Invenio installation, if you have
access to the server's files. Use the format extension .bft for your file.</p>
<p>You should also check that user <code>www-data</code> has read/write access to the file,
if you want to be able to modify the code and the attributes of the template through the web interface.</p>
<h3><a name="removeFormatTemplate">3.2 Remove a Format Template</a></h3>
<p>To remove a format template, go to the <a href="bibformatadmin.py/format_templates_manage">Manage Format Templates</a> page and click on the "Delete" button facing the format template you want to delete. If you cannot click on the button (the button is not enabled), this means that you do not have sufficent priviledge to do so (Format is protected. Contact the administrator of the system).</p>
<strong>For developers and adventurers only:</strong>
<p>You can directly remove the format template from the /etc/bibformat/format_templates/ directory of your CDS Invenio installation.</p>
<h3><a name="codeFormatTemplate">3.3 Edit the Code of a Format Template</a></h3>
<p>You can change the formatting of records by modifying the code of a template.
<p>To edit the code of a format template
go to the <a href="bibformatadmin.py/format_templates_manage">Manage Format Templates</a> page. Click on
the format template you want to edit to load the template editor.</p>
<p>The format template editor contains three panels. The left upper panel is the code editor. This is were
you write the code that specifies the formatting of a template. The right-most panel is a short documentation
on the "bricks" you can use in your format template code. The panel at the bottom of the page allows you to preview the template. </p>
- <img src="<WEBURL>/img/admin/bibformat-guide-bft_editor.png" alt="Template Editor Page"/>
+ <img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bft_editor.png" alt="Template Editor Page"/>
<p>The following sections explain how to write the code that specifies the formatting.</p>
<h4><a name="editFormatTemplate">3.4 Basic Editing</a></h4>
<p>The first thing you have to know before editing the code is that everything you write in the
code editor is printed as such by BibFormat. Well almost everything (as you will discover later).</p>
<p>For example if you write "My Text", then for every record the output will be "My Text". Now let's say
you write "&lt;b&gt;My Text&lt;/b&gt;": the output will still be "&lt;b&gt;My Text&lt;/b&gt;", but as we display in a web browser, it will look like
"<b>My Text</b>" (The browser interprets the text inside tags &lt;b&gt;&lt;/b&gt; as "bold". Also note that the look may depend on the CSS style of your page).</p>
<p>Basically it means that you can write HTML to do the formatting. If you are not experienced with HTML you can use an HTML editor to create your layout, and the copy-paste the HTML code inside the template.</p>
<p>Do not forget to save your work by clicking on the save button before you leave the editor!</p>
<strong>For developers and adventurers only:</strong>
<p>
You can edit the code of a template using exactly the same syntax as in the web interface. The code of the template
is in the template file located in the /etc/bibformat/format_templates/ directory of your CDS Invenio installation. You just
have to take care of the attributes of the template, which are saved in the same file as the code. See <a href="#attrsFormatTemplate">Edit the Attributes of a Format Template</a> to learn more about it.
</p>
<h4><a name="elementsInFormatTemplate">3.5 Use Format Elements</a></h4>
<p>To add a dynamic behaviour to your format templates, that is display for example a different title
for each record or a different background color depending on the type of record, you can use the format elements.</p>
<p>Format elements are the smart bricks you can copy-paste in your code to get the attributes of template
that change depending on the record. A format element looks like a regular HTML tag. </p>
<p>For example, to print
the title of a record, you can write <code>&lt;BFE_TITLE /&gt;</code> in your template code where you want to diplay the title</p>
<p>Format elements can take values as parameters. This allows to customize the behaviour of an element. For example you can write <code>&lt;BFE_TITLE prefix="Title: " /&gt;</code>, and BibFormat will take care of printing the title for you, with prefix "Title: ". The difference between <code>Title: &lt;BFE_TITLE /&gt;</code> and <code>&lt;BFE_TITLE prefix="Title: " /&gt;</code> is that the first option will always write "Title: " while the second one will only print "Title: " if there exist a title for the record in the database. Of course there are chances that there is always a title for each record, but this can be useful for less common fields.</p>
<p>Some parameters are available for all elements. This is the case for the following ones:
<ul>
<li><code>prefix</code>: a prefix printed only if the record has a value for the element.</li>
<li><code>suffix</code>: a suffix printed only if the record has a value for the element.</li>
<li><code>default</code>: a default value printed if the record has no value for the element. In that case <code>prefix</code> and <code>suffix</code> are not printed.</li>
</ul>
</p>
<p>Some parameters are specific to elements. To get information on all available format elements you can read the <a href="bibformatadmin.py/format_elements_doc">Format Elements Documentation</a>, which is generated dynamically for all existing elements. it will show you what the element do and what parameters it can take.</p>
<p>While format elements looks like HTML tags, they differ in the followings ways from traditional ones:
<ul>
<li>A format element is a single tag: you cannot have <code>&lt;BFE_TITLE &gt;some text&lt;BFE_TITLE /&gt;</code> but only <code>&lt;BFE_TITLE /&gt;</code>.</li>
<li>The values of the parameters accept any characters, including &lt; and &gt;. The only limitation is that you cannot use the type of quotes that delimit that value: you can have for example <code>&lt;BFE_TITLE someParam="a lot of single quotes ' ' ' ' "/&gt;</code> or <code>&lt;BFE_TITLE someParam='a lot of double quotes " " " '/&gt;</code>, but not <code>&lt;BFE_TITLE someParam="a lot of same quotes as delimiter " " " "/&gt;</code>.</li>
<li>Format elements names always start with <code>BFE_</code>.</li>
<li>Format element can expand on multiple lines.</li>
</ul>
</p>
<p><b>Tips:</b> you can use the special element <code>&lt;BFE_FIELD tag="" /&gt;</code> to print the value
of any field of a record in your templates. This practice is however not
recommended because it would necessitate to revise all format
templates if you did change the meaning of the MARC code schema.</p>
<h4><a name="previewFormatTemplate">3.6 Preview a Format Template</a></h4>
<p>To preview a format template go to the <a href="bibformatadmin.py/format_templates_manage">Manage Format Templates</a> page and click on the format template you want to preview to open the template editor. The editor contains a preview panel at the bottom of the page.</p>
- <img src="<WEBURL>/img/admin/bibformat-guide-bft_preview.png" alt="Preview Panel"/>
+ <img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bft_preview.png" alt="Preview Panel"/>
<p>Simply click on " Reload Preview" button to preview the template (you do not need to save the code before previewing).<br/>
Use the "Language" menu to preview the template in a given language</p>
<p>You can fill in the "Search Pattern" field to preview a specific record. The search pattern uses exactly the same
syntax as the one used in the web interface. The only difference with the regular search engine is that only the first matching record is shown.</p>
<strong>For developers and adventurers only:</strong>
<p>If you do not want to use the web interface to edit the templates but still would like to get previews, you can open the preview frame of any format in a new window/tab. In this mode you get a preview of the template (if it is placed in the /etc/bibformat/format_templates/ directory of your CDS Invenio installation). The parameters of the preview are specified in the url: <ul>
<li><code>bft</code>: the filename of the format template to preview</li>
<li><code>ln</code>: the language to use for the preview</li>
<li><code>pattern_for_preview</code>: the search pattern to use for the preview</li>
</ul></p>
<h4><a name="internationalizationTemplate">3.7 Internationalization (i18n)</a></h4>
<p>You can add translations to your format templates. To do so enclose the text you want to localize
with tags corresponding to the two letters of the language. For example if we want to localize "title", write <code>&lt;en&gt;Title&lt;/en&gt;</code>. Repeat this for each language in which you want to make "title" available: <code>&lt;en&gt;Title&lt;/en&gt;&lt;fr&gt;Titre&lt;/fr&gt;&lt;de&gt;Titel&lt;/de&gt;</code>.
Finally enclose everything with <code>&lt;lang&gt; &lt;/lang&gt;</code> tags: <code>&lt;lang&gt;&lt;en&gt;Title&lt;/en&gt;&lt;fr&gt;Titre&lt;/fr&gt;&lt;de&gt;Titel&lt;/de&gt;&lt;/lang&gt;</code></p>
<p>For each &lt;lang&gt; group only the text in the user's language is displayed. If user's language is not
available in the &lt;lang&gt; group, your default CDS Invenio language is used.</p>
<h4><a name="escapeFormatTemplate">3.8 Escaping special HTML/XML characters</a></h4>
<p>By default, BibFormat escapes all values returned by format
elements. As a format template designer, you can assume in almost all
cases that the values you get from a format element will be escaped
for you. For special cases, you can set the parameter
<code>escape</code> of the element to '0' when calling it, to make
BibFormat understand that it must not escape the values of the
element, or to '1' to force the escaping. </p>
<p>
For example
<code>&lt;bfe_abstract /&gt;</code> will return:<br/>
<code>[...]We find that for spatially-flat cosmologies, background lensing</code><br/>
<code>clusters with reasonable mass-to-light ratios lying in the</code><br/>
<code>redshift range 0<span style="color:red;">&amp;lt;</span>1 are strongly excluded, [...]</code><br/><br/>
while <code>&lt;bfe_abstract escape="0"/&gt;</code> will return:<br/>
<code>[...]We find that for spatially-flat cosmologies, background lensing</code><br/>
<code>clusters with reasonable mass-to-light ratios lying in the</code><br/>
<code>redshift range 0<span style="color:red;">&lt;</span>1 are strongly excluded, [...]</code><br/><br/>
</p>
<p>In most cases, you will not set <code>escape</code> to 1, nor 0, but
just let the developer of the element take care of that for you.</p>
<p>Please note that values given in special parameters
<code>prefix</code>, <code>suffix</code>, <code>default</code> and
<code>nbMax</code> are never escaped, whatever the value of
<code>escape</code> is (but other parameters will). You have to take
care of that in your format template, as well as of all other values that
are not returned by the format elements.</p>
<h3><a name="attrsFormatTemplate">3.9 Edit the Attributes of a Format Template</a></h4>
<p>To edit the attributes of a format template
go to the <a href="bibformatadmin.py/format_templates_manage">Manage Format Templates</a> page, click on
the format template you want to edit, and then in the menu click on "Modify Template Attributes".</p>
<p>
A format template contains two attributes:
<ul>
<li><code>Name</code>: the name of the template</li>
<li><code>Description</code>: a short description of the template</li>
</ul>
<p>Note that changing these parameters has no impact on the formatting. Their purpose in only to
document the template.</p>
<p>If the name you have chosen already exists for another template, you name will be suffixed with an integer so that the name is unique.</p>
<p>You should also be aware that if you change the name of a format template, all output formats that were linking to this template will be changed to match the new name.</p>
<strong>For developers and adventurers only:</strong>
<p>You can change the attributes of a template by editing its file in the /etc/bibformat/format_templates/ directory of your CDS Invenio installation. The attributes must be enclosed with tags <code>&lt;name&gt; &lt;/name&gt;</code> and <code>&lt;description&gt; &lt;/description&gt;</code> and should ideally be placed at the beginning of the file.</p>
<p>Also note that the admin web interface tries to keep the name of the template in sync with the filename of the template. If the name is changed through the web interface, the filename of the template is changed, and all output formats that use this template are updated. You have to do update output formats manually if you change the filename of the template without the web interface.</p>
<h3><a name="dependenciesFormatTemplate">3.10 Check the Dependencies of a Format Template</a></h3>
<p>To check the dependencies of a format template
go to the <a href="bibformatadmin.py/format_template_manage">Manage Format Template</a> page, click on
the format template you want to check, and then in the menu click on "Check Dependencies".</p>
- <img src="<WEBURL>/img/admin/bibformat-guide-bfo_check_deps.png" alt="Check Dependencies menu"/>
+ <img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_check_deps.png" alt="Check Dependencies menu"/>
<p>The next page shows you:
<ul> <li>The output formats that use this format template</li>
<li>the elements used in the template (and Marc tags use in these elements in parentheses)</li>
<li>A summary of all the Marc tags involved in the elements of the template</li>
</ul>
Note that some Marc tags might be omitted.</p>
<h3><a name="validityFormatTemplate">3.11 Check the Validity a Format Template</a></h3>
<p>To check the validity of a format template, simply go to the <a href="bibformatadmin.py/format_templates_manage">Manage Format Templates</a> page, and look at the column 'status' for the format template you want to check. If message "Ok" is there,
then no problem was found with the template. If message 'Not Ok' is in the column, click on it to see
the problems that have been found for the template.</p>
<h3><a name="xslFormatTemplate">3.12 XSL Format Templates</a></h3>
<p>In addition to the HTML-like syntax introduced in previous sections, BibFormat also
has support for server-side XSL transformation. Although you can do all the formatting using this custom HTML syntax, there are
cases where an XSL stylesheet might be preferred. XSLT is for example a natural choice
when you need to output complex XML, especially when your XML has a deep tree structure.
You might also prefer using XSLT if you already feel comfortable with XSL syntax.</p>
<p>XSL format templates are written using regular XSL. The template file has to be placed in the same folder
as regular format template files, and its file extension must be <code>.xsl</code>. The XSL template
are also visible through the web interface, as any regular format template file. However, some
functions like the "Dependencies checker" or the possibility to create a template or edit its attributes are not
available for the XSL templates.</p>
<p>In BibFormat XSL you have access to the following functions, provided you have declared <code>xmlns:fn="http://cdsweb.cern.ch/bibformat/fn"</code> in your stylesheet:</p>
<dl>
<dt><b><code>fn:modification_date(<i>recID</i>)</code></b></dt>
<dd>Returns the record modification date. Eg: <code>&lt;xsl:value-of select="fn:modification_date(445)"/&gt;</code> returns modification date of record 445</dd>
</dl>
<dl>
<dt><b><code>fn:creation_date(<i>recID</i>)</code></b></dt>
<dd>Returns the record creation date. Eg: <code>&lt;xsl:value-of select="fn:creation_date(445)"/&gt;</code> returns creation date of record 445</dd>
</dl>
<dl>
<dt><b><code>fn:eval_bibformat(<i>recID</i>, <i>bibformat_template_code</i>)</code></b></dt>
<dd>Returns the results of the evaluation of the format template code. Eg: <code>&lt;xsl:value-of select="fn:eval_bibformat(marc:controlfield[@tag='001'],'&amp;lt;BFE_SERVER_INFO var=&amp;quot;recurl&amp;quot;>')" /&gt;</code> returns the url of the current record. The parameter <code><i>bibformat_template_code</i></code> is regular code used inside BibFormat format templates, with <code>&lt;</code> escaped as <code>&amp;lt;</code> and <code>"</code>(quotes) escaped as <code>&amp;quot;</code></dd>
</dl>
<p>Finally, please note that you will need to install a supported XSLT parser in order
to format using XSL stylesheets.</p>
<h2><a name="FormatElements">4. Format Elements</a></h2>
<p>Format elements are the bricks used in format templates to provide dynamic content to the formatting process.
Their purpose is to allow non computer literate persons to easily integrate data from the records in the database into their templates. </p>
<p>Format elements are typically written in Python (there is an exception to that point which is dicussed in <a href="#addFormatElement">Add a Format Element</a>). This brings great flexibily and power to the formatting process. This however restricts the creation of format elements to developers.</p>
<h3><a name="addFormatElement">4.1 Add a Format Element</a></h3>
<p>The most typical way of adding a format element is to drop a <code>.py</code> file in the lib/python/invenio/bibformat_elements directory of your CDS Invenio installation. See <a href="#codeFormatElement">Edit the Code of a Format Element</a> to learn how to implement an element.</p>
- <p>The most simple way to add a format element is to add a en entry in the "<a href="<WEBURL>//admin/bibindex/bibindexadmin.py/field">Logical Fields</a>" management interface of the BibIndex module. When BibFormat cannot find the Python format element corresponding to a given name, it looks into this table for the name and prints the value of the field declared for this name. This lightweight way of doing is straightforward but does not allow complex handling of the data (it limits to printing the value of the field, or the values of the fields if multiple fields are declared under the same label).</p>
+ <p>The most simple way to add a format element is to add a en entry in the "<a href="<CFG_SITE_URL>//admin/bibindex/bibindexadmin.py/field">Logical Fields</a>" management interface of the BibIndex module. When BibFormat cannot find the Python format element corresponding to a given name, it looks into this table for the name and prints the value of the field declared for this name. This lightweight way of doing is straightforward but does not allow complex handling of the data (it limits to printing the value of the field, or the values of the fields if multiple fields are declared under the same label).</p>
<h3><a name="removeFormatElement">4.2 Remove a Format Element</a></h3>
<p>To remove a Python format element simply remove the corresponding file from the lib/python/invenio/bibformat_elements directory of your CDS Invenio installation.</p>
- <p>To remove a format element declared in the "<a href="<WEBURL>//admin/bibindex/bibindexadmin.py/field">Logical Fields</a>" management interface of the BibIndex module simply remove the entry from the table.</p>
+ <p>To remove a format element declared in the "<a href="<CFG_SITE_URL>//admin/bibindex/bibindexadmin.py/field">Logical Fields</a>" management interface of the BibIndex module simply remove the entry from the table.</p>
<h3><a name="codeFormatElement">4.3 Edit the Code of a Format Element</a></h3>
- <p>This section only applies to Python format elements. Basic format elements declared in "<a href="<WEBURL>//admin/bibindex/bibindexadmin.py/field">Logical Fields</a>" have non configurable behaviour.</p>
+ <p>This section only applies to Python format elements. Basic format elements declared in "<a href="<CFG_SITE_URL>//admin/bibindex/bibindexadmin.py/field">Logical Fields</a>" have non configurable behaviour.</p>
<p>A format element file is like any regular Python program. It has to implement a <code>format</code> function, which returns a <code>string</code> and takes at least <code>bfo</code> as first parameter (but can take as many others as needed).</p>
<p>Here is for example the code of the "bfe_title.py" element:
<pre>
def format(bfo, separator=" "):
"""
Prints the title of a record.
@param separator separator between the different titles
"""
titles = []
title = bfo.field('245__a')
title_remainder = bfo.field('245__b')
titles.append( title + title_remainder )
title = bfo.field('0248_a')
if len(title) > 0:
titles.append( title )
title = bfo.field('246__a')
if len(title) > 0:
titles.append( title )
title = bfo.field('246_1a')
if len(title) > 0:
titles.append( title )
return separator.join(titles)
</pre>
In format templates this element can be called like a function, using HTML syntax:<br/>
<code>&lt;BFE_TITLE separator="; "/&gt;</code><br/>
Notice that the call uses (almost) the filename of your element. To find out which element to use, BibFormat tries different filenames until the element is found: it tries to <ol>
<li>ignore the letter case</li>
<li>replace underscore with spaces</li>
<li>remove the BFE_ from the name</li>
</ol>
This means that even if the filename of your element is "my element.py", BibFormat can resolve the call &lt;BFE_MY_ELEMENT /&gt; in a format template. This also means that you must take care no to have two format elements filenames that only differ in term of the above parameters.
</p>
<p>The <code>string</code> returned by the <code>format</code> function corresponds to the value that is printed instead of the format element name in the format template.</p>
<p>The <code>bfo</code> object taken as parameter by <code>format</code> stands for BibFormatObject: it is an object that represents the context in which the formatting takes place. For example it allows to retrieve the value of a given field for the record that is being formatted, or the language of the user. We see the details of the BibFormatObject further below.</p>
<p>The <code>format</code> function of an element can take other parameters, as well as default values for these parameters. The idea is that these parameters are accessible from the format template when calling the elements, and allow to parametrize the behaviour of the format element.</p>
<p>It is very important to document your element: this allows to generate a documentation for the elements accessible to people writing format templates. It is the only way for them to know what your element do. The key points are:
<ul>
<li>Provide a docstring for the <code>format</code> function</li>
<li>For each of the parameters of the <code>format</code> function (except for <code>bfo</code>), provide a description using a Java-like doc syntax in the doc string:<br/> <code>@param my_param description for my param</code> (one line per parameter)</li>
<li>You can use one <code>@see</code> followed by a comma separated list of elements filenames to provide a reference to other elements of interests related to this one: <br/> <code>@see my_element1.py, my element2.py</code></li>
</ul>
</p>
<p>Typically you will need to get access to some fields of a record to display as output. There are two ways to this: you can access the <code>bfo</code> object given as parameter and use the provided (basic) accessors, or import a dedicated module and use its advanced functionalities.</p>
<p><b>Method 1: Use accessors of <code>bfo</code></b>:<br/>
<code>bfo</code> is an instance of the <code>BibFormatObject</code> class. The following methods are available:
<ul>
<li><code>get_record()</code>: Returns the record of this BibFormatObject instance as a BibRecord structure. Allows advanced access on the structure using <code>BibRecord</code>.</li>
<li><code>control_field(tag)</code>: Returns the value of control field given by MARC <code>tag</code>.</li>
<li><code>field(tag)</code>:Returns the value of the field corresponding to MARC <code>tag</code>. If the value does not exist, return empty string.</li>
<li><code>fields(tag)</code>: Returns the list of values corresonding to MARC <code>tag</code>.If tag has an undefined subcode (such as 999C5), the function returns a list of dictionaries, whoose keys are the subcodes and the values are the values of tag.subcode. If the tag has a subcode, simply returns list of values corresponding to tag.</li>
<li><code>kb(kb, string, default="")</code>: Returns the value of the <code>string</code> in the knowledge base <code>kb</code>. If kb does not exist or string does not exist in kb, returns <code>default</code> string.</li>
</ul>
You can also get access to other information through <code>bfo</code>, such as the language in which the formatting should occur with <code>bfo.lang</code>. To learn more
-about the possibilities offered by the <code>bfo</code>, read the <a href="<WEBURL>/help/hacking/bibformat-api">BibFormat APIs</a>
+about the possibilities offered by the <code>bfo</code>, read the <a href="<CFG_SITE_URL>/help/hacking/bibformat-api">BibFormat APIs</a>
</p>
<p><b>Method 2: Use module <code>BibRecord</code></b>:<br/>
BibRecord is a module that provides advanced functionalities
regarding access to the field of a record
<code>bfo.get_record()</code> returns a structure that can be
understood by BibRecord's functions. Therefore you can import
the module's functions to get access to the fields you want.
</p>
<h3><a name="previewFormatElement">4.4 Preview a Format Element</a></h3>
<p>
You can play with a format element parameters and see the result
of the element directly in the
<a href="bibformatadmin.py/format_elements_doc">format elements documentation</a>:
for each element, under the section "See
also", click on "Test this element". You are redirected to a page
where you can enter a value for the parameters. A description is
associated with each parameter as well as an indication of the
default value of the parameter if you do not provide a custom
value. Click on the "Test!" button to see the result of the
element with your parameters.</p>
<h3><a name="internationalizationFormatElement">4.5 Internationalization (i18n)</a></h3>
<p>You can follow the standard internationalization procedure in
use accross CDS Invenio sources. For example the following code
will get you the translation for "Welcome" (assuming "Welcome" has
been translated):
<pre>
from invenio.messages import gettext_set_language
ln = bfo.ln
_ = gettext_set_language(ln)
translated_welcome = &#95;("Welcome")
</pre>
</p>
<p>Notice the access to <code>bfo.ln</code> to get access to the
current language of the user. For simpler translations or
behaviour depending on the language you can simply check the value
<code>bfo.ln</code> to return your custom text.</p>
<h3><a name="escapeFormatElement">4.6 Escaping special HTML/XML characters</a></h3>
<p>In most cases, that is cases where your
element does not return HTML output, you do not have to take any
particular action in order to escape values that you output: the
BibFormat engine will take care of escaping the returned value of the element
for you. In cases where you want to return text that should not
be escaped (for example when you return HTML links), you can make
the formatting engine know that it should not escape your
value. This is done by implementing the
<code>escape_values(bfo)</code> function in your element, that
will return (int) 0 when escape should not be done (or 1 when
escaping should be done):
<pre>def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
</pre>
Note that the function is given a <code>bfo</code> object as
parameter, such that you can do additional testing if your element
should really return 1 or 0 (for very special cases).<br/> Also note
that the behavior defined by the <code>escape_values()</code> function
will be overriden by the <code>escape</code> parameter used in the
format template if it is specified.
</p>
<p> Finally, be cautious when you disable escaping: you will have
to take care of escaping values "manually" in your format element
code, in order to avoid non valid outputs or XSS
vulnerabilities. This can be done easily when using the
<code>field</code>, <code>fields</code> and
<code>controlfield</code> functions of bfo with <code>escape</code>
parameter:
<pre>
title = bfo.field('245__a', escape="1")
abstract = bfo.field('520__a', escape="2")
</pre>
The <code>escape</code> parameter can be one of the following values:
<ul><li>0 - no escaping</li>
<li>1 - escape all HTML characters (escaped chars are shown as escaped)</li>
<li>2 - remove dangerous HTML tags to avoid XSS, but
keep basic one (such as &lt;br /&gt;)
This is particularly useful if you want to store HTML text in your
metadata but still want to escape some tags to prevent
XSS vulnerabilities. Note that this method is slower than
basic escaping of mode 1.<br/>
Escaped tags are removed.</li>
<li>3 - mix of mode 1 and mode 2. If field_value starts with &lt;!--HTML--&gt;,
then use mode 2. Else use mode 1.</li>
<li>4 - remove all HTML/XML tags </li>
</ul>
These modes are the same for <code>escape_values(bfo)</code> function.
<p>
You can also decide not to use the <code>escape</code> parameter and escape values
using any other Python function/library you want to use (such as <code>cgi.escape()</code>).
</p>
<h3><a name="attrsFormatElement">4.7 Edit the Attributes of a Format Element</a></h3>
<p>A format element has mainly four kinds of attributes: <ul>
<li>Name: it corresponds to the filename of the element.</li>
<li>Description: the description is in the <code>docstring</code> of the <code>format</code> function (excepted lines prefixed with <code>@param</code> and <code>@see</code>).</li>
<li>Parameters descriptions: for each parameter of the <code>format</code> function, a line beginning with <code>@param</code> <i>parameter_name</i> and followed by the description of the parameter is present in the <code>docstring</code> of the <code>format</code> function.</li>
<li>Reference to other elements: one line beginning with <code>@see</code> and followed by a list of comma-separated format elements filenames in the in the <code>docstring</code> of the <code>format</code> function provides a link to related elements.</li>
</ul>
</p>
<h3><a name="dependenciesFormatElement">4.8 Check the Dependencies of a Format Element</a></h3>
<p>There are two ways to check the dependencies of a format element. The simplest way is to go to the <a href="bibformatadmin.py/format_elements_doc">format elements documentation</a> and click on "Dependencies of this element" for the element you want to check.</p>
<p>The second method to check the dependencies of an element is through regular unix tools: for example <code>$ grep -r -i 'bfe_<i>your_element_name</i>' .</code> inside the format templates directory will tell you which templates call your element.</p>
<h3><a name="validityFormatElement">4.9 Check the Validity of a Format Element</a></h3>
<p>There are two ways to check the validity of an element. The simplest one is to go to the <a href="bibformatadmin.py/format_elements_doc">format elements documentation</a> and click on "Correctness of this element" for the element you want to check.</p>
<p>The second method to check the validity of an element is through regular Python methods: you can for example import the element in the interactive interpreter and feed it with test parameters. Notice that you will need to build a BibFormatObject instance to pass as <code>bfo</code> parameter to the <code>format</code> function of your element.</p>
<h3><a name="browseDocFormatElement">4.10 Browse the Format Elements Documentation</a></h3>
<p>Go to the <a href="bibformatadmin.py/format_elements_doc">format elements documentation</a>. There is a summary of all available format elements at the top of the page. You can click on an element to go to its detailed description in the second part of the page.</p>
<p>Each detailed documentation shows you:
<ul>
<li>A description of what the element does.</li>
<li>A list of all parameters you can use for this element.</li>
<li>For each parameter, a description and the default value when parameter is ommitted.</li>
<li>A link to a tool to track the dependencies of your element.</li>
<li>A link to a tool to check the correctness of your element.</li>
<li>A link to a tool to test your element with custom parameters.</li>
</ul></p>
<h2><a name="KBs">5. Knowledge Bases</a></h2>
<p>Knowledge bases are a way to define easily extendable repositories of mappings. Their use is various, but their main purpose is to get, given a value, the normalized version of this value. For example you may use a knowledge base to hold a list of all ways to abbreviate the name of a journal, and map these abbreviations to the full journal name. This would be useful to get a normalized journal name accross all of your records.</p>
<p>The knowledge base itself offers no method to do this normalization. It is limited to the archiving of this knowledge. To benefit from the normalization you need to use a format element which is knowledge-base-aware. The element will look by iteself into the knowledge base to format a record. In that way you can extend the formatting capabilities of this element without having to modify it.</p>
<h3><a name="addKB">5.1 Add a Knowledge Base</a></h3>
<p>To add a knowledge base go to the <a href="bibformatadmin.py/kb_manage">Manage Knowledge Bases</a> administration page.
At the bottom of the page click on the "Add New Knowledge Base" button. The knowledge base has been created and you are asked to fill in its attribute. See <a href="#attrsKB">Edit the Attributes of a Knowledge Base</a> to learn more about the attributes of knowledge bases.</p>
<h3><a name="removeKB">5.2 Remove a Knowledge Base</a></h3>
<p>To remove a knowledge base go to the <a href="bibformatadmin.py/kb_manage">Manage Knowledge Bases</a> administration page. Click on the "Delete" button facing the knowledge base you want to remove and confim. The knowledge base and all the mapping it includes are removed.</p>
<h3><a name="addMappingKB">5.3 Add a Mapping</a></h3>
<p>Go to the <a href="bibformatadmin.py/kb_manage">Manage Knowledge Bases</a> administration page and click on the knowledge base for which you want to add a mapping. Fill in the form of the "Add New Mapping" section on the left of the page with the new mapping, and click on "Add New Mapping". The mapping has been created. Alternatively you can create the mapping without its attributes, and fill them afterward (See <a href="#editMappingKB">Edit a Mapping</a>).<p/>
<h3><a name="removeMappingKB">5.4 Remove a Mapping</a></h3>
<p>Go to the <a href="bibformatadmin.py/kb_manage">Manage Knowledge Bases</a> administration page and click on the knowledge base for which you want to remove a mapping. Click on the "Delete" button facing the mapping you want to delete.
<h3><a name="editMappingKB">5.5 Edit a Mapping</a></h3>
<p>Go to the <a href="bibformatadmin.py/kb_manage">Manage Knowledge Bases</a> administration page and click on the knowledge base for which you want to edit a mapping. Locate the mapping in the list. You can click on the column headers to order the list by <i>Map From</i> or by <i>Map To</i> to help you find it. Once you have edited the mapping click on the corresponding "Save" button.
<h3><a name="attrsKB">5.6 Edit the Attributes of a Knowledge Base</a></h3>
Go to the <a href="bibformatadmin.py/kb_manage">Manage Knowledge Bases</a> administration page and click on the knowledge base you want to edit. In the top menu, click on "Knowledge Base Attributes". You can then give your knowledge base a name and a description. Finally click on the "Update Base Attributes" button.
<h3><a name="validityKB">5.7 Check the Dependencies a Knowledge Base</a></h3>
To check the dependencies of a knowledge base
go to the <a href="bibformatadmin.py/kb_manage">Manage Knowledge Bases</a> page, click on
the knowledge base you want to check, and then in the menu click on "Knowledge Base Dependencies".</p>
<p>The next page shows you the list of format elements that use this knowledge base.</p>
<p>Note that some format elements might be omitted.</p>
<h2><a name="BibReformat">6. Run BibReformat</a></h2>
<p>While records can be formatted on-the-fly using BibFormat, it is usually necessary to preformat the records
in order to decrease the load of your server. To do so, use the <code>bibreformat</code> command line tool.</p>
<h3><a name="runBibReformat">6.1 Run BibReformat</a></h3>
<p>The following options are available for running <code>bibreformat</code>:<p>
<blockquote>
<pre> Usage: bibreformat [options]
-u, --user=USER User name to submit the task as, password needed.
-h, --help Print this help.
-V, --version Print version information.
-v, --verbose=LEVEL Verbose level (0=min,1=normal,9=max).
-s, --sleeptime=SLEEP Time after which to repeat tasks (no)
-t, --time=DATE Moment for the task to be active (now).
-a, --all All records
-c, --collection Select records by collection
-f, --field Select records by field.
-p, --pattern Select records by pattern.
-o, --format Specify output format to be (re-)created. (default HB)
-n, --noprocess Count records to be processed only (no processing done)
Example: bibreformat -n Show how many records are to be bibreformated.
</pre>
</blockquote>
For example, to reformat all records in HB (=HTML brief) format, you'd launch:
<blockquote>
<pre>$ bibreformat -a -oHB
</pre>
</blockquote>
and you watch the progress of the process via <code>bibsched</code>.
<p>Note that BibReformat understands <code>-p</code>, <code>-f</code>,
and <code>-c</code> arguments that enable you to easily reformat only
the records you need. For example, to reformat the Pictures
collection, launch:
</p><blockquote>
<p>
<pre>$ bibreformat -cPictures -oHB
</pre>
</blockquote>
or to reformat HD (=HTML detailed) format for records #10 to #20, you
launch:
<blockquote>
<pre>$ bibreformat -p"recid:10-&gt;20" -oHD
</pre>
</blockquote>
<p>Last but not least, if you launch bibreformat without arguments:
</p><blockquote>
<pre>$ bibreformat
</pre>
</blockquote>
</p>
it will process all the records that have been modified since the last
run of BibReformat, as well as all newly inputted records. This is
suitable for running BibReformat in a periodical daemon mode via
-BibSched. See our <a href="<WEBURL>/help/admin/howto-run">HOWTO Run
+BibSched. See our <a href="<CFG_SITE_URL>/help/admin/howto-run">HOWTO Run
Your CDS Invenio Installation</a> guide for more information.</p>
<h2><a name="Appendix">7. Appendix</a></h2>
<h3><a name="marcNotation">7.1 MARC Notation in Formats</a></h3>
<p>The notation for accessing fields of a record are quite flexible. You can use a syntax strict regarding MARC 21, but also
a shortcut syntax, or a syntax that can have a special meaning.</p>
<p>The MARC syntax is the following one:
<code>tag[indicator1][indicator2] [$ subfield]</code> where <code>tag</code> is 3 digits, <code>indicator1</code> and <code>indicator2</code> are 1 character each, and <code>subfield</code> is 1 letter.
</p>
<p>For example to get access to an abstract you can use the MARC notation <code>520 $a</code>. You can use this syntax in BibFormat. However you can also:
<ul>
<li>Omit any whitespace character (or use as many as you want)</li>
<li>Omit the <code>$</code> character (or use as many as you want)</li>
<li>Omit or use both indicators. You cannot specify only one indicator. If you need to use only one, use underscore <code>_</code> character for the other indicator.</li>
<li>Use percent '<code>%</code>' instead of any character to specify all ("don't care" or wildcard character) for that character. </li>
</ul></p>
<h3><a name="migration">7.2 Migrating from Previous BibFormat</a></h3>
<p>The new Python BibFormat formats are not backward compatible with the previous formats. New concepts and capabilities have been introduced and some have been dropped. If you have not modified the "Formats" or modified only a
little bit the "Behaviours" (or modified "Knowledge Bases"), then the transition will be painless and
automatic. Otherwise you will have to manually rewrite some of the
formats. This should however not be a big problem.</p>
<p>The first thing you should do is to read the <a href="#shortIntro">Five Minutes Introduction to BibFormat</a> to understand how the new BibFormat works. We also assume that you are familiar with the concepts of the old BibFormat. As the new formats separate the presentation from the business logic (i.e. the bindings to the database), it is not possible to automatically handle the translation. This is why you should at least be able to read and understand the formats that you want to migrate.</p>
<h4>Differences between old and new BibFormat</h4>
<p>
The most noticeable differences are:<br/>
<br/>
a) "Behaviours" have been renamed "Output formats".<br/>
b) "Formats" have been renamed "Format templates". They are now
written in HTML.<br/>
c) "User defined functions" have been dropped.<br/>
d) "Extraction rules" have been dropped.<br/>
e) "Link rules" have been dropped.<br/>
f) "File formats" have been dropped.<br/>
g) "Format elements" have been introduced. They are written in Python,
and can simulate c), d) and e).<br/>
h) Formats can be managed through web interface or through
human-readable config files.<br/>
i) Introduction of tools like validator and dependencies checker.<br/>
j) Better support for multi-language formatting.<br/>
</p>
<p>
Some of the advantages are:<br/>
<br/>
+ Management of formats is much clearer and easier (less concepts,
more tools).<br/>
+ Writing formats is easier to learn : less concepts
to learn, redesigned work-flow, use of existing well known and
well documented languages.<br/>
+ Editing formats is easier: You can use your preferred HTML editor such as
Emacs, Dreamweaver or Frontpage to modify templates, or any text
editor for output formats and format elements. You can also use the
simplified web administration interface.<br/>
+ Faster and more powerful templating system.<br/>
+ Separation of business logic (output formats, format elements)
and presentation layer (format templates). This makes the management
of formats simpler.<br/>
</p>
<p>
The disadvantages are:<br/>
<br/>
- No backward compatibility with old formats.<br/>
- Stricter separation of business logic and presentation layer:<br/>
no more use of statements such as if(), forall() inside templates,
and this requires more work to put logic inside format elements.<br/>
</p>
<h4>Migrating <i>behaviours</i> to <i>output formats</i></h4>
<p>Behaviours were previously stored in the database and did require to use the evaluation language to
provide the logic that choose which format to use for a record. They also let you enrich records
with some custom data. Now their use has been simplified and rectricted to equivalence tests on the value of a field
of the record to define the format template to use.</p>
<p>For example, the following behaviour:</p>
<table>
<tbody><tr align="center" bgcolor="#0000ff">
<td colspan="2"><font color="white"><b>CONDITIONS</b></font></td>
</tr>
<tr bgcolor="#33ccff">
<td align="center"><b>0</b></td>
<td width="95%"><b>$980.a="PICTURE"</b></td>
</tr>
<tr bgcolor="#ffffff">
<td bgcolor="#33ccff"><font size="2"><b>Action (0)</b></font></td>
<td>"&lt;record&gt;
<br />&nbsp;&lt;controlfield&nbsp;tag=\"001\"&gt;"&nbsp;$001&nbsp;"&lt;/controlfield&gt;
<br />&nbsp;&lt;datafield&nbsp;tag=\"FMT\"&nbsp;ind1=\"\"&nbsp;ind2=\"\"&gt;&nbsp;
<br />&nbsp;&lt;subfield&nbsp;code=\"f\"&gt;hb&lt;/subfield&gt;&nbsp;
<br />&nbsp;&lt;subfield&nbsp;code=\"g\"&gt;"&nbsp;
<br />xml_text(format("PICTURE_HTML_BRIEF"))
<br />"&nbsp;&lt;/subfield&gt;&nbsp;
<br />&nbsp;&lt;/datafield&gt;
<br />&lt;/record&gt;"</td>
</tr>
<tr align="center" bgcolor="#cccccc">
<td colspan="2">&nbsp;
</td>
</tr>
<tr bgcolor="#33ccff">
<td align="center"><b>100</b></td>
<td width="95%"><b>""=""</b></td>
</tr>
<tr bgcolor="#ffffff">
<td bgcolor="#33ccff"><font size="2"><b>Action (0)</b></font></td>
<td>"&lt;record&gt;
<br />&nbsp;&lt;controlfield&nbsp;tag=\"001\"&gt;"&nbsp;$001&nbsp;"&lt;/controlfield&gt;
<br />&nbsp;&lt;datafield&nbsp;tag=\"FMT\"&nbsp;ind1=\"\"&nbsp;ind2=\"\"&gt;&nbsp;
<br />&nbsp;&lt;subfield&nbsp;code=\"f\"&gt;hb&lt;/subfield&gt;&nbsp;
<br />&nbsp;&lt;subfield&nbsp;code=\"g\"&gt;"&nbsp;
<br />xml_text(format("DEFAULT_HTML_BRIEF"))
<br />"&nbsp;&lt;/subfield&gt;&nbsp;
<br />&nbsp;&lt;/datafield&gt;
<br />&lt;/record&gt;"</td>
</tr>
<tr align="center" bgcolor="#cccccc">
<td colspan="2">
&nbsp;
</td>
</tr>
</tbody>
<table>
<p>translates to the following output format (in textual configuration file):</p>
<p>
<code>
tag 980__a:<br/>
PICTURE --- Picture_HTML_brief.bft<br/>
default: Default_HTML_brief.bft<br/>
</code><p/>
<p>or visual representation through web interface:<br/>
-<img src="<WEBURL>/img/admin/bibformat-guide-bfo_hb_migrate.png" alt="Image representation of HB output format" />
+<img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfo_hb_migrate.png" alt="Image representation of HB output format" />
</p>
<h4>Migrating <i>formats</i> to <i>format templates</i> and <i>format elements</i></h4>
<p>The migration of formats is the most difficult part of the migration. You will need to separate the presentation code (HTML) from the business code (iterations, tests and calls to the database). Here are some tips on how you can do this:</p>
<ul>
<li>If you want to save the time of unescaping all HTML characters and understanding how the layout should look like, just go with your web browser to a formatted version of the format in your CDS Invenio installation, and copy the source of the web page. Identify the parts of the HTML code which are specific to the current record, and replace them with a call to the corresponding format element.</li>
<li>If you have made small modifications to the old default provided formats, we suggest that you use the new provided ones and modify them according to your needs.</li>
</ul>
<h4>Migrating <i>UDFs</i> and <i>Link rules</i></h4>
<p><i>User Defined Functions</i> and <i>Link rules</i> have been dropped in the new BibFormat. These concepts have no reasons to be as they can be fully implemented in the <i>format elements</i>. For example the <code>AUTHOR_SEARCH</code> link rule can directly be implemented in the <code>Authors.bfe</code> element.</p>
<p>As for the UDFs, most of them are directly built-in functions of Python. Whenever a special function as to be implemented, it can be defined in a regular Python file and used in any element.</p>
<h4>The Migration Kit</h4>
<p>The migration kit is only available in older versions of Invenio.<br/>
To enable the migration kit in this release, you need to copy the required files from an older release.
</p>
<h4><a name="runSideBySide">Run old and new formats side by side</a></h4>
<p>This was possible by default only in older versions of Invenio.<br/>
To enable this functionality in this release, you need to copy the required files from an older release.
</p>
<h3><a name="integrationDreamweaver">7.3 Integrating BibFormat into Dreamweaver MX</a></h3>
<p>BibFormat templates have been thought to be editable in custom HTML editors. We propose in this section
a way to extend one particular editor, Dreamweaver.</p>
<h4>Make Dreamweaver Recognize Format Elements in Layout View</h4>
<p>To make Dreamweaver understand the format elements and display an icon for each of them in the layout editor, you must
edit a Dreamweaver configuration file named <code>Tags.xml</code> located inside /Configuration/ThirdPartyTags directory
of your Dreamweaver installation folder. At the end of this file, copy-paste the following lines:
<pre>
&lt;!-- BibFormat (CDS Invenio) --&gt;
&lt;tagspec tag_name="BIBFORMAT" start_string="&lt;BFE_" end_string="/&gt;" parse_attributes="false" detect_in_attribute="true" icon="bibformat.gif" icon_width="25" icon_height="16"&gt;&lt;/tagspec &gt;
&lt;tagspec tag_name="BIBFORMAT" start_string="&lt;bfe_" end_string="/&gt;" parse_attributes="false" detect_in_attribute="true" icon="bibformat.gif" icon_width="25" icon_height="16"&gt;&lt;/tagspec &gt;
</pre>
- Also copy this icon <img src="<WEBURL>/img/admin/bibformat-guide-bfe.gif" alt="bibformat.gif"/> in the same directory as <code>Tags.xml</code> (right-click on icon, or ctrl-click on one-button mouse, and "Save Image As..."). Make sure the downloaded image is named "<code>bibformat.gif</code>".
+ Also copy this icon <img src="<CFG_SITE_URL>/img/admin/bibformat-guide-bfe.gif" alt="bibformat.gif"/> in the same directory as <code>Tags.xml</code> (right-click on icon, or ctrl-click on one-button mouse, and "Save Image As..."). Make sure the downloaded image is named "<code>bibformat.gif</code>".
</p>
<p>Note that Dreamweaver might not recognize Format Elements when complex formatting is involved due to these elements.</p>
<h4>Add a Format Elements Floating Panel</h4>
<p>You can add a floating panel that will you to insert Format Elements in your document and read the documentation
of all available Format Elements.</p>
<p>The first step is to declare in which menu of Dreamweaver this floating panel is going to be available.
To do so, edit file "<code>Menu.xml</code>" located inside /Configuration/Menus of your Dreamweaver
application directory and copy-paste the following line in the menu you want
(typically inside tag <code>'menu'</code> with attribute <code>id='DWMenu_Window_Others')</code>:</p>
<pre>
&lt;menuitem name="BibFormat Elements" enabled="true" command="dw.toggleFloater('BibFormat_floater.html')" checked="dw.getFloaterVisibility('BibFormat_floater.html')" /&gt;
</pre>
</p>
<p>Once this is done, you can <a href="bibformatadmin.py/download_dreamweaver_floater">download the floating palette</a> (if file opens in your browser instead of downloading, right-click on icon, or ctrl-click on one-button mouse, and "Save Target As...") and move the dowloaded file "<code>BibFormat_floater.html</code>" (do not rename it) into /Configuration/Floaters directory of your Dreamweaver application folder.</p>
<p>To use the BibFormat floating panel, open Dreamweaver, and choose <code>Window &gt; Others &gt; BibFormat Elements</code>.</p>
<p>Whenever a new version of the palette is available, you can skip the edition of file "<code>Menu.xml</code>" and just replace the old "<code>BibFormat_floater</code>" file with the new one.</p>
<h3><a name="faq">7.4 FAQ</a></h3>
<h4>Why do we need output formats? Wouldn't format templates be sufficient?</h4>
<p>As you potentially have a lot of records, it is not conceivable to specify for each of them which
format template they should use. This is why this rule-based decision layer has been introduced.</p>
<h4>How can I protect a format?</h4>
<p>As a web user, you cannot protect a format. If you are administrator of the
system and have access to the format files, you can simply use the permission rights of your system, as BibFormat
is aware of it.</p>
<h4>Why cannot I edit/delete a format?</h4>
<p>The format file has certainly been protected by the administrator of the server. You must ask the
administrator to unprotect the file if you want to edit it.</p>
<h4>How can I add a format element from the web interface?</h4>
<p>Format elements cannot be added, removed or edited through the web interface. This limitation
-has been introduced to limit the security risks caused by the upload of Pythonic files on the server. The only possibility to add a basic format element from the web interface is to add a en entry in the "<a href="<WEBURL>//admin/bibindex/bibindexadmin.py/field">Logical Fields</a>" management interface of the BibIndex module (see <a href="#addFormatElement">Add a Format Element</a>)</p>
+has been introduced to limit the security risks caused by the upload of Pythonic files on the server. The only possibility to add a basic format element from the web interface is to add a en entry in the "<a href="<CFG_SITE_URL>//admin/bibindex/bibindexadmin.py/field">Logical Fields</a>" management interface of the BibIndex module (see <a href="#addFormatElement">Add a Format Element</a>)</p>
<h4>Why are some Marc codes omitted in the "Check Dependencies" pages?</h4>
<p>When you check the dependencies of a format, the page reminds you that
some use of Marc codes might not be indicated. This is because it is not
possible (or at least not trivial) to guess that the call to <code>field(str(5+4)+"80"+"__a")</code>
is equal to a call to <code>field("980__a")</code>. You should then not completely rely on this indication.</p>
<h4>How are displayed deleted record?</h4>
<p>By default, CDS Invenio displays a standard "The record has been deleted." message for all
output formats with a 'text/html' content type. Your output format, format templates and format elements
are bypassed by the engine.
However, for more advanced output formats, CDS Invenio
goes through the regular formatting process and let your formats do the job. This allows you to customize how a record should be displayed once it has been deleted.</p>
<h4>Why are some format elements omitted in the "Knowledge Base Dependencies" page?</h4>
<p>When you check the dependencies of a knowledge base, the page
reminds you that format elements using this knowledge base might not
be indicated. This is because it is not possible (or at least not
trivial) to guess that the call to
<code>kb(e.upper()+"journal"+"s")</code> in a format element is equal
to a call to <code>kb("Ejournals")</code>. You should then not
completely rely on this indication.</p>
<h4>Why are some format elements defined in field table omitted in the format element documentation?</h4>
<p>Some format elements defined in the "Logical Fields" management
interface of the BibIndex module (the basic format elements) are not
shown in the format elements documentation pages. We do not show such
an element if its name starts with a number. This is to reduce the
number of elements shown in the documentation as the logical fields
table contains a lot of not so useful fields to be used in
templates.</p>
<h4>How can I get access to repeatable subfields from inside a format element?</h4>
<p>Given that repeatable subfields are not frequent, the
<code>bfo.fields(..)</code> function has been implemented to return the most convenient structure for most cases, that is a '<em>list of strings</em>' (<b>Case 1</b> below) or '<em>list of dict of strings</em>' (<b>Case 2</b> below). For eg. with the following metadata:
<pre> 999C5 $a value_1a $b value_1b
999C5 $b value_2b
999C5 $b value_3b $b value_3b_bis
>> bfo.fields('999C5b') <b>(1)</b>
>> ['value_1b', 'value_2b', 'value_3b', 'value_3b_bis']
>> bfo.fields('999C5') <b>(2)</b>
>> [{'a':'value_1a', 'b':'value_1b'},
{'b':'value_2b'},
{'b':'value_3b'}]
</pre>
In this example <code>value3b_bis</code> is not shown for
<code>bfo.fields('999C5')</code> (<b>Case 2</b>). If it were to be taken into account, the
returned structure would have to be a '<em>list of dict of list of strings</em>', thus making for most cases
the access to the data a bit more complex.<br/>
In order to consider the repeatable subfields, use the additional <code>repeatable_subfields_p</code> parameter:
<pre> >> bfo.fields('999C5b', repeatable_subfields_p=True) <b>(1 bis)</b>
>> ['value_1b', 'value_2b', 'value_3b']
>> bfo.fields('999C5', repeatable_subfields_p=True) <b>(2 bis)</b>
>> [{'a':['value_1a'], 'b':['value_1b']},
{'b':['value_2b']},
{'b':['value_3b', 'value3b_bis']}]
</pre>
Another solution would be to access the BibRecord structure with
<code>bfo.getRecord()</code> and use the lower-level BibRecord module with this structure.
</p>
<br/>
diff --git a/modules/bibformat/doc/hacking/bibformat-api.webdoc b/modules/bibformat/doc/hacking/bibformat-api.webdoc
index 82079b894..2c2f9af8f 100644
--- a/modules/bibformat/doc/hacking/bibformat-api.webdoc
+++ b/modules/bibformat/doc/hacking/bibformat-api.webdoc
@@ -1,885 +1,885 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibFormat API -->
-<!-- WebDoc-Page-Navtrail: <a class=navtrail href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibformat-internals">BibFormat Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class=navtrail href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibformat-internals">BibFormat Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<protect>
<pre>
****************************************************************************
** IMPORTANT NOTE: Note that this documentation is an updated version of **
** an earlier technical draft of BibFormat specifications. Please first **
** refer to the BibFormat admin guide. **
****************************************************************************
Technical Overview of the new BibFormat
=======================================
Contents:
1. Python API
2. The philosophy behind BibFormat
3. Differences between the old PHP version and the new Pythonic version
4. Migrating from the previous PHP BibFormat version to the new Pythonic version
5. Specifications of the new BibFormat configuration files.
1. Python API
The APIs of bibformat.py consists in these functions:
def format_record(recID, of, ln=CFG_SITE_LANG, verbose=0,
search_pattern=None, xml_record=None, user_info=None,
on_the_fly=False):
"""
Formats a record given its ID (or its XML representation)
and an output format.
Returns a formatted version of the record in the specified
language, with pattern context, and specified output format.
The function will define by itself which format template must be
applied.
Parameters that allow contextual formatting (like 'search_pattern'
and 'user_info') are useful only when doing on-the-fly
formatting, or when caching with care (e.g. caching all formatted
versions of a record for each possible 'ln').
The arguments are as follows:
recID - the ID of the record to format. If ID does not exist
the function returns empty string or an error
string, depending on level of verbosity.
If 'xml_record' parameter is specified, 'recID'
is ignored
of - an output format code. If 'of' does not exist as code in
output format, the function returns empty
string or an error string, depending on level
of verbosity. ;of' is case insensitive.
ln - the language to use to format the record. If
'ln' is an unknown language, or translation
does not exist, default CFG_SITE_LANG language
will be applied whenever possible.
Allows contextual formatting.
verbose - the level of verbosity in case of errors/warnings
0 - Silent mode
5 - Prints only errors
9 - Prints errors and warnings
search_pattern - the pattern used as search query when asked to
format this record (User request in web
interface). Allows contextual formatting.
xml_record - an XML string representation of the record to
format. If it is specified, recID parameter is
ignored. The XML must be pasable by BibRecord.
user_info - allows to grant access to some functionalities
on a page depending on the user's
priviledges. 'user_info' is the same structure
as the one returned by webuser.collect_user_info(req),
(that is a dictionary).
on_the_fly - if False, try to return an already preformatted
version of the record in the database.
"""
Example:
>> from invenio.bibformat import format_record
>> format_record(5, "hb", "fr")
def format_records(recIDs, of, ln=CFG_SITE_LANG, verbose=0, search_pattern=None,
xml_records=None, user_info=None, record_prefix=None,
record_separator=None, record_suffix=None,
prologue="", epilogue="", req=None, on_the_fly=False):
"""
Returns a list of formatted records given by a list of record IDs or a
list of records as xml.
Adds a prefix before each record, a suffix after each record,
plus a separator between records.
Also add optional prologue and epilogue to the complete formatted list.
You can either specify a list of record IDs to format, or a list of
xml records, but not both (if both are specified recIDs is ignored).
'record_separator' is a function that returns a string as separator between
records. The function must take an integer as unique parameter,
which is the index in recIDs (or xml_records) of the record that has
just been formatted. For example separator(i) must return the separator
between recID[i] and recID[i+1]. Alternatively separator can be a single
string, which will be used to separate all formatted records.
The same applies to 'record_prefix' and 'record_suffix'.
'req' is an optional parameter on which the result of the function
are printed lively (prints records after records) if it is given.
Note that you should set 'req' content-type by yourself, and send
http header before calling this function as it will not do it.
This function takes the same parameters as 'format_record' except for:
recIDs - a list of record IDs to format
xml_records - a list of xml string representions of the records to
format. If this list is specified, 'recIDs' is ignored.
record_prefix - a string or a function the takes the index of the record
in 'recIDs' or 'xml_records' for which the function must
return a string.
Printed before each formatted record.
record_separator - either a string or a function that returns string to
separate formatted records. The function takes the index
of the record in 'recIDs' or 'xml_records' that is being
formatted.
record_prefix - a string or a function the takes the index of the record
in 'recIDs' or 'xml_records' for which the function must
return a string.
Printed after each formatted record
req - an optional request object on which formatted records
can be printed (for "live" output )
prologue - a string printed before all formatted records string
epilogue - a string printed after all formatted records string
on_the_fly - if False, try to return an already preformatted version
of the records in the database
"""
def get_output_format_content_type(of):
"""
Returns the content type (eg. 'text/html' or 'application/ms-excel') \
of the given output format.
The function takes this mandatory parameter:
of - the code of output format for which we want to get the content type
"""
def record_get_xml(recID, format='xm', decompress=zlib.decompress):
"""
Returns an XML string of the record given by recID.
The function builds the XML directly from the database,
without using the standard formatting process.
'format' allows to define the flavour of XML:
- 'xm' for standard XML
- 'marcxml' for MARC XML
- 'oai_dc' for OAI Dublin Core
- 'xd' for XML Dublin Core
If record does not exist, returns empty string.
The function takes the following parameters:
recID - the id of the record to retrieve
format - the XML flavor in which we want to get the record
decompress _ a function used to decompress the record from the database
"""
The API of the BibFormat Object ('bfo') given as a parameter to
format function of format elements consist in the following
functions. This API is to be used only inside format elements.
def control_field(self, tag, escape='0'):
"""
Returns the value of control field given by tag in record.
If the value does not exist, returns empty string
The returned value is always a string.
'escape' parameter allows to escape special characters
of the field. The value of escape can be:
0 - no escaping
1 - escape all HTML characters
2 - escape all HTML characters by default. If field starts with <!--HTML-->,
escape only unsafe characters, but leave basics HTML tags.
This is particularly useful if you want to store HTML text in your
metadata but still want to escape some tags to prevent
XSS vulnerabilities. Note that this method is slower than
basic escaping of mode 1.
The arguments are:
tag - the marc code of a field
escape - 1 if returned value should be escaped. Else 0.
(see above for other modes)
"""
def field(self, tag, escape='0'):
"""
Returns the value of the field corresponding to tag in the
current record.
If the value does not exist, returns empty string
The returned value is always a string.
'escape' parameter allows to escape special characters
of the field. The value of escape can be:
0 - no escaping
1 - escape all HTML characters
2 - escape all HTML characters by default. If field starts with <!--HTML-->,
escape only unsafe characters, but leaves basic HTML tags.
This is particularly useful if you want to store HTML text in your
metadata but still want to escape some tags to prevent
XSS vulnerabilities. Note that this method is slower than
basic escaping of mode 1.
The arguments are:
tag - the marc code of a field
escape - 1 if returned value should be escaped. Else 0.
(see above for other modes)
"""
def fields(self, tag, escape='0', repeatable_subfields_p=False):
"""
Returns the list of values corresonding to "tag".
If tag has an undefined subcode (such as 999C5),
the function returns a list of dictionaries, whoose keys
are the subcodes and the values are the values of tag.subcode.
If the tag has a subcode, simply returns list of values
corresponding to tag.
Eg. for given MARC:
999C5 $a value_1a $b value_1b
999C5 $b value_2b
999C5 $b value_3b $b value_3b_bis
>> bfo.fields('999C5b')
>> ['value_1b', 'value_2b', 'value_3b', 'value_3b_bis']
>> bfo.fields('999C5')
>> [{'a':'value_1a', 'b':'value_1b'},
{'b':'value_2b'},
{'b':'value_3b'}]
By default the function returns only one value for each
subfield (that is it considers that repeatable subfields are
not allowed). It is why in the above example 'value3b_bis' is
not shown for bfo.fields('999C5'). (Note that it is not
defined which of value_3b or value_3b_bis is returned). This
is to simplify the use of the function, as most of the time
subfields are not repeatable (in that way we get a string
instead of a list). You can allow repeatable subfields by
setting 'repeatable_subfields_p' parameter to True. In
this mode, the above example would return:
>> bfo.fields('999C5b', repeatable_subfields_p=True)
>> ['value_1b', 'value_2b', 'value_3b']
>> bfo.fields('999C5', repeatable_subfields_p=True)
>> [{'a':['value_1a'], 'b':['value_1b']},
{'b':['value_2b']},
{'b':['value_3b', 'value3b_bis']}]
NOTICE THAT THE RETURNED STRUCTURE IS DIFFERENT. Also note
that whatever the value of 'repeatable_subfields_p' is,
bfo.fields('999C5b') always show all fields, even repeatable
ones. This is because the parameter has no impact on the
returned structure (it is always a list).
'escape' parameter allows to escape special characters
of the field. The value of escape can be:
0 - no escaping
1 - escape all HTML characters
2 - escape all HTML characters by default. If field starts with <!--HTML-->,
escape only unsafe characters, but leaves basic HTML tags.
This is particularly useful if you want to store HTML text in your
metadata but still want to escape some tags to prevent
XSS vulnerabilities. Note that this method is slower than
basic escaping of mode 1.
The arguments are:
tag - the marc code of a field
escape - 1 if returned value should be escaped. Else 0.
(see above for other modes)
"""
def kb(self, kb, string, default=""):
"""
Returns the value of the "string" in the knowledge base "kb".
If kb does not exist or string does not exist in kb,
returns 'default' string or empty string if not specified
The arguments are as follows:
kb - the knowledge base name in which we want to find the mapping.
If it does not exist the function returns the original
'string' parameter value. The name is case insensitive (Uses
the SQL 'LIKE' syntax to retrieve value).
string - the value for which we want to find a translation-
If it does not exist the function returns 'default' string.
The string is case insensitive (Uses the SQL 'LIKE' syntax
to retrieve value).
default - a default value returned if 'string' not found in 'kb'.
"""
def get_record(self):
"""
Returns the record encapsulated in bfo as a BibRecord structure.
You can get full access to the record through bibrecord.py functions.
"""
Example (from inside BibFormat element):
>> bfo.field("520.a")
>> 'We present a quantitative appraisal of the physics potential
for neutrino experiments.'
>>
>> bfo.control_field("001")
>> '12'
>>
>> bfo.fields("700.a")
>>['Alekhin, S I', 'Anselmino, M', 'Ball, R D', 'Boglione, M']
>>
>> bfo.kb("DBCOLLID2COLL", "ARTICLE")
>> 'Published Article'
>>
>> bfo.kb("DBCOLLID2COLL", "not in kb", "My Value")
>> 'My Value'
Moreover you can have access to the language requested for the
formatting, the search pattern used by the user in the web
interface and the userID by directly getting the attribute from 'bfo':
bfo.lang
"""
Returns the language that was asked to be used for the
formatting. Always returns a string.
"""
bfo.search_pattern
"""
Returns the search pattern specified by the user when
the record had to be formatted. Always returns a string.
"""
bfo.user_info
"""
Returns a dictionary with information about current user.
The returned dictionary has the following structure:
user_info = {
'remote_ip' : '',
'remote_host' : '',
'referer' : '',
'uri' : '',
'agent' : '',
'apache_user' : '',
'apache_group' : [],
'uid' : -1,
'nickname' : '',
'email' : '',
'group' : [],
'guest' : '1'
}
"""
bfo.uid
"""
! DEPRECATED: use bfo.user_info['uid'] instead
"""
bfo.recID
"""
Returns the id of the record
"""
bfo.req
"""
! DEPRECATED: use bfo.user_info instead
"""
bfo.format
"""
Returns the format in which the record is being formatted
"""
Example (from inside BibFormat element):
>> bfo.lang
>> 'en'
>>
>> bfo.search_pattern
>> 'mangano and neutrino and factory'
2. The philosophy behind BibFormat
BibFormat is in charge of formatting the bibliographic records that
are displayed to your users. As you potentially have a huge amount of
bibliographic records, you cannot specify manually for each of them
how it should be formatted. This is why you can define rules that will
allow BibFormat to understand which kind of formatting to apply to a given
record. You define this set of rules in what is called an "output
format".
You can have different output formats, each with its own characteristics.
For example you certainly want that when multiple bibliographic records are
displayed at the same time (as it happens in search results), only
short versions are shown to the user, while a detailed record is
preferable when a single record is displayed. You might also want to
let your users decide which kind of output they want. For example you
might need to display HTML for regular web browsing, but would also
give a BibTeX version of the bibliographic reference for direct
inclusion in a LaTeX document.
See section 5.1 to learn how to create or modify output formats.
While output formats define what kind of formatting must be applied,
they do not define HOW the formatting is done. This is the role of the
"format templates", which define the layout and look of a
bibliographic reference. These format templates are rather easy to
write if you know a little bit of HTML (see section 5.2 "Format
templates specifications"). You will certainly have to create
different format templates, for different kinds of records. For
example you might want records that contain pictures to display them,
maybe with captions, while records that do not have pictures limited
to printing a title and an abstract.
In summary, you have different output formats (like 'brief HTML',
'detailed HTML' or 'BibTeX') that call different format templates
according to some criteria.
There is still one kind of configuration file that we have not talked
about: the "format elements". These are the "bricks" that you use in
format templates, to get the values of a record. You will learn to use
them in your format template in section 5.2 "Format templates
specifications", but you will mostly not need to modify them or create
new ones. However if you do need to edit one, read section 5.3 "Format
elements specifications" (And if you know Python it will be easy, as
they are written in Python).
Finally BibFormat can make use of mapping tables called "knowledge
bases". Their primary use is to act like a translation table, to
normalize records before displaying them. For example, you can say
that records that have value "Phys Rev D" or "Physical Review D" for
field "published in" must display "Phys Rev : D." to users. See
section 5.4 to learn how to edit knowledge bases.
In summary, there are three layers. Output formats:
+-----------------------------------------------------+
| Output Format | (Layer 1)
| eg: HTML_Brief.bfo |
+-----------------------------------------------------+
call one of several `format templates':
+-------------------------+ +-------------------------+
| Format Template | | Format Template | (Layer 2)
| eg: preprint.bft | | eg: default.bft |
+-------------------------+ +-------------------------+
that use one or several format elements:
+--------------+ +----------------+ +-----------------+
|Format Element| |Format Element | | Format Element | (Layer 3)
|eg: authors.py| |eg: abstract.py | | eg: title.py |
+--------------+ +----------------+ +-----------------+
3. Differences between the old PHP version and the new Pythonic version
The most noticeable differences are:
a) "Behaviours" have been renamed "Output formats".
b) "Formats" have been renamed "Format templates". They are now
written in HTML.
c) "User defined functions" have been dropped.
d) "Extraction rules" have been dropped.
e) "Link rules" have been dropped.
f) "File formats" have been dropped.
g) "Format elements" have been introduced. They are written in Python,
and can simulate c), d) and e).
h) Formats can be managed through web interface or through
human-readable config files.
i) Introduction of tools like validator and dependencies checker.
j) Better support for multi-language formatting.
Some of the advantages are:
+ Management of formats is much clearer and easier (less concepts,
more tools).
+ Writing formats is easier to learn : less concepts
to learn, redesigned work-flow, use of existing well known and
well documented languages.
+ Editing formats is easier: You can use your preferred HTML editor such as
Emacs, Dreamweaver or Frontpage to modify templates, or any text
editor for output formats and format elements. You can also use the
simplified web administration interface.
+ Faster and more powerful templating system.
+ Separation of business logic (output formats, format elements)
and presentation layer (format templates). This makes the management
of formats simpler.
The disadvantages are:
- No backward compatibility with old formats.
- Stricter separation of business logic and presentation layer:
no more use of statements such as if(), forall() inside templates,
and this requires more work to put logic inside format elements.
4. Migrating from the previous PHP BibFormat version to the new Pythonic version
Old BibFormat formats are no longer compatible with the new BibFormat
files. If you have not modified the "Formats" or modified only a
little bit the "Behaviours", then the transition will be painless and
automatic. Otherwise you will have to manually rewrite some of the
formats. This should however not be a big problem. Firstly because the
CDS Invenio installation will provide both versions of BibFormat for
some time. Secondly because both BibFormat versions can run side by
side, so that you can migrate your formats while your server still
works with the old formats. Thirdly because we provide a migration
kit that can help you go through this process. Finally because the
migration is not so difficult, and because it will be much easier for
you to customize how BibFormat formats your bibliographic data.
Concerning the migration kit it can:
a) Effortlessly migrate your behaviours, unless they include complex
logic, which usually they don't.
b) Help you migrate formats to format templates and format elements.
c) Effortlessly migrate your knowledge bases.
Point b) is the most difficult to achieve: previous formats did mix
business logic and code for the presentation, and could use PHP
functions. The new BibFormat separates business logic and
presentation, and does not support PHP. The transition kit will try to
move business logic to the format elements, and the presentation to
the format templates. These files will be created for you, includes
the original code and, if possible, a proposal of Python
translation. We recommend that you do not to use the transition kit to
translate formats, especially if you have not modified default
formats, or only modified default formats in some limited places. You
will get cleaner code if you write format elements and format
templates yourself.
5. Specifications of the new BibFormat configuration files.
BibFormat uses human readable configuration files. However (apart
from format elements) these files can be edited and managed through
a web interface.
5.1 Output formats specifications
Output formats specify rules that define which format template
to use to format a record.
While the syntax of output formats is basic, we recommend that you use
the web interface do edit them, to be sure that you make no error.
The syntax of output format is the following one. First you
define which field code you put as the conditon for the rule.
You suffix it with a column. Then on next lines, define the values of
the condition, followed by --- and then the filename of the template
to use:
tag 980.a:
PICTURE --- PICTURE_HTML_BRIEF.bft
PREPRINT --- PREPRINT_HTML_BRIEF.bft
PUBLICATION --- PUBLICATION_HTML_BRIEF.bft
This means that if value of field 980.a is equal to PICTURE, then we
will use format template PICTURE_HTML_BRIEF.bft. Note that you must
use the filename of the template, not the name. Also note that spaces
at the end or beginning are not considered. On the following lines,
you can either put other conditions on tag 980.a, or add another tag on
which you want to put conditions.
At the end you can add a default condition:
default: PREPRINT_HTML_BRIEF.bft
which means that if no condition is matched, a format suitable for
Preprints will be used to format the current record.
The output format file could then look like this:
tag 980.a:
PICTURE --- PICTURE_HTML_BRIEF.bft
PREPRINT --- PREPRINT_HTML_BRIEF.bft
PUBLICATION --- PUBLICATION_HTML_BRIEF.bft
tag 8560.f:
.*@cern.ch --- SPECIAL_MEMBER_FORMATTING.bft
default: PREPRINT_HTML_BRIEF.bft
You can add as many rules as you want. Keep in mind that they are read
in the order they are defined, and that only first rule that
matches will be used.
Notice the condition on tag 8560.f: it uses a regular expression to
match any email address that ends with @cern.ch (the regular
expression must be understandable by Python)
Some other considerations on the management of output formats:
- Format outputs must be placed inside directory
/etc/bibformat/outputs/ of your CDS Invenio installation.
- Note that as long as you have not provided a name to an output
THROUGH the web interface, it will not be available as a choice
for your users in some parts of CDS Invenio.
- You should remove output formats THROUGH the web interface.
- The format extension of output format is .bfo
5.2 Format templates specifications
Format templates are written in HTML-like syntax. You can use the
standard HTML and CSS markup languague to do the formatting. The best
thing to do is to create a page in your favourite editor, and once you
are glad with it, add the dynamic part of the page, that is print the
fields of the records. Let's say you have defined this page:
&lt;h1>Some title&lt;/h1>
&lt;p>&lt;i>Abstract: &lt;/i>Some abstract&lt;/p>
Then you want that instead of "Some title" and "Some abstract", the
value of the current record that is being displayed is used. To do so,
you must use a format element brick. Either you know the name of the
brick by heart, or you look for it in the elements documentation (see
section 5.3). For example you would find there that you can print the
title of the record by writting the HTML tag &lt;BFE_TITLE /> in your
format template, with parameter 'default' for a default value.
&lt;h1>&lt;BFE_TITLE default="No Title"/>&lt;/h1>
&lt;p>&lt;BFE_ABSTRACT limit="1" prefix="&lt;i>Abstract: &lt;/i>"
default="No abstract"/>&lt;/p>
Notice that &lt;BFE_ABSTRACT /> has a parameter "limit" that &lt;BFE_title/>
had not ("limit" allows to limit the number of sentences of the
abstract, according to the documentation). Note that while format
elements might have different parameters, they always can take the the
three following ones: "prefix" and "suffix", whose values are printed
only if the element is not empty, and "default", which is printed only
if element is an empty string. We have used "prefix" for the abstract,
so that the label "&lt;i>Abstract: &lt;/i>" is only printed if the record
has an abstract.
You should also provide these tags in all of your templates:
-&lt;name>a name for this template in the admin web interface&lt;/name>
-&lt;description>a description to be used in admin web interface for
this template&lt;/description>
Another feature of the templates is the support for multi-languages
outputs. You can include &lt;lang> tags, which contain tags labeled with
the names of the languages supported in CDS Invenio. For example, one
might write:
&lt;lang>&lt;en>A record:&lt;/en>&lt;fr>Une notice:&lt;/fr>&lt;/lang>
&lt;h1>&lt;BFE_TITLE default="No Title"/>&lt;/h1>
&lt;p>&lt;BFE_ABSTRACT limit="1" prefix="&lt;i>Abstract: &lt;/i>"
default="No abstract"/>&lt;/p>
When doing this you should at least make sure that the default
language of your server installation is available in each &lt;lang>
tag. It is the one that is used if the requested language to display
the record is not available. Note that we could also provide a
translation in a similar way for the "No Title" default value inside
&lt;BFE_Title /> tag.
Some other considerations on the use of elements inside templates:
-Format elements names are not case sensitive
-Format elements names always start with &lt;BFE_
-Format elements parameters can contain '&lt;' characters,
and quotes different from the kind that delimit parameters (you can
for example have &lt;BFE_Title default='&lt;a href="#">No Title&lt;/a>'/> )
-Format templates must be placed inside the directory
/etc/bibformat/templates/ of your CDS Invenio installation
-The format extension of a template is .bft
Trick: you can use the &lt;BFE_FIELD tag="245__a" /> to print the value
of any field 245 $a in your templates. This practice is however not
recommended because it would necessitate to revise all format
templates if you change meaning of the MARC code schema.
5.3 Format elements specifications
Format elements are the bricks used in format templates to provide the
dynamic contents inside format templates.
For the most basic format elements, you do not even need to write
them: as long as you define `tag names' for MARC tags in the BibIndex
Admin's Manage logical fields interface (database table tag),
BibFormat knows which field must be printed when &lt;BFE_tag_name/> is
used inside a template.
However for more complex processing, you will need to write a format
element. A format element is written in Python. Therefore its file
extension is ".py". The name you choose for the file is the one that
will be used inside format template to call the element, so choose it
carefully such that it is not too long, but self explanatory (you can
prefix the filename with BFE or not, but the element will always be called
with prefix &lt;BFE_ inside templates). Then you just need to drop the
file in the /lib/python/invenio/bibformat_elements/ directory
of your CDS Invenio installation. Inside your file you have to define a
function named "format", which takes at least a "bfo" parameter (bfo
for BibFormat Object). The function must return a string:
def format(bfo):
out = ""
return out
You can have as many parameters as you want, as long as you make sure
that parameter bfo is here. Let's see how to define an element that
will print a brief title. It will take a parameter 'limit' that will
limit the number of characters printed. We can provide some
documentation for the elemen in the docstring of the
function.
def format(bfo, limit="10"):
"""
Prints a short title
@param limit a limit for the number of printed characters
"""
out = ""
return out
Note that we put a default value of 10 in the 'limit' parameter. To
get some value of a field, we must request the 'bfo' object. For
example we can get the value of field 245.a (field "title"):
def format(bfo, limit="10"):
"""
Prints a short title
@param limit a limit for the number of printed characters
"""
title = bfo.field('245.a')
limit = int(limit)
if limit > len(title):
limit = len(title)
return title[:limit]
As format elements are written in Python, we have decided not to give
permission to edit elements through the web interface. Firstly for
security reasons. Secondly because Python requires correct indentation,
which is difficult to achieve through a web interface.
You can have access to the documentation of your element through a web
interface. This is very useful when you are writing a format template,
to see which elements are available, what they do, which parameters they
take, what are the default values of parameters, etc. The
documentation is automatically extracted from format elements.
Here follows an sample documentation generated for the element
&lt;BFE_TITLE />:
+--------------------------------------------------------------------------------------------+
| TITLE |
| ----- |
| &lt;BFE_TITLE separator="..." prefix="..." suffix="..." default="..." /> |
| |
| Prints the title of a record. |
| |
| Parameters: |
| separator - separator between the different titles. |
| prefix - A prefix printed only if the record has a value for this element. |
| suffix - A suffix printed only if the record has a value for this element. |
| default - A default value printed if the record has no value for this element. |
| |
| See also: |
| Format templates that use this element |
| The Python code of this element |
+--------------------------------------------------------------------------------------------+
The more you provide documentation in the docstring of your elements,
the easier it will be to write format template afterwards.
Some remarks concerning format elements:
-parameters are always string values
-if no value is given as parameter in format the template, then the
value of parameter is "" (emtpy string)
-the docstring should contain a description, followed by
"@param parameter some description for parameter" for each
parameter (to give description for each parameter
in element documentation), and @see an_element.py, another_element.py
(to link to other elements in the documentation). Similar to JavaDoc.
-the following names cannot be used as parameters:
"default", "prefix", "suffix" and escape. They can however always be
used in the format template for any element.
Another important remark concerns the 'escaping' of output of format
elements. In most cases, format elements output is to be used for
HTML/XML. Therefore special characters such as < or & have to be
'escaped', replaced by '&lt;' and '&amp;'. This is why all outputs
produced by format elements are automatically escaped by BibFormat,
unless specified otherwise. This means that you do not have to care
about meta-data that would break your HTML displaying or XML export
such as a physics formula like 'a < b'. Please also note that value
given in 'prefix', 'suffix' and 'default' parameters are not escaped,
such that you can safely use HTML tags for these.
There are always cases where the default 'escaping' behaviour of
BibFormat is not desired. For example when you explicitely output HTML
text, like links: you do not want to see them escaped. The first way
to avoid this is to modify the call to your format element in the
format template, by setting the default 'escape' parameter to 0:
<BFE_ABSTRACT escape='0'>
This is however inconvenient as you have to possibly need to modify a
lot of templates. The other way of doing is to add another function to
your format element, named 'escape':
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
In that way all calls to your format element will produce unescaped
output. You will have to take care of escaping values "manually" in
your format element code, in order to avoid non valid outputs or XSS
vulnerabilities. There are methods to ease the escaping in your code
described in section 1.
Please also note that if you use this method, your element can still
be escaped if a call to your element from a format template
explicitely specifies to escape value using parameter 'escape'.
5.4 Knowledge bases specifications
Knowledge bases cannot be managed through configuration files.
You can very easily add new bases and mappings using the given web GUI.
-- End of file --
</pre>
</protect>
diff --git a/modules/bibformat/doc/hacking/bibformat-internals.webdoc b/modules/bibformat/doc/hacking/bibformat-internals.webdoc
index 0d339a176..ec69a228d 100644
--- a/modules/bibformat/doc/hacking/bibformat-internals.webdoc
+++ b/modules/bibformat/doc/hacking/bibformat-internals.webdoc
@@ -1,35 +1,35 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibFormat Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>This page summarizes all the information suitable to dig inside
BibFormat internals.</p>
<blockquote>
<dl>
<dt><a href="bibformat-api">BibFormat API</a> <dd>Explains how to call
the formatting engine from your Python programs, should a need be.
</dl>
</blockquote>
diff --git a/modules/bibformat/etc/format_templates/Default_HTML_actions.bft b/modules/bibformat/etc/format_templates/Default_HTML_actions.bft
index 41ad67f7b..84416c0be 100644
--- a/modules/bibformat/etc/format_templates/Default_HTML_actions.bft
+++ b/modules/bibformat/etc/format_templates/Default_HTML_actions.bft
@@ -1,14 +1,14 @@
<name>Actions HTML detailed</name>
<description>Output the various actions available on a record</description>
<ul class="detailedrecordactions">
- <li><a href="<BFE_SERVER_INFO var='weburl'/>yourbaskets/add?ln=<BFE_SERVER_INFO var='lang'/>&amp;recid=<BFE_RECORD_ID/>">_(Add to personal basket)_</a></li>
+ <li><a href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>yourbaskets/add?ln=<BFE_SERVER_INFO var='lang'/>&amp;recid=<BFE_RECORD_ID/>">_(Add to personal basket)_</a></li>
<li>_(Export as)_
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='weburl'/>record/<BFE_RECORD_ID/>/export/hx">BibTeX</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='weburl'/>record/<BFE_RECORD_ID/>/export/hm">MARC</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='weburl'/>record/<BFE_RECORD_ID/>/export/xm">MARCXML</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='weburl'/>record/<BFE_RECORD_ID/>/export/xd">DC</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='weburl'/>record/<BFE_RECORD_ID/>/export/xe">EndNote</a>,
- <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='weburl'/>record/<BFE_RECORD_ID/>/export/xn">NLM</a></li>
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/hx">BibTeX</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/hm">MARC</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xm">MARCXML</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xd">DC</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xe">EndNote</a>,
+ <a style="text-decoration:underline;font-weight:normal" href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>record/<BFE_RECORD_ID/>/export/xn">NLM</a></li>
<BFE_EDIT_RECORD prefix="<li>" suffix="</li>" />
</ul>
\ No newline at end of file
diff --git a/modules/bibformat/lib/bibformat.py b/modules/bibformat/lib/bibformat.py
index 22a1a70e1..da7884135 100644
--- a/modules/bibformat/lib/bibformat.py
+++ b/modules/bibformat/lib/bibformat.py
@@ -1,449 +1,447 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Format records using specified format.
API functions: format_record, format_records, create_excel,
get_output_format_content_type
Used to wrap the BibFormat engine and associated functions. This is
also where special formatting of multiple records (that the engine
does not handle, as it works on a single record basis) should be put,
with name create_*.
SEE: bibformat_utils.py
FIXME: currently copies record_exists() code from search engine. Refactor later.
"""
__revision__ = "$Id$"
import zlib
from invenio import bibformat_dblayer
from invenio import bibformat_engine
from invenio import bibformat_utils
-from invenio.config import CFG_SITE_LANG, weburl, CFG_PATH_PHP
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, CFG_PATH_PHP
from invenio.bibformat_config import CFG_BIBFORMAT_USE_OLD_BIBFORMAT
try:
import invenio.template
websearch_templates = invenio.template.load('websearch')
except:
pass
import getopt
import sys
# Functions to format a single record
##
def format_record(recID, of, ln=CFG_SITE_LANG, verbose=0, search_pattern=[],
xml_record=None, user_info=None, on_the_fly=False):
"""
Formats a record given output format.
Returns a formatted version of the record in the specified
language, search pattern, and with the specified output format.
The function will define which format template must be applied.
The record to be formatted can be specified with its ID (with
'recID' parameter) or given as XML representation(with
'xml_record' parameter). If both are specified 'recID' is ignored.
'user_info' allows to grant access to some functionalities on a
page depending on the user's priviledges. The 'user_info' object
makes sense only in the case of on-the-fly formatting. 'user_info'
is the same object as the one returned by
'webuser.collect_user_info(req)'
@param recID the ID of record to format
@param of an output format code (or short identifier for the output format)
@param ln the language to use to format the record
@param verbose the level of verbosity from 0 to 9 (O: silent,
5: errors,
7: errors and warnings, stop if error in format elements
9: errors and warnings, stop if error (debug mode ))
@param search_pattern list of strings representing the user request in web interface
@param xml_record an xml string represention of the record to format
@param user_info the information of the user who will view the formatted page (if applicable)
@param on_the_fly if False, try to return an already preformatted version of the record in the database
@return formatted record
"""
out = ""
if verbose == 9:
out += """\n<span class="quicknote">
Formatting record %i with output format %s.
</span>""" % (recID, of)
############### FIXME: REMOVE WHEN MIGRATION IS DONE ###############
if CFG_BIBFORMAT_USE_OLD_BIBFORMAT and CFG_PATH_PHP:
return bibformat_engine.call_old_bibformat(recID, format=of, on_the_fly=on_the_fly)
############################# END ##################################
if not on_the_fly and \
(ln==CFG_SITE_LANG or CFG_BIBFORMAT_USE_OLD_BIBFORMAT):
# Try to fetch preformatted record
# Only possible for records formatted in CFG_SITE_LANG language (other are never stored)
res = bibformat_dblayer.get_preformatted_record(recID, of)
if res is not None:
# record 'recID' is formatted in 'of', so return it
if verbose == 9:
last_updated = bibformat_dblayer.get_preformatted_record_date(recID, of)
out += """\n<br/><span class="quicknote">
Found preformatted output for record %i (cache updated on %s).
</span><br/>""" % (recID, last_updated)
out += res
return out
else:
if verbose == 9:
out+= """\n<br/><span class="quicknote">
No preformatted output found for record %s.
</span>"""% recID
# Live formatting of records in all other cases
if verbose == 9:
out+= """\n<br/><span class="quicknote">
Formatting record %i on-the-fly.
</span>""" % recID
try:
out += bibformat_engine.format_record(recID=recID,
of=of,
ln=ln,
verbose=verbose,
search_pattern=search_pattern,
xml_record=xml_record,
user_info=user_info)
return out
except Exception, e:
#Failsafe execution mode
if verbose == 9:
out+= """\n<br/><span class="quicknote">
An error occured while formatting record %i. (%s)
</span>""" % (recID, str(e))
if of.lower() == 'hd':
if verbose == 9:
out+= """\n<br/><span class="quicknote">
Formatting record %i with websearch_templates.tmpl_print_record_detailed.
</span><br/>""" % recID
return out + websearch_templates.tmpl_print_record_detailed(
ln = ln,
recID = recID,
- weburl = weburl,
)
if verbose == 9:
out+= """\n<br/><span class="quicknote">
Formatting record %i with websearch_templates.tmpl_print_record_brief.
</span><br/>""" % recID
return out + websearch_templates.tmpl_print_record_brief(ln = ln,
recID = recID,
- weburl = weburl,
)
def record_get_xml(recID, format='xm', decompress=zlib.decompress):
"""
Returns an XML string of the record given by recID.
The function builds the XML directly from the database,
without using the standard formatting process.
'format' allows to define the flavour of XML:
- 'xm' for standard XML
- 'marcxml' for MARC XML
- 'oai_dc' for OAI Dublin Core
- 'xd' for XML Dublin Core
If record does not exist, returns empty string.
@param recID the id of the record to retrieve
@return the xml string of the record
"""
return bibformat_utils.record_get_xml(recID=recID, format=format, decompress=decompress)
# Helper functions to do complex formatting of multiple records
#
# You should not modify format_records when adding a complex
# formatting of multiple records, but add a create_* method
# that relies on format_records to do the formatting.
##
def format_records(recIDs, of, ln=CFG_SITE_LANG, verbose=0, search_pattern=None,
xml_records=None, user_info=None, record_prefix=None,
record_separator=None, record_suffix=None, prologue="",
epilogue="", req=None, on_the_fly=False):
"""
Returns a list of formatted records given by a list of record IDs
or a list of records as xml. Adds a prefix before each record, a
suffix after each record, plus a separator between records.
Also add optional prologue and epilogue to the complete formatted
list.
You can either specify a list of record IDs to format, or a list
of xml records, but not both (if both are specified recIDs is
ignored).
'record_separator' is a function that returns a string as
separator between records. The function must take an integer as
unique parameter, which is the index in recIDs (or xml_records) of
the record that has just been formatted. For example separator(i)
must return the separator between recID[i] and recID[i+1].
Alternatively separator can be a single string, which will be used
to separate all formatted records. The same applies to
'record_prefix' and 'record_suffix'.
'req' is an optional parameter on which the result of the function
are printed lively (prints records after records) if it is given.
Note that you should set 'req' content-type by yourself, and send
http header before calling this function as it will not do it.
This function takes the same parameters as 'format_record' except for:
@param recIDs a list of record IDs
@param xml_records a list of xml string representions of the records to format
@param header a string printed before all formatted records
@param separator either a string or a function that returns string to separate formatted records
@param req an optional request object where to print records
@param on_the_fly if False, try to return an already preformatted version of the record in the database
"""
if req is not None:
req.write(prologue)
formatted_records = ''
#Fill one of the lists with Nones
if xml_records is not None:
recIDs = map(lambda x:None, xml_records)
else:
xml_records = map(lambda x:None, recIDs)
total_rec = len(recIDs)
last_iteration = False
for i in range(total_rec):
if i == total_rec - 1:
last_iteration = True
#Print prefix
if record_prefix is not None:
if isinstance(record_prefix, str):
formatted_records += record_prefix
if req is not None:
req.write(record_prefix)
else:
string_prefix = record_prefix(i)
formatted_records += string_prefix
if req is not None:
req.write(string_prefix)
#Print formatted record
formatted_record = format_record(recIDs[i], of, ln, verbose, \
search_pattern, xml_records[i],\
user_info, on_the_fly)
formatted_records += formatted_record
if req is not None:
req.write(formatted_record)
#Print suffix
if record_suffix is not None:
if isinstance(record_suffix, str):
formatted_records += record_suffix
if req is not None:
req.write(record_suffix)
else:
string_suffix = record_suffix(i)
formatted_records += string_suffix
if req is not None:
req.write(string_suffix)
#Print separator if needed
if record_separator is not None and not last_iteration:
if isinstance(record_separator, str):
formatted_records += record_separator
if req is not None:
req.write(record_separator)
else:
string_separator = record_separator(i)
formatted_records += string_separator
if req is not None:
req.write(string_separator)
if req is not None:
req.write(epilogue)
return prologue + formatted_records + epilogue
def create_excel(recIDs, req=None, ln=CFG_SITE_LANG):
"""
Returns an Excel readable format containing the given recIDs.
If 'req' is given, also prints the output in 'req' while individual
records are being formatted.
This method shows how to create a custom formatting of multiple
records.
The excel format is a basic HTML table that most spreadsheets
applications can parse.
@param recIDs a list of record IDs
@return a string in Excel format
"""
# Prepare the column headers to display in the Excel file
column_headers_list = ['Title',
'Authors',
'Addresses',
'Affiliation',
'Date',
'Publisher',
'Place',
'Abstract',
'Keywords',
'Notes']
# Prepare Content
column_headers = '</b></td><td style="border-color:black; border-style:solid; border-width:thin; background-color:black;color:white"><b>'.join(column_headers_list) + ''
column_headers = '<table style="border-collapse: collapse;">\n'+ '<td style="border-color:black; border-style:solid; border-width:thin; background-color:black;color:white"><b>' + column_headers + '</b></td>'
footer = '</table>'
#Apply content_type and print column headers
if req is not None:
req.content_type = get_output_format_content_type('excel')
req.headers_out["Content-Disposition"] = "inline; filename=%s" % 'results.xls'
req.send_http_header()
#Format the records
excel_formatted_records = format_records(recIDs, 'excel', ln=CFG_SITE_LANG,
record_separator='\n',
prologue = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><table>',
epilogue = footer,
req=req)
return excel_formatted_records
# Utility functions
##
def get_output_format_content_type(of):
"""
Returns the content type (eg. 'text/html' or 'application/ms-excel') \
of the given output format.
@param of the code of output format for which we want to get the content type
"""
content_type = bibformat_dblayer.get_output_format_content_type(of)
if content_type == '':
content_type = 'text/html'
return content_type
def usage(exitcode=1, msg=""):
"""Prints usage info."""
if msg:
sys.stderr.write("Error: %s.\n" % msg)
print """BibFormat: outputs the result of the formatting of a record.
Usage: bibformat required [options]
Examples:
$ bibformat -i 10 -o HB
$ bibformat -i 10,11,13 -o HB
$ bibformat -i 10:13
$ bibformat -i 10 -o HB -v 9
Required:
-i, --id=ID[ID2,ID3:ID5] ID (or range of IDs) of the record(s) to be formatted.
Options:
-o, --output=CODE short code of the output format used for formatting (default HB).
-l, --lang=LN language used for formatting.
-y, --onthefly on-the-fly formatting, avoiding caches created by BibReformat.
General options:
-h, --help print this help and exit
-v, --verbose=LEVEL verbose level (from 0 to 9, default 0)
-V --version print the script version
"""
sys.exit(exitcode)
def main():
"""main entry point for biformat via command line"""
options = {} # will hold command-line options
options["verbose"] = 0
options["onthefly"] = False
options["lang"] = CFG_SITE_LANG
options["output"] = "HB"
options["recID"] = None
try:
opts, args = getopt.getopt(sys.argv[1:],
"hVv:yl:i:o:",
["help",
"version",
"verbose=",
"onthefly",
"lang=",
"id=",
"output="])
except getopt.GetoptError, err:
usage(1, err)
pass
try:
for opt in opts:
if opt[0] in ["-h", "--help"]:
usage(0)
elif opt[0] in ["-V", "--version"]:
print __revision__
sys.exit(0)
elif opt[0] in ["-v", "--verbose"]:
options["verbose"] = int(opt[1])
elif opt[0] in ["-y", "--onthefly"]:
options["onthefly"] = True
elif opt[0] in ["-l", "--lang"]:
options["lang"] = opt[1]
elif opt[0] in ["-i", "--id"]:
recIDs = []
for recID in opt[1].split(','):
if ":" in recID:
start = int(recID.split(':')[0])
end = int(recID.split(':')[1])
recIDs.extend(range(start, end))
else:
recIDs.append(int(recID))
options["recID"] = recIDs
elif opt[0] in ["-o", "--output"]:
options["output"] = opt[1]
if options["recID"] == None:
usage(1, "-i argument is needed")
except StandardError, e:
usage(e)
print format_records(recIDs=options["recID"],
of=options["output"],
ln=options["lang"],
verbose=options["verbose"],
on_the_fly=options["onthefly"])
return
if __name__ == "__main__":
main()
diff --git a/modules/bibformat/lib/bibformat_regression_tests.py b/modules/bibformat/lib/bibformat_regression_tests.py
index d8ffe5139..afd74bda3 100644
--- a/modules/bibformat/lib/bibformat_regression_tests.py
+++ b/modules/bibformat/lib/bibformat_regression_tests.py
@@ -1,410 +1,410 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSearch module regression tests."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl, CFG_SITE_LANG
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG
from invenio.testutils import make_test_suite, \
warn_user_about_tests_and_run, \
test_web_page_content
from invenio.bibformat import format_record
class BibFormatAPITest(unittest.TestCase):
"""Check BibFormat API"""
def test_basic_formatting(self):
"""bibformat - Checking BibFormat API"""
result = format_record(recID=73,
of='hx',
ln=CFG_SITE_LANG,
verbose=0,
search_pattern=[],
xml_record=None,
user_info=None,
on_the_fly=True)
- pageurl = weburl + '/record/73?of=hx'
+ pageurl = CFG_SITE_URL + '/record/73?of=hx'
result = test_web_page_content(pageurl,
expected_text=result)
class BibFormatBibTeXTest(unittest.TestCase):
"""Check output produced by BibFormat for BibTeX output for
various records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_74_hx = '''<pre>
@article{Wang:74,
author = "Wang, B and Lin, C Y and Abdalla, E",
title = "Quasinormal modes of Reissner-Nordstrom Anti-de Sitter
Black Holes",
journal = "Phys. Lett., B",
number = "hep-th/0003295",
volume = "481",
pages = "79-88",
year = "2000",
}
</pre>'''
def test_bibtex_output(self):
"""bibformat - BibTeX output"""
- pageurl = weburl + '/record/74?of=hx'
+ pageurl = CFG_SITE_URL + '/record/74?of=hx'
result = test_web_page_content(pageurl,
expected_text=self.record_74_hx)
self.assertEqual([], result)
class BibFormatDetailedHTMLTest(unittest.TestCase):
"""Check output produced by BibFormat for detailed HTML ouput for
various records"""
def setUp(self):
"""Prepare some ideal outputs"""
# Record 7 (Article)
self.record_74_hd_header = '''<table border="0" width="100%">
<tr>
<td>Published Article<small> / Particle Physics - Theory</small></td>
<td><small><strong></strong></small></td>
<td align="right"><strong>hep-th/0003295</strong></td>
</tr>
</table>'''
self.record_74_hd_title = '''<center><big><big><strong>Quasinormal modes of Reissner-Nordstrom Anti-de Sitter Black Holes</strong></big></big></center>'''
- self.record_74_hd_authors = '''<a href="%(weburl)s/search?f=author&amp;p=Wang%%2C%%20B&amp;ln=%(lang)s">Wang, B</a><small> (Fudan University)</small> ; <a href="%(weburl)s/search?f=author&amp;p=Lin%%2C%%20C%%20Y&amp;ln=%(lang)s">Lin, C Y</a> ; <a href="%(weburl)s/search?f=author&amp;p=Abdalla%%2C%%20E&amp;ln=%(lang)s">Abdalla, E</a><br />'''% \
- {'weburl' : weburl,
+ self.record_74_hd_authors = '''<a href="%(siteurl)s/search?f=author&amp;p=Wang%%2C%%20B&amp;ln=%(lang)s">Wang, B</a><small> (Fudan University)</small> ; <a href="%(siteurl)s/search?f=author&amp;p=Lin%%2C%%20C%%20Y&amp;ln=%(lang)s">Lin, C Y</a> ; <a href="%(siteurl)s/search?f=author&amp;p=Abdalla%%2C%%20E&amp;ln=%(lang)s">Abdalla, E</a><br />'''% \
+ {'siteurl' : CFG_SITE_URL,
'lang': CFG_SITE_LANG}
self.record_74_hd_abstract = '''<small><strong>Abstract: </strong>Complex frequencies associated with quasinormal modes for large Reissner-Nordstr$\ddot{o}$m Anti-de Sitter black holes have been computed. These frequencies have close relation to the black hole charge and do not linearly scale withthe black hole temperature as in Schwarzschild Anti-de Sitter case. In terms of AdS/CFT correspondence, we found that the bigger the black hole charge is, the quicker for the approach to thermal equilibrium in the CFT. The propertiesof quasinormal modes for $l&gt;0$ have also been studied.</small><br />'''
self.record_74_hd_pubinfo = '''<strong>Published in: </strong><a href="http://weblib.cern.ch/cgi-bin/ejournals?publication=Phys.%20Lett.%2C%20B&amp;volume=481&amp;year=2000&amp;page=79">Phys. Lett., B :481 2000 79-88</a>'''
self.record_74_hd_fulltext = '''0003295.pdf"><img style="border:none"'''
- self.record_74_hd_citations = '''<strong>Cited by:</strong> try citation search for <a href="%(weburl)s/search?f=reference&amp;p=hep-th/0003295&amp;ln=%(lang)s">hep-th/0003295</a>'''% \
- {'weburl' : weburl,
+ self.record_74_hd_citations = '''<strong>Cited by:</strong> try citation search for <a href="%(siteurl)s/search?f=reference&amp;p=hep-th/0003295&amp;ln=%(lang)s">hep-th/0003295</a>'''% \
+ {'siteurl' : CFG_SITE_URL,
'lang': CFG_SITE_LANG}
self.record_74_hd_references = '''<li><small>[17]</small> <small>A. Chamblin, R. Emparan, C. V. Johnson and R. C. Myers, Phys. Rev., D60: 104026 (1999) 5070 90 110 130 150 r+ 130 230 330 50 70 90 110 130 150 r+</small> </li>'''
# Record 7 (Picture)
self.record_7_hd_header = '''<table border="0" width="100%">
<tr>
<td>Pictures<small> / Life at CERN</small></td>
<td><small><strong></strong></small></td>
<td align="right"><strong>CERN-GE-9806033</strong></td>
</tr>
</table>'''
self.record_7_hd_title = '''<center><big><big><strong>Tim Berners-Lee</strong></big></big></center>'''
self.record_7_hd_date = '''<center>28 Jun 1998</center>'''
self.record_7_hd_abstract = '''<p><span class="blocknote">
Caption</span><br /> <small>Conference "Internet, Web, What's next?" on 26 June 1998 at CERN : Tim Berners-Lee, inventor of the World-Wide Web and Director of the W3C, explains how the Web came to be and give his views on the future.</small></p><p><span class="blocknote">
Légende</span><br /><small>Conference "Internet, Web, What's next?" le 26 juin 1998 au CERN: Tim Berners-Lee, inventeur du World-Wide Web et directeur du W3C, explique comment le Web est ne, et donne ses opinions sur l'avenir.</small></p>'''
- self.record_7_hd_resource = '''<img src="%s/record/7/files/icon-9806033.gif" alt="" /><br /><font size="-2"><b>© CERN Geneva</b></font>''' % weburl
- self.record_7_hd_resource_link = '%s/record/7/files/9806033.jpeg' % weburl
+ self.record_7_hd_resource = '''<img src="%s/record/7/files/icon-9806033.gif" alt="" /><br /><font size="-2"><b>© CERN Geneva</b></font>''' % CFG_SITE_URL
+ self.record_7_hd_resource_link = '%s/record/7/files/9806033.jpeg' % CFG_SITE_URL
def test_detailed_html_output(self):
"""bibformat - Detailed HTML output"""
# Test record 74 (Article)
- pageurl = weburl + '/record/74?of=hd'
+ pageurl = CFG_SITE_URL + '/record/74?of=hd'
result = test_web_page_content(pageurl,
expected_text=[self.record_74_hd_header,
self.record_74_hd_title,
self.record_74_hd_authors,
self.record_74_hd_abstract,
self.record_74_hd_pubinfo,
self.record_74_hd_fulltext,
#self.record_74_hd_citations,
#self.record_74_hd_references
])
self.assertEqual([], result)
# Test record 7 (Picture)
- pageurl = weburl + '/record/7?of=hd'
+ pageurl = CFG_SITE_URL + '/record/7?of=hd'
result = test_web_page_content(pageurl,
expected_text=[self.record_7_hd_header,
self.record_7_hd_title,
self.record_7_hd_date,
self.record_7_hd_abstract,
self.record_7_hd_resource,
self.record_7_hd_resource_link])
self.assertEqual([], result)
def test_detailed_html_edit_record(self):
"""bibformat - Detailed HTML output edit record link presence"""
- pageurl = weburl + '/record/74?of=hd'
+ pageurl = CFG_SITE_URL + '/record/74?of=hd'
result = test_web_page_content(pageurl, username='admin',
expected_text="Edit This Record")
self.assertEqual([], result)
def test_detailed_html_no_error_message(self):
"""bibformat - Detailed HTML output without error message"""
# No error message should be displayed in the web interface, whatever happens
- pageurl = weburl + '/record/74?of=hd'
+ pageurl = CFG_SITE_URL + '/record/74?of=hd'
result = test_web_page_content(pageurl, username='admin',
expected_text=["Exception",
"Could not"])
self.assertNotEqual([], result)
- pageurl = weburl + '/record/7?of=hd'
+ pageurl = CFG_SITE_URL + '/record/7?of=hd'
result = test_web_page_content(pageurl, username='admin',
expected_text=["Exception",
"Could not"])
self.assertNotEqual([], result)
class BibFormatNLMTest(unittest.TestCase):
"""Check output produced by BibFormat for NLM output for various
records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_70_xn = '''<?xml version="1.0" encoding="UTF-8"?>
<articles>
<article xmlns:xlink="http://www.w3.org/1999/xlink/">
<front>
<journal-meta>
<journal-title>J. High Energy Phys.</journal-title>
<abbrev-journal-title>J. High Energy Phys.</abbrev-journal-title>
<issn>1126-6708</issn>
</journal-meta>
<article-meta>
<title-group>
<article-title>AdS/CFT For Non-Boundary Manifolds</article-title>
</title-group>
<contrib-group>
<contrib contrib-type="author">
<name>
<surname>McInnes</surname>
<given-names>B</given-names>
</name>
<aff>
<institution>National University of Singapore</institution>
</aff>
</contrib>
</contrib-group>
<pub-date pub-type="pub">
<year>2000</year>
</pub-date>
<volume>05</volume>
<fpage/>
<lpage/>
- <self-uri xlink:href="%(weburl)s/record/70"/>
- <self-uri xlink:href="%(weburl)s/record/70/files/0003291.pdf"/>
- <self-uri xlink:href="%(weburl)s/record/70/files/0003291.ps.gz"/>
+ <self-uri xlink:href="%(siteurl)s/record/70"/>
+ <self-uri xlink:href="%(siteurl)s/record/70/files/0003291.pdf"/>
+ <self-uri xlink:href="%(siteurl)s/record/70/files/0003291.ps.gz"/>
</article-meta>
<abstract>In its Euclidean formulation, the AdS/CFT correspondence begins as a study of Yang-Mills conformal field theories on the sphere, S^4. It has been successfully extended, however, to S^1 X S^3 and to the torus T^4. It is natural tohope that it can be made to work for any manifold on which it is possible to define a stable Yang-Mills conformal field theory. We consider a possible classification of such manifolds, and show how to deal with the most obviousobjection : the existence of manifolds which cannot be represented as boundaries. We confirm Witten's suggestion that this can be done with the help of a brane in the bulk.</abstract>
</front>
<article-type>research-article</article-type>
<ref/>
</article>
-</articles>''' % {'weburl': weburl}
+</articles>''' % {'siteurl': CFG_SITE_URL}
def test_nlm_output(self):
"""bibformat - NLM output"""
- pageurl = weburl + '/record/70?of=xn'
+ pageurl = CFG_SITE_URL + '/record/70?of=xn'
result = test_web_page_content(pageurl,
expected_text=self.record_70_xn)
self.assertEqual([], result)
class BibFormatBriefHTMLTest(unittest.TestCase):
"""Check output produced by BibFormat for brief HTML ouput for
various records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_76_hb = '''<strong>Ιθάκη</strong>
/ <a href="%s/search?f=author&amp;p=%%CE%%9A%%CE%%B1%%CE%%B2%%CE%%AC%%CF%%86%%CE%%B7%%CF%%82%%2C%%20%%CE%%9A%%20%%CE%%A0&amp;ln=%s">Καβάφης, Κ Π</a>
<br /><small>
Σα βγεις στον πηγαιμό για την Ιθάκη,<br />
να εύχεσαι νάναι μακρύς ο δρόμος,<br />
-γεμάτος περιπέτειες, γεμάτος γνώσεις [...] </small>''' % (weburl, CFG_SITE_LANG)
+γεμάτος περιπέτειες, γεμάτος γνώσεις [...] </small>''' % (CFG_SITE_URL, CFG_SITE_LANG)
def test_brief_html_output(self):
"""bibformat - Brief HTML output"""
- pageurl = weburl + '/record/76?of=HB'
+ pageurl = CFG_SITE_URL + '/record/76?of=HB'
result = test_web_page_content(pageurl,
expected_text=self.record_76_hb)
self.assertEqual([], result)
class BibFormatMARCXMLTest(unittest.TestCase):
"""Check output produced by BibFormat for MARCXML ouput for various records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_9_xm = '''<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<controlfield tag="001">9</controlfield>
<datafield tag="041" ind1=" " ind2=" ">
<subfield code="a">eng</subfield>
</datafield>
<datafield tag="088" ind1=" " ind2=" ">
<subfield code="a">PRE-25553</subfield>
</datafield>
<datafield tag="088" ind1=" " ind2=" ">
<subfield code="a">RL-82-024</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Ellis, J</subfield>
<subfield code="u">University of Oxford</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Grand unification with large supersymmetry breaking</subfield>
</datafield>
<datafield tag="260" ind1=" " ind2=" ">
<subfield code="c">Mar 1982</subfield>
</datafield>
<datafield tag="300" ind1=" " ind2=" ">
<subfield code="a">18 p</subfield>
</datafield>
<datafield tag="650" ind1="1" ind2="7">
<subfield code="2">SzGeCERN</subfield>
<subfield code="a">General Theoretical Physics</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Ibanez, L E</subfield>
</datafield>
<datafield tag="700" ind1=" " ind2=" ">
<subfield code="a">Ross, G G</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="0">
<subfield code="y">1982</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="0">
<subfield code="b">11</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="u">Oxford Univ.</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="u">Univ. Auton. Madrid</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="u">Rutherford Lab.</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="1">
<subfield code="c">1990-01-28</subfield>
<subfield code="l">50</subfield>
<subfield code="m">2002-01-04</subfield>
<subfield code="o">BATCH</subfield>
</datafield>
<datafield tag="909" ind1="C" ind2="S">
<subfield code="s">h</subfield>
<subfield code="w">1982n</subfield>
</datafield>
<datafield tag="980" ind1=" " ind2=" ">
<subfield code="a">PREPRINT</subfield>
</datafield>
</record>
</collection>'''
def test_marcxml_output(self):
"""bibformat - MARCXML output"""
- pageurl = weburl + '/record/9?of=xm'
+ pageurl = CFG_SITE_URL + '/record/9?of=xm'
result = test_web_page_content(pageurl,
expected_text=self.record_9_xm)
self.assertEqual([], result)
class BibFormatMARCTest(unittest.TestCase):
"""Check output produced by BibFormat for MARC ouput for various
records"""
def setUp(self):
"""Prepare some ideal outputs"""
self.record_29_hm = '''000000029 001__ 29
000000029 041__ $$aeng
000000029 080__ $$a517.11
000000029 100__ $$aKleene, Stephen Cole$$uUniversity of Wisconsin
000000029 245__ $$aIntroduction to metamathematics
000000029 260__ $$aAmsterdam$$bNorth-Holland$$c1952 (repr.1964.)
000000029 300__ $$a560 p
000000029 490__ $$aBibl. Matematica$$v1
000000029 909C0 $$y1952
000000029 909C0 $$b21
000000029 909C1 $$c1990-01-27$$l00$$m2002-04-12$$oBATCH
000000029 909CS $$sm$$w198606
000000029 980__ $$aBOOK'''
def test_marc_output(self):
"""bibformat - MARC output"""
- pageurl = weburl + '/record/29?of=hm'
+ pageurl = CFG_SITE_URL + '/record/29?of=hm'
result = test_web_page_content(pageurl,
expected_text=self.record_29_hm)
self.assertEqual([], result)
class BibFormatTitleFormattingTest(unittest.TestCase):
"""Check title formatting produced by BibFormat."""
def test_subtitle_in_html_brief(self):
"""bibformat - title subtitle in HTML brief formats"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=statistics+computer',
+ test_web_page_content(CFG_SITE_URL + '/search?p=statistics+computer',
expected_text="Statistics: a computer approach"))
def test_subtitle_in_html_detailed(self):
"""bibformat - title subtitle in HTML detailed formats"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=statistics+computer&of=HD',
+ test_web_page_content(CFG_SITE_URL + '/search?p=statistics+computer&of=HD',
expected_text="Statistics: a computer approach"))
def test_title_edition_in_html_brief(self):
"""bibformat - title edition in HTML brief formats"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=2nd',
+ test_web_page_content(CFG_SITE_URL + '/search?p=2nd',
expected_text="Introductory statistics: a decision map; 2nd ed"))
def test_title_edition_in_html_detailed(self):
"""bibformat - title edition in HTML detailed formats"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=2nd&of=HD',
+ test_web_page_content(CFG_SITE_URL + '/search?p=2nd&of=HD',
expected_text="Introductory statistics: a decision map; 2nd ed"))
test_suite = make_test_suite(BibFormatBibTeXTest,
BibFormatDetailedHTMLTest,
BibFormatBriefHTMLTest,
BibFormatNLMTest,
BibFormatMARCTest,
BibFormatMARCXMLTest,
BibFormatAPITest,
BibFormatTitleFormattingTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibformat/lib/bibformat_templates.py b/modules/bibformat/lib/bibformat_templates.py
index be6f468bc..b6ee2c9be 100644
--- a/modules/bibformat/lib/bibformat_templates.py
+++ b/modules/bibformat/lib/bibformat_templates.py
@@ -1,2301 +1,2301 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""HTML Templates for BibFormat administration"""
__revision__ = "$Id$"
# non Invenio imports
import cgi
# Invenio imports
from invenio.messages import gettext_set_language
-from invenio.config import weburl, CFG_SITE_SECURE_URL
+from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL
from invenio.messages import language_list_long
from invenio.config import CFG_PATH_PHP
class Template:
"""Templating class, refer to bibformat.py for examples of call"""
def tmpl_admin_index(self, ln, warnings, is_admin):
"""
Returns the main BibFormat admin page.
@param ln language
@param warnings a list of warnings to display at top of page. None if no warning
@param is_admin indicate if user is authorized to use BibFormat
@return main BibFormat admin page
"""
_ = gettext_set_language(ln) # load the right message language
out = ''
if warnings:
out += '''
<table width="66%%" class="errorbox" style="margin-left: auto; margin-right: auto;">
<tr>
<th class="errorboxheader">
%(warnings)s
</th>
</tr>
</table>
''' % {'warnings': '<br/>'.join(warnings)}
out += '''
<p>
This is where you can edit the formatting styles available for the records. '''
if not is_admin:
out += '''You need to
- <a href="%(weburl)s/youraccount/login?referer=%(weburl)s/admin/bibformat/bibformatadmin.py">login</a> to enter.
- ''' % {'weburl':weburl}
+ <a href="%(siteurl)s/youraccount/login?referer=%(siteurl)s/admin/bibformat/bibformatadmin.py">login</a> to enter.
+ ''' % {'siteurl':CFG_SITE_URL}
out += '''
</p>
<dl>
- <dt><a href="%(weburl)s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%(ln)s">Manage Format Templates</a></dt>
+ <dt><a href="%(siteurl)s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%(ln)s">Manage Format Templates</a></dt>
<dd>Define how to format a record.</dd>
</dl>
<dl>
- <dt><a href="%(weburl)s/admin/bibformat/bibformatadmin.py/output_formats_manage?ln=%(ln)s">Manage Output Formats</a></dt>
+ <dt><a href="%(siteurl)s/admin/bibformat/bibformatadmin.py/output_formats_manage?ln=%(ln)s">Manage Output Formats</a></dt>
<dd>Define which template is applied to which record for a given output.</dd>
</dl>
<dl>
- <dt><a href="%(weburl)s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%(ln)s">Manage Knowledge Bases</a></dt>
+ <dt><a href="%(siteurl)s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%(ln)s">Manage Knowledge Bases</a></dt>
<dd>Define mappings of values, for standardizing records or declaring often used values.</dd>
</dl>
<br/>
<dl>
- <dt><a href="%(weburl)s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%(ln)s">Format Elements Documentation</a></dt>
+ <dt><a href="%(siteurl)s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%(ln)s">Format Elements Documentation</a></dt>
<dd>Documentation of the format elements to be used inside format templates.</dd>
</dl>
<dl>
- <dt><a href="%(weburl)s/help/admin/bibformat-admin-guide">BibFormat Admin Guide</a></dt>
+ <dt><a href="%(siteurl)s/help/admin/bibformat-admin-guide">BibFormat Admin Guide</a></dt>
<dd>Documentation about BibFormat administration</dd>
</dl>
- '''% {'weburl':weburl, 'ln':ln}
+ '''% {'siteurl':CFG_SITE_URL, 'ln':ln}
if CFG_PATH_PHP:
#Show PHP admin only if PHP is enabled
out += '''
<br/><br/><br/><br/>
<div style="background-color:rgb(204, 204, 204);">
<h2><span style="color:rgb(204, 0, 0);">Old</span>
BibFormat admin interface (in gray box)</h2>
<em>
<p>The BibFormat admin interface enables you to specify how the
bibliographic data is presented to the end user in the search
interface and search results pages. For example, you may specify that
titles should be printed in bold font, the abstract in small italic,
etc. Moreover, the BibFormat is not only a simple bibliographic data
<em>output formatter</em>, but also an automated <em>link
constructor</em>. For example, from the information on journal name
and pages, it may automatically create links to publisher's site based
on some configuration rules.
<h2>Configuring BibFormat</h2>
<p>By default, a simple HTML format based on the most common fields
(title, author, abstract, keywords, fulltext link, etc) is defined.
You certainly want to define your own ouput formats in case you have a
specific metadata structure.
<p>Here is a short guide of what you can configure:
<blockquote>
<dl>
<dt><a href="BEH_display.php">Behaviours</a>
<dd>Define one or more output BibFormat behaviours. These are then
passed as parameters to the BibFormat modules while executing
formatting.
<br /><em>Example:</em> You can tell BibFormat that is has to enrich the
incoming metadata file by the created format, or that it only has to
print the format out.
<dt><a href="OAIER_display.php">Extraction Rules</a>
<dd>Define how the metadata tags from input are mapped into internal
BibFormat variable names. The variable names can afterwards be used
in formatting and linking rules.
<br /><em>Example:</em> You can tell that <code>100 $a</code> field
should be mapped into <code>$100.a</code> internal variable that you
could use later.
<dt><a href="LINK_display.php">Link Rules</a>
<dd>Define rules for automated creation of URI links from mapped
internal variables.
<br /><em>Example:</em> You can tell a rule how to create a link to
People database out of the <code>$100.a</code> internal variable
repesenting author's name. (The <code>$100.a</code> variable was mapped
in the previous step, see the Extraction Rules.)
<dt><a href="LINK_FORMAT_display.php">File Formats</a>
<dd>Define file format types based on file extensions. This will be
used when proposing various fulltext services.
<br /><em>Example:</em> You can tell that <code>*.pdf</code> files will
be treated as PDF files.
<dt><a href="UDF_display.php">User Defined Functions (UDFs)</a>
<dd>Define your own functions that you can reuse when creating your
own output formats. This enables you to do complex formatting without
ever touching the BibFormat core code.
<br /><em>Example:</em> You can define a function how to match and
extract email addresses out of a text file.
<dt><a href="FORMAT_display.php">Formats</a>
<dd>Define the output formats, i.e. how to create the output out of
internal BibFormat variables that were extracted in a previous step.
This is the functionality you would want to configure most of the
time. It may reuse formats, user defined functions, knowledge bases,
etc.
<br /><em>Example:</em> You can tell that authors should be printed in
italic, that if there are more than 10 authors only the first three
should be printed, etc.
<dt><a href="KB_display.php">Knowledge Bases (KBs)</a>
<dd>Define one or more knowledge bases that enables you to transform
various forms of input data values into the unique standard form on
the output.
<br /><em>Example:</em> You can tell that <em>Phys Rev D</em> and
<em>Physical Review D</em> are both the same journal and that these
names should be standardized to <em>Phys Rev : D</em>.
<dt><a href="test.php">Execution Test</a>
<dd>Enables you to test your formats on your sample data file. Useful
when debugging newly created formats.
</dl>
</blockquote>
<p>To learn more on BibFormat configuration, you can consult the <a
href="guide.html">BibFormat Admin Guide</a>.</small>
<h2>Running BibFormat</h2>
<h3>From the Web interface</h3>
<p>
Run <a href="BIBREFORMAT_display.php">Reformat Records</a> tool.
This tool permits you to update stored formats for bibliographic records.
<br />
It should normally be used after configuring BibFormat's
<a href="BEH_display.php">Behaviours</a> and
<a href="FORMAT_display.php">Formats</a>.
When these are ready, you can choose to rebuild formats for selected
collections or you can manually enter a search query and the web interface
will accomplish all necessary formatting steps.
<br />
<i>Example:</i> You can request Photo collections to have their HTML
brief formats rebuilt, or you can reformat all the records written by Ellis.
<h3>From the command-line interface</h3>
<p>Consider having an XML MARC data file that is to be uploaded into
the CDS Invenio. (For example, it might have been harvested from other
sources and processed via <a href="../bibconvert/">BibConvert</a>.)
Having configured BibFormat and its default output type behaviour, you
would then run this file throught BibFormat as follows:
<blockquote>
<pre>
$ bibformat < /tmp/sample.xml > /tmp/sample_with_fmt.xml
<pre>
</blockquote>
that would create default HTML formats and would "enrich" the input
XML data file by this format. (You would then continue the upload
procedure by calling successively <a
href="../bibupload/">BibUpload</a> and <a
href="../bibindex/">BibIndex</a>.)
<p>Now consider a different situation. You would like to add a new
possible format, say "HTML portfolio" and "HTML captions" in order to
nicely format multiple photographs in one page. Let us suppose that
these two formats are called <code>hp</code> and <code>hc</code> and
are already loaded in the <code>collection_format</code> table.
(TODO: describe how this is done via WebAdmin.) You would then
proceed as follows: firstly, you would prepare the corresponding <a
href="BEH_display.php">output behaviours</a> called <code>HP</code>
and <code>HC</code> (TODO: note the uppercase!) that would not enrich
the input file but that would produce an XML file with only
<code>001</code> and <code>FMT</code> tags. (This is in order not to
update the bibliographic information but the formats only.) You would
also prepare corresponding <a href="FORMAT_display.php">formats</a>
at the same time. Secondly, you would launch the formatting as
follows:
<blockquote>
<pre>
$ bibformat otype=HP,HC < /tmp/sample.xml > /tmp/sample_fmts_only.xml
<pre>
</blockquote>
that should give you an XML file containing only 001 and FMT tags.
Finally, you would upload the formats:
<blockquote>
<pre>
$ bibupload < /tmp/sample_fmts_only.xml
<pre>
</blockquote>
and that's it. The new formats should now appear in <a
- href="%(weburl)s">WebSearch</a>.
+ href="%(siteurl)s">WebSearch</a>.
</em>
</div>
- ''' % {'weburl':weburl, 'ln':ln}
+ ''' % {'siteurl':CFG_SITE_URL, 'ln':ln}
return out
def tmpl_admin_format_template_show_attributes(self, ln, name, description, filename, editable,
all_templates=[], new=False):
"""
Returns a page to change format template name and description
If template is new, offer a way to create a duplicate from an
existing template
@param ln language
@param name the name of the format
@param description the description of the format
@param filename the filename of the template
@param editable True if we let user edit, else False
@param all_templates a list of tuples (filename, name) of all other templates
@param new if True, the format template has just been added (is new)
@return editor for 'format'
"""
_ = gettext_set_language(ln) # load the right message language
out = ""
out += '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="format_templates_manage?ln=%(ln)s">%(close_editor)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small><a href="format_template_show?ln=%(ln)s&amp;bft=%(filename)s">%(template_editor)s</a></small>&nbsp;</td>
<td>2.&nbsp;<small>%(modify_template_attributes)s</small>&nbsp;</td>
<td>3.&nbsp;<small><a href="format_template_show_dependencies?ln=%(ln)s&amp;bft=%(filename)s">%(check_dependencies)s</a></small>&nbsp;</td>
</tr>
</table><br/>
''' % {'ln':ln,
'menu':_("Menu"),
'filename':filename,
'close_editor': _("Close Editor"),
'modify_template_attributes': _("Modify Template Attributes"),
'template_editor': _("Template Editor"),
'check_dependencies': _("Check Dependencies")
}
disabled = ""
readonly = ""
if not editable:
disabled = 'disabled="disabled"'
readonly = 'readonly="readonly"'
out += '''
<form action="format_template_update_attributes?ln=%(ln)s&amp;bft=%(filename)s" method="POST">
''' % {'ln':ln,
'filename':filename}
if new:
#Offer the possibility to make a duplicate of existing format template code
out += '''
<table><tr>
- <th class="adminheaderleft">Make a copy of format template:&nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#addFormatTemplate">?</a>]</th>
+ <th class="adminheaderleft">Make a copy of format template:&nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#addFormatTemplate">?</a>]</th>
</tr>
<tr>
<td><select tabindex="1" name="duplicate" id="duplicate" %(readonly)s>
<option value="">None (Blank Page)</option>
<option value="" disabled="disabled">-------------</option>
- ''' % {'weburl': weburl,
+ ''' % {'siteurl': CFG_SITE_URL,
'readonly':readonly}
for (o_filename, o_name) in all_templates:
out += '''<option value="%(template_filename)s">%(template_name)s</option>''' % {'template_name':o_name,
'template_filename': o_filename}
out += ''' </select>
</td></tr></table>'''
out += '''
<table><tr>
- <th colspan="2" class="adminheaderleft">%(name)s attributes&nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#attrsFormatTemplate">?</a>]</th>
+ <th colspan="2" class="adminheaderleft">%(name)s attributes&nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#attrsFormatTemplate">?</a>]</th>
</tr>
<tr>
<td class="admintdright">
<input type="hidden" name="key" value="%(name)s"/>
<label for="name">%(name_label)s</label>:&nbsp;</td>
<td><input tabindex="2" name="name" type="text" id="name" size="25" value="%(name)s" %(readonly)s/>
<input type="hidden" value="%(filename)s"/>
</td>
</tr>
''' % {"name": name,
'ln':ln,
'filename':filename,
'disabled':disabled,
'readonly':readonly,
'name_label': _("Name"),
- 'weburl':weburl
+ 'siteurl':CFG_SITE_URL
}
out += '''
<tr>
<td class="admintdright" valign="top"><label for="description">%(description_label)s</label>:&nbsp;</td>
<td><textarea tabindex="3" name="description" id="description" rows="4" cols="25" %(readonly)s>%(description)s</textarea> </td>
</tr>
<tr>
<td>&nbsp;</td>
<td align="right"><input tabindex="6" class="adminbutton" type="submit" value="%(update_format_attributes)s" %(disabled)s/></td>
</tr>
</table></form>
''' % {"description": description,
'ln':ln,
'filename':filename,
'disabled':disabled,
'readonly':readonly,
'description_label': _("Description"),
'update_format_attributes': _("Update Format Attributes"),
- 'weburl':weburl
+ 'siteurl':CFG_SITE_URL
}
return out
def tmpl_admin_format_template_show_dependencies(self, ln, name, filename, output_formats, format_elements, tags):
"""
Shows the dependencies (on elements) of the given format.
@param name the name of the template
@param filename the filename of the template
@param format_elements the elements (and list of tags in each element) this template depends on
@param output_formats the output format that depend on this template
@param tags the tags that are called by format elements this template depends on.
"""
_ = gettext_set_language(ln) # load the right message language
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="format_templates_manage?ln=%(ln)s">%(close_editor)s</a>&nbsp;</small></td>
<td>1.&nbsp;<small><a href="format_template_show?ln=%(ln)s&amp;bft=%(filename)s">%(template_editor)s</a></small>&nbsp;</td>
<td>2.&nbsp;<small><a href="format_template_show_attributes?ln=%(ln)s&amp;bft=%(filename)s">%(modify_template_attributes)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small>%(check_dependencies)s</small>&nbsp;</td>
</tr>
</table>
<table width="90%%" class="admin_wvar" cellspacing="0"><tr>
<th class="adminheaderleft">Output Formats that use %(name)s</th>
<th class="adminheaderleft">Format Elements used by %(name)s*</th>
<th class="adminheaderleft">All Tags Called*</th>
</tr>
<tr>
<td valign="top">&nbsp;<br/>
''' % {'ln':ln,
'filename':filename,
'menu': _("Menu"),
'close_editor': _("Close Editor"),
'modify_template_attributes': _("Modify Template Attributes"),
'template_editor': _("Template Editor"),
'check_dependencies': _("Check Dependencies"),
'name': name }
#Print output formats
if len(output_formats) == 0:
out += '<p align="center"><i>No output format uses this format template.</i></p>'
for output_format in output_formats:
name = output_format['names']['generic']
filename = output_format['filename']
out += ''' <a href="output_format_show?ln=%(ln)s&amp;bfo=%(filename)s">%(name)s</a>''' % {'filename':filename,
'name':name,
'ln':ln}
if len(output_format['tags']) > 0:
out += "("+", ".join(output_format['tags'])+")"
out += "<br/>"
#Print format elements (and tags)
out += '</td><td valign="top">&nbsp;<br/>'
if len(format_elements) == 0:
out += '<p align="center"><i>This format template uses no format element.</i></p>'
for format_element in format_elements:
name = format_element['name']
out += ''' <a href="format_elements_doc?ln=%(ln)s#%(anchor)s">%(name)s</a>''' % {'name':"bfe_"+name.lower(),
'anchor':name.upper(),
'ln':ln}
if len(format_element['tags']) > 0:
out += "("+", ".join(format_element['tags'])+")"
out += "<br/>"
#Print tags
out += '</td><td valign="top">&nbsp;<br/>'
if len(tags) == 0:
out += '<p align="center"><i>This format template uses no tag.</i></p>'
for tag in tags:
out += '''%(tag)s<br/>''' % { 'tag':tag}
out += '''
</td>
</tr>
</table>
<b>*Note</b>: Some tags linked with this format template might not be shown. Check manually.
'''
return out
def tmpl_admin_format_template_show(self, ln, name, description, code, filename, ln_for_preview, pattern_for_preview, editable, content_type_for_preview, content_types):
"""
Returns the editor for format templates. Edit 'format'
@param ln language
@param format the format to edit
@param filename the filename of the template
@param ln_for_preview the language for the preview (for bfo)
@param pattern_for_preview the search pattern to be used for the preview (for bfo)
@param editable True if we let user edit, else False
@param code the code of the template of the editor
@return editor for 'format'
"""
_ = gettext_set_language(ln) # load the right message language
out = ""
# If xsl, hide some options in the menu
nb_menu_options = 4
if filename.endswith('.xsl'):
nb_menu_options = 2
out += '''
<style type="text/css">
<!--
.ed_button {
font-size: x-small;
}
-->
</style>
- <script src="%(weburl)s/admin/bibformat/js_quicktags.js" type="text/javascript"></script>
+ <script src="%(siteurl)s/admin/bibformat/js_quicktags.js" type="text/javascript"></script>
<script type="text/javascript">
function getByID( id ) {
if (document.getElementById)
var returnVar = document.getElementById(id);
else if (document.all)
var returnVar = document.all[id];
else if (document.layers)
var returnVar = document.layers[id];
return returnVar;
}
window.onresize= resizeViews;
window.onload= prepareLayout;
function prepareLayout(){
resizeViews();
}
function resizeViews(){
var myWidth = 0, myHeight = 0;
if( typeof( window.innerWidth ) == 'number' ) {
//Non-IE
myWidth = window.innerWidth;
myHeight = window.innerHeight;
} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myWidth = document.documentElement.clientWidth;
myHeight = document.documentElement.clientHeight;
} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
//IE 4 compatible
myWidth = document.body.clientWidth;
myHeight = document.body.clientHeight;
}
if (myHeight <= 400) {
getByID("code").style.height=10;
getByID("previewiframe").style.height=10;
} else{
getByID("code").style.height=((myHeight-400)/2);
getByID("previewiframe").style.height=((myHeight-400)/2);
}
getByID("previewiframe").style.height=200;
// Resize documentation
var height = document.documentElement.clientHeight;
height -= getByID('shortDocFrame').offsetTop
//height -= 20;
getByID('shortDocFrame').style.height = height +"px";
}
</script>
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="%(nb_menu_options)s" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="format_templates_manage?ln=%(ln)s">%(close_editor)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small>%(template_editor)s</small>&nbsp;</td>
''' % {'ln': ln, 'filename': filename,
'menu': _("Menu"),
'label_show_doc': _("Show Documentation"),
'label_hide_doc': _("Hide Documentation"),
'close_editor': _("Close Editor"),
'modify_template_attributes': _("Modify Template Attributes"),
'template_editor': _("Template Editor"),
'check_dependencies': _("Check Dependencies"),
'nb_menu_options': nb_menu_options,
- 'weburl': CFG_SITE_SECURE_URL or weburl
+ 'siteurl': CFG_SITE_SECURE_URL or CFG_SITE_URL
}
if not filename.endswith('.xsl'):
out +='''<td>2.&nbsp;<small><a href="format_template_show_attributes?ln=%(ln)s&amp;bft=%(filename)s">%(modify_template_attributes)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small><a href="format_template_show_dependencies?ln=%(ln)s&amp;bft=%(filename)s">%(check_dependencies)s</a></small>&nbsp;</td>
''' % {'ln': ln, 'filename': filename,
'menu': _("Menu"),
'label_show_doc': _("Show Documentation"),
'label_hide_doc': _("Hide Documentation"),
'close_editor': _("Close Editor"),
'modify_template_attributes': _("Modify Template Attributes"),
'template_editor': _("Template Editor"),
'check_dependencies': _("Check Dependencies"),
- 'weburl': CFG_SITE_SECURE_URL or weburl
+ 'siteurl': CFG_SITE_SECURE_URL or CFG_SITE_URL
}
out +='''
</tr>
</table>
<script type="text/javascript">
function toggle_doc_visibility(){
var doc = document.getElementById('docTable');
var link = document.getElementById('docLink');
if (doc.style.display=='none'){
doc.style.display = '';
link.innerHTML = "%(label_hide_doc)s"
} else {
doc.style.display = 'none';
link.innerHTML = "%(label_show_doc)s"
}
}
</script>
''' % {'ln': ln, 'filename': filename,
'menu': _("Menu"),
'label_show_doc': _("Show Documentation"),
'label_hide_doc': _("Hide Documentation"),
'close_editor': _("Close Editor"),
'modify_template_attributes': _("Modify Template Attributes"),
'template_editor': _("Template Editor"),
'check_dependencies': _("Check Dependencies"),
- 'weburl': CFG_SITE_SECURE_URL or weburl
+ 'siteurl': CFG_SITE_SECURE_URL or CFG_SITE_URL
}
disabled = ""
readonly = ""
- toolbar = """<script type="text/javascript">edToolbar('%s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%s');</script>""" % (weburl, ln)
+ toolbar = """<script type="text/javascript">edToolbar('%s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%s');</script>""" % (CFG_SITE_URL, ln)
if not editable:
disabled = 'disabled="disabled"'
readonly = 'readonly="readonly"'
toolbar = ''
#First column: template code and preview
out += '''
<table width="90%%" cellspacing="5">
<tr>
<td valign="top">
<form action="format_template_show_preview_or_save?ln=%(ln)s&amp;bft=%(filename)s" method="POST" target="previewiframe">
<table width="100%%" id="mainTable"><tr>
<th class="adminheaderleft"><div style="float:left;">Format template code</div>
<div style="float:right;">
<a id="docLink" href="#" onclick="toggle_doc_visibility()">%(label_hide_doc)s</a>
</div>
</th>
</tr>
<tr><td colspan="2" id="codetd">
%(toolbar)s
<textarea name="code" id="code" rows="25" %(readonly)s
style="width:100%%">%(code)s</textarea>
<script type="text/javascript">var edCanvas = document.getElementById('code');</script>
</td></tr>
<tr><td align="right" valign="top">
<input type="submit" class="adminbutton" name="save_action" value="Save Changes" %(disabled)s/>
</td>
</tr>
</table>
<table width="100%%">
<tr><th class="adminheaderleft">
Preview
</th>
</tr>
<tr><td align="right" valign="top" style="font-size: small;">
<nobr>
<label for="content_type_for_preview">Content-type (MIME):</label> <select id="content_type_for_preview" name="content_type_for_preview" style="font-size: x-small;">
''' % {'ln':ln,
- 'weburl':weburl,
+ 'siteurl':CFG_SITE_URL,
'filename':filename,
'label_hide_doc':_("Hide Documentation"),
'code':code,
'readonly':readonly,
'disabled':disabled,
'toolbar':toolbar}
for content_type in content_types:
if content_type == content_type_for_preview:
out += '''<option value="%(content_type)s" selected="selected">%(content_type)s</option>''' % {'content_type':content_type}
else:
out += '''<option value="%(content_type)s">%(content_type)s</option>''' % {'content_type':content_type}
out += '''
</select></nobr>
<nobr><label for="ln_for_preview">Language:</label> <select id="ln_for_preview" name="ln_for_preview" style="font-size: x-small;">
'''
for lang in language_list_long():
if lang[0] == ln_for_preview:
out += '''<option value="%(ln)s" selected="selected">%(language)s</option>''' % {'ln':lang[0],
'language':lang[1]}
else:
out += '''<option value="%(ln)s">%(language)s</option>''' % {'ln':lang[0], 'language':lang[1]}
out += '''
</select></nobr>
&nbsp;
<nobr><label for="pattern_for_preview">Search Pattern: </label><input type="text" value="%(pattern_for_preview)s" size="8" name="pattern_for_preview" id="pattern_for_preview" style="font-size: x-small;"/></nobr>&nbsp;
<input type="submit" class="adminbutton" name="preview_action" value="Reload Preview"/>
</td>
</tr>
<tr><td>
- <iframe src ="%(weburl)s/admin/bibformat/bibformatadmin.py/format_template_show_preview_or_save?ln=%(ln)s&amp;ln_for_preview=%(ln_for_preview)s&amp;pattern_for_preview=%(pattern_for_preview)s&amp;bft=%(filename)s" name="previewiframe" id="previewiframe" width="100%%" height="400"></iframe>
+ <iframe src ="%(siteurl)s/admin/bibformat/bibformatadmin.py/format_template_show_preview_or_save?ln=%(ln)s&amp;ln_for_preview=%(ln_for_preview)s&amp;pattern_for_preview=%(pattern_for_preview)s&amp;bft=%(filename)s" name="previewiframe" id="previewiframe" width="100%%" height="400"></iframe>
</td></tr>
</table>
</form>
</td>
''' % {'code':code, 'ln':ln,
- 'weburl':weburl, 'filename':filename,
+ 'siteurl':CFG_SITE_URL, 'filename':filename,
'ln_for_preview':ln_for_preview,
'pattern_for_preview':pattern_for_preview
}
#Second column Print documentation
out += '''
<td valign="top" id="docTable">
<table width="100%%"><tr>
<th class="adminheaderleft">Elements Documentation</th>
</tr>
</table>
<table width="100%%"><tr>
<td class="admintdright">
<form action="format_template_show_short_doc?ln=%(ln)s" method="POST" target="shortDocFrame">
<nobr><label for="search_doc_pattern">Search for:&nbsp;</label><input type="text" size="15" name="search_doc_pattern" id="search_doc_pattern" value=""/> <input type="submit" class="adminbutton" name="search_in_doc" value="Search" /></nobr>
</form>
</td>
</tr>
</table>
- <iframe name="shortDocFrame" id="shortDocFrame" src ="%(weburl)s/admin/bibformat/bibformatadmin.py/format_template_show_short_doc?ln=%(ln)s" height="90%%" width="98%%"></iframe>
+ <iframe name="shortDocFrame" id="shortDocFrame" src ="%(siteurl)s/admin/bibformat/bibformatadmin.py/format_template_show_short_doc?ln=%(ln)s" height="90%%" width="98%%"></iframe>
</td>
</tr>
</table>
- ''' % {'weburl':weburl, 'ln':ln}
+ ''' % {'siteurl':CFG_SITE_URL, 'ln':ln}
return out
def tmpl_admin_format_template_show_short_doc(self, ln, format_elements):
"""
Prints the format element documentation in a condensed way to display
inside format template editor.
This page is different from others: it is displayed inside a <iframe>
tag in template tmpl_admin_format_template_show.
@param ln language
@param format_elements a list of format elements structures as returned by get_format_elements
"""
out = '''
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>BibFormat Short Documentation of Format Elements</title>
- <link rel="stylesheet" href="%(weburl)s/img/cds.css">
- <script src="%(weburl)s/admin/bibformat/js_quicktags.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="%(siteurl)s/img/cds.css">
+ <script src="%(siteurl)s/admin/bibformat/js_quicktags.js" type="text/javascript"></script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<script type="text/javascript">
function toggle_visibility(element, show, r,g,b){
var children = element.childNodes
var child
for(x=0; x<children.length; x++){
if (children[x].id == 'params'){
child = children[x]
}
}
if (show=='show'){
element.style.background='rgb(201, 218, 255)'
element.style.cursor='pointer'
child.style.display=''
} else {
element.style.background="rgb("+r+","+g+","+b+")"
child.style.display='none'
}
}
///// FROM JS QuickTags ///////
// Copyright (c) 2002-2005 Alex King
// http://www.alexking.org/
//
// Licensed under the LGPL license
// http://www.gnu.org/copyleft/lesser.html
function insertAtCursor(myField, myValue) {
//IE support
if (document.selection) {
myField.focus();
sel = document.selection.createRange();
sel.text = myValue;
}
//MOZILLA/NETSCAPE support
else if (myField.selectionStart || myField.selectionStart == '0') {
var startPos = myField.selectionStart;
var endPos = myField.selectionEnd;
myField.value = myField.value.substring(0, startPos)
+ myValue
+ myField.value.substring(endPos, myField.value.length);
} else {
myField.value += myValue;
}
}
///// END FROM JS QuickTags /////
function insert_my_code_into_container(code){
var codeArea = parent.document.getElementById("code");
if (codeArea.readOnly == false){
//var clean_code = code.replace(=#,'="');
//clean_code = clean_code.replace(# ,'" ');
insertAtCursor(codeArea, code);
}
}
</script>
- ''' % {'weburl': CFG_SITE_SECURE_URL or weburl}
+ ''' % {'siteurl': CFG_SITE_SECURE_URL or CFG_SITE_URL}
if len(format_elements) == 0:
out += '''
<em>No format elements found</em>
'''
else:
line = 0
#Print elements doc
for format_element in format_elements:
format_attributes = format_element['attrs']
row_content = ""
name = format_attributes['name']
description = format_attributes['description']
params = [x['name'] + '=\u0022'+x['default']+'\u0022' for x in format_attributes['params']]
builtin_params = [x['name'] + '=\u0022'+x['default']+'\u0022' for x in format_attributes['builtin_params']]
code = "<BFE_" + name + ' ' + ' '.join(builtin_params)+ ' ' + ' '.join(params) +"/>"
if line % 2:
row_content += '''<div onmouseover="toggle_visibility(this, 'show', 235, 247, 255);"
onmouseout="toggle_visibility(this, 'hide', 235, 247, 255);"
style="background-color: rgb(235, 247, 255);"
onclick="insert_my_code_into_container('%s')"
><hr/>''' % code
else:
row_content += '''<div onmouseover="toggle_visibility(this, 'show', 255, 255, 255);"
onmouseout="toggle_visibility(this, 'hide', 255, 255, 255);"
onclick="insert_my_code_into_container('%s')"
>''' % code
params_names = ""
for param in format_attributes['params']:
params_names += "<b>"+param['name'] +'</b> '
row_content += '''
<code> <b>&lt;BFE_%(name)s/&gt;</b><br/></code>
<small>%(description)s.</small>
<div id="params" style="display:none;">
<ul>
''' % {'params_names':params_names, 'name':name, 'description':description}
for param in format_attributes['params']:
row_content += '''
<li><small><b>%(name)s</b>:&nbsp;%(description)s</small></li>
''' % {'name':param['name'],
'description':param['description']}
for param in format_attributes['builtin_params']:
row_content += '''
<li><small><b>%(name)s</b>:&nbsp;%(description)s</small></li>
''' % {'name':param['name'],
'description':param['description']}
row_content += '</ul></div>'
if line % 2:
row_content += '''<hr/></div>'''
else:
row_content += '</div>'
line += 1
out += row_content
out += '''</body></html>'''
return out
def tmpl_admin_format_templates_management(self, ln, formats):
"""
Returns the management console for formats. Includes list of formats and
associated administration tools.
@param ln language
@param formats a list of dictionaries with formats attributes
@return format management console as html
"""
_ = gettext_set_language(ln) # load the right message language
#top of the page and table header
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small>%(manage_format_templates)s</small>&nbsp;</td>
<td>1.&nbsp;<small><a href="output_formats_manage?ln=%(ln)s">%(manage_output_formats)s</a>&nbsp;</td>
<td>2.&nbsp;<small><a href="format_elements_doc?ln=%(ln)s">%(format_elements_documentation)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small><a href="kb_manage?ln=%(ln)s">%(manage_knowledge_bases)s</a></small>&nbsp;</td>
</tr>
</table>
<p>From here you can create, edit or delete formats templates.
Have a look at the <a href="format_elements_doc?ln=%(ln)s">format elements documentation</a> to
learn which elements you can use in your templates.</p>
<table class="admin_wvar" width="95%%" cellspacing="0">
<tr>
<th class="adminheaderleft" >&nbsp;</th>
<th class="adminheaderleft" >%(name)s</th>
<th class="adminheaderleft" >%(description)s</th>
<th class="adminheaderleft" >%(status)s</th>
<th class="adminheaderleft" >%(last_modification_date)s</th>
- <th class="adminheadercenter" >%(action)s&nbsp;&nbsp;&nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#formatTemplates">?</a>]</th>
+ <th class="adminheadercenter" >%(action)s&nbsp;&nbsp;&nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#formatTemplates">?</a>]</th>
</tr>
''' % {'name':_("Name"),
'description':_("Description"),
'menu': _("Menu"),
'status':_("Status"),
'last_modification_date':_("Last Modification Date"),
'action':_("Action"),
'ln':ln,
'manage_output_formats':_("Manage Output Formats"),
'manage_format_templates':_("Manage Format Templates"),
'format_elements_documentation':_("Format Elements Documentation"),
'manage_knowledge_bases':_("Manage Knowledge Bases"),
- 'weburl':weburl}
+ 'siteurl':CFG_SITE_URL}
#table content: formats names, description and buttons
if len(formats) == 0:
out += '''<tr>
<td colspan="6" class="admintd" align="center"><em>No format</em></td>
</tr>'''
else:
line = 0
for attrs in formats:
filename = attrs['filename']
if filename == "":
filename = "&nbsp;"
name = attrs['name']
if name == "":
name = "&nbsp;"
description = attrs['description']
if description == "":
description = "&nbsp;"
last_mod_date = attrs['last_mod_date']
status = attrs['status']
disabled = ""
if not attrs['editable']:
disabled = 'disabled="disabled"'
style = 'style="vertical-align: middle;'
if line % 2:
style = 'style="vertical-align: middle;background-color: rgb(235, 247, 255);'
line += 1
row_content = '''<tr>
<td class="admintdright" %(style)s">&nbsp;</td>
<td class="admintdleft" %(style)s white-space: nowrap;"><a href="format_template_show?bft=%(filename)s&amp;ln=%(ln)s">%(name)s</a></td>
<td class="admintdleft" %(style)s" >%(description)s</td>
<td class="admintdleft" %(style)s white-space: nowrap;" >%(status)s</td>
<td class="admintdleft" %(style)s white-space: nowrap;" >%(last_mod_date)s</td>
<td class="admintd" %(style)s white-space: nowrap;">
<form method="post" action="format_template_delete?ln=%(ln)s&amp;bft=%(filename)s">
<input class="adminbutton" type="submit" value="%(delete)s" %(disabled)s/>
</form>
</td>
</tr>
''' % {'filename':filename,
'name':name,
'description':description,
'ln':ln,
'style':style,
'disabled':disabled,
'last_mod_date':last_mod_date,
'status':status,
'delete':_("Delete")
}
out += row_content
#table footer, buttons and bottom of the page
out += '''
<tr>
<td align="left" colspan="3">
<form action="format_templates_manage?ln=%(ln)s">
<input type="hidden" name="checking" value="1"></input>
<input class="adminbutton" type="submit" value="%(extensive_checking)s"/>
</form>
</td>
<td align="right" colspan="3">
<form action="format_template_add?ln=%(ln)s">
<input class="adminbutton" type="submit" value="%(add_format_template)s"/>
</form>
</td>
</tr>
</table>
''' % {'ln':ln,
'add_format_template':_("Add New Format Template"),
'extensive_checking':_("Check Format Templates Extensively")}
return out
def tmpl_admin_output_formats_management(self, ln, output_formats):
"""
Returns the main management console for formats. Includes list of formats and
associated administration tools.
@param ln language
@param output_formats a list of output formats
@return main management console as html
"""
_ = gettext_set_language(ln) # load the right message language
#top of the page and table header
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="format_templates_manage?ln=%(ln)s">%(manage_format_templates)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small>%(manage_output_formats)s</small>&nbsp;</td>
<td>2.&nbsp;<small><a href="format_elements_doc?ln=%(ln)s">%(format_elements_documentation)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small><a href="kb_manage?ln=%(ln)s">%(manage_knowledge_bases)s</a></small>&nbsp;</td>
</tr>
</table>
<p>From here you can add, edit or delete output formats available for collections. Output formats define which template to use. <br/>To edit templates go to the <a href="format_templates_manage?ln=%(ln)s">template administration page</a>.</p>
<table class="admin_wvar" width="95%%" cellspacing="0">
<tr>
<th class="adminheaderleft" >&nbsp;</th>
<th class="adminheaderleft" ><a href="output_formats_manage?ln=%(ln)s&amp;sortby=code">%(code)s</a></th>
<th class="adminheaderleft" ><a href="output_formats_manage?ln=%(ln)s&amp;sortby=name">%(name)s</a></th>
<th class="adminheaderleft" >%(description)s</th>
<th class="adminheaderleft" >%(status)s</th>
<th class="adminheaderleft" >%(last_modification_date)s</th>
- <th class="adminheadercenter" >%(action)s&nbsp;&nbsp;&nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#outputFormats">?</a>]</th>
+ <th class="adminheadercenter" >%(action)s&nbsp;&nbsp;&nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#outputFormats">?</a>]</th>
</tr>
''' % {'code':_("Code"),
'name':_("Name"),
'description':_("Description"),
'status':_("Status"),
'last_modification_date':_("Last Modification Date"),
'action':_("Action"),
'ln':ln,
'manage_output_formats':_("Manage Output Formats"),
'manage_format_templates':_("Manage Format Templates"),
'format_elements_documentation':_("Format Elements Documentation"),
'manage_knowledge_bases':_("Manage Knowledge Bases"),
'menu': _("Menu"),
- 'weburl':weburl}
+ 'siteurl':CFG_SITE_URL}
#table content: formats names, description and buttons
if len(output_formats) == 0:
out += '''<tr>
<td colspan="5" class="admintd" align="center"><em>No format</em></td>
</tr>'''
else:
line = 0
for output_format in output_formats:
format_attributes = output_format['attrs']
name = format_attributes['names']['generic']
if name == "":
name = "&nbsp;"
description = format_attributes['description']
if description == "":
description = "&nbsp;"
code = format_attributes['code']
if code == "":
code = "&nbsp;"
last_mod_date = output_format['last_mod_date']
status = output_format['status']
disabled = ""
if not output_format['editable']:
disabled = 'disabled="disabled"'
style = "vertical-align: middle;"
if line % 2:
style = 'vertical-align: middle; background-color: rgb(235, 247, 255);'
line += 1
row_content = '''<tr>
<td class="admintdright" style="%(style)s">&nbsp;</td>
<td class="admintdleft" style="white-space: nowrap; %(style)s">
<a href="output_format_show?bfo=%(code)s">%(code)s</a>
</td>
<td class="admintdleft" style="white-space: nowrap; %(style)s">
<a href="output_format_show?bfo=%(code)s">%(name)s</a>
</td>
<td class="admintdleft"style="%(style)s" >
%(description)s
</td>
<td class="admintd" style="white-space: nowrap; %(style)s" >%(status)s</td>
<td class="admintdleft" style="white-space: nowrap;%(style)s" >%(last_mod_date)s</td>
<td class="admintd" style="white-space: nowrap; %(style)s">
<form method="POST" action="output_format_delete?ln=%(ln)s&amp;bfo=%(code)s">
<input class="adminbutton" type="submit" value="Delete" %(disabled)s />
</form>
</td>
</tr>
''' % {'style':style,
'code':code,
'description':description,
'name':name,
'ln':ln,
'disabled':disabled,
'last_mod_date':last_mod_date,
'status':status}
out += row_content
#table footer, buttons and bottom of the page
out += '''
<tr>
<td align="right" colspan="7">
<form method="GET" action="output_format_add?ln=%(ln)s">
<input class="adminbutton" type="submit" value="%(add_output_format)s"/>
</form>
</td>
</tr>
</table>
''' % {'ln':ln,
'add_output_format':_("Add New Output Format")}
return out
def tmpl_admin_output_format_show(self, ln, code, name, rules, default, format_templates, editable):
"""
Returns the content of an output format
rules is an ordered list of dict (sorted by evaluation order),
with keys 'field', 'value' and 'template'
IMPORTANT: we display rules evaluation index starting at 1 in
interface, but we start internally at 0
@param ln language
@param code the code of the output to show
@param name the name of this output format
@param rules the list of rules for this output format
@param default the default format template of the output format
@param format_templates the list of format_templates
@param editable True if we let user edit, else False
@return the management console for this output format
"""
_ = gettext_set_language(ln)
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="output_formats_manage?ln=%(ln)s">%(close_output_format)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small>%(rules)s</small>&nbsp;</td>
<td>2.&nbsp;<small><a href="output_format_show_attributes?ln=%(ln)s&amp;bfo=%(code)s">%(modify_output_format_attributes)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small><a href="output_format_show_dependencies?ln=%(ln)s&amp;bfo=%(code)s">%(check_dependencies)s</a></small>&nbsp;</td>
</tr>
</table>
<p>Define here the rules the specifies which template to use for a given record.</p>
''' % {'code':code,
'ln':ln,
'menu':_("menu"),
'close_output_format':_("Close Output Format"),
'rules':_("Rules"),
'modify_output_format_attributes':_("Modify Output Format Attributes"),
'check_dependencies':_("Check Dependencies")
}
out += '''
<form name="rules" action="output_format_show?ln=%(ln)s&amp;bfo=%(code)s" method="post">
<table>
<tr>
<td>
''' % {'ln': ln, 'code':code}
disabled = ""
readonly = ""
if not editable:
disabled = 'disabled="disabled"'
readonly = 'readonly="readonly"'
if len(rules) == 0:
out += '''<p align="center"><em>No special rule</em></p>'''
line = 1
for rule in rules:
out += '''
<table align="center" class="admin_wvar" cellspacing="0">
<tr>
'''
out += '''
<td rowspan="2" class="adminheader" style="vertical-align: middle;">'''
if line > 1:
out += '''
- <input type="image" src="%(weburl)s/img/smallup.gif" alt="Increase priority of rule %(row)s" name="+ %(row)s" value="+ %(row)s" %(disabled)s/></div>
- ''' % {'weburl':weburl, 'row':line, 'disabled':disabled}
+ <input type="image" src="%(siteurl)s/img/smallup.gif" alt="Increase priority of rule %(row)s" name="+ %(row)s" value="+ %(row)s" %(disabled)s/></div>
+ ''' % {'siteurl':CFG_SITE_URL, 'row':line, 'disabled':disabled}
out += '''<div>%(row)s</div>''' % { 'row':line}
if line < len(rules):
out += '''
- <input type="image" src="%(weburl)s/img/smalldown.gif" alt="Decrease priority of rule %(row)s" name="- %(row)s" value="- %(row)s" %(disabled)s/>
- ''' % {'weburl':weburl,
+ <input type="image" src="%(siteurl)s/img/smalldown.gif" alt="Decrease priority of rule %(row)s" name="- %(row)s" value="- %(row)s" %(disabled)s/>
+ ''' % {'siteurl':CFG_SITE_URL,
'row':line,
'disabled':disabled}
out += '''</td>
<td class="adminheaderleft">&nbsp;</td>
'''
out += '''
<td class="adminheaderleft" style="white-space: nowrap;">
Use template&nbsp;<select name="r_tpl" %(disabled)s>''' % {'disabled':disabled}
for template in format_templates:
attrs = format_templates[template]['attrs']
attrs['template'] = template
if template.endswith('.xsl') and not \
attrs['name'].endswith(' (XSL)'):
attrs['name'] += ' (XSL)'
if template != rule['template']:
out += '''<option value="%(template)s">%(name)s</option>''' % attrs
else:
out += '''<option value="%(template)s" selected="selected">%(name)s</option>''' % attrs
if not format_templates.has_key(rule['template']) and rule['template'] != "":
#case where a non existing format template is use in output format
#we need to add it as option
out += '''<option value="%s" selected="selected">%s</option>''' % (rule['template'],
rule['template'])
################ FIXME remove when migration is done ####################
#Let the user choose a non existing template, that is a placeholder
#meaning that the template has not been migrated
selected = ''
if rule['template'] == 'migration_in_progress':
selected = 'selected="selected"'
if CFG_PATH_PHP or selected != '':
out += '''<option disabled="disabled">For Migration:</option>'''
out += '''<option value="migration_in_progress" %s>defined in old BibFormat</option>''' % selected
################ END FIXME ####################
out += '''</select>&nbsp;if field
&nbsp;<input type="text" name="r_fld" value="%(field)s" size="10" %(readonly)s/>&nbsp;is equal to&nbsp;<input type="text" value="%(value)s" name="r_val" %(readonly)s/>
</td>
<td class="adminheaderright" style="vertical-align: middle;">
- &nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#rulesOutputFormat">?</a>]
+ &nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#rulesOutputFormat">?</a>]
</td>
</tr>
- ''' % {'weburl':weburl,
+ ''' % {'siteurl':CFG_SITE_URL,
'field': rule['field'],
'value':rule['value'],
'readonly':readonly}
out += '''
<tr>
<td colspan ="3" class="adminheaderright" style="vertical-align: middle; white-space: nowrap;">
<input type="submit" class="adminbutton" name="r_upd" value="%(remove_rule_label)s %(row)s" %(disabled)s/>&nbsp;
</td>
</tr>
</table>
''' % {'remove_rule_label': _("Remove Rule"),
'row':line,
'disabled':disabled}
line += 1
out += '''
<table width="100%" align="center" class="admin_wvar" cellspacing="0">
<tr>
'''
out += '''
<td width="30" class="adminheaderleft">&nbsp;</td>
<td class="adminheaderleft">By default use <select id="default" name="default" %(disabled)s>''' % {'disabled':disabled}
for template in format_templates:
attrs = format_templates[template]['attrs']
attrs['template'] = template
if template.endswith('.xsl') and not \
attrs['name'].endswith(' (XSL)'):
attrs['name'] += ' (XSL)'
if template != default:
out += '''<option value="%(template)s">%(name)s</option>''' % attrs
else:
out += '''<option value="%(template)s" selected="selected">%(name)s</option>''' % attrs
if not format_templates.has_key(default) and default!= "":
#case where a non existing format tempate is use in output format
#we need to add it as option (only if it is not empty string)
out += '''<option value="%s" selected="selected">%s</option>''' % (default,default)
################ FIXME remove when migration is done ####################
#Let the user choose a non existing template, that is a placeholder
#meaning that the template has not been migrated
selected = ''
if default == 'migration_in_progress':
selected = 'selected="selected"'
if CFG_PATH_PHP or selected != '':
out += '''<option disabled="disabled">For Migration:</option>'''
out += '''<option value="migration_in_progress" %s>defined in old BibFormat</option>''' % selected
################ END FIXME ####################
out += '''</select></td>
</tr>
</table>
<div align="right">
<input tabindex="6" class="adminbutton" type="submit" name="r_upd" value="%(add_new_rule_label)s" %(disabled)s/>
<input tabindex="7" class="adminbutton" type="submit" name="r_upd" value="%(save_changes_label)s" %(disabled)s/>
</div>
</td>
</tr>
</table>
</form>
''' % {'add_new_rule_label':_("Add New Rule"),
'save_changes_label':_("Save Changes"),
'disabled':disabled
}
return out
def tmpl_admin_output_format_show_attributes(self, ln,
name,
description,
content_type,
code,
names_trans,
editable,
visible):
"""
Returns a page to change output format name and description
names_trans is an ordered list of dicts with keys 'lang' and 'trans'
@param ln language
@param name the name of the format
@param description the description of the format
@param code the code of the format
@param content_type the (MIME) content type of the ouput format
@param names_trans the translations in the same order as the languages from get_languages()
@param editable True if we let user edit, else False
@param visible True if output format should be shown in list of available output formats
@return editor for output format attributes
"""
_ = gettext_set_language(ln) # load the right message language
out = ""
out += '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="output_formats_manage?ln=%(ln)s">%(close_output_format)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small><a href="output_format_show?ln=%(ln)s&amp;bfo=%(code)s">%(rules)s</a></small>&nbsp;</td>
<td>2.&nbsp;<small>%(modify_output_format_attributes)s</small>&nbsp;</td>
<td>3.&nbsp;<small><a href="output_format_show_dependencies?ln=%(ln)s&amp;bfo=%(code)s">%(check_dependencies)s</a></small>&nbsp;</td>
</tr>
</table><br/>
''' % {'ln':ln,
'code':code,
'close_output_format':_("Close Output Format"),
'rules':_("Rules"),
'modify_output_format_attributes':_("Modify Output Format Attributes"),
'check_dependencies':_("Check Dependencies"),
'menu':_("Menu")
}
disabled = ""
readonly = ""
if not editable:
disabled = 'disabled="disabled"'
readonly = 'readonly="readonly"'
out += '''
<form action="output_format_update_attributes?ln=%(ln)s&amp;bfo=%(code)s" method="POST">
<table class="admin_wvar" cellspacing="0">
<tr>
<th colspan="2" class="adminheaderleft">
- Output Format Attributes&nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#attrsOutputFormat">?</a>]</th>
+ Output Format Attributes&nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#attrsOutputFormat">?</a>]</th>
</tr>
<tr>
<td class="admintdright"><label for="outputFormatCode">Code</label>:&nbsp;</td>
<td><input tabindex="0" name="code" type="text" id="outputFormatCode" maxlength="6" size="6" value="%(code)s" %(readonly)s/></td>
</tr>
<tr>
<td class="admintdright">Visibility:&nbsp;</td>
<td><input tabindex="1" name="visibility" type="checkbox" id="outputFormatVisibility" %(visibility)s %(disabled)s value="1" /><small><label for="outputFormatVisibility">Show in list of available output formats (on public pages)</label></small></td>
</tr>
<td class="admintdright"><label for="outputFormatContentType">Content type</label>:&nbsp;</td>
<td><input tabindex="2" name="content_type" type="text" id="outputFormatContentType" size="25" value="%(content_type)s" %(readonly)s/> <small>Mime content-type. Specifies how the browser should handle this output.</small></td>
<tr>
<td class="admintdright"><label for="outputFormatName">Name</label>:&nbsp;</td>
<td><input tabindex="3" name="name" type="text" id="outputFormatName" size="25" value="%(name)s" %(readonly)s/></td>
</tr>
''' % {'name': name,
'ln':ln,
'code':code,
'content_type':content_type,
'readonly':readonly,
- 'weburl':weburl,
+ 'siteurl':CFG_SITE_URL,
'visibility': visible==1 and 'checked="checked"' or '',
'disabled':disabled}
#Add translated names
i = 3
for name_trans in names_trans:
i += 1
out += '''
<tr>
<td class="admintdright"><label for="outputFormatName%(i)s">%(lang)s Name</label>:&nbsp;</td>
<td><input tabindex="%(i)s" name="names_trans" type="text" id="outputFormatName%(i)s" size="25" value="%(name)s" %(readonly)s/></td>
</tr>''' % {'name':name_trans['trans'],
'lang':name_trans['lang'],
'i':i,
'readonly':readonly}
#Description and end of page
out += '''
<tr>
<td class="admintdright" valign="top"><label for="outputFormatDescription">Description</label>:&nbsp;</td>
<td><textarea tabindex="%(tabindexdesc)s" name="description" id="outputFormatDescription" rows="4" cols="25" %(readonly)s>%(description)s</textarea> </td>
</tr>
<tr>
<td colspan="2" align="right"><input tabindex="%(tabindexbutton)s" class="adminbutton" type="submit" value="Update Output Format Attributes" %(disabled)s/></td>
</tr>
</table>
</form>
''' % {'description': description,
'tabindexdesc': i + 1,
'tabindexbutton': i + 2,
'readonly':readonly,
'disabled':disabled}
return out
def tmpl_admin_output_format_show_dependencies(self, ln, name, code, format_templates):
"""
Shows the dependencies of the given format.
@param name the name of the output format
@param code the code of the output format
@param format_templates format templates that depend on this format (and also elements and tags)
"""
_ = gettext_set_language(ln) # load the right message language
out = '''
<table class="admin_wvar">
<tr><th colspan="4" class="adminheaderleft" cellspacing="0">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="output_formats_manage?ln=%(ln)s">%(close_output_format)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small><a href="output_format_show?ln=%(ln)s&amp;bfo=%(code)s">%(rules)s</a></small>&nbsp;</td>
<td>2.&nbsp;<small><a href="output_format_show_attributes?ln=%(ln)s&amp;bfo=%(code)s">%(modify_output_format_attributes)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small>%(check_dependencies)s</small>&nbsp;</td>
</tr>
</table><br/>
<table width="90%%" class="admin_wvar" cellspacing="0"><tr>
<th class="adminheaderleft">Format Templates that use %(name)s</th>
<th class="adminheaderleft">Format Elements used by %(name)s</th>
<th class="adminheaderleft">Tags Called*</th>
</tr>
''' % {'name': name,
'code': code,
'ln':ln,
'close_output_format':_("Close Output Format"),
'rules':_("Rules"),
'modify_output_format_attributes':_("Modify Output Format Attributes"),
'check_dependencies':_("Check Dependencies"),
'menu': _("Menu")
}
if len(format_templates) == 0:
out += '''<tr><td colspan="3"><p align="center">
<i>This output format uses no format template.</i></p></td></tr>'''
for format_template in format_templates:
name = format_template['name']
filename = format_template['filename']
out += '''<tr><td><a href="format_template_show?bft=%(filename)s&amp;ln=%(ln)s">%(name)s</a></td>
<td>&nbsp;</td><td>&nbsp;</td></tr>''' % {'filename':filename,
'name':name,
'ln':ln}
for format_element in format_template['elements']:
name = format_element['name']
filename = format_element['filename']
out += '''<tr><td>&nbsp;</td>
<td><a href="format_elements_doc?ln=%(ln)s#%(anchor)s">%(name)s</a></td>
<td>&nbsp;</td></tr>''' % {'anchor':name.upper(),
'name':name,
'ln':ln}
for tag in format_element['tags']:
out += '''<tr><td>&nbsp;</td><td>&nbsp;</td>
<td>%(tag)s</td></tr>''' % {'tag':tag}
out += '''
</table>
<b>*Note</b>: Some tags linked with this format template might not be shown. Check manually.
'''
return out
def tmpl_admin_format_elements_documentation(self, ln, format_elements):
"""
Returns the main management console for format elements. Includes list of formats elements and
associated administration tools.
@param ln language
@param formats a list of dictionaries with formats elements attributes
@return main management console as html
"""
_ = gettext_set_language(ln) # load the right message language
#top of the page and table header
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="format_templates_manage?ln=%(ln)s">%(manage_format_templates)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small><a href="output_formats_manage?ln=%(ln)s">%(manage_output_formats)s</a></small>&nbsp;</td>
<td>2.&nbsp;<small>%(format_elements_documentation)s</small>&nbsp;</td>
<td>3.&nbsp;<small><a href="kb_manage?ln=%(ln)s">%(manage_knowledge_bases)s</a></small>&nbsp;</td>
</tr>
</table>
<p>Here you can read the APIs of the formats elements, the elementary bricks for formats.</p>
''' % {'ln':ln,
'menu': _("Menu"),
'manage_output_formats':_("Manage Output Formats"),
'manage_format_templates':_("Manage Format Templates"),
'format_elements_documentation':_("Format Elements Documentation"),
'manage_knowledge_bases':_("Manage Knowledge Bases")
}
#table content: formats names, description and actions
if len(format_elements) == 0:
out += '''
<em>No format elements found</em>
'''
else:
#Print summary of elements (name + decription)
out += '''<h2>Summary table of elements</h2>'''
out += '''<table width="90%">'''
for format_element in format_elements:
format_attributes = format_element['attrs']
out += '''
<tr>
<td>
<code><a href="#%(name)s">&lt;BFE_%(name)s/&gt;</a></code>
</td>
<td>
%(description)s
</td>
</tr>
''' % format_attributes
out += "</table>"
#Print details of elements
out += '''<h2>Details of elements</h2>'''
for format_element in format_elements:
format_attributes = format_element['attrs']
element_name = format_attributes['name']
out += self.tmpl_admin_print_format_element_documentation(ln, element_name, format_attributes)
#table footer, buttons and bottom of the page
out += '''
<table align="center" width="95%">
</table>'''
return out
def tmpl_admin_print_format_element_documentation(self, ln, name, attributes, print_see_also=True):
"""
Prints the formatted documentation of a single element. Used in main documentation of element and
in creation of floater for Dreamweaver.
@param ln language
@param name the name of the element
@param attributes the attributes of the element, as returned by get_format_element_attrs_from_*
@param print_see_also if True, prints links to other sections related to element
"""
params_names = ""
for param in attributes['params']:
params_names += "<b>"+param['name'] +'</b>="..." '
out = '''
<a name="%(name)s"></a><h3>%(name)s</h3>
<b>&lt;BFE_%(name)s</b> %(params_names)s<b>/&gt;</b><br/><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>%(description)s.</em><br/><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>Parameters:</b><br/>
''' % {'params_names': params_names,
'name':name,
'description': attributes['description']}
for param in attributes['params']:
out += '''
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<code>%(name)s</code> - %(description)s. ''' % param
if param['default'] != "":
default = cgi.escape(param['default'])
if default.strip() == "":
default = "&nbsp;"
out += '''
Default value is &laquo;<code>%s</code>&raquo;
''' % default
out += '<br/>'
for param in attributes['builtin_params']:
out += '''
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<code>%(name)s</code> - %(description)s. ''' % param
if param['default'] != "":
default = cgi.escape(param['default'])
if default.strip() == "":
default = "&nbsp;"
out += '''
Default value is &laquo;<code>%s</code>&raquo;
''' % default
out += '<br/>'
if print_see_also:
out += '''<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<b>See also:</b><br/>'''
for element in attributes['seealso']:
element_name = element.split('.')[0].upper()
out += '''
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href="#%(name)s">Element <em>%(name)s</em></a><br/>''' % {'name':element_name}
out += '''
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href ="format_element_show_dependencies?ln=%(ln)s&amp;bfe=%(bfe)s">Dependencies of this element</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href ="validate_format?ln=%(ln)s&amp;bfe=%(bfe)s">The correctness of this element</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href ="format_element_test?ln=%(ln)s&amp;bfe=%(bfe)s">Test this element</a><br/>
''' % {'ln':ln, 'bfe':name}
return out
def tmpl_admin_format_element_show_dependencies(self, ln, name, format_templates, tags):
"""
Shows the dependencies of the given format element
@param name the name of the element
@param format_templates format templates that depend on this element
@param tags the tags that are called by this format element
"""
out = '''
<p>Go back to <a href="format_elements_doc?ln=%(ln)s#%(name)s">documentation</a></p>
''' % {'ln':ln, 'name':name.upper()}
out += ''' <table width="90%" class="admin_wvar" cellspacing="0"><tr>'''
out += '''
<th class="adminheaderleft">Format Templates that use %(name)s</th>
<th class="adminheaderleft">Tags Called*</th>
</tr>
<tr>
<td>&nbsp;<br/>''' % {"name": name}
#Print format elements (and tags)
if len(format_templates) == 0:
out += '''<p align="center">
<i>This format element is not used in any format template.</i></p>'''
for format_template in format_templates:
name = format_template['name']
filename = format_template['filename']
out += '''<a href="format_template_show?ln=%(ln)s&amp;bft=%(filename)s">%(name)s</a><br/>''' % {'filename':filename,
'name':name,
'ln':ln}
#Print tags
out += "</td><td>&nbsp;<br/>"
if len(tags) == 0:
out += '''<p align="center">
<i>This format element uses no tag.</i></p>'''
for tag in tags:
out += '''%(tag)s<br/>''' % {'tag':tag}
out += '''
</td>
</tr>
</table>
<b>*Note</b>: Some tags linked with this format template might not be shown. Check manually.
'''
return out
def tmpl_admin_format_element_test(self, ln, bfe, description, param_names, param_values, param_descriptions, result):
"""
Prints a page where the user can test the given format element with his own parameters.
@param ln language
@param bfe the format element name
@param description a description of the element
@param param_names a list of parameters names/labels
@param param_values a list of values for parameters
@param param_descriptions a list of description for parameters
@param result the result of the evaluation
"""
out = '''
<p>Go back to <a href="format_elements_doc?ln=%(ln)s#%(name)s">documentation</a></p>
''' % {'ln':ln, 'name':bfe.upper()}
out += '''
<h3>&lt;BFE_%(bfe)s /&gt;</h3>
<p>%(description)s</p>
<table width="100%%"><tr><td>
<form method="post" action="format_element_test?ln=%(ln)s&amp;bfe=%(bfe)s">
<table>
''' % {'bfe':bfe, 'ln':ln, 'description':description }
for i in range(len(param_names)):
out += '''
<tr>
<td class="admintdright">%(name)s</td>
<td class="admintdright"><input type="text" name="param_values" value="%(value)s"/></td>
<td class="admintdleft">%(description)s&nbsp;</td>
</tr>
''' % {'name':param_names[i],
'value':param_values[i],
'description':param_descriptions[i]}
out += '''
<tr><td colspan="2" class="admintdright"><input type="submit" class="adminbutton" value="Test!"/></td>
<td>&nbsp;</td>
</tr>
</table>
</form>
<fieldset style="display:inline;margin-left:auto;margin-right:auto;">
<legend>Result:</legend>%(result)s</fieldset>
''' % {'result':result}
out += '''
</td></tr><tr><td>
'''
#out += self.tmpl_admin_print_format_element_documentation(ln, bfe, attributes, False)
out += '''</td></tr></table>'''
return out
def tmpl_admin_add_format_element(self, ln):
"""
Shows how to add a format element (mainly doc)
@param ln language
"""
_ = gettext_set_language(ln) # load the right message language
out = '''
- <p>To add a new basic element (only fetch the value of a field, without special post-processing), go to the <a href="%(weburl)sadmin/bibindex/bibindexadmin.py/field">BibEdit "Manage Logical Fields"</a> page and add a name for a field. Make sure that the name is unique and corresponds well to the field. For example, to add an element that fetch the value of field 245__%, add a new logical field with name "title" and field "245__%". Then in your template, call BFE_TITLE to print the title.</p>
+ <p>To add a new basic element (only fetch the value of a field, without special post-processing), go to the <a href="%(siteurl)sadmin/bibindex/bibindexadmin.py/field">BibEdit "Manage Logical Fields"</a> page and add a name for a field. Make sure that the name is unique and corresponds well to the field. For example, to add an element that fetch the value of field 245__%, add a new logical field with name "title" and field "245__%". Then in your template, call BFE_TITLE to print the title.</p>
<p>To add a new complex element (for eg. special formatting of the field, condition on the value, etc.) you must go to the lib/python/invenio/bibformat_elements directory of your Invenio installation, and add a new format element file. Read documentation for more information.</p>
- ''' % {'weburl':weburl}
+ ''' % {'siteurl':CFG_SITE_URL}
return out
def tmpl_admin_kbs_management(self, ln, kbs):
"""
Returns the main management console for knowledge bases.
@param ln language
@param kbs a list of dictionaries with knowledge bases attributes
@return main management console as html
"""
_ = gettext_set_language(ln) # load the right message language
#top of the page and table header
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="format_templates_manage?ln=%(ln)s">%(manage_format_templates)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small><a href="output_formats_manage?ln=%(ln)s">%(manage_output_formats)s</a></small>&nbsp;</td>
<td>2.&nbsp;<small><a href="format_elements_doc?ln=%(ln)s">%(format_elements_documentation)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small>%(manage_knowledge_bases)s</small>&nbsp;</td>
</tr>
</table>
<table class="admin_wvar" width="95%%" cellspacing="0">
<tr>
<th class="adminheaderleft" >&nbsp;</th>
<th class="adminheaderleft" >Name</th>
<th class="adminheaderleft" >Description</th>
- <th class="adminheadercenter" >Action&nbsp;&nbsp;&nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#KBs">?</a>]</th>
+ <th class="adminheadercenter" >Action&nbsp;&nbsp;&nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#KBs">?</a>]</th>
</tr>''' % {'ln':ln,
'menu':_("Menu"),
'manage_output_formats':_("Manage Output Formats"),
'manage_format_templates':_("Manage Format Templates"),
'format_elements_documentation':_("Format Elements Documentation"),
'manage_knowledge_bases':_("Manage Knowledge Bases"),
- 'weburl':weburl}
+ 'siteurl':CFG_SITE_URL}
#table content: kb names, description and actions
if len(kbs) == 0:
out += '''<tr>
<td colspan="5" class="admintd" align="center"><em>No Knowledge Base</em></td>
</tr>'''
else:
line = 0
for kb_attributes in kbs :
kb_attributes['style'] = ""
if line % 2:
kb_attributes['style'] = 'background-color: rgb(235, 247, 255);'
line += 1
kb_attributes['ln'] = ln
- kb_attributes['weburl'] = weburl
+ kb_attributes['siteurl'] = CFG_SITE_URL
row_content = '''<tr>
<td class="admintdright" style="vertical-align: middle; %(style)s">&nbsp;</td>
<td class="admintdleft" style="vertical-align: middle; %(style)s white-space: nowrap;"><a href="kb_show?ln=%(ln)s&amp;amp;kb=%(id)s">%(name)s</a></td>
<td class="admintdleft"style="vertical-align: middle; %(style)s">%(description)s</td>
<td class="admintd" style="vertical-align: middle; %(style)s white-space: nowrap;">
<form action="kb_delete?ln=%(ln)s" type="POST">
<input type="submit" class="adminbutton" value="Delete">
<input type="hidden" id="kb" name="kb" value="%(id)s">
</form>
</td>
</tr>
''' % kb_attributes
out += row_content
#table footer, buttons and bottom of the page
out += ''' </table>
<table align="center" width="95%">
<tr>
<td align="left" valign="top">&nbsp;</td>
'''
out += '''
<td align="right">
<form action="kb_add?ln=%(ln)s">
<input class="adminbutton" type="submit" value="Add New Knowledge Base"/>
</form>
</td>
</tr>
</table>''' % {'ln': ln}
return out
def tmpl_admin_kb_show(self, ln, kb_id, kb_name, mappings, sortby):
"""
Returns the content of a knowledge base.
@param ln language
@param kb_id the id of the kb
@param kb_name the name of the kb
@param content a list of dictionaries with mappings
@param sortby the sorting criteria ('from' or 'to')
@return main management console as html
"""
_ = gettext_set_language(ln) # load the right message language
#top of the page and main table that split screen in two parts
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="kb_manage?ln=%(ln)s&amp;sortby=%(sortby)s">%(close)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small>%(mappings)s</small>&nbsp;</td>
<td>2.&nbsp;<small><a href="kb_show_attributes?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s">%(attributes)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small><a href="kb_show_dependencies?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s">%(dependencies)s</a></small>&nbsp;</td>
</tr>
</table> ''' % {'ln':ln,
'kb_id':kb_id,
'sortby':sortby,
'close': _("Close Editor"),
'mappings': _("Knowledge Base Mappings"),
'attributes':_("Knowledge Base Attributes"),
'dependencies':_("Knowledge Base Dependencies"),
'menu': _("Menu")}
out += '''
<p>Here you can add new mappings to this base and change the base attributes.</p>
<table width="100%" align="center">
<tr>
'''
#First column of table: add mapping form
out += '''
<td width="300" valign="top">
<form name="addNewMapping"
action="kb_add_mapping?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s" method="post">''' % {'ln':ln,
'kb_id':kb_id,
'sortby':sortby}
out += '''
<table class="admin_wvar" width="100%%" cellspacing="0">
<tr>
- <th colspan="2" class="adminheaderleft">Add New Mapping &nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#addMappingKB">?</a>]</th>
+ <th colspan="2" class="adminheaderleft">Add New Mapping &nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#addMappingKB">?</a>]</th>
</tr>
<tr>
<td class="admintdright"><label for="mapFrom"><span style="white-space: nowrap;">Map From</span></label>:&nbsp;</td>
<td><input tabindex="1" name="mapFrom" type="text" id="mapFrom" size="25"/></td>
</tr>
<tr>
<td class="admintdright"><label for="mapTo">To</label>:&nbsp;</td>
<td><input tabindex="2" name="mapTo" type="text" id="mapTo" size="25"/></td>
</tr>
<tr>
<td colspan="2" align="right"><input tabindex="3" class="adminbutton" type="submit" value="Add new Mapping"/></td>
</tr>
</table>
</form>
</td>
- ''' % {'weburl':weburl}
+ ''' % {'siteurl':CFG_SITE_URL}
#Second column: mappings table
#header and footer
out += '''
<td valign="top">
<table class="admin_wvar">
<thead>
<tr>
<th class="adminheaderleft" width="25">&nbsp;</th>
<th class="adminheaderleft" width="34%%"><a href="kb_show?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=from">Map From</a></th>
<th class="adminheaderleft">&nbsp;</th>
<th class="adminheaderleft" width="34%%"><a href="kb_show?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=to">To</a></th>
- <th class="adminheadercenter" width="25%%">Action&nbsp;&nbsp;&nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#removeMappingKB">?</a>]</th>
+ <th class="adminheadercenter" width="25%%">Action&nbsp;&nbsp;&nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#removeMappingKB">?</a>]</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="5">&nbsp;</td>
</tr>
</tfoot>
<tbody>
''' % {'ln':ln,
'kb_id':kb_id,
- 'weburl':weburl}
+ 'siteurl':CFG_SITE_URL}
#table content: key, value and actions
if len(mappings) == 0:
out += '''
<tr>
<td colspan="5" class="admintd" align="center"><em>Knowledge base is empty</em></td>
</tr></tbody>'''
else:
line = 0
tabindex_key = 6
tabindex_value = 7
tabindex_save_button = 8
for mapping in mappings:
style = "vertical-align: middle;"
if line % 2:
style += 'background-color: rgb(235, 247, 255);'
line += 1
tabindex_key += 3
tabindex_value += 3
tabindex_save_button += 3
row_content = '''
<tr>
<td colspan="5">
<form action="kb_edit_mapping?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s" name="%(key)s" method="post">
<table>
<tr>
<td class="admintdright" style="%(style)s" width="5">
&nbsp;
<input type="hidden" name="key" value="%(key)s"/>
</td>
<td class="admintdleft" style="%(style)s">
<input type="text" name="mapFrom" size="30" maxlength="255" value="%(key)s" tabindex="%(tabindex_key)s"/>
</td>
<td class="admintdleft" style="%(style)s white-space: nowrap;" width="5">=&gt;</td>
<td class="admintdleft"style="%(style)s">
<input type="text" name="mapTo" size="30" value="%(value)s" tabindex="%(tabindex_value)s">
</td>
<td class="admintd" style="%(style)s white-space: nowrap;">
<input class="adminbutton" type="submit" name="update" value="Save" tabindex="%(tabindex_save_button)s"/>
<input class="adminbutton" type="submit" name="delete"value="Delete"/></td>
</tr></table></form></td></tr>
''' % {'key': mapping['key'],
'value':mapping['value'],
'ln':ln,
'style':style,
'tabindex_key': tabindex_key,
'tabindex_value': tabindex_value,
'tabindex_save_button': tabindex_save_button,
'kb_id':kb_id,
'sortby':sortby}
out += row_content
#End of tables
out += '''</tbody></table>
</td>
'''
out+= '''
<td width="20%">&nbsp;</td>
</tr>
</table>
'''
#add script that will put focus on first field of "add mapping" form
out += '''
<script type="text/javascript">
self.focus();document.addNewMapping.mapFrom.focus()
</script>
'''
return out
def tmpl_admin_kb_show_attributes(self, ln, kb_id, kb_name, description, sortby):
"""
Returns the attributes of a knowledge base.
@param ln language
@param kb_id the id of the kb
@param kb_name the name of the kb
@param description the description of the kb
@param sortby the sorting criteria ('from' or 'to')
@return main management console as html
"""
_ = gettext_set_language(ln) # load the right message language
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="kb_manage?ln=%(ln)s&amp;sortby=%(sortby)s">%(close)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small><a href="kb_show?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s">%(mappings)s</a></small>&nbsp;</td>
<td>2.&nbsp;<small>%(attributes)s</small>&nbsp;</td>
<td>3.&nbsp;<small><a href="kb_show_dependencies?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s">%(dependencies)s</a></small>&nbsp;</td>
</tr>
</table> ''' % {'ln':ln,
'kb_id':kb_id,
'sortby':sortby,
'close': _("Close Editor"),
'menu': _("Menu"),
'mappings': _("Knowledge Base Mappings"),
'attributes':_("Knowledge Base Attributes"),
'dependencies':_("Knowledge Base Dependencies")}
out += '''
<form name="updateAttributes"
action="kb_update_attributes?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s" method="post">
<table class="admin_wvar" cellspacing="0">
<tr>
''' % {'ln':ln,
'kb_id':kb_id,
'sortby':sortby}
out += '''
- <th colspan="2" class="adminheaderleft">%(kb_name)s attributes&nbsp;[<a href="%(weburl)s/help/admin/bibformat-admin-guide#attrsKB">?</a>]</th>''' % {'kb_name': kb_name,
- 'weburl': weburl}
+ <th colspan="2" class="adminheaderleft">%(kb_name)s attributes&nbsp;[<a href="%(siteurl)s/help/admin/bibformat-admin-guide#attrsKB">?</a>]</th>''' % {'kb_name': kb_name,
+ 'siteurl': CFG_SITE_URL}
out += '''
</tr>
<tr>
<td class="admintdright">
<input type="hidden" name="key" value="%(kb_id)s"/>
<label for="name">Name</label>:&nbsp;</td>
<td><input tabindex="4" name="name" type="text" id="name" size="25" value="%(kb_name)s"/></td>
</tr>
<tr>
<td class="admintdright" valign="top"><label for="description">Description</label>:&nbsp;</td>
<td><textarea tabindex="5" name="description" id="description" rows="4" cols="25">%(kb_description)s</textarea> </td>
</tr>
<tr>
<td>&nbsp;</td>
<td align="right"><input tabindex="6" class="adminbutton" type="submit" value="Update Base Attributes"/></td>
</tr>
</table>
</form></td>''' % {'kb_name': kb_name,
'kb_description': description,
'kb_id':kb_id}
return out
def tmpl_admin_kb_show_dependencies(self, ln, kb_id, kb_name, sortby, format_elements):
"""
Returns the attributes of a knowledge base.
@param ln language
@param kb_id the id of the kb
@param kb_name the name of the kb
@param sortby the sorting criteria ('from' or 'to')
@param format_elements the elements that use this kb
"""
_ = gettext_set_language(ln) # load the right message language
out = '''
<table class="admin_wvar" cellspacing="0">
<tr><th colspan="4" class="adminheaderleft">%(menu)s</th></tr>
<tr>
<td>0.&nbsp;<small><a href="kb_manage?ln=%(ln)s&amp;sortby=%(sortby)s">%(close)s</a></small>&nbsp;</td>
<td>1.&nbsp;<small><a href="kb_show?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s">%(mappings)s</a></small>&nbsp;</td>
<td>2.&nbsp;<small><a href="kb_show_attributes?ln=%(ln)s&amp;kb=%(kb_id)s&amp;sortby=%(sortby)s">%(attributes)s</a></small>&nbsp;</td>
<td>3.&nbsp;<small>%(dependencies)s</small>&nbsp;</td>
</tr>
</table> <br/>''' % {'ln':ln,
'kb_id':kb_id,
'sortby':sortby,
'close': _("Close Editor"),
'menu' : _("Menu"),
'mappings': _("Knowledge Base Mappings"),
'attributes':_("Knowledge Base Attributes"),
'dependencies':_("Knowledge Base Dependencies")}
out += ''' <table width="90%" class="admin_wvar" cellspacing="0"><tr>'''
out += '''
<th class="adminheaderleft">Format Elements used by %(name)s*</th>
</tr>
<tr>
<td valign="top">&nbsp;''' % {"name": kb_name}
if len(format_elements) == 0:
out += '<p align="center"><i>This knowledge base is not used in any format elements.</i></p>'
for format_element in format_elements:
name = format_element['name']
out += '''<a href="format_elements_doc?ln=%(ln)s#%(anchor)s">%(name)s</a><br/>''' % {'name':"bfe_"+name.lower(),
'anchor':name.upper(),
'ln':ln}
out += '''
</td>
</tr>
</table>
<b>*Note</b>: Some knowledge base usages might not be shown. Check manually.
'''
return out
def tmpl_dreamweaver_floater(self, ln, format_elements):
"""
Returns the content of the BibFormat palette for Dreamweaver. This
'floater' will let users of Dreamweaver to insert Format elements
into their code right from the floater.
@param format_elements an ordered list of format elements structures as returned by get_format_elements
"""
names_list = [] # list of element names such as ['Authors', 'Title']
codes_list = [] # list of element code such as ['<BFE_AUTHORS limit="" separator="," />', '<BFE_TITLE />']
docs_list = [] # list of HTML doc for each element
for format_element in format_elements:
format_attributes = format_element['attrs']
name = format_attributes['name']
#description = format_attributes['description']
params = [x['name'] + '="'+x['default']+'"' for x in format_attributes['params']]
builtin_params = [x['name'] + '="'+x['default']+'"' for x in format_attributes['builtin_params']]
code = ("<BFE_" + name + ' ' + ' '.join(builtin_params)+ ' ' + ' '.join(params) +"/>").replace("'", r"\'")
doc = self.tmpl_admin_print_format_element_documentation(ln, name, format_attributes, print_see_also=False).replace("'", r"\'")
names_list.append(name)
codes_list.append(code)
docs_list.append(doc)
out = '''
<!DOCTYPE HTML SYSTEM "-//Macromedia//DWExtension layout-engine5.0//floater">
<html>
<head>
<!-- This file is to be used as floating panel for Dreamweaver.
To install, drag and drop inside /Configuration/Floaters of your Dreamweaver
application directory. You also have to enable a menu to open the floater:
Edit file Menu.xml located inside /Configuration/Menus of your Dreamweaver
application directory and copy-paste the following line in the menu you want
(typically inside tag 'menu' with attribute id = 'DWMenu_Window_Others'):
<menuitem name="BibFormat Elements" enabled="true" command="dw.toggleFloater('BibFormat_floater.html')" checked="dw.getFloaterVisibility('BibFormat_floater.html')" />
-->
<title>BibFormat Elements</title>
<script language="JavaScript">
var docs = new Array(%(docs)s);
var codes = new Array(%(codes)s);
function selectionChanged(){
// get the selected node
var theDOM = dw.getDocumentDOM();
var theNode = theDOM.getSelectedNode();
// check if node is a BibFormat Element
if (theNode.nodeType == Node.COMMENT_NODE && theNode.data.length >= 5 && theNode.data.toLowerCase().substring(0,5) == "<bfe_"){
var names = document.elementsList.options;
for (i=0;i<names.length; i++){
if (names[i].text.toLowerCase() == theNode.data.split(' ')[0].toLowerCase() ||
names[i].text.toLowerCase() == theNode.data.split(' ')[0].toLowerCase().substring(5,theNode.data.length)){
document.elementsList.selectedIndex = i;
selectElement(document.elementsList);
return;
}
}
}
}
function isAvailableInCodeView(){
return true;
}
function selectElement(elementsList){
document.infoBFE.innerHTML = docs[elementsList.selectedIndex];
}
function insertElement(){
// insert selection into code
var element_code = codes[document.elementsList.selectedIndex];
// get the DOM
var theDOM = dw.getDocumentDOM();
var theDocEl = theDOM.documentElement;
var theWholeDoc = theDocEl.outerHTML;
// Get the offsets of the selection
var theSel = theDOM.getSelection();
theDocEl.outerHTML = theWholeDoc.substring(0,theSel[0]) + element_code + theWholeDoc.substring(theSel[1]);
}
</script>
</head>
<body>
<table width="100%%" border="0" cellspacing="0" cellpadding="3">
<tr>
<td valign="top">
<select name="elementsList" id="elementsList" size="15" onChange="selectElement(this)">
%(names)s
</select><br/>
<input type="submit" name="Submit" value="Insert" onClick="insertElement()">
</td>
<td valign="top" width="100%%">
<div id="infoBFE">
<center>No Format Element selected. Select one from the list on the right.</center>
</div>
</td>
</tr>
</table>
</body>
</html>
''' % {'docs': ', '.join(["'"+x+"'" for x in docs_list]).replace('\n','\\n'),
'codes': ', '.join(["'"+x+"'" for x in codes_list]).replace('\n','\\n'),
'names': '\n'.join(['<option>'+x+'</option>' for x in names_list])}
return out
def tmpl_admin_validate_format(self, ln, errors):
"""
Prints the errors of the validation of a format (might be any
kind of format)
@param ln language
@param errors a list of tuples (error code, string error message)
"""
_ = gettext_set_language(ln) # load the right message language
out = ""
if len(errors) == 0:
out += '''<span style="color: rgb(0, 255, 0);" >%s.</span>''' % _('No problem found with format')
elif len(errors) == 1:
out += '''<span style="color: rgb(255, 0, 0);" >%s:</span><br/>''' % _('An error has been found')
else:
out += '''<span style="color: rgb(255, 0, 0);" >%s:</span><br/>''' % _('The following errors have been found')
for error in errors:
out += error + "<br/>"
return out
def tmpl_admin_dialog_box(self, url, ln, title, message, options):
"""
Prints a dialog box with given title, message and options
@param url the url of the page that must process the result of the dialog box
@param ln language
@param title the title of the dialog box
@param message a formatted message to display inside dialog box
@param options a list of string options to display as button to the user
"""
out = ""
out += '''
<div style="text-align:center;">
<fieldset style="display:inline;margin-left:auto;margin-right:auto;">
<legend>%(title)s:</legend>
<p>%(message)s</p>
<form method="post" action="%(url)s">
''' % {'title':title,
'message':message,
'url':url}
for option in options:
out += '''<input type="submit" class="adminbutton" name="chosen_option" value="%(value)s" />&nbsp;''' % {'value':option}
out += '''</form></fieldset></div>'''
return out
diff --git a/modules/bibformat/lib/bibformat_xslt_engine.py b/modules/bibformat/lib/bibformat_xslt_engine.py
index bb4299b46..33b3a1832 100644
--- a/modules/bibformat/lib/bibformat_xslt_engine.py
+++ b/modules/bibformat/lib/bibformat_xslt_engine.py
@@ -1,381 +1,381 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
bibformat_xslt_engine - Wrapper for an XSLT engine.
Some functions are registered in order to be used in XSL code:
- creation_date(recID)
- modification_date(recID)
Dependencies: Need one of the following XSLT processors:
- libxml2 & libxslt
- 4suite
Used by: bibformat_engine.py
"""
__revision__ = "$Id$"
import sys
import os
from invenio.config import \
- weburl
+ CFG_SITE_URL
from invenio.bibformat_config import \
CFG_BIBFORMAT_TEMPLATES_PATH
from invenio.bibformat_dblayer import \
get_creation_date, \
get_modification_date
# The namespace used for BibFormat function
CFG_BIBFORMAT_FUNCTION_NS = "http://cdsweb.cern.ch/bibformat/fn"
# Import one XSLT processor
#
# processor_type:
# -1 : No processor found
# 0 : libxslt
# 1 : 4suite
processor_type = -1
try:
# libxml2 & libxslt
processor_type = 0
import libxml2
import libxslt
except ImportError:
pass
if processor_type == -1:
try:
# 4suite
processor_type = 1
from Ft.Xml.Xslt import Processor
from Ft.Xml import InputSource
from xml.dom import Node
except ImportError:
pass
if processor_type == -1:
# No XSLT processor found
sys.stderr.write('No XSLT processor could be found.\n' \
'No output produced.\n')
sys.exit(1)
##################################################################
# Support for 'creation_date' and 'modification_date' functions #
def get_creation_date_libxslt(ctx, recID):
"""
libxslt extension function:
Bridge between BibFormat and XSL stylesheets.
Returns record creation date.
Can be used in that way in XSL stylesheet
(provided xmlns:fn="http://cdsweb.cern.ch/bibformat/fn" has been declared):
<xsl:value-of select="fn:creation_date(445)"/> where 445 is a recID
if recID is string, value is converted to int
if recID is Node, first child node (text node) is taken as value
"""
try:
if isinstance(recID, str):
recID_int = int(recID)
elif isinstance(recID, int):
recID_int = recID
else:
recID_int = libxml2.xmlNode(_obj=recID[0]).children.content
return get_creation_date(recID_int)
except Exception, err:
sys.stderr.write("Error during formatting function evaluation: " + \
str(err) + \
'\n')
return ''
def get_creation_date_4suite(ctx, recID):
"""
4suite extension function:
Bridge between BibFormat and XSL stylesheets.
Returns record creation date.
Can be used in that way in XSL stylesheet
(provided xmlns:fn="http://cdsweb.cern.ch/bibformat/fn" has been declared):
<xsl:value-of select="fn:creation_date(445)"/>
if value is int, value is converted to string
if value is Node, first child node (text node) is taken as value
"""
try:
if len(recID) > 0 and isinstance(recID[0], Node):
recID_int = recID[0].firstChild.nodeValue
if recID_int is None:
return ''
else:
recID_int = int(recID_int)
return get_creation_date(recID_int)
except Exception, err:
sys.stderr.write("Error during formatting function evaluation: " + \
str(err) + \
'\n')
return ''
def get_modification_date_libxslt(ctx, recID):
"""
libxslt extension function:
Bridge between BibFormat and XSL stylesheets.
Returns record modification date.
Can be used in that way in XSL stylesheet
(provided xmlns:fn="http://cdsweb.cern.ch/bibformat/fn" has been declared):
<xsl:value-of select="fn:creation_date(445)"/> where 445 is a recID
if recID is string, value is converted to int
if recID is Node, first child node (text node) is taken as value
"""
try:
if isinstance(recID, str):
recID_int = int(recID)
elif isinstance(recID, int):
recID_int = recID
else:
recID_int = libxml2.xmlNode(_obj=recID[0]).children.content
return get_modification_date(recID_int)
except Exception, err:
sys.stderr.write("Error during formatting function evaluation: " + \
str(err) + \
'\n')
return ''
def get_modification_date_4suite(ctx, recID):
"""
4suite extension function:
Bridge between BibFormat and XSL stylesheets.
Returns record modification date.
Can be used in that way in XSL stylesheet
(provided xmlns:fn="http://cdsweb.cern.ch/bibformat/fn" has been declared):
<xsl:value-of select="fn:modification_date(445)"/>
if value is int, value is converted to string
if value is Node, first child node (text node) is taken as value
"""
try:
if len(recID) > 0 and isinstance(recID[0], Node):
recID_int = recID[0].firstChild.nodeValue
if recID_int is None:
return ''
else:
recID_int = int(recID_int)
return get_modification_date(recID_int)
except Exception, err:
sys.stderr.write("Error during formatting function evaluation: " + \
str(err) + \
'\n')
return ''
def eval_bibformat_libxslt(ctx, recID, template_code):
"""
libxslt extension function:
Bridge between BibFormat and XSL stylesheets.
Returns the evaluation of the given piece of format template
Can be used in that way in XSL stylesheet
(provided xmlns:fn="http://cdsweb.cern.ch/bibformat/fn" has been declared):
<xsl:value-of select="fn:eval_bibformat(marc:controlfield[@tag='001'],'&lt;BFE_SERVER_INFO var=&quot;recurl&quot;>')" />
if recID is string, value is converted to int
if recID is Node, first child node (text node) is taken as value
template_code is evaluated as a format template piece of code. '<'
and '"' need to be escaped with '&lt;' and '&quot;'
"""
from invenio.bibformat_engine import \
format_with_format_template, \
BibFormatObject
try:
if isinstance(recID, str):
recID_int = int(recID)
elif isinstance(recID, int):
recID_int = recID
else:
recID_int = libxml2.xmlNode(_obj=recID[0]).children.content
bfo = BibFormatObject(recID_int)
return format_with_format_template(None, bfo,
verbose=0,
format_template_code=template_code)[0]
except Exception, err:
sys.stderr.write("Error during formatting function evaluation: " + \
str(err) + \
'\n')
return ''
def eval_bibformat_4suite(ctx, recID, template_code):
"""
4suite extension function:
Bridge between BibFormat and XSL stylesheets.
Returns the evaluation of the given piece of format template
Can be used in that way in XSL stylesheet
(provided xmlns:fn="http://cdsweb.cern.ch/bibformat/fn" has been declared):
<xsl:value-of select="fn:eval_bibformat(marc:controlfield[@tag='001'],'&lt;BFE_SERVER_INFO var=&quot;recurl&quot;>')" />
if recID is string, value is converted to int
if recID is Node, first child node (text node) is taken as value
template_code is evaluated as a format template piece of code. '<'
and '"' need to be escaped with '&lt;' and '&quot;'
"""
from invenio.bibformat_engine import \
format_with_format_template, \
BibFormatObject
try:
if len(recID) > 0 and isinstance(recID[0], Node):
recID_int = recID[0].firstChild.nodeValue
if recID_int is None:
return ''
else:
recID_int = int(recID_int)
bfo = BibFormatObject(recID_int)
return format_with_format_template(None, bfo,
verbose=0,
format_template_code=template_code)[0]
except Exception, err:
sys.stderr.write("Error during formatting function evaluation: " + \
str(err) + \
'\n')
return ''
# End of date-related functions #
##################################################################
def format(xmltext, template_filename=None, template_source=None):
"""
Processes an XML text according to a template, and returns the result.
The template can be given either by name (or by path) or by source.
If source is given, name is ignored.
bibformat_xslt_engine will look for template_filename in standard directories
for templates. If not found, template_filename will be assumed to be a path to
a template. If none can be found, return None.
@param xmltext The string representation of the XML to process
@param template_filename The name of the template to use for the processing
@param template_source The configuration describing the processing.
@return the transformed XML text.
"""
if processor_type == -1:
# No XSLT processor found
sys.stderr.write('No XSLT processor could be found.')
sys.exit(1)
# Retrieve template and read it
if template_source:
template = template_source
elif template_filename:
try:
path_to_templates = (CFG_BIBFORMAT_TEMPLATES_PATH + os.sep +
template_filename)
if os.path.exists(path_to_templates):
template = file(path_to_templates).read()
elif os.path.exists(template_filename):
template = file(template_filename).read()
else:
sys.stderr.write(template_filename +' does not exist.')
return None
except IOError:
sys.stderr.write(template_filename +' could not be read.')
return None
else:
sys.stderr.write(template_filename +' was not given.')
return None
result = ""
if processor_type == 0:
# libxml2 & libxslt
# Register BibFormat functions for use in XSL
libxslt.registerExtModuleFunction("creation_date",
CFG_BIBFORMAT_FUNCTION_NS,
get_creation_date_libxslt)
libxslt.registerExtModuleFunction("modification_date",
CFG_BIBFORMAT_FUNCTION_NS,
get_modification_date_libxslt)
libxslt.registerExtModuleFunction("eval_bibformat",
CFG_BIBFORMAT_FUNCTION_NS,
eval_bibformat_libxslt)
# Load template and source
template_xml = libxml2.parseDoc(template)
processor = libxslt.parseStylesheetDoc(template_xml)
source = libxml2.parseDoc(xmltext)
# Transform
result_object = processor.applyStylesheet(source, None)
result = processor.saveResultToString(result_object)
# Deallocate
processor.freeStylesheet()
source.freeDoc()
result_object.freeDoc()
elif processor_type == 1:
# 4suite
# Init
processor = Processor.Processor()
# Register BibFormat functions for use in XSL
processor.registerExtensionFunction(CFG_BIBFORMAT_FUNCTION_NS,
"creation_date",
get_creation_date_4suite)
processor.registerExtensionFunction(CFG_BIBFORMAT_FUNCTION_NS,
"modification_date",
get_modification_date_4suite)
processor.registerExtensionFunction(CFG_BIBFORMAT_FUNCTION_NS,
"eval_bibformat",
eval_bibformat_4suite)
# Load template and source
transform = InputSource.DefaultFactory.fromString(template,
- uri=weburl)
+ uri=CFG_SITE_URL)
source = InputSource.DefaultFactory.fromString(xmltext,
- uri=weburl)
+ uri=CFG_SITE_URL)
processor.appendStylesheet(transform)
# Transform
result = processor.run(source)
else:
sys.stderr.write("No XSLT processor could be found")
return result
if __name__ == "__main__":
pass
diff --git a/modules/bibformat/lib/bibformatadmin_regression_tests.py b/modules/bibformat/lib/bibformatadmin_regression_tests.py
index 74c522bf0..8ab2b8961 100644
--- a/modules/bibformat/lib/bibformatadmin_regression_tests.py
+++ b/modules/bibformat/lib/bibformatadmin_regression_tests.py
@@ -1,72 +1,72 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat Admin Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class BibFormatAdminWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibFormat Admin web pages whether they are up or not."""
def test_bibformat_admin_interface_availability(self):
"""bibformatadmin - availability of BibFormat Admin interface pages"""
- baseurl = weburl + '/admin/bibformat/'
+ baseurl = CFG_SITE_URL + '/admin/bibformat/'
_exports = ['bibformatadmin.py/format_templates_manage',
'bibformatadmin.py/output_formats_manage',
'bibformatadmin.py/format_elements_doc',
'bibformatadmin.py/kb_manage']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'not authorized to perform'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_bibformat_admin_guide_availability(self):
"""bibformatadmin - availability of BibFormat Admin guide pages"""
- url = weburl + '/help/admin/bibformat-admin-guide'
+ url = CFG_SITE_URL + '/help/admin/bibformat-admin-guide'
error_messages = test_web_page_content(url,
expected_text="BibFormat Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(BibFormatAdminWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibformat/lib/bibformatadminlib.py b/modules/bibformat/lib/bibformatadminlib.py
index 75b51ce2d..69f15c5c1 100644
--- a/modules/bibformat/lib/bibformatadminlib.py
+++ b/modules/bibformat/lib/bibformatadminlib.py
@@ -1,1621 +1,1621 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Handle requests from the web interface to configure BibFormat.
"""
__revision__ = "$Id$"
import os
import re
import stat
import time
import cgi
-from invenio.config import CFG_SITE_LANG, weburl, CFG_ETCDIR
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, CFG_ETCDIR
from invenio.bibformat_config import \
CFG_BIBFORMAT_TEMPLATES_PATH, \
CFG_BIBFORMAT_OUTPUTS_PATH, \
CFG_BIBFORMAT_ELEMENTS_PATH, \
CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION
from invenio.urlutils import wash_url_argument
from invenio.errorlib import get_msgs_for_code_list
from invenio.messages import gettext_set_language, wash_language, language_list_long
from invenio.search_engine import perform_request_search, encode_for_xml
from invenio import bibformat_dblayer
from invenio import bibformat_engine
import invenio.template
bibformat_templates = invenio.template.load('bibformat')
def getnavtrail(previous = '', ln=CFG_SITE_LANG):
"""Get the navtrail"""
previous = wash_url_argument(previous, 'str')
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail = '''<a class="navtrail" href="%s/help/admin">%s</a> &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py?ln=%s">%s</a> ''' % \
- (weburl, _("Admin Area"), weburl, ln, _("BibFormat Admin"))
+ (CFG_SITE_URL, _("Admin Area"), CFG_SITE_URL, ln, _("BibFormat Admin"))
navtrail = navtrail + previous
return navtrail
def perform_request_index(ln=CFG_SITE_LANG, warnings=None, is_admin=False):
"""
Returns the main BibFormat admin page.
@param ln language
@param warnings a list of messages to display at top of the page, that prevents writability in etc
@param is_admin indicate if user is authorized to use BibFormat
@return the main admin page
"""
if warnings is not None and len(warnings) > 0:
warnings = get_msgs_for_code_list(warnings, 'warning', ln)
warnings = [x[1] for x in warnings] # Get only message, not code
return bibformat_templates.tmpl_admin_index(ln, warnings, is_admin)
def perform_request_format_templates_management(ln=CFG_SITE_LANG, checking=0):
"""
Returns the main management console for format templates
@param ln language
@param checking the level of checking (0: basic, 1:extensive (time consuming) )
@return the main page for format templates management
"""
# Reload in case a format was changed
bibformat_engine.clear_caches()
# Get formats lists of attributes
formats = bibformat_engine.get_format_templates(with_attributes=True)
formats_attrs = []
for filename in formats:
attrs = formats[filename]['attrs']
attrs['filename'] = filename
if filename.endswith('.xsl'):
attrs['name'] += ' (XSL)'
attrs['editable'] = can_write_format_template(filename)
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + filename
try:
attrs['last_mod_date'] = time.ctime(os.stat(path)[stat.ST_MTIME])
except OSError:
# File does not exist. Happens with temporary files
# created by editors.
continue
status = check_format_template(filename, checking)
if len(status) > 1 or (len(status)==1 and status[0][0] != 'ERR_BIBFORMAT_CANNOT_READ_TEMPLATE_FILE'):
status = '''
<a style="color: rgb(255, 0, 0);"
- href="%(weburl)s/admin/bibformat/bibformatadmin.py/validate_format?ln=%(ln)s&amp;bft=%(bft)s">Not OK</a>
- ''' % {'weburl':weburl,
+ href="%(siteurl)s/admin/bibformat/bibformatadmin.py/validate_format?ln=%(ln)s&amp;bft=%(bft)s">Not OK</a>
+ ''' % {'siteurl':CFG_SITE_URL,
'ln':ln,
'bft':filename}
else:
status = '<span style="color: rgb(0, 255, 0);">OK</span>'
attrs['status'] = status
formats_attrs.append(attrs)
def sort_by_attr(seq):
"""
Sort 'seq' by attribute name.
@param seq a list of dictionaries, containing each one key named 'name'
"""
intermed = [ (x['name'].lower(), i, x) for i, x in enumerate(seq)]
intermed.sort()
return [x[-1] for x in intermed]
sorted_format_templates = sort_by_attr(formats_attrs)
return bibformat_templates.tmpl_admin_format_templates_management(ln, sorted_format_templates)
def perform_request_format_template_show(bft, ln=CFG_SITE_LANG, code=None,
ln_for_preview=CFG_SITE_LANG, pattern_for_preview="",
content_type_for_preview="text/html"):
"""
Returns the editor for format templates.
@param ln language
@param bft the template to edit
@param code, the code being edited
@param ln_for_preview the language for the preview (for bfo)
@param pattern_for_preview the search pattern to be used for the preview (for bfo)
@return the main page for formats management
"""
format_template = bibformat_engine.get_format_template(filename=bft, with_attributes=True)
# Either use code being edited, or the original code inside template
if code is None:
code = cgi.escape(format_template['code'])
# Build a default pattern if it is empty
if pattern_for_preview == "":
recIDs = perform_request_search()
if len(recIDs) > 0:
recID = recIDs[0]
pattern_for_preview = "recid:%s" % recID
editable = can_write_format_template(bft)
# Look for all existing content_types
content_types = bibformat_dblayer.get_existing_content_types()
# Add some standard content types if not already there
standard_content_types = ['text/xml', 'application/rss+xml', 'text/plain', 'text/html']
content_types.extend([content_type for content_type in standard_content_types
if content_type not in content_types])
return bibformat_templates.tmpl_admin_format_template_show(ln, format_template['attrs']['name'],
format_template['attrs']['description'],
code, bft,
ln_for_preview=ln_for_preview,
pattern_for_preview=pattern_for_preview,
editable=editable,
content_type_for_preview=content_type_for_preview,
content_types=content_types)
def perform_request_format_template_show_dependencies(bft, ln=CFG_SITE_LANG):
"""
Show the dependencies (on elements) of the given format.
@param ln language
@param bft the filename of the template to show
"""
format_template = bibformat_engine.get_format_template(filename=bft, with_attributes=True)
name = format_template['attrs']['name']
output_formats = get_outputs_that_use_template(bft)
format_elements = get_elements_used_by_template(bft)
tags = []
for output_format in output_formats:
for tag in output_format['tags']:
tags.append(tag)
for format_element in format_elements:
for tag in format_element['tags']:
tags.append(tag)
tags.sort()
return bibformat_templates.tmpl_admin_format_template_show_dependencies(ln,
name,
bft,
output_formats,
format_elements,
tags)
def perform_request_format_template_show_attributes(bft, ln=CFG_SITE_LANG, new=False):
"""
Page for template name and descrition attributes edition.
If format template is new, offer the possibility to
make a duplicate of an existing format template.
@param ln language
@param bft the template to edit
@param new if True, the template has just been added (is new)
@return the main page for format templates attributes edition
"""
all_templates = []
if new:
all_templates_attrs = bibformat_engine.get_format_templates(with_attributes=True)
if all_templates_attrs.has_key(bft): # Sanity check. Should always be true at this stage
del all_templates_attrs[bft] # Remove in order not to make a duplicate of self..
# Sort according to name, inspired from Python Cookbook
def sort_by_name(seq, keys):
"""
Sort the sequence 'seq' by 'keys'
"""
intermed = [(x['attrs']['name'], keys[i], i, x) for i, x in enumerate(seq)]
intermed.sort()
return [(x[1], x[0]) for x in intermed]
all_templates = sort_by_name(all_templates_attrs.values(), all_templates_attrs.keys())
#keys = all_templates_attrs.keys()
#keys.sort()
#all_templates = map(lambda x: (x, all_templates_attrs.get(x)['attrs']['name']), keys)
format_template = bibformat_engine.get_format_template(filename=bft, with_attributes=True)
name = format_template['attrs']['name']
description = format_template['attrs']['description']
editable = can_write_format_template(bft)
return bibformat_templates.tmpl_admin_format_template_show_attributes(ln,
name,
description,
bft,
editable,
all_templates,
new)
def perform_request_format_template_show_short_doc(ln=CFG_SITE_LANG, search_doc_pattern=""):
"""
Returns the format elements documentation to be included inside format templated editor.
Keep only elements that have 'search_doc_pattern' text inside description,
if pattern not empty
@param ln language
@param search_doc_pattern a search pattern that specified which elements to display
@return a brief version of the format element documentation
"""
# Get format elements lists of attributes
elements = bibformat_engine.get_format_elements(with_built_in_params=True)
keys = elements.keys()
keys.sort()
elements = map(elements.get, keys)
def filter_elem(element):
"""Keep element if is string representation contains all keywords of search_doc_pattern,
and if its name does not start with a number (to remove 'garbage' from elements in tags table)"""
if element['type'] != 'python' and \
element['attrs']['name'][0] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
return False
text = str(element).upper() # Basic text representation
if search_doc_pattern != "":
for word in search_doc_pattern.split():
if word.upper() != "AND" and text.find(word.upper()) == -1:
return False
return True
elements = filter(filter_elem, elements)
return bibformat_templates.tmpl_admin_format_template_show_short_doc(ln, elements)
def perform_request_format_elements_documentation(ln=CFG_SITE_LANG):
"""
Returns the main management console for format elements.
Includes list of format elements and associated administration tools.
@param ln language
@return the main page for format elements management
"""
# Get format elements lists of attributes
elements = bibformat_engine.get_format_elements(with_built_in_params=True)
keys = elements.keys()
keys.sort()
elements = map(elements.get, keys)
# Remove all elements found in table and that begin with a number (to remove 'garbage')
filtered_elements = [element for element in elements \
if element is not None and \
element['type'] == 'python' and \
element['attrs']['name'][0] not in \
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']]
return bibformat_templates.tmpl_admin_format_elements_documentation(ln, filtered_elements)
def perform_request_format_element_show_dependencies(bfe, ln=CFG_SITE_LANG):
"""
Show the dependencies of the given format.
@param ln language
@param bfe the filename of the format element to show
"""
format_templates = get_templates_that_use_element(bfe)
tags = get_tags_used_by_element(bfe)
return bibformat_templates.tmpl_admin_format_element_show_dependencies(ln,
bfe,
format_templates,
tags)
def perform_request_format_element_test(bfe, ln=CFG_SITE_LANG, param_values=None, user_info=None):
"""
Show the dependencies of the given format.
'param_values' is the list of values to pass to 'format'
function of the element as parameters, in the order ...
If params is None, this means that they have not be defined by user yet.
@param ln language
@param bfe the name of the format element to show
@param params the list of parameters to pass to element format function
@param user_info the user_info of this request
"""
_ = gettext_set_language(ln)
format_element = bibformat_engine.get_format_element(bfe, with_built_in_params=True)
# Load parameter names and description
##
param_names = []
param_descriptions = []
# First value is a search pattern to choose the record
param_names.append(_("Test with record:")) # Caution: keep in sync with same text below
param_descriptions.append(_("Enter a search query here."))
# Parameters defined in this element
for param in format_element['attrs']['params']:
param_names.append(param['name'])
param_descriptions.append(param['description'])
# Parameters common to all elements of a kind
for param in format_element['attrs']['builtin_params']:
param_names.append(param['name'])
param_descriptions.append(param['description'])
# Load parameters values
##
if param_values is None: #First time the page is loaded
param_values = []
# Propose an existing record id by default
recIDs = perform_request_search()
if len(recIDs) > 0:
recID = recIDs[0]
param_values.append("recid:%s" % recID)
# Default values defined in this element
for param in format_element['attrs']['params']:
param_values.append(param['default'])
#Parameters common to all elements of a kind
for param in format_element['attrs']['builtin_params']:
param_values.append(param['default'])
# Execute element with parameters
##
params = dict(zip(param_names, param_values))
# Find a record corresponding to search pattern
search_pattern = params[_("Test with record:")] # Caution keep in sync with same text above and below
recIDs = perform_request_search(p=search_pattern)
del params[_("Test with record:")] # Caution keep in sync with same text above
if len(recIDs) > 0:
bfo = bibformat_engine.BibFormatObject(recID = recIDs[0],
ln = ln,
search_pattern = search_pattern.split(' '),
xml_record = None,
user_info = user_info)
(result, errors) = bibformat_engine.eval_format_element(format_element, bfo, params)
else:
result = get_msgs_for_code_list([("ERR_BIBFORMAT_NO_RECORD_FOUND_FOR_PATTERN", search_pattern)],
stream='error', ln=CFG_SITE_LANG)[0][1]
return bibformat_templates.tmpl_admin_format_element_test(ln,
bfe,
format_element['attrs']['description'],
param_names,
param_values,
param_descriptions,
result)
def perform_request_output_formats_management(ln=CFG_SITE_LANG, sortby="code"):
"""
Returns the main management console for output formats.
Includes list of output formats and associated administration tools.
@param ln language
@param sortby the sorting crieteria (can be 'code' or 'name')
@return the main page for output formats management
"""
# Reload in case a format was changed
bibformat_engine.clear_caches()
# Get output formats lists of attributes
output_formats_list = bibformat_engine.get_output_formats(with_attributes=True)
output_formats = {}
for filename in output_formats_list:
output_format = output_formats_list[filename]
code = output_format['attrs']['code']
path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + filename
output_format['editable'] = can_write_output_format(code)
try:
output_format['last_mod_date'] = time.ctime(os.stat(path)[stat.ST_MTIME])
except OSError:
# File does not exist. Happens with temporary files
# created by editors.
continue
# Validate the output format
status = check_output_format(code)
# If there is an error but the error is just 'format is not writable', do not display as error
if len(status) > 1 or (len(status)==1 and status[0][0] != 'ERR_BIBFORMAT_CANNOT_WRITE_OUTPUT_FILE'):
status = '''
<a style="color: rgb(255, 0, 0);"
- href="%(weburl)s/admin/bibformat/bibformatadmin.py/validate_format?ln=%(ln)s&bfo=%(bfo)s">Not OK</a>
- ''' % {'weburl':weburl,
+ href="%(siteurl)s/admin/bibformat/bibformatadmin.py/validate_format?ln=%(ln)s&bfo=%(bfo)s">Not OK</a>
+ ''' % {'siteurl':CFG_SITE_URL,
'ln':ln,
'bfo':code}
else:
status = '<span style="color: rgb(0, 255, 0);">OK</span>'
output_format['status'] = status
output_formats[filename] = output_format
# Sort according to code or name, inspired from Python Cookbook
def get_attr(dic, attr):
"""
Returns the value given by 'attr' in the dictionary 'dic', representing
an output format attributes.
If attr is equal to 'code', returns the code attribute of the dictionary.
Else returns the generic name
@param dic a dictionary of the attribute of an output format, as returned by bibformat_engine.get_output_format
@param the attribute we want to fetch. Either 'code' or any other string
"""
if attr == "code":
return dic['attrs']['code']
else:
return dic['attrs']['names']['generic']
def sort_by_attr(seq, attr):
"""
Sort dictionaries given in 'seq' according to parameter 'attr'
"""
intermed = [ (get_attr(x, attr), i, x) for i, x in enumerate(seq)]
intermed.sort()
return [x[-1] for x in intermed]
if sortby != "code" and sortby != "name":
sortby = "code"
sorted_output_formats = sort_by_attr(output_formats.values(), sortby)
return bibformat_templates.tmpl_admin_output_formats_management(ln, sorted_output_formats)
def perform_request_output_format_show(bfo, ln=CFG_SITE_LANG, r_fld=[], r_val=[], r_tpl=[], default="", r_upd="", args={}):
"""
Returns the editing tools for a given output format.
The page either shows the output format from file, or from user's
POST session, as we want to let him edit the rules without
saving. Policy is: r_fld, r_val, rules_tpl are list of attributes
of the rules. If they are empty, load from file. Else use
POST. The i th value of each list is one of the attributes of rule
i. Rule i is the i th rule in order of evaluation. All list have
the same number of item.
r_upd contains an action that has to be performed on rules. It
can composed of a number (i, the rule we want to modify) and an
operator : "save" to save the rules, "add" or "del".
syntax: operator [number]
For eg: r_upd = _("Save Changes") saves all rules (no int should be specified).
For eg: r_upd = _("Add New Rule") adds a rule (no int should be specified).
For eg: r_upd = _("Remove Rule") + " 5" deletes rule at position 5.
The number is used only for operation delete.
An action can also be in **args. We must look there for string starting
with '(+|-) [number]' to increase (+) or decrease (-) a rule given by its
index (number).
For example "+ 5" increase priority of rule 5 (put it at fourth position).
The string in **args can be followed by some garbage that looks like .x
or .y, as this is returned as the coordinate of the click on the
<input type="image">. We HAVE to use args and reason on its keys, because for <input> of
type image, iexplorer does not return the value of the tag, but only the name.
Action is executed only if we are working from user's POST session
(means we must have loaded the output format first, which is
totally normal and expected behaviour)
IMPORTANT: we display rules evaluation index starting at 1 in
interface, but we start internally at 0
@param ln language
@param bfo the filename of the output format to show
@param r_fld the list of 'field' attribute for each rule
@param r_val the list of 'value' attribute for each rule
@param r_tpl the list of 'template' attribute for each rule
@param default the default format template used by this output format
@param r_upd the rule that we want to increase/decrease in order of evaluation
"""
output_format = bibformat_engine.get_output_format(bfo, with_attributes=True)
format_templates = bibformat_engine.get_format_templates(with_attributes=True)
name = output_format['attrs']['names']['generic']
rules = []
debug = ""
if len(r_fld) == 0 and r_upd=="":
# Retrieve rules from file
rules = output_format['rules']
default = output_format['default']
else:
# Retrieve rules from given lists
# Transform a single rule (not considered as a list with length
# 1 by the templating system) into a list
if not isinstance(r_fld, list):
r_fld = [r_fld]
r_val = [r_val]
r_tpl = [r_tpl]
for i in range(len(r_fld)):
rule = {'field': r_fld[i],
'value': r_val[i],
'template': r_tpl[i]}
rules.append(rule)
# Execute action
_ = gettext_set_language(ln)
if r_upd.startswith(_("Remove Rule")):
# Remove rule
index = int(r_upd.split(" ")[-1]) -1
del rules[index]
elif r_upd.startswith(_("Save Changes")):
# Save
update_output_format_rules(bfo, rules, default)
elif r_upd.startswith(_("Add New Rule")):
# Add new rule
rule = {'field': "",
'value': "",
'template': ""}
rules.append(rule)
else:
# Get the action in 'args'
# The action must be constructed from string of the kind:
# + 5 or - 4 or + 5.x or -4.y
for button_val in args.keys():#for all elements of form not handled yet
action = button_val.split(" ")
if action[0] == '-' or action[0] == '+':
index = int(action[1].split(".")[0]) -1
if action[0] == '-':
# Decrease priority
rule = rules[index]
del rules[index]
rules.insert(index + 1, rule)
# debug = 'Decrease rule '+ str(index)
break
elif action[0] == '+':
# Increase priority
rule = rules[index]
del rules[index]
rules.insert(index - 1, rule)
# debug = 'Increase rule ' + str(index)
break
editable = can_write_output_format(bfo)
return bibformat_templates.tmpl_admin_output_format_show(ln,
bfo,
name,
rules,
default,
format_templates,
editable)
def perform_request_output_format_show_dependencies(bfo, ln=CFG_SITE_LANG):
"""
Show the dependencies of the given format.
@param ln language
@param bfo the filename of the output format to show
"""
output_format = bibformat_engine.get_output_format(code=bfo, with_attributes=True)
name = output_format['attrs']['names']['generic']
format_templates = get_templates_used_by_output(bfo)
return bibformat_templates.tmpl_admin_output_format_show_dependencies(ln,
name,
bfo,
format_templates)
def perform_request_output_format_show_attributes(bfo, ln=CFG_SITE_LANG):
"""
Page for output format names and description attributes edition.
@param ln language
@param bfo filename of output format to edit
@return the main page for output format attributes edition
"""
output_format = bibformat_engine.get_output_format(code=bfo, with_attributes=True)
name = output_format['attrs']['names']['generic']
description = output_format['attrs']['description']
content_type = output_format['attrs']['content_type']
visible = output_format['attrs']['visibility']
# Get translated names. Limit to long names now.
# Translation are given in order of languages in language_list_long()
names_trans = []
for lang in language_list_long():
name_trans = output_format['attrs']['names']['ln'].get(lang[0], "")
names_trans.append({'lang':lang[1], 'trans':name_trans})
editable = can_write_output_format(bfo)
return bibformat_templates.tmpl_admin_output_format_show_attributes(ln,
name,
description,
content_type,
bfo,
names_trans,
editable,
visible)
def perform_request_knowledge_bases_management(ln=CFG_SITE_LANG):
"""
Returns the main page for knowledge bases management.
@param ln language
@return the main page for knowledge bases management
"""
kbs = bibformat_dblayer.get_kbs()
return bibformat_templates.tmpl_admin_kbs_management(ln, kbs)
def perform_request_knowledge_base_show(kb_id, ln=CFG_SITE_LANG, sortby="to"):
"""
Show the content of a knowledge base
@param ln language
@param kb a knowledge base id
@param sortby the sorting criteria ('from' or 'to')
@return the content of the given knowledge base
"""
name = bibformat_dblayer.get_kb_name(kb_id)
mappings = bibformat_dblayer.get_kb_mappings(name, sortby)
return bibformat_templates.tmpl_admin_kb_show(ln, kb_id, name, mappings, sortby)
def perform_request_knowledge_base_show_attributes(kb_id, ln=CFG_SITE_LANG, sortby="to"):
"""
Show the attributes of a knowledge base
@param ln language
@param kb a knowledge base id
@param sortby the sorting criteria ('from' or 'to')
@return the content of the given knowledge base
"""
name = bibformat_dblayer.get_kb_name(kb_id)
description = bibformat_dblayer.get_kb_description(name)
return bibformat_templates.tmpl_admin_kb_show_attributes(ln, kb_id, name, description, sortby)
def perform_request_knowledge_base_show_dependencies(kb_id, ln=CFG_SITE_LANG, sortby="to"):
"""
Show the dependencies of a kb
@param ln language
@param kb a knowledge base id
@param sortby the sorting criteria ('from' or 'to')
@return the dependencies of the given knowledge base
"""
name = bibformat_dblayer.get_kb_name(kb_id)
format_elements = get_elements_that_use_kb(name)
return bibformat_templates.tmpl_admin_kb_show_dependencies(ln, kb_id, name, sortby, format_elements)
def add_format_template():
"""
Adds a new format template (mainly create file with unique name)
@return the filename of the created format
"""
(filename, name) = bibformat_engine.get_fresh_format_template_filename("Untitled")
out = '<name>%(name)s</name><description></description>' % {'name':name}
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + filename
format = open(path, 'w')
format.write(out)
format.close
return filename
def delete_format_template(filename):
"""
Delete a format template given by its filename
If format template is not writable, do not remove
@param filename the format template filename
"""
if not can_write_format_template(filename):
return
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + filename
os.remove(path)
bibformat_engine.clear_caches()
def update_format_template_code(filename, code=""):
"""
Saves code inside template given by filename
"""
format_template = bibformat_engine.get_format_template_attrs(filename)
name = format_template['name']
description = format_template['description']
out = '''
<name>%(name)s</name>
<description>%(description)s</description>
%(code)s
''' % {'name':name, 'description':description, 'code':code}
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + filename
format = open(path, 'w')
format.write(out)
format.close
bibformat_engine.clear_caches()
def update_format_template_attributes(filename, name="", description="", duplicate=None):
"""
Saves name and description inside template given by filename.
the filename must change according to name, and every output format
having reference to filename must be updated.
If name already exist, use fresh filename (we never overwrite other templates) amd
remove old one.
if duplicate is different from None and is not empty string, then it means that we must copy
the code of the template whoose filename is given in 'duplicate' for the code
of our template.
@param duplicate the filename of a template that we want to copy
@return the filename of the modified format
"""
if filename.endswith('.'+CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION):
format_template = bibformat_engine.get_format_template(filename, with_attributes=True)
if duplicate is not None and duplicate != "":
format_template_to_copy = bibformat_engine.get_format_template(duplicate)
code = format_template_to_copy['code']
else:
code = format_template['code']
if format_template['attrs']['name'] != name:
# Name has changed, so update filename
old_filename = filename
old_path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + old_filename
# Remove old one
os.remove(old_path)
(filename, name) = bibformat_engine.get_fresh_format_template_filename(name)
# Change output formats that calls this template
output_formats = bibformat_engine.get_output_formats()
for output_format_filename in output_formats:
if can_read_output_format(output_format_filename) and can_write_output_format(output_format_filename):
output_path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + output_format_filename
format = open(output_path, 'r')
output_text = format.read()
format.close
output_pattern = re.compile("---(\s)*" + old_filename, re.IGNORECASE)
mod_output_text = output_pattern.sub("--- " + filename, output_text)
if output_text != mod_output_text:
format = open(output_path, 'w')
format.write(mod_output_text)
format.close
description = cgi.escape(description)
name = cgi.escape(name)
# Write updated format template
out = '''<name>%(name)s</name><description>%(description)s</description>%(code)s''' % {'name':name,
'description':description,
'code':code}
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + filename
format = open(path, 'w')
format.write(out)
format.close
bibformat_engine.clear_caches()
return filename
def add_output_format():
"""
Adds a new output format (mainly create file with unique name)
@return the code of the created format
"""
(filename, code) = bibformat_engine.get_fresh_output_format_filename("UNTLD")
# Add entry in database
bibformat_dblayer.add_output_format(code)
bibformat_dblayer.set_output_format_name(code, "Untitled", lang="generic")
bibformat_dblayer.set_output_format_content_type(code, "text/html")
# Add file
out = ""
path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + filename
format = open(path, 'w')
format.write(out)
format.close
return code
def delete_output_format(code):
"""
Delete a format template given by its code
if file is not writable, don't remove
@param code the 6 letters code of the output format to remove
"""
if not can_write_output_format(code):
return
# Remove entry from database
bibformat_dblayer.remove_output_format(code)
# Remove file
filename = bibformat_engine.resolve_output_format_filename(code)
path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + filename
os.remove(path)
bibformat_engine.clear_caches()
def update_output_format_rules(code, rules=[], default=""):
"""
Saves rules inside output format given by code
"""
# Generate output format syntax
# Try to group rules by field
previous_field = ""
out = ""
for rule in rules:
field = rule["field"]
value = rule["value"]
template = rule["template"]
if previous_field != field:
out += "tag %s:\n" % field
out +="%(value)s --- %(template)s\n" % {'value':value, 'template':template}
previous_field = field
out += "default: %s" % default
filename = bibformat_engine.resolve_output_format_filename(code)
path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + filename
format = open(path, 'w')
format.write(out)
format.close
bibformat_engine.clear_caches()
def update_output_format_attributes(code, name="", description="", new_code="",
content_type="", names_trans=[], visibility=1):
"""
Saves name and description inside output format given by filename.
If new_code already exist, use fresh code (we never overwrite other output).
@param description the new description
@param name the new name
@param code the new short code (== new bfo) of the output format
@param code the code of the output format to update
@param names_trans the translations in the same order as the languages from get_languages()
@param content_type the new content_type of the output format
@param visibility the visibility of the output format in the output formats list (public pages)
@return the filename of the modified format
"""
bibformat_dblayer.set_output_format_description(code, description)
bibformat_dblayer.set_output_format_content_type(code, content_type)
bibformat_dblayer.set_output_format_visibility(code, visibility)
bibformat_dblayer.set_output_format_name(code, name, lang="generic")
i = 0
for lang in language_list_long():
if names_trans[i] != "":
bibformat_dblayer.set_output_format_name(code, names_trans[i], lang[0])
i += 1
new_code = new_code.upper()
if code != new_code:
# If code has changed, we must update filename with a new unique code
old_filename = bibformat_engine.resolve_output_format_filename(code)
old_path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + old_filename
(new_filename, new_code) = bibformat_engine.get_fresh_output_format_filename(new_code)
new_path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + new_filename
os.rename(old_path, new_path)
bibformat_dblayer.change_output_format_code(code, new_code)
bibformat_engine.clear_caches()
return new_code
def add_kb_mapping(kb_name, key, value=""):
"""
Adds a new mapping to given kb
@param kb_name the name of the kb where to insert the new value
@param key the key of the mapping
@param value the value of the mapping
"""
bibformat_dblayer.add_kb_mapping(kb_name, key, value)
def remove_kb_mapping(kb_name, key):
"""
Delete an existing kb mapping in kb
@param kb_name the name of the kb where to insert the new value
@param key the key of the mapping
"""
bibformat_dblayer.remove_kb_mapping(kb_name, key)
def update_kb_mapping(kb_name, old_key, key, value):
"""
Update an existing kb mapping with key old_key with a new key and value
@param kb_name the name of the kb where to insert the new value
@param the key of the mapping in the kb
@param key the new key of the mapping
@param value the new value of the mapping
"""
bibformat_dblayer.update_kb_mapping(kb_name, old_key, key, value)
def kb_exists(kb_name):
"""Returns True if a kb with the given name exists"""
return bibformat_dblayer.kb_exists(kb_name)
def get_kb_name(kb_id):
"""
Returns the name of the kb given by id
"""
return bibformat_dblayer.get_kb_name(kb_id)
def update_kb_attributes(kb_name, new_name, new_description):
"""
Updates given kb_name with a new name and new description
@param kb_name the name of the kb to update
@param new_name the new name for the kb
@param new_description the new description for the kb
"""
bibformat_dblayer.update_kb(kb_name, new_name, new_description)
def add_kb(kb_name="Untitled"):
"""
Adds a new kb in database, and returns its id
The name of the kb will be 'Untitled#'
such that it is unique.
@param kb_name the name of the kb
@return the id of the newly created kb
"""
name = kb_name
i = 1
while bibformat_dblayer.kb_exists(name):
name = kb_name + " " + str(i)
i += 1
kb_id = bibformat_dblayer.add_kb(name, "")
return kb_id
def delete_kb(kb_name):
"""
Deletes given kb from database
"""
bibformat_dblayer.delete_kb(kb_name)
def can_read_format_template(filename):
"""
Returns 0 if we have read permission on given format template, else
returns other integer
"""
path = "%s%s%s" % (CFG_BIBFORMAT_TEMPLATES_PATH, os.sep, filename)
return os.access(path, os.R_OK)
def can_read_output_format(bfo):
"""
Returns 0 if we have read permission on given output format, else
returns other integer
"""
filename = bibformat_engine.resolve_output_format_filename(bfo)
path = "%s%s%s" % (CFG_BIBFORMAT_OUTPUTS_PATH, os.sep, filename)
return os.access(path, os.R_OK)
def can_read_format_element(name):
"""
Returns 0 if we have read permission on given format element, else
returns other integer
"""
filename = bibformat_engine.resolve_format_element_filename(name)
path = "%s%s%s" % (CFG_BIBFORMAT_ELEMENTS_PATH, os.sep, filename)
return os.access(path, os.R_OK)
def can_write_format_template(bft):
"""
Returns 0 if we have write permission on given format template, else
returns other integer
"""
if not can_read_format_template(bft):
return False
path = "%s%s%s" % (CFG_BIBFORMAT_TEMPLATES_PATH, os.sep, bft)
return os.access(path, os.W_OK)
def can_write_output_format(bfo):
"""
Returns 0 if we have write permission on given output format, else
returns other integer
"""
if not can_read_output_format(bfo):
return False
filename = bibformat_engine.resolve_output_format_filename(bfo)
path = "%s%s%s" % (CFG_BIBFORMAT_OUTPUTS_PATH, os.sep, filename)
return os.access(path, os.W_OK)
def can_write_etc_bibformat_dir():
"""
Returns true if we can write in etc/bibformat dir.
"""
path = "%s%sbibformat" % (CFG_ETCDIR, os.sep)
return os.access(path, os.W_OK)
def get_outputs_that_use_template(filename):
"""
Returns a list of output formats that call the given format template.
The returned output formats also give their dependencies on tags.
We don't return the complete output formats but some reference to
them (filename + names)
[ {'filename':"filename_1.bfo"
'names': {'en':"a name", 'fr': "un nom", 'generic':"a name"}
'tags': ['710__a', '920__']
},
...
]
Returns output formats references sorted by (generic) name
@param filename a format template filename
"""
output_formats_list = {}
tags = []
output_formats = bibformat_engine.get_output_formats(with_attributes=True)
for output_format in output_formats:
name = output_formats[output_format]['attrs']['names']['generic']
# First look at default template, and add it if necessary
if output_formats[output_format]['default'] == filename:
output_formats_list[name] = {'filename':output_format,
'names':output_formats[output_format]['attrs']['names'],
'tags':[]}
# Second look at each rule
found = False
for rule in output_formats[output_format]['rules']:
if rule['template'] == filename:
found = True
tags.append(rule['field']) #Also build dependencies on tags
# Finally add dependency on template from rule (overwrite default dependency,
# which is weaker in term of tag)
if found:
output_formats_list[name] = {'filename':output_format,
'names':output_formats[output_format]['attrs']['names'],
'tags':tags}
keys = output_formats_list.keys()
keys.sort()
return map(output_formats_list.get, keys)
def get_elements_used_by_template(filename):
"""
Returns a list of format elements that are called by the given format template.
The returned elements also give their dependencies on tags.
Dependencies on tag might be approximative. See get_tags_used_by_element()
doc string.
We must handle usage of bfe_field in a special way if we want to retrieve
used tag: used tag is given in "tag" parameter, not inside element code.
The list is returned sorted by name
[ {'filename':"filename_1.py"
'name':"filename_1"
'tags': ['710__a', '920__']
},
...
]
Returns elements sorted by name
@param filename a format template filename
"""
format_elements = {}
format_template = bibformat_engine.get_format_template(filename=filename, with_attributes=True)
code = format_template['code']
format_elements_iter = bibformat_engine.pattern_tag.finditer(code)
for result in format_elements_iter:
function_name = result.group("function_name").lower()
if function_name is not None and not format_elements.has_key(function_name) \
and not function_name == "field":
filename = bibformat_engine.resolve_format_element_filename("BFE_"+function_name)
if filename is not None:
tags = get_tags_used_by_element(filename)
format_elements[function_name] = {'name':function_name.lower(),
'filename':filename,
'tags':tags}
elif function_name == "field":
# Handle bfe_field element in a special way
if not format_elements.has_key(function_name):
#Indicate usage of bfe_field if not already done
filename = bibformat_engine.resolve_format_element_filename("BFE_"+function_name)
format_elements[function_name] = {'name':function_name.lower(),
'filename':filename,
'tags':[]}
# Retrieve value of parameter "tag"
all_params = result.group('params')
function_params_iterator = bibformat_engine.pattern_function_params.finditer(all_params)
for param_match in function_params_iterator:
name = param_match.group('param')
if name == "tag":
value = param_match.group('value')
if not value in format_elements[function_name]['tags']:
format_elements[function_name]['tags'].append(value)
break
keys = format_elements.keys()
keys.sort()
return map(format_elements.get, keys)
# Format Elements Dependencies
##
def get_tags_used_by_element(filename):
"""
Returns a list of tags used by given format element
APPROXIMATIVE RESULTS: the tag are retrieved in field(), fields()
and control_field() function. If they are used computed, or saved
in a variable somewhere else, they are not retrieved
@TODO: There is room for improvements. For example catch
call to BibRecord functions.
Returns tags sorted by value
@param filename a format element filename
"""
tags = {}
format_element = bibformat_engine.get_format_element(filename)
if format_element is None:
return []
elif format_element['type']=="field":
tags = format_element['attrs']['tags']
return tags
filename = bibformat_engine.resolve_format_element_filename(filename)
path = CFG_BIBFORMAT_ELEMENTS_PATH + os.sep + filename
format = open(path, 'r')
code = format.read()
format.close
tags_pattern = re.compile('''
(field|fields|control_field)\s* #Function call
\(\s* #Opening parenthesis
[\'"]+ #Single or double quote
(?P<tag>.+?) #Tag
[\'"]+\s* #Single or double quote
(,[^\)]+)* #Additional function param
\) #Closing parenthesis
''', re.VERBOSE | re.MULTILINE)
tags_iter = tags_pattern.finditer(code)
for result in tags_iter:
tags[result.group("tag")] = result.group("tag")
return tags.values()
def get_templates_that_use_element(name):
"""
Returns a list of format templates that call the given format element.
The returned format templates also give their dependencies on tags.
[ {'filename':"filename_1.bft"
'name': "a name"
'tags': ['710__a', '920__']
},
...
]
Returns templates sorted by name
@param name a format element name
"""
format_templates = {}
tags = []
files = os.listdir(CFG_BIBFORMAT_TEMPLATES_PATH) #Retrieve all templates
for possible_template in files:
if possible_template.endswith(CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION):
format_elements = get_elements_used_by_template(possible_template) #Look for elements used in template
format_elements = map(lambda x: x['name'].lower(), format_elements)
try: #Look for element
format_elements.index(name.lower()) #If not found, get out of "try" statement
format_template = bibformat_engine.get_format_template(filename=possible_template, with_attributes=True)
template_name = format_template['attrs']['name']
format_templates[template_name] = {'name':template_name,
'filename':possible_template}
except:
pass
keys = format_templates.keys()
keys.sort()
return map(format_templates.get, keys)
# Output Formats Dependencies
##
def get_templates_used_by_output(code):
"""
Returns a list of templates used inside an output format give by its code
The returned format templates also give their dependencies on elements and tags
[ {'filename':"filename_1.bft"
'name': "a name"
'elements': [{'filename':"filename_1.py", 'name':"filename_1", 'tags': ['710__a', '920__']
}, ...]
},
...
]
Returns templates sorted by name
"""
format_templates = {}
output_format = bibformat_engine.get_output_format(code, with_attributes=True)
filenames = map(lambda x: x['template'], output_format['rules'])
if output_format['default'] != "":
filenames.append(output_format['default'])
for filename in filenames:
template = bibformat_engine.get_format_template(filename, with_attributes=True)
name = template['attrs']['name']
elements = get_elements_used_by_template(filename)
format_templates[name] = {'name':name,
'filename':filename,
'elements':elements}
keys = format_templates.keys()
keys.sort()
return map(format_templates.get, keys)
# Knowledge Bases Dependencies
##
def get_elements_that_use_kb(name):
"""
Returns a list of elements that call given kb
[ {'filename':"filename_1.py"
'name': "a name"
},
...
]
Returns elements sorted by name
"""
format_elements = {}
files = os.listdir(CFG_BIBFORMAT_ELEMENTS_PATH) #Retrieve all elements in files
for filename in files:
if filename.endswith(".py"):
path = CFG_BIBFORMAT_ELEMENTS_PATH + os.sep + filename
format = open(path, 'r')
code = format.read()
format.close
# Search for use of kb inside code
kb_pattern = re.compile('''
(bfo.kb)\s* #Function call
\(\s* #Opening parenthesis
[\'"]+ #Single or double quote
(?P<kb>%s) #kb
[\'"]+\s* #Single or double quote
, #comma
''' % name, re.VERBOSE | re.MULTILINE | re.IGNORECASE)
result = kb_pattern.search(code)
if result is not None:
name = ("".join(filename.split(".")[:-1])).lower()
if name.startswith("bfe_"):
name = name[4:]
format_elements[name] = {'filename':filename, 'name': name}
keys = format_elements.keys()
keys.sort()
return map(format_elements.get, keys)
# Validation tools
##
def perform_request_format_validate(ln=CFG_SITE_LANG, bfo=None, bft=None, bfe=None):
"""
Returns a page showing the status of an output format or format
template or format element. This page is called from output
formats management page or format template management page or
format elements documentation.
The page only shows the status of one of the format, depending on
the specified one. If multiple are specified, shows the first one.
@param ln language
@param bfo an output format 6 chars code
@param bft a format element filename
@param bfe a format element name
"""
if bfo is not None:
errors = check_output_format(bfo)
messages = get_msgs_for_code_list(code_list = errors, ln=ln)
elif bft is not None:
errors = check_format_template(bft, checking=1)
messages = get_msgs_for_code_list(code_list = errors, ln=ln)
elif bfe is not None:
errors = check_format_element(bfe)
messages = get_msgs_for_code_list(code_list = errors, ln=ln)
if messages is None:
messages = []
messages = map(lambda x: encode_for_xml(x[1]), messages)
return bibformat_templates.tmpl_admin_validate_format(ln, messages)
def check_output_format(code):
"""
Returns the list of errors in the output format given by code
The errors are the formatted errors defined in bibformat_config.py file.
@param code the 6 chars code of the output format to check
@return a list of errors
"""
errors = []
filename = bibformat_engine.resolve_output_format_filename(code)
if can_read_output_format(code):
path = CFG_BIBFORMAT_OUTPUTS_PATH + os.sep + filename
format = open(path)
current_tag = ''
i = 0
for line in format:
i += 1
if line.strip() == "":
# Ignore blank lines
continue
clean_line = line.rstrip("\n\r ") #remove spaces and eol
if line.strip().endswith(":") or (line.strip().lower().startswith("tag") and line.find('---') == -1):
# Check tag
if not clean_line.endswith(":"):
# Column misses at the end of line
errors.append(("ERR_BIBFORMAT_OUTPUT_RULE_FIELD_COL", line, i))
if not clean_line.lower().startswith("tag"):
# Tag keyword is missing
errors.append(("ERR_BIBFORMAT_OUTPUT_TAG_MISSING", line, i))
elif not clean_line.startswith("tag"):
# Tag was not lower case
errors.append(("ERR_BIBFORMAT_OUTPUT_WRONG_TAG_CASE", line, i))
clean_line = clean_line.rstrip(": ") #remove : and spaces at the end of line
current_tag = "".join(clean_line.split()[1:]).strip() #the tag starts at second position
if len(clean_line.split()) > 2: #We should only have 'tag' keyword and tag
errors.append(("ERR_BIBFORMAT_INVALID_OUTPUT_RULE_FIELD", i))
else:
if len(check_tag(current_tag)) > 0:
# Invalid tag
errors.append(("ERR_BIBFORMAT_INVALID_OUTPUT_RULE_FIELD_tag", current_tag, i))
if not clean_line.startswith("tag"):
errors.append(("ERR_BIBFORMAT_INVALID_OUTPUT_RULE_FIELD", i))
elif line.find('---') != -1:
# Check condition
if current_tag == "":
errors.append(("ERR_BIBFORMAT_OUTPUT_CONDITION_OUTSIDE_FIELD", line, i))
words = line.split('---')
if len(words) != 2:
errors.append(("ERR_BIBFORMAT_INVALID_OUTPUT_CONDITION", line, i))
template = words[-1].strip()
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + template
if not os.path.exists(path):
errors.append(("ERR_BIBFORMAT_WRONG_OUTPUT_RULE_TEMPLATE_REF", template, i))
elif line.find(':') != -1 or (line.strip().lower().startswith("default") and line.find('---') == -1):
# Check default template
clean_line = line.strip()
if line.find(':') == -1:
# Column misses after default
errors.append(("ERR_BIBFORMAT_OUTPUT_RULE_DEFAULT_COL", line, i))
if not clean_line.startswith("default"):
# Default keyword is missing
errors.append(("ERR_BIBFORMAT_OUTPUT_DEFAULT_MISSING", line, i))
if not clean_line.startswith("default"):
# Default was not lower case
errors.append(("ERR_BIBFORMAT_OUTPUT_WRONG_DEFAULT_CASE", line, i))
default = "".join(line.split(':')[1]).strip()
path = CFG_BIBFORMAT_TEMPLATES_PATH + os.sep + default
if not os.path.exists(path):
errors.append(("ERR_BIBFORMAT_WRONG_OUTPUT_RULE_TEMPLATE_REF", default, i))
else:
# Check others
errors.append(("ERR_BIBFORMAT_WRONG_OUTPUT_LINE", line, i))
else:
errors.append(("ERR_BIBFORMAT_CANNOT_READ_OUTPUT_FILE", filename, ""))
return errors
def check_format_template(filename, checking=0):
"""
Returns the list of errors in the format template given by its filename
The errors are the formatted errors defined in bibformat_config.py file.
@param filename the filename of the format template to check
@param checking the level of checking (0:basic, >=1 extensive (time-consuming))
@return a list of errors
"""
errors = []
if can_read_format_template(filename):#Can template be read?
if filename.endswith('.'+CFG_BIBFORMAT_FORMAT_TEMPLATE_EXTENSION):
#format_template = bibformat_engine.get_format_template(filename, with_attributes=True)
format = open("%s%s%s" % (CFG_BIBFORMAT_TEMPLATES_PATH, os.sep, filename))
code = format.read()
format.close()
# Look for name
match = bibformat_engine.pattern_format_template_name.search(code)
if match is None:#Is tag <name> defined in template?
errors.append(("ERR_BIBFORMAT_TEMPLATE_HAS_NO_NAME", filename))
# Look for description
match = bibformat_engine.pattern_format_template_desc.search(code)
if match is None:#Is tag <description> defined in template?
errors.append(("ERR_BIBFORMAT_TEMPLATE_HAS_NO_DESCRIPTION", filename))
format_template = bibformat_engine.get_format_template(filename, with_attributes=False)
code = format_template['code']
# Look for calls to format elements
# Check existence of elements and attributes used in call
elements_call = bibformat_engine.pattern_tag.finditer(code)
for element_match in elements_call:
element_name = element_match.group("function_name")
filename = bibformat_engine.resolve_format_element_filename(element_name)
if filename is None and not bibformat_dblayer.tag_exists_for_name(element_name): #Is element defined?
errors.append(("ERR_BIBFORMAT_TEMPLATE_CALLS_UNDEFINED_ELEM", filename, element_name))
else:
format_element = bibformat_engine.get_format_element(element_name, with_built_in_params=True)
if format_element is None:#Can element be loaded?
if not can_read_format_element(element_name):
errors.append(("ERR_BIBFORMAT_TEMPLATE_CALLS_UNREADABLE_ELEM", filename, element_name))
else:
errors.append(("ERR_BIBFORMAT_TEMPLATE_CALLS_UNLOADABLE_ELEM", element_name, filename))
else:
# Are the parameters used defined in element?
params_call = bibformat_engine.pattern_function_params.finditer(element_match.group())
all_params = {}
for param_match in params_call:
param = param_match.group("param")
value = param_match.group("value")
all_params[param] = value
allowed_params = []
# Built-in params
for allowed_param in format_element['attrs']['builtin_params']:
allowed_params.append(allowed_param['name'])
# Params defined in element
for allowed_param in format_element['attrs']['params']:
allowed_params.append(allowed_param['name'])
if not param in allowed_params:
errors.append(("ERR_BIBFORMAT_TEMPLATE_WRONG_ELEM_ARG",
element_name, param, filename))
# The following code is too much time consuming. Only do where really requested
if checking > 0:
# Try to evaluate, with any object and pattern
recIDs = perform_request_search()
if len(recIDs) > 0:
recID = recIDs[0]
bfo = bibformat_engine.BibFormatObject(recID, search_pattern="Test")
(result, errors_) = bibformat_engine.eval_format_element(format_element, bfo, all_params, verbose=7)
errors.extend(errors_)
else:# Template cannot be read
errors.append(("ERR_BIBFORMAT_CANNOT_READ_TEMPLATE_FILE", filename, ""))
return errors
def check_format_element(name):
"""
Returns the list of errors in the format element given by its name
The errors are the formatted errors defined in bibformat_config.py file.
@param name the name of the format element to check
@return a list of errors
"""
errors = []
filename = bibformat_engine.resolve_format_element_filename(name)
if filename is not None:#Can element be found in files?
if can_read_format_element(name):#Can element be read?
# Try to load
try:
module_name = filename
if module_name.endswith(".py"):
module_name = module_name[:-3]
module = __import__("invenio.bibformat_elements."+module_name)
function_format = module.bibformat_elements.__dict__[module_name].format
# Try to evaluate, with any object and pattern
recIDs = perform_request_search()
if len(recIDs) > 0:
recID = recIDs[0]
bfo = bibformat_engine.BibFormatObject(recID, search_pattern="Test")
element = bibformat_engine.get_format_element(name)
(result, errors_) = bibformat_engine.eval_format_element(element, bfo, verbose=7)
errors.extend(errors_)
except Exception, e:
errors.append(("ERR_BIBFORMAT_IN_FORMAT_ELEMENT", name, e))
else:
errors.append(("ERR_BIBFORMAT_CANNOT_READ_ELEMENT_FILE", filename, ""))
elif bibformat_dblayer.tag_exists_for_name(name):#Can element be found in database?
pass
else:
errors.append(("ERR_BIBFORMAT_CANNOT_RESOLVE_ELEMENT_NAME", name))
return errors
def check_tag(tag):
"""
Checks the validity of a tag
"""
errors = []
return errors
def perform_request_dreamweaver_floater():
"""
Returns a floater for Dreamweaver with all Format Elements avalaible.
"""
# Get format elements lists of attributes
elements = bibformat_engine.get_format_elements(with_built_in_params=True)
keys = elements.keys()
keys.sort()
elements = map(elements.get, keys)
def filter_elem(element):
"""Keep element if is string representation contains all keywords of search_doc_pattern,
and if its name does not start with a number (to remove 'garbage' from elements in tags table)"""
if element['type'] != 'python' and \
element['attrs']['name'][0] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
return False
else:
return True
elements = filter(filter_elem, elements)
return bibformat_templates.tmpl_dreamweaver_floater(CFG_SITE_LANG, elements)
diff --git a/modules/bibformat/lib/bibreformat.py b/modules/bibformat/lib/bibreformat.py
index eefc92107..3ed5cbd38 100644
--- a/modules/bibformat/lib/bibreformat.py
+++ b/modules/bibformat/lib/bibreformat.py
@@ -1,572 +1,570 @@
## -*- mode: python; coding: utf-8; -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Call BibFormat engine and create HTML brief (and other) formats for
bibliographic records. Upload formats via BibUpload."""
__revision__ = "$Id$"
import sys
try:
from invenio.dbquery import run_sql
from invenio.config import \
- weburl,\
+ CFG_SITE_URL,\
CFG_TMPDIR,\
CFG_BINDIR
from invenio.search_engine import perform_request_search, search_pattern
from invenio.search_engine import print_record, encode_for_xml
from invenio.bibformat import format_record
from invenio.bibformat_utils import encode_for_xml
from invenio.bibformat_config import CFG_BIBFORMAT_USE_OLD_BIBFORMAT
from invenio.bibtask import task_init, write_message, task_set_option, \
task_get_option, task_update_progress, task_has_option
import sys
import os
import time
except ImportError, e:
print "Error: %s" % e
sys.exit(1)
sql_queries = [] # holds SQL queries to be executed
cds_query = {} # holds CDS query parameters (fields, collection, pattern)
process_format = 0 # flag, process records without created format
process = 1 # flag, process records (unless count only)
fmt = "hb" # default format to be processed
### run the bibreformat task bibsched scheduled
###
def bibreformat_task(sql, sql_queries, cds_query, process_format):
"""
BibReformat main task
"""
global process, fmt
t1 = os.times()[4]
### Query the database
###
task_update_progress('Fetching records to process')
if process_format: # '-without' parameter
write_message("Querying database for records without cache...")
without_format = without_fmt(sql)
recIDs = []
if cds_query['field'] != "" or \
cds_query['collection'] != "" or \
cds_query['pattern'] != "":
write_message("Querying database (CDS query)...")
if cds_query['collection'] == "":
# use search_pattern() whenever possible, as it can search
# even in private collections
res = search_pattern(p=cds_query['pattern'],
f=cds_query['field'],
m=cds_query['matching']).tolist()
else:
# use perform_request_search when '-c' argument has been
# defined, as it is not supported by search_pattern()
res = perform_request_search(req=None, of='id',
c=cds_query['collection'],
p=cds_query['pattern'],
f=cds_query['field'])
for item in res:
recIDs.append(item)
for sql_query in sql_queries:
write_message("Querying database (SQL query) ...")
res = run_sql(sql_query)
for item in res:
recIDs.append(item[0])
### list of corresponding record IDs was retrieved
### now format the selected records
if process_format:
write_message("Records to be processed: %d" % (len(recIDs) \
+ len(without_format)))
write_message("Out of it records without existing cache: %d" % len(without_format))
else:
write_message("Records to be processed: %d" % (len(recIDs)))
### Initialize main loop
total_rec = 0 # Total number of records
tbibformat = 0 # time taken up by external call
tbibupload = 0 # time taken up by external call
### Iterate over all records prepared in lists I (option)
if process:
if CFG_BIBFORMAT_USE_OLD_BIBFORMAT: # FIXME: remove this
# when migration from php to
# python bibformat is done
(total_rec_1, tbibformat_1, tbibupload_1) = iterate_over_old(recIDs,
- weburl,
fmt)
else:
(total_rec_1, tbibformat_1, tbibupload_1) = iterate_over_new(recIDs,
fmt)
total_rec += total_rec_1
tbibformat += tbibformat_1
tbibupload += tbibupload_1
### Iterate over all records prepared in list II (no_format)
if process_format and process:
if CFG_BIBFORMAT_USE_OLD_BIBFORMAT: # FIXME: remove this
# when migration from php to
# python bibformat is done
(total_rec_2, tbibformat_2, tbibupload_2) = iterate_over_old(without_format,
- weburl,
fmt)
else:
(total_rec_2, tbibformat_2, tbibupload_2) = iterate_over_new(without_format,
fmt)
total_rec += total_rec_2
tbibformat += tbibformat_2
tbibupload += tbibupload_2
### Final statistics
t2 = os.times()[4]
elapsed = t2 - t1
message = "total records processed: %d" % total_rec
write_message(message)
message = "total processing time: %2f sec" % elapsed
write_message(message)
message = "Time spent on external call (os.system):"
write_message(message)
message = " bibformat: %2f sec" % tbibformat
write_message(message)
message = " bibupload: %2f sec" % tbibupload
write_message(message)
### Result set operations
###
def lhdiff(l1, l2):
"Does list difference via intermediate hash."
d = {}
ld = []
for e in l2:
d[e] = 1
for e in l1:
if not d.has_key(e):
ld.append(e)
return ld
### Result set operations
###
def ldiff(l1, l2):
"Returns l1 - l2."
ld = []
for e in l1:
if not e in l2:
ld.append(e)
return ld
### Identify recIDs of records with missing format
###
def without_fmt(sql):
"List of record IDs to be reformated, not having the specified format yet"
rec_ids_with_cache = []
all_rec_ids = []
q1 = sql['q1']
q2 = sql['q2']
## get complete recID list
all_rec_ids = [x[0] for x in run_sql(q1)]
## get complete recID list of formatted records
rec_ids_with_cache = [x[0] for x in run_sql(q2)]
return lhdiff(all_rec_ids, rec_ids_with_cache)
### Bibreformat all selected records (using new python bibformat)
### (see iterate_over_old further down)
def iterate_over_new(list, fmt):
"Iterate over list of IDs"
global total_rec
n_it_rec = 0 # Number of records for current iteration
n_it_max = 10000 # Number of max records in one iteration
total_rec = 0 # Number of formatted records
formatted_records = '' # (string-)List of formatted record of an iteration
tbibformat = 0 # time taken up by external call
tbibupload = 0 # time taken up by external call
for recID in list:
total_rec += 1
n_it_rec += 1
task_update_progress('Formatting %s out of %s' %(total_rec, len(list)))
message = "Processing record %d with format %s (New BibFormat)" % (recID, fmt)
write_message(message, verbose=9)
### bibformat external call
###
t1 = os.times()[4]
formatted_record = format_record(recID, fmt, on_the_fly=True)
t2 = os.times()[4]
tbibformat = tbibformat + (t2 - t1)
# Encapsulate record in xml tags that bibupload understands
prologue = '''
<record>
<controlfield tag="001">%s</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">%s</subfield>
<subfield code="g">''' % (recID, fmt)
epilogue = '''
</subfield>
</datafield>
</record>'''
formatted_records += prologue + encode_for_xml(formatted_record) + epilogue
# every n_it_max record, upload all formatted records.
# also upload if recID is last one
if n_it_rec > n_it_max or total_rec == len(list):
#Save formatted records to disk for bibupload
finalfilename = "%s/rec_fmt_%s.xml" % (CFG_TMPDIR, time.strftime('%Y%m%d_%H%M%S'))
filehandle = open(finalfilename, "w")
filehandle.write("<collection>" + \
formatted_records + \
"</collection>")
filehandle.close()
### bibupload external call
###
t1 = os.times()[4]
message = "START bibupload external call"
write_message(message, verbose=9)
command = "%s/bibupload -f %s" % (CFG_BINDIR, finalfilename)
os.system(command)
t2 = os.times()[4]
tbibupload = tbibupload + (t2 - t1)
message = "END bibupload external call (time elapsed:%2f)" % (t2-t1)
write_message(message, verbose=9)
#Reset iteration state
n_it_rec = 0
formatted_records = ''
return (total_rec, tbibformat, tbibupload)
-def iterate_over_old(list, weburl, fmt):
+def iterate_over_old(list, fmt):
"Iterate over list of IDs"
n_rec = 0
n_max = 10000
xml_content = '' # hold the contents
tbibformat = 0 # time taken up by external call
tbibupload = 0 # time taken up by external call
total_rec = 0 # Number of formatted records
for record in list:
n_rec = n_rec + 1
total_rec = total_rec + 1
message = "Processing record: %d" % (record)
write_message(message, verbose=9)
query = "id=%d&of=xm" % (record)
count = 0
contents = print_record(record, 'xm')
while (contents == "") and (count < 10):
contents = print_record(record, 'xm')
count = count + 1
time.sleep(10)
if count == 10:
- sys.stderr.write("Failed to download %s from %s after 10 attempts... terminating" % (query, weburl))
+ sys.stderr.write("Failed to download %s from %s after 10 attempts... terminating" % (query, CFG_SITE_URL))
sys.exit(0)
xml_content = xml_content + contents
if xml_content:
if n_rec >= n_max:
finalfilename = "%s/rec_fmt_%s.xml" % (CFG_TMPDIR, time.strftime('%Y%m%d_%H%M%S'))
filename = "%s/bibreformat.xml" % CFG_TMPDIR
filehandle = open(filename ,"w")
filehandle.write(xml_content)
filehandle.close()
### bibformat external call
###
t11 = os.times()[4]
message = "START bibformat external call"
write_message(message, verbose=9)
command = "%s/bibformat otype='%s' < %s/bibreformat.xml > %s 2> %s/bibreformat.err" % (CFG_BINDIR, fmt.upper(), CFG_TMPDIR, finalfilename, CFG_TMPDIR)
os.system(command)
t22 = os.times()[4]
message = "END bibformat external call (time elapsed:%2f)" % (t22-t11)
write_message(message, verbose=9)
tbibformat = tbibformat + (t22 - t11)
### bibupload external call
###
t11 = os.times()[4]
message = "START bibupload external call"
write_message(message, verbose=9)
command = "%s/bibupload -f %s" % (CFG_BINDIR, finalfilename)
os.system(command)
t22 = os.times()[4]
message = "END bibupload external call (time elapsed:%2f)" % (t22-t11)
write_message(message, verbose=9)
tbibupload = tbibupload + (t22- t11)
n_rec = 0
xml_content = ''
### Process the last re-formated chunk
###
if n_rec > 0:
write_message("Processing last record set (%d)" % n_rec, verbose=9)
finalfilename = "%s/rec_fmt_%s.xml" % (CFG_TMPDIR, time.strftime('%Y%m%d_%H%M%S'))
filename = "%s/bibreformat.xml" % CFG_TMPDIR
filehandle = open(filename ,"w")
filehandle.write(xml_content)
filehandle.close()
### bibformat external call
###
t11 = os.times()[4]
message = "START bibformat external call"
write_message(message, verbose=9)
command = "%s/bibformat otype='%s' < %s/bibreformat.xml > %s 2> %s/bibreformat.err" % (CFG_BINDIR, fmt.upper(), CFG_TMPDIR, finalfilename, CFG_TMPDIR)
os.system(command)
t22 = os.times()[4]
message = "END bibformat external call (time elapsed:%2f)" % (t22 - t11)
write_message(message, verbose=9)
tbibformat = tbibformat + (t22 - t11)
### bibupload external call
###
t11 = os.times()[4]
message = "START bibupload external call"
write_message(message, verbose=9)
command = "%s/bibupload -f %s" % (CFG_BINDIR, finalfilename)
os.system(command)
t22 = os.times()[4]
message = "END bibupload external call (time elapsed:%2f)" % (t22 - t11)
write_message(message, verbose=9)
tbibupload = tbibupload + (t22 - t11)
return (total_rec, tbibformat, tbibupload)
def task_run_core():
"""Runs the task by fetching arguments from the BibSched task queue. This is what BibSched will be invoking via daemon call."""
global process, fmt, process_format
## initialize parameters
fmt = task_get_option('format')
sql = {
"all" : "select br.id from bibrec as br, bibfmt as bf where bf.id_bibrec=br.id and bf.format ='%s'" % fmt,
"last": "select br.id from bibrec as br, bibfmt as bf where bf.id_bibrec=br.id and bf.format='%s' and bf.last_updated < br.modification_date" % fmt,
"q1" : "select br.id from bibrec as br",
"q2" : "select br.id from bibrec as br, bibfmt as bf where bf.id_bibrec=br.id and bf.format ='%s'" % fmt
}
if task_has_option("all"):
sql_queries.append(sql['all'])
if task_has_option("without"):
process_format = 1
if task_has_option("noprocess"):
process = 0
if task_has_option("last"):
sql_queries.append(sql['last'])
if task_has_option("collection"):
cds_query['collection'] = task_get_option('collection')
else:
cds_query['collection'] = ""
if task_has_option("field"):
cds_query['field'] = task_get_option('field')
else:
cds_query['field'] = ""
if task_has_option("pattern"):
cds_query['pattern'] = task_get_option('pattern')
else:
cds_query['pattern'] = ""
if task_has_option("matching"):
cds_query['matching'] = task_get_option('matching')
else:
cds_query['matching'] = ""
### sql commands to be executed during the script run
###
bibreformat_task(sql, sql_queries, cds_query, process_format)
return True
def main():
"""Main that construct all the bibtask."""
task_set_option('format', 'hb')
task_init(authorization_action='runbibformat',
authorization_msg="BibReformat Task Submission",
description="""
BibReformat formats the records and saves the produced outputs for
later retrieval.
BibReformat is usually run periodically via BibSched in order to (1)
format new records in the database and to (2) reformat records for
which the meta data has been modified.
BibReformat has to be run manually when (3) format config files have
been modified, in order to see the changes in the web interface.
Although it is not necessary to run BibReformat to display formatted
records in the web interface, BibReformat allows to improve serving
speed by precreating the outputs. It is suggested to run
BibReformat for 'HB' output.
Option -m cannot be used at the same time as option -c.
Option -c prevents from finding records in private collections.
Examples:
bibreformat Format all new or modified records (in HB).
bibreformat -o HD Format all new or modified records in HD.
bibreformat -a Force reformatting all records (in HB).
bibreformat -c 'Photos' Force reformatting all records in 'Photos' collection (in HB).
bibreformat -c 'Photos' -o HD Force reformatting all records in 'Photos' collection in HD.
bibreformat -n Show how many records are to be (re)formatted.
bibreformat -n -c 'Articles' Show how many records are to be (re)formatted in 'Articles' collection.
bibreformat -oHB -s1h Format all new and modified records every hour, in HB.
""", help_specific_usage=""" -o, --format \t Specify output format (default HB)
-n, --noprocess \t Count records to be formatted (no processing done)
Reformatting options:
-a, --all \t Force reformatting all records
-c, --collection \t Force reformatting records by collection
-f, --field \t Force reformatting records by field
-p, --pattern \t Force reformatting records by pattern
Pattern options:
-m, --matching \t Specify if pattern is exact (e), regular expression (r),
\t partial (p), any of the words (o) or all of the words (a)
""",
version=__revision__,
specific_params=("ac:f:p:lo:nm:",
["all",
"collection=",
"matching=",
"field=",
"pattern=",
"format=",
"noprocess"]),
task_submit_check_options_fnc=task_submit_check_options,
task_submit_elaborate_specific_parameter_fnc=task_submit_elaborate_specific_parameter,
task_run_fnc=task_run_core)
def task_submit_check_options():
"""Last checks and updating on the options..."""
if not (task_has_option('all') or task_has_option('collection') \
or task_has_option('field') or task_has_option('pattern') \
or task_has_option('matching')):
task_set_option('without', 1)
task_set_option('last', 1)
return True
def task_submit_elaborate_specific_parameter(key, value, opts, args):
"""Elaborate specific CLI parameters of BibReformat."""
if key in ("-a", "--all"):
task_set_option("all", 1)
task_set_option("without", 1)
elif key in ("-c", "--collection"):
task_set_option("collection", value)
elif key in ("-n", "--noprocess"):
task_set_option("noprocess", 1)
elif key in ("-f", "--field"):
task_set_option("field", value)
elif key in ("-p","--pattern"):
task_set_option("pattern", value)
elif key in ("-m", "--matching"):
task_set_option("matching", value)
elif key in ("-o","--format"):
task_set_option("format", value)
else:
return False
return True
### okay, here we go:
if __name__ == '__main__':
main()
diff --git a/modules/bibformat/lib/elements/bfe_abstract.py b/modules/bibformat/lib/elements/bfe_abstract.py
index 13809c0c2..20fc4247e 100644
--- a/modules/bibformat/lib/elements/bfe_abstract.py
+++ b/modules/bibformat/lib/elements/bfe_abstract.py
@@ -1,165 +1,165 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints English and French abstract.
"""
__revision__ = "$Id$"
#import cgi
from invenio import bibformat_utils
from urllib import quote
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
import re
def format(bfo, prefix_en, prefix_fr, suffix_en, suffix_fr, limit, max_chars,
extension_en="[...] ",extension_fr="[...] ", contextual="no",
highlight='no', print_lang='en,fr', escape="3",
separator_en="<br/>", separator_fr="<br/>"):
""" Prints the abstract of a record in HTML. By default prints English and French versions.
Printed languages can be chosen with the 'print_lang' parameter.
@param prefix_en a prefix for english abstract (printed only if english abstract exists)
@param prefix_fr a prefix for french abstract (printed only if french abstract exists)
@param limit the maximum number of sentences of the abstract to display (for each language)
@param max_chars the maximum number of chars of the abstract to display (for each language)
@param extension_en a text printed after english abstracts longer than parameter 'limit'
@param extension_fr a text printed after french abstracts longer than parameter 'limit'
@param suffix_en a suffix for english abstract(printed only if english abstract exists)
@param suffix_fr a suffix for french abstract(printed only if french abstract exists)
@parmm contextual if 'yes' prints sentences the most relative to user search keyword (if limit < abstract)
@param highlight if 'yes' highlights words from user search keyword
@param print_lang the comma-separated list of languages to print. Now restricted to 'en' and 'fr'
@param escape escaping method
@param separator_en a separator between each english abstract
@param separator_fr a separator between each french abstract
"""
out = ''
if print_lang == 'auto':
print_lang = bfo.lang
languages = print_lang.split(',')
abstract_en = bfo.fields('520__a', escape=int(escape))
abstract_en.extend(bfo.fields('520__b', escape=int(escape)))
#abstract_en = [cgi.escape(val) for val in abstract_en]
abstract_en = separator_en.join(abstract_en)
abstract_fr = bfo.fields('590__a', escape=int(escape))
abstract_fr.extend(bfo.fields('590__b', escape=int(escape)))
#abstract_fr = [cgi.escape(val) for val in abstract_fr]
abstract_fr = separator_fr.join(abstract_fr)
if contextual == 'yes' and limit != "" and \
limit.isdigit() and int(limit) > 0:
context_en = bibformat_utils.get_contextual_content(abstract_en,
bfo.search_pattern,
max_lines=int(limit))
#FIXME add something like [...] before and after
#contextual sentences when not at beginning/end of abstract
#if not abstract_en.strip().startswith(context_en[0].strip()):
# out += '[...]'
abstract_en = "<br/>".join(context_en)
context_fr = bibformat_utils.get_contextual_content(abstract_fr,
bfo.search_pattern,
max_lines=int(limit))
abstract_fr = "<br/>".join(context_fr)
if len(abstract_en) > 0 and 'en' in languages:
out += prefix_en
print_extension = False
if max_chars != "" and max_chars.isdigit() and \
int(max_chars) < len(abstract_en):
print_extension = True
abstract_en = abstract_en[:int(max_chars)]
if limit != "" and limit.isdigit():
s_abstract = abstract_en.split(".")
if int(limit) < len(s_abstract):
print_extension = True
s_abstract = s_abstract[:int(limit)]
#for sentence in s_abstract:
# out += sentence + "."
out = '.'.join(s_abstract)
# Add final dot if needed
if abstract_en.endswith('.'):
out += '.'
if print_extension:
out += " " + extension_en
else:
out += abstract_en
out += suffix_en
if len(abstract_fr) > 0 and 'fr' in languages:
out += prefix_fr
print_extension = False
if max_chars != "" and max_chars.isdigit() and \
int(max_chars) < len(abstract_fr):
print_extension = True
abstract_fr = abstract_fr[:int(max_chars)]
if limit != "" and limit.isdigit():
s_abstract = abstract_fr.split(".")
if int(limit) < len(s_abstract):
print_extension = True
s_abstract = s_abstract[:int(limit)]
#for sentence in s_abstract:
# out += sentence + "."
out = '.'.join(s_abstract)
# Add final dot if needed
if abstract_fr.endswith('.'):
out += '.'
if print_extension:
out += " "+extension_fr
else:
out += abstract_fr
out += suffix_fr
if highlight == 'yes':
out = bibformat_utils.highlight(out, bfo.search_pattern)
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_addresses.py b/modules/bibformat/lib/elements/bfe_addresses.py
index aec5639c7..a542c3ae5 100644
--- a/modules/bibformat/lib/elements/bfe_addresses.py
+++ b/modules/bibformat/lib/elements/bfe_addresses.py
@@ -1,60 +1,60 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints list of addresses
"""
__revision__ = "$Id$"
import cgi
from urllib import quote
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
def format(bfo, separator="; ", print_link="yes"):
"""
Prints a list of addresses linked to this report
@param separator the separator between addresses.
@param print_link Links the addresses to search engine (HTML links) if 'yes'
"""
addresses = bfo.fields('270')
list_addresses = []
if print_link.lower() == 'yes':
for address in addresses:
- list_addresses.append('<a href="'+weburl+'/search?f=author&p='+ \
+ list_addresses.append('<a href="'+CFG_SITE_URL+'/search?f=author&p='+ \
quote(address.get('p', "")) + \
'&amp;ln=' + bfo.lang + \
'">'+cgi.escape(address.get('p', "")) + \
'</a>')
list_addresses.append(cgi.escape(address.get('g', "")))
else:
for address in addresses:
list_addresses.append(cgi.escape(address.get('p', "")))
list_addresses.append(cgi.escape(address.get('g', "")))
return separator.join(list_addresses)
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_authors.py b/modules/bibformat/lib/elements/bfe_authors.py
index 1d91b3d4a..5a9dec841 100644
--- a/modules/bibformat/lib/elements/bfe_authors.py
+++ b/modules/bibformat/lib/elements/bfe_authors.py
@@ -1,140 +1,140 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints authors
"""
__revision__ = "$Id$"
def format(bfo, limit, separator=' ; ',
extension='[...]',
print_links="yes",
print_affiliations='no',
affiliation_prefix = ' (',
affiliation_suffix = ')',
interactive="no",
highlight="no"):
"""
Prints the list of authors of a record.
@param limit the maximum number of authors to display
@param separator the separator between authors.
@param extension a text printed if more authors than 'limit' exist
@param print_links if yes, prints the authors as HTML link to their publications
@param print_affiliations if yes, make each author name followed by its affiliation
@param affiliation_prefix prefix printed before each affiliation
@param affiliation_suffix suffix printed after each affiliation
@param interactive if yes, enable user to show/hide authors when there are too many (html + javascript)
@param highlight highlights authors corresponding to search query if set to 'yes'
"""
from urllib import quote
from cgi import escape
- from invenio.config import weburl
+ from invenio.config import CFG_SITE_URL
from invenio.messages import gettext_set_language
_ = gettext_set_language(bfo.lang) # load the right message language
authors = []
authors_1 = bfo.fields('100__')
authors_2 = bfo.fields('700__')
authors.extend(authors_1)
authors.extend(authors_2)
nb_authors = len(authors)
# Process authors to add link, highlight and format affiliation
for author in authors:
if author.has_key('a'):
if highlight == 'yes':
from invenio import bibformat_utils
author['a'] = bibformat_utils.highlight(author['a'],
bfo.search_pattern)
if print_links.lower() == "yes":
- author['a'] = '<a href="' + weburl + \
+ author['a'] = '<a href="' + CFG_SITE_URL + \
'/search?f=author&amp;p='+ quote(author['a']) + \
'&amp;ln='+ bfo.lang + \
'">'+escape(author['a'])+'</a>'
if author.has_key('u'):
if print_affiliations == "yes":
author['u'] = affiliation_prefix + author['u'] + \
affiliation_suffix
# Flatten author instances
if print_affiliations == 'yes':
authors = [author.get('a', '') + author.get('u', '')
for author in authors]
else:
authors = [author.get('a', '')
for author in authors]
if limit.isdigit() and nb_authors > int(limit) and interactive != "yes":
return separator.join(authors[:int(limit)]) + extension
elif limit.isdigit() and nb_authors > int(limit) and interactive == "yes":
out = '''
<script type="text/javascript">
function toggle_authors_visibility(){
var more = document.getElementById('more');
var link = document.getElementById('link');
var extension = document.getElementById('extension');
if (more.style.display=='none'){
more.style.display = '';
extension.style.display = 'none';
link.innerHTML = "%(show_less)s"
} else {
more.style.display = 'none';
extension.style.display = '';
link.innerHTML = "%(show_more)s"
}
link.style.color = "rgb(204,0,0);"
}
function set_up(){
var extension = document.getElementById('extension');
extension.innerHTML = "%(extension)s";
toggle_authors_visibility();
}
</script>
'''%{'show_less':_("Hide"),
'show_more':_("Show all %i authors") % nb_authors,
'extension':extension}
out += '<a name="show_hide" />'
out += separator.join(authors[:int(limit)])
out += '<span id="more" style="">' + separator + \
separator.join(authors[int(limit):]) + '</span>'
out += ' <span id="extension"></span>'
out += ' <small><i><a id="link" href="#" onclick="toggle_authors_visibility()" style="color:rgb(204,0,0);"></a></i></small>'
out += '<script type="text/javascript">set_up()</script>'
return out
elif nb_authors > 0:
return separator.join(authors)
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_cited_by.py b/modules/bibformat/lib/elements/bfe_cited_by.py
index 5d687ec5c..6b6ef1f9e 100644
--- a/modules/bibformat/lib/elements/bfe_cited_by.py
+++ b/modules/bibformat/lib/elements/bfe_cited_by.py
@@ -1,61 +1,61 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints reference to documents citing this one
"""
__revision__ = "$Id$"
import cgi
def format(bfo, separator='; '):
"""
Prints a list of records citing this record
@param separator a separator between citations
"""
from urllib import quote
- from invenio.config import weburl
+ from invenio.config import CFG_SITE_URL
primary_report_numbers = bfo.fields('037__a')
additional_report_numbers = bfo.fields('088__a')
- primary_citations = ['<a href="' + weburl + \
+ primary_citations = ['<a href="' + CFG_SITE_URL + \
'/search?f=reference&amp;p=' + quote(report_number) + \
'&amp;ln='+ bfo.lang +'">' + \
cgi.escape(report_number) + '</a>' \
for report_number in primary_report_numbers]
- additional_citations = ['<a href="' + weburl + \
+ additional_citations = ['<a href="' + CFG_SITE_URL + \
'/search?f=reference&amp;p=' + quote(report_number)+ \
'&amp;ln='+ bfo.lang + '">' + \
cgi.escape(report_number) + '</a>' \
for report_number in additional_report_numbers]
citations = primary_citations
citations.extend(additional_citations)
return separator.join(citations)
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_contact.py b/modules/bibformat/lib/elements/bfe_contact.py
index c4dd7fadf..51418bc83 100644
--- a/modules/bibformat/lib/elements/bfe_contact.py
+++ b/modules/bibformat/lib/elements/bfe_contact.py
@@ -1,46 +1,46 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints contact information
"""
__revision__ = "$Id$"
def format(bfo, separator='; ', link="yes"):
"""
Prints contact information for the record
@param separator the separator between addresses.
@param link Link the addresses to search engine (HTML links) if 'yes'
"""
from urllib import quote
- from invenio.config import weburl
+ from invenio.config import CFG_SITE_URL
addresses = bfo.fields('270__p')
if link == "yes":
- addresses = ['<a href="'+weburl+'/search?f=author&p='+ \
+ addresses = ['<a href="'+CFG_SITE_URL+'/search?f=author&p='+ \
quote(address) +'&amp;ln=' + bfo.lang + '">' + \
address +'</a>' for address in addresses]
return separator.join(addresses)
diff --git a/modules/bibformat/lib/elements/bfe_edit_record.py b/modules/bibformat/lib/elements/bfe_edit_record.py
index 7b95c5668..1d1f137b8 100644
--- a/modules/bibformat/lib/elements/bfe_edit_record.py
+++ b/modules/bibformat/lib/elements/bfe_edit_record.py
@@ -1,57 +1,57 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints a link to BibEdit
"""
__revision__ = "$Id$"
def format(bfo, style):
"""
Prints a link to BibEdit, if authorization is granted
@param style the CSS style to be applied to the link.
"""
- from invenio.config import weburl
+ from invenio.config import CFG_SITE_URL
from invenio.access_control_engine import acc_authorize_action
out = ""
user_info = bfo.user_info
(auth_code, auth_message) = acc_authorize_action(user_info['uid'], \
'runbibedit')
if auth_code == 0:
print_style = ''
if style != '':
print_style = 'style="' + style + '"'
- out += '<a href="' + weburl + \
+ out += '<a href="' + CFG_SITE_URL + \
'/admin/bibedit/bibeditadmin.py/index?recid=' + \
str(bfo.recID) + '&amp;ln=' + bfo.lang +'" ' + \
print_style + \
'>Edit This Record</a>'
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_editors.py b/modules/bibformat/lib/elements/bfe_editors.py
index 077eaea1c..0542b90c2 100644
--- a/modules/bibformat/lib/elements/bfe_editors.py
+++ b/modules/bibformat/lib/elements/bfe_editors.py
@@ -1,54 +1,54 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints editors
"""
__revision__ = "$Id$"
def format(bfo, limit, separator=' ; ', extension='[...]', print_links="yes"):
"""
Prints the list of editors of a record.
@param limit the maximum number of editors to display
@param separator the separator between editors.
@param extension a text printed if more editors than 'limit' exist
@param print_links if yes, print the editors as HTML link to their publications
"""
from urllib import quote
- from invenio.config import weburl
+ from invenio.config import CFG_SITE_URL
from invenio import bibrecord
authors = bibrecord.record_get_field_instances(bfo.get_record(), '100')
editors = [bibrecord.field_get_subfield_values(author, 'a')[0]
for author in authors if len(bibrecord.field_get_subfield_values(author, "e")) > 0 and bibrecord.field_get_subfield_values(author, "e")[0]=="ed." ]
if print_links.lower() == "yes":
- editors = ['<a href="' + weburl + '/search?f=author&p=' + \
+ editors = ['<a href="' + CFG_SITE_URL + '/search?f=author&p=' + \
quote(editor) + \
'&amp;ln='+ bfo.lang + \
'">' + editor + '</a>'
for editor in editors]
if limit.isdigit() and len(editors) > int(limit):
return separator.join(editors[:int(limit)]) + extension
elif len(editors) > 0:
return separator.join(editors)
diff --git a/modules/bibformat/lib/elements/bfe_fulltext.py b/modules/bibformat/lib/elements/bfe_fulltext.py
index 0ed568bd0..6bfd34fcd 100644
--- a/modules/bibformat/lib/elements/bfe_fulltext.py
+++ b/modules/bibformat/lib/elements/bfe_fulltext.py
@@ -1,206 +1,206 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints a links to fulltext
"""
__revision__ = "$Id$"
from invenio.bibdocfile import BibRecDocs, file_strip_ext
from invenio.messages import gettext_set_language
-from invenio.config import weburl, CFG_CERN_SITE
+from invenio.config import CFG_SITE_URL, CFG_CERN_SITE
from cgi import escape
from urlparse import urlparse
from os.path import basename
def format(bfo, style, separator='; ', show_icons='no'):
"""
This is the default format for formatting fulltext links.
@param separator the separator between urls.
@param style CSS class of the link
@param show_icons if 'yes', print icons for fulltexts
"""
_ = gettext_set_language(bfo.lang)
out = ''
# Retrieve files
(parsed_urls, old_versions, additionals) = get_files(bfo)
main_urls = parsed_urls['main_urls']
others_urls = parsed_urls['others_urls']
if parsed_urls.has_key('cern_urls'):
cern_urls = parsed_urls['cern_urls']
# Prepare style and icon
if style != "":
style = 'class="'+style+'"'
if show_icons.lower() == 'yes':
- file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (weburl, _("Download fulltext"))
+ file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (CFG_SITE_URL, _("Download fulltext"))
else:
file_icon = ''
# Build urls list.
# Escape special chars for <a> tag value.
additional_str = ''
if additionals:
- additional_str = ' <small>(<a '+style+' href="'+weburl+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("additional files")
+ additional_str = ' <small>(<a '+style+' href="'+CFG_SITE_URL+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("additional files")
versions_str = ''
if old_versions:
- versions_str = ' <small>(<a '+style+' href="'+weburl+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("older versions")
+ versions_str = ' <small>(<a '+style+' href="'+CFG_SITE_URL+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("older versions")
if main_urls:
last_name = ""
for descr, urls in main_urls.items():
out += "<strong>%s:</strong> " % descr
url_list = []
urls.sort(lambda (url1, name1, format1), (url2, name2, format2): url1 < url2 and -1 or url1 > url2 and 1 or 0)
for url, name, format in urls:
if not name == last_name and len(main_urls) > 1:
print_name = "<em>%s</em> - " % name
else:
print_name = ""
last_name = name
url_list.append(print_name + '<a '+style+' href="'+escape(url)+'">'+ \
file_icon + format.upper()+'</a>')
out += separator.join(url_list) + additional_str + versions_str + '<br />'
if CFG_CERN_SITE and cern_urls:
link_word = len(cern_urls) == 1 and _('link') or _('links')
out += '<strong>%s</strong>: ' % _("CERN %(link_or_links)s" % {'link_or_links' : link_word})
url_list = []
for url, descr in cern_urls:
url_list.append('<a '+style+' href="'+escape(url)+'">'+ \
file_icon + escape(str(descr))+'</a>')
out += separator.join(url_list)
if others_urls:
link_word = len(others_urls) == 1 and _('link') or _('links')
out += '<strong>%s</strong>: ' % _("External %(link_or_links)s" % {'link_or_links' : link_word})
url_list = []
for url, descr in others_urls:
url_list.append('<a '+style+' href="'+escape(url)+'">'+ \
file_icon + escape(str(descr))+'</a>')
out += separator.join(url_list) + '<br />'
if out.endswith('<br />'):
out = out[:-len('<br />')]
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
def get_files(bfo):
"""
Returns the files available for the given record.
Returned structure is a tuple (parsed_urls, old_versions, additionals):
- parsed_urls: contains categorized URLS (see details below)
- old_versions: set to True if we can have access to old versions
- additionals: set to True if we have other documents than the 'main' document
Returned dictionary is of the form:
- {'main_urls' : {'Main' : [('http://weburl/record/1/files/aFile.pdf', 'aFile', 'PDF'),
- ('http://weburl/record/1/files/aFile.gif', 'aFile', 'GIF')],
- 'Additional': [('http://weburl/record/1/files/bFile.pdf', 'bFile', 'PDF')]},
+ {'main_urls' : {'Main' : [('http://CFG_SITE_URL/record/1/files/aFile.pdf', 'aFile', 'PDF'),
+ ('http://CFG_SITE_URL/record/1/files/aFile.gif', 'aFile', 'GIF')],
+ 'Additional': [('http://CFG_SITE_URL/record/1/files/bFile.pdf', 'bFile', 'PDF')]},
'other_urls': [('http://externalurl.com/aFile.pdf', 'Fulltext'), # url(8564_u), description(8564_z/y)
('http://externalurl.com/bFile.pdf', 'Fulltext')],
'cern_urls' : [('http://cern.ch/aFile.pdf', 'Fulltext'), # url(8564_u), description(8564_z/y)
('http://cern.ch/bFile.pdf', 'Fulltext')],
}
Some notes about returned structure:
- key 'cern_urls' is only available on CERN site
- keys in main_url dictionaries are defined by the BibDoc.
- older versions are not part of the parsed urls
"""
_ = gettext_set_language(bfo.lang)
urls = bfo.fields("8564_")
bibarchive = BibRecDocs(bfo.recID)
old_versions = False # We can provide link to older files
additionals = False # We have additional files
# Prepare object to return
parsed_urls = {'main_urls':{}, # Urls hosted by Invenio (bibdocs)
'others_urls':[] # External urls
}
if CFG_CERN_SITE:
parsed_urls['cern_urls'] = [] # cern.ch urls
# Parse URLs
for complete_url in urls:
if complete_url.has_key('u'):
url = complete_url['u']
(dummy, host, path, dummy, dummy, dummy) = urlparse(url)
filename = basename(path)
name = file_strip_ext(filename)
format = filename[len(name):]
if format.startswith('.'):
format = format[1:]
descr = ''
if complete_url.has_key('z'): # Let's take the description
descr = complete_url['z']
elif complete_url.has_key('y'):
descr = complete_url['y']
- if not url.startswith(weburl): # Not a bibdoc?
+ if not url.startswith(CFG_SITE_URL): # Not a bibdoc?
if not descr: # For not bibdoc let's have a description
if '/setlink?' in url: # Setlink (i.e. hosted on doc.cern.ch)
descr = _("Fulltext") # Surely a fulltext
else:
#FIXME remove eventual ?parameters
descr = filename or host # Let's take the name from the url
if CFG_CERN_SITE and 'cern.ch' in host:
parsed_urls['cern_urls'].append((url, descr)) # Obsolete cern.ch url (we're migrating)
else:
parsed_urls['others_urls'].append((url, descr)) # external url
else: # It's a bibdoc!
assigned = False
for doc in bibarchive.list_bibdocs():
if int(doc.get_latest_version()) > 1:
old_versions = True
if filename in [f.fullname for f in doc.list_all_files()]:
assigned = True
#doc.getIcon()
if not doc.doctype == 'Main':
additionals = True
else:
if not descr:
descr = _('Main file(s)')
if not parsed_urls['main_urls'].has_key(descr):
parsed_urls['main_urls'][descr] = []
parsed_urls['main_urls'][descr].append((url, name, format))
if not assigned: # Url is not a bibdoc :-S
if not descr:
descr = filename
parsed_urls['others_urls'].append((url, descr)) # Let's put it in a general other url
return (parsed_urls, old_versions, additionals)
diff --git a/modules/bibformat/lib/elements/bfe_fulltext_mini.py b/modules/bibformat/lib/elements/bfe_fulltext_mini.py
index 9c8dd6783..f3a02e99b 100644
--- a/modules/bibformat/lib/elements/bfe_fulltext_mini.py
+++ b/modules/bibformat/lib/elements/bfe_fulltext_mini.py
@@ -1,136 +1,136 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints a links to fulltext
"""
__revision__ = "$Id$"
from invenio.bibformat_elements.bfe_fulltext import get_files
from invenio.messages import gettext_set_language
-from invenio.config import weburl, CFG_CERN_SITE
+from invenio.config import CFG_SITE_URL, CFG_CERN_SITE
from cgi import escape
def format(bfo, style, separator='; ', show_icons='no'):
"""
This is the format for formatting fulltext links in the mini panel.
@param separator the separator between urls.
@param style CSS class of the link
@param show_icons if 'yes', print icons for fulltexts
"""
_ = gettext_set_language(bfo.lang)
out = ''
# Retrieve files
(parsed_urls, old_versions, additionals) = get_files(bfo)
main_urls = parsed_urls['main_urls']
others_urls = parsed_urls['others_urls']
if parsed_urls.has_key('cern_urls'):
cern_urls = parsed_urls['cern_urls']
# Prepare style
if style != "":
style = 'class="'+style+'"'
# Build urls list.
# Escape special chars for <a> tag value.
additional_str = ''
if additionals:
- additional_str = separator + '<small>(<a '+style+' href="'+weburl+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("additional files")
+ additional_str = separator + '<small>(<a '+style+' href="'+CFG_SITE_URL+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("additional files")
versions_str = ''
if old_versions:
- versions_str = separator + '<small>(<a '+style+' href="'+weburl+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("older versions")
+ versions_str = separator + '<small>(<a '+style+' href="'+CFG_SITE_URL+'/record/'+str(bfo.recID)+'/files/">%s</a>)</small>' % _("older versions")
if main_urls:
# Put a big file icon if only one file
if len(main_urls.keys()) == 1 and len(main_urls.items()[0][1]) == 1 and \
(not CFG_CERN_SITE or len(cern_urls) == 0) and len(others_urls) == 0 and \
show_icons.lower() == 'yes':
- file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (weburl, _("Download fulltext"))
+ file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (CFG_SITE_URL, _("Download fulltext"))
elif show_icons.lower() == 'yes':
- file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (weburl, _("Download fulltext"))
+ file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (CFG_SITE_URL, _("Download fulltext"))
else:
file_icon = ''
last_name = ""
for descr, urls in main_urls.items():
out += '<small class="detailedRecordActions">%s:</small> ' % descr
url_list = []
urls.sort(lambda (url1, name1, format1), (url2, name2, format2): url1 < url2 and -1 or url1 > url2 and 1 or 0)
for url, name, format in urls:
if not name == last_name and len(main_urls) > 1:
print_name = "<em>%s</em> - " % name
else:
print_name = ""
last_name = name
url_list.append(print_name + '<a '+style+' href="'+escape(url)+'">'+file_icon+format.upper()+'</a>')
out += separator + separator.join(url_list) + \
additional_str + versions_str + '</div>'
if CFG_CERN_SITE and cern_urls:
# Put a big file icon if only one file
if len(main_urls.keys()) == 0 and \
len(cern_urls) == 1 and len(others_urls) == 0 and \
show_icons.lower() == 'yes':
- file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (weburl, _("Download fulltext"))
+ file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (CFG_SITE_URL, _("Download fulltext"))
elif show_icons.lower() == 'yes':
- file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (weburl, _("Download fulltext"))
+ file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (CFG_SITE_URL, _("Download fulltext"))
else:
file_icon = ''
link_word = len(cern_urls) == 1 and _('link') or _('links')
out += '<small class="detailedRecordActions">(%s)</small><br />' % _("CERN %(link_or_links)s" % {'link_or_links' : link_word})
url_list = []
for url, descr in cern_urls:
url_list.append('<a '+style+' href="'+escape(url)+'">'+file_icon+escape(str(descr))+'</a>')
out += separator.join(url_list)
if others_urls:
# Put a big file icon if only one file
if len(main_urls.keys()) == 0 and \
(not CFG_CERN_SITE or len(cern_urls) == 0) and len(others_urls) == 1 and \
show_icons.lower() == 'yes':
- file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (weburl, _("Download fulltext"))
+ file_icon = '<img style="border:none" src="%s/img/file-icon-text-34x48.gif" alt="%s" /><br />' % (CFG_SITE_URL, _("Download fulltext"))
elif show_icons.lower() == 'yes':
- file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (weburl, _("Download fulltext"))
+ file_icon = '<img style="border:none" src="%s/img/file-icon-text-12x16.gif" alt="%s"/>' % (CFG_SITE_URL, _("Download fulltext"))
else:
file_icon = ''
link_word = len(others_urls) == 1 and _('link') or _('links')
out += '<small class="detailedRecordActions">(%s)</small>%s' % (_("external %(link_or_links)s") % {'link_or_links' : link_word}, separator)
url_list = []
for url, descr in others_urls:
url_list.append('<a '+style+' href="'+escape(url)+'">'+file_icon+escape(str(descr))+'</a>')
out += '<small>' + separator.join(url_list) + '</small>'
if out.endswith('<br />'):
out = out[:-len('<br />')]
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_keywords.py b/modules/bibformat/lib/elements/bfe_keywords.py
index 7cb5cee17..0154aa55f 100644
--- a/modules/bibformat/lib/elements/bfe_keywords.py
+++ b/modules/bibformat/lib/elements/bfe_keywords.py
@@ -1,61 +1,61 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints keywords
"""
__revision__ = "$Id$"
import cgi
from urllib import quote
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
def format(bfo, keyword_prefix, keyword_suffix, separator=' ; ', link='yes'):
"""
Display keywords of the record.
@param keyword_prefix a prefix before each keyword
@param keyword_suffix a suffix after each keyword
@param separator a separator between keywords
@param link links the keywords if 'yes' (HTML links)
"""
keywords = bfo.fields('6531_a')
if len(keywords) > 0:
if link == 'yes':
- keywords = ['<a href="' + weburl + '/search?f=keyword&amp;p='+ \
+ keywords = ['<a href="' + CFG_SITE_URL + '/search?f=keyword&amp;p='+ \
quote(keyword) + \
'&amp;ln='+ bfo.lang+ \
'">' + cgi.escape(keyword) + '</a>'
for keyword in keywords]
else:
keywords = [cgi.escape(keyword)
for keyword in keywords]
keywords = [keyword_prefix + keyword + keyword_suffix
for keyword in keywords]
return separator.join(keywords)
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_photo_resources_brief.py b/modules/bibformat/lib/elements/bfe_photo_resources_brief.py
index 4f0242845..ac2955e0c 100644
--- a/modules/bibformat/lib/elements/bfe_photo_resources_brief.py
+++ b/modules/bibformat/lib/elements/bfe_photo_resources_brief.py
@@ -1,47 +1,47 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints brief HTML picture and links to resources
"""
__revision__ = "$Id$"
def format(bfo):
"""
Prints html image and link to photo resources.
"""
- from invenio.config import weburl
+ from invenio.config import CFG_SITE_URL
resources = bfo.fields("8564_")
out = ""
for resource in resources:
if resource.get("x", "") == "icon" and resource.get("u", "") == "":
- out += '<a href="'+weburl+'/record/'+bfo.control_field("001")+ \
+ out += '<a href="'+CFG_SITE_URL+'/record/'+bfo.control_field("001")+ \
'?ln='+ bfo.lang + '"><img src="' + resource.get("q", "").replace(" ","") \
+ '" alt="" border="0"/></a>'
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_references.py b/modules/bibformat/lib/elements/bfe_references.py
index 535161d02..280fa0d6d 100644
--- a/modules/bibformat/lib/elements/bfe_references.py
+++ b/modules/bibformat/lib/elements/bfe_references.py
@@ -1,88 +1,88 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints references
"""
__revision__ = "$Id$"
def format(bfo, reference_prefix, reference_suffix):
"""
Prints the references of this record
@param reference_prefix a prefix displayed before each reference
@param reference_suffix a suffix displayed after each reference
"""
- from invenio.config import weburl
+ from invenio.config import CFG_SITE_URL
references = bfo.fields("999C5", escape=1)
out = ""
for reference in references:
ref_out = ''
if reference.has_key('o'):
if out != "":
ref_out = '</li>'
ref_out += "<li><small>"+ reference['o']+ "</small> "
if reference.has_key('m'):
ref_out += "<small>"+ reference['m']+ "</small> "
if reference.has_key('r'):
- ref_out += '<small> [<a href="'+weburl+'/search?f=reportnumber&amp;p='+ \
+ ref_out += '<small> [<a href="'+CFG_SITE_URL+'/search?f=reportnumber&amp;p='+ \
reference['r']+ \
'&amp;ln=' + bfo.lang + \
'">'+ reference['r']+ "</a>] </small> <br />"
if reference.has_key('t'):
ejournal = bfo.kb("ejournals", reference.get('t', ""))
if ejournal != "":
ref_out += ' <small> <a href="http://weblib.cern.ch/cgi-bin/ejournals?publication='\
+ reference['t'].replace(" ", "+") \
+"&amp;volume="+reference.get('v', "")+"&amp;year="+\
reference.get('y', "")+"&amp;page="+\
reference.get('p',"").split("-")[0]+'">'
ref_out += reference['t']+": "+reference.get('v', "")+\
" ("+reference.get('y', "")+") "
ref_out += reference.get('p', "")+"</a> </small> <br />"
else:
ref_out += " <small> "+reference['t']+ reference.get('v', "")+\
reference.get('y',"")+ reference.get('p',"")+ \
" </small> <br />"
if reference_prefix is not None and ref_out != '':
ref_out = reference_prefix + ref_out
if reference_suffix is not None and ref_out != '':
ref_out += reference_suffix
out += ref_out
if out != '':
out += '</li>'
return out
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/lib/elements/bfe_server_info.py b/modules/bibformat/lib/elements/bfe_server_info.py
index 3baa5f15e..aa4b9ac2f 100644
--- a/modules/bibformat/lib/elements/bfe_server_info.py
+++ b/modules/bibformat/lib/elements/bfe_server_info.py
@@ -1,71 +1,71 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints server info
"""
__revision__ = "$Id$"
-from invenio.config import CFG_SITE_URL, CFG_SITE_ADMIN_EMAIL, CFG_SITE_LANG, CFG_SITE_NAME, weburl, CFG_VERSION, CFG_SITE_NAME_INTL, CFG_SITE_SUPPORT_EMAIL
+from invenio.config import CFG_SITE_URL, CFG_SITE_ADMIN_EMAIL, CFG_SITE_LANG, CFG_SITE_NAME, CFG_SITE_URL, CFG_VERSION, CFG_SITE_NAME_INTL, CFG_SITE_SUPPORT_EMAIL
# FIXME: what about admin_email? needed? or use new arguments like CFG_SITE_ADMIN_EMAIL?
def format(bfo, var=''):
'''
Print several server specific variables.
- @param var the name of the desired variable. Can be one of: name, i18n_name, lang, CFG_VERSION, admin_email, support_email, weburl, searchurl, recurl
+ @param var the name of the desired variable. Can be one of: name, i18n_name, lang, CFG_VERSION, admin_email, support_email, CFG_SITE_URL, searchurl, recurl
name: the name of the server
i18n_name: internationalized name
lang: the default language of the server
CFG_VERSION: the software version
admin_email: the admin email
support_email: the support email
- weburl: the base url for the server
+ CFG_SITE_URL: the base url for the server
searchurl: the search url for the server
recurl: the base url for the record
'''
recID = bfo.recID
if var == '':
out = ''
elif var == 'name':
out = CFG_SITE_NAME
elif var == 'i18n_name':
out = CFG_SITE_NAME_INTL.get(bfo.lang, CFG_SITE_NAME)
elif var == 'lang':
out = CFG_SITE_LANG
elif var == 'CFG_VERSION':
out = 'CDS Invenio v' + str(CFG_VERSION)
elif var in ['email', 'admin_email']:
out = CFG_SITE_ADMIN_EMAIL
elif var == 'support_email':
out = CFG_SITE_SUPPORT_EMAIL
- elif var == 'weburl':
- out = weburl
+ elif var == 'CFG_SITE_URL':
+ out = CFG_SITE_URL
if not out.endswith('/'):
out += '/'
elif var == 'searchurl':
out = CFG_SITE_URL + '/search'
if not out.endswith('/'):
out += '/'
elif var == 'recurl':
- out = weburl
+ out = CFG_SITE_URL
if not out.endswith('/'):
out += '/'
out += 'record/' + str(recID)
else:
out = 'Unknown variable: %s' % (var)
return out
diff --git a/modules/bibformat/lib/elements/bfe_title.py b/modules/bibformat/lib/elements/bfe_title.py
index 93703d6b9..aa4f81269 100644
--- a/modules/bibformat/lib/elements/bfe_title.py
+++ b/modules/bibformat/lib/elements/bfe_title.py
@@ -1,93 +1,93 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibFormat element - Prints titles
"""
__revision__ = "$Id$"
import cgi
from urllib import quote
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
import re
def format(bfo, separator=" ", highlight='no'):
"""
Prints the titles of a record.
@param separator separator between the different titles
@param highlight highlights the words corresponding to search query if set to 'yes'
"""
titles = []
title = bfo.field('245__a')
title_remainder = bfo.field('245__b')
edition_statement = bfo.field('250__a')
if len(title) > 0:
if title_remainder:
titles.append( title + ': ' + title_remainder )
else:
titles.append( title )
title = bfo.field('0248_a')
if len(title) > 0:
titles.append( title )
title = bfo.field('246__a')
if len(title) > 0:
titles.append( title )
title = bfo.field('246__b')
if len(title) > 0:
titles.append( title )
title = bfo.field('246_1a')
if len(title) > 0:
titles.append( title )
if len(titles) > 0:
#Display 'Conference' title only if other titles were not found
title = bfo.field('111__a')
if len(title) > 0:
titles.append( title )
titles = [cgi.escape(x) for x in titles]
if highlight == 'yes':
from invenio import bibformat_utils
titles = [bibformat_utils.highlight(x, bfo.search_pattern) for x in titles]
if len(edition_statement) > 0:
return separator.join(titles) + "; " + edition_statement
else:
return separator.join(titles)
def escape_values(bfo):
"""
Called by BibFormat in order to check if output of this element
should be escaped.
"""
return 0
diff --git a/modules/bibformat/web/admin/bibformatadmin.py b/modules/bibformat/web/admin/bibformatadmin.py
index 853e08203..70e6339ca 100644
--- a/modules/bibformat/web/admin/bibformatadmin.py
+++ b/modules/bibformat/web/admin/bibformatadmin.py
@@ -1,1469 +1,1469 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio BibFormat Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import MySQLdb
from invenio import bibformatadminlib, \
config, \
bibformat_dblayer,\
bibformat_engine
from invenio.bibrankadminlib import check_user
from invenio.webpage import page, create_error_box
from invenio.webuser import getUid, page_not_authorized, collect_user_info
from invenio.messages import wash_language, gettext_set_language
from invenio.urlutils import wash_url_argument, redirect_to_url
from invenio.search_engine import search_pattern, \
create_basic_search_units
def index(req, ln=config.CFG_SITE_LANG):
"""
Main BibFormat administration page.
Displays a warning if we find out that etc/biformat dir is not writable by us
(as most opeation of BibFormat must write in this directory).
@param ln: language
"""
warnings = []
if not bibformatadminlib.can_write_etc_bibformat_dir():
warnings.append(("WRN_BIBFORMAT_CANNOT_WRITE_IN_ETC_BIBFORMAT"))
ln = wash_language(ln)
_ = gettext_set_language(ln)
# Check if user is authorized to administer
# If not, still display page but offer to log in
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
is_admin = True
else:
is_admin = False
navtrail = '''<a class="navtrail" href="%s/help/admin">%s</a>''' % \
- (config.weburl, _("Admin Area"))
+ (config.CFG_SITE_URL, _("Admin Area"))
return page(title=_("BibFormat Admin"),
body=bibformatadminlib.perform_request_index(ln=ln,
warnings=warnings,
is_admin=is_admin),
language=ln,
uid=uid,
navtrail = navtrail,
lastupdated=__lastupdated__,
req=req,
warnings=warnings)
def output_formats_manage(req, ln=config.CFG_SITE_LANG, sortby="code"):
"""
Main page for output formats management. Check for authentication and print output formats list.
@param ln language
@param sortby the sorting crieteria (can be 'code' or 'name')
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = bibformatadminlib.getnavtrail()
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
sortby = wash_url_argument(sortby, 'str')
return page(title=_("Manage Output Formats"),
body=bibformatadminlib.perform_request_output_formats_management(ln=ln, sortby=sortby),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def output_format_show(req, bfo, ln=config.CFG_SITE_LANG,
r_fld=[], r_val=[], r_tpl=[],
default="", r_upd="", chosen_option="",
**args):
"""
Show a single output format. Check for authentication and print output format settings.
The page either shows the output format from file, or from user's
POST session, as we want to let him edit the rules without
saving. Policy is: r_fld, r_val, rules_tpl are list of attributes
of the rules. If they are empty, load from file. Else use
POST. The i th value of each list is one of the attributes of rule
i. Rule i is the i th rule in order of evaluation. All list have
the same number of item.
r_upd contains an action that has to be performed on rules. It
can composed of a number (i, the rule we want to modify) and an
operator : "save" to save the rules, "add" or "del".
syntax: operator [number]
For eg: r_upd = _("Save Changes") saves all rules (no int should be specified).
For eg: r_upd = _("Add New Rule") adds a rule (no int should be specified).
For eg: r_upd = _("Remove Rule") + " 5" deletes rule at position 5.
The number is used only for operation delete.
An action can also be in **args. We must look there for string starting
with '(+|-) [number]' to increase (+) or decrease (-) a rule given by its
index (number).
For example "+ 5" increase priority of rule 5 (put it at fourth position).
The string in **args can be followed by some garbage that looks like .x
or .y, as this is returned as the coordinate of the click on the
<input type="image">. We HAVE to use args and reason on its keys, because for <input> of
type image, iexplorer does not return the value of the tag, but only the name.
Action is executed only if we are working from user's POST session
(means we must have loaded the output format first, which is
totally normal and expected behaviour)
@param ln language
@param bfo the filename of the output format to show
@param r_fld the list of 'field' attribute for each rule
@param r_val the list of 'value' attribute for each rule
@param r_tpl the list of 'template' attribute for each rule
@param default the default format template used by this output format
@param r_upd the rule that we want to increase/decrease in order of evaluation
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/output_formats_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Output Formats")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/output_formats_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Output Formats")))
code = wash_url_argument(bfo, 'str')
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
bfo = wash_url_argument(bfo, 'str')
default = wash_url_argument(default, 'str')
r_upd = wash_url_argument(r_upd, 'str')
if not bibformatadminlib.can_read_output_format(bfo): #No read permission
return page(title=_("Restricted Output Format"),
body = """You don't have permission to
view this output format.""",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_CANNOT_READ_OUTPUT_FILE",
bfo ,
"")],
lastupdated=__lastupdated__,
req=req)
output_format = bibformat_engine.get_output_format(code=bfo,
with_attributes=True)
name = output_format['attrs']['names']['generic']
if name == "":
name = bfo
if not bibformatadminlib.can_write_output_format(bfo) and \
chosen_option == "":#No write permission
return dialog_box(req=req,
ln=ln,
title="File Permission on %s" % name,
message="You don't have write permission" \
"on <i>%s</i>.<br/> You can view the output " \
"format, but not edit it." % name,
navtrail=navtrail_previous_links,
options=[ _("Ok")])
return page(title=_('Output Format %s Rules' % name),
body=bibformatadminlib.perform_request_output_format_show(bfo=bfo,
ln=ln,
r_fld=r_fld,
r_val=r_val,
r_tpl=r_tpl,
default=default,
r_upd=r_upd,
args=args),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def output_format_show_attributes(req, bfo, ln=config.CFG_SITE_LANG):
"""
Page for output format names and descrition attributes edition.
@param ln language
@param bfo the filename of the template to show
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.weburl, ln , _("Manage Output Formats")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln , _("Manage Output Formats")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
bfo = wash_url_argument(bfo, 'str')
if not bibformatadminlib.can_read_output_format(bfo): #No read permission
return page(title=_("Restricted Output Format"),
body = """You don't have permission to
view this output format.""",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_CANNOT_READ_OUTPUT_FILE", bfo ,"")],
lastupdated=__lastupdated__,
req=req)
output_format = bibformat_engine.get_output_format(code=bfo,
with_attributes=True)
name = output_format['attrs']['names']['generic']
return page(title=_("Output Format %s Attributes" % name),
body=bibformatadminlib.perform_request_output_format_show_attributes(bfo, ln=ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links ,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg)
def output_format_show_dependencies(req, bfo, ln=config.CFG_SITE_LANG):
"""
Show the dependencies of the given output format.
@param ln language
@param bfo the filename of the output format to show
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s </a>''' % (config.weburl, ln, _("Manage Output Formats")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s </a>''' % (config.CFG_SITE_URL, ln, _("Manage Output Formats")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
bfo = wash_url_argument(bfo, 'str')
if not bibformatadminlib.can_read_output_format(bfo): #No read permission
return page(title=_("Restricted Output Format"),
body = """You don't have permission
to view this output format.""",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_CANNOT_READ_OUTPUT_FILE",
bfo ,
"")],
lastupdated=__lastupdated__,
req=req)
format_name = bibformat_engine.get_output_format_attrs(bfo)['names']['generic']
return page(title=_("Output Format %s Dependencies" % format_name),
body=bibformatadminlib.perform_request_output_format_show_dependencies(bfo, ln=ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg)
def output_format_update_attributes(req, bfo, ln=config.CFG_SITE_LANG,
name = "", description="",
code="", content_type="",
names_trans=[], visibility="0"):
"""
Update the name, description and code of given output format
@param ln language
@param description the new description
@param name the new name
@param code the new short code (== new bfo) of the output format
@param content_type the new content_type of the output format
@param bfo the filename of the output format to update
@param names_trans the translations in the same order as the languages from get_languages()
@param visibility the visibility of the output format in the output formats list (public pages)
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
name = wash_url_argument(name, 'str')
description = wash_url_argument(description, 'str')
bfo = wash_url_argument(bfo, 'str')
code = wash_url_argument(code, 'str')
visibility = wash_url_argument(visibility, 'int')
bfo = bibformatadminlib.update_output_format_attributes(bfo,
name,
description,
code,
content_type,
names_trans,
visibility)
redirect_to_url(req, "output_format_show?ln=%(ln)s&bfo=%(bfo)s" % {'ln':ln,
'bfo':bfo,
'names_trans':names_trans})
else:
return page_not_authorized(req=req,
text=auth_msg,
chosen_option="")
def output_format_delete(req, bfo, ln=config.CFG_SITE_LANG, chosen_option=""):
"""
Delete an output format
@param bfo the filename of the output format to delete
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/output_formats_manage?ln=%s">%s</a> &gt; %s''' % (config.weburl, ln, _("Manage Output Formats"), _("Delete Output Format")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/output_formats_manage?ln=%s">%s</a> &gt; %s''' % (config.CFG_SITE_URL, ln, _("Manage Output Formats"), _("Delete Output Format")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
#Ask confirmation to user if not already done
chosen_option = wash_url_argument(chosen_option, 'str')
if chosen_option == "":
bfo = wash_url_argument(bfo, 'str')
format_name = bibformat_dblayer.get_output_format_names(bfo)['generic']
return dialog_box(req=req,
ln=ln,
title="Delete %s"%format_name,
message="Are you sure you want to" \
"delete output format <i>%s</i>?" % format_name,
navtrail=navtrail_previous_links,
options=[_("Cancel"), _("Delete")])
elif chosen_option==_("Delete"):
bibformatadminlib.delete_output_format(bfo)
redirect_to_url(req, "output_formats_manage?ln=%(ln)s"%{'ln':ln})
else:
return page_not_authorized(req=req, text=auth_msg)
def output_format_add(req, ln=config.CFG_SITE_LANG):
"""
Adds a new output format
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
bfo = bibformatadminlib.add_output_format()
redirect_to_url(req, "output_format_show_attributes?ln=%(ln)s&bfo=%(bfo)s" % {'ln':ln, 'bfo':bfo})
else:
return page_not_authorized(req=req, text=auth_msg)
def format_templates_manage(req, ln=config.CFG_SITE_LANG, checking='0'):
"""
Main page for formats templates management. Check for authentication and print formats list.
@param ln language
@param checking if 0, basic checking. Else perform extensive checking (time-consuming)
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = bibformatadminlib.getnavtrail()
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
checking_level = wash_url_argument(checking, 'int')
return page(title=_("Manage Format Templates"),
body=bibformatadminlib.perform_request_format_templates_management(ln=ln, checking=checking_level),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def format_template_show(req, bft, code=None, ln=config.CFG_SITE_LANG,
ln_for_preview=config.CFG_SITE_LANG,
pattern_for_preview="",
content_type_for_preview="text/html",
chosen_option=""):
"""
Main page for template edition. Check for authentication and print formats editor.
@param ln language
@param code the code being edited
@param bft the name of the template to show
@param ln_for_preview the language for the preview (for bfo)
@param pattern_for_preview the search pattern to be used for the preview (for bfo)
@param content_type_for_preview the (MIME) content type of the preview
@param chosen_option returned value for dialog_box warning
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = bibformatadminlib.getnavtrail('''
- &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.weburl, ln , _("Manage Format Templates")))
+ &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln , _("Manage Format Templates")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
format_template = wash_url_argument(bft, 'str')
ln_preview = wash_language(ln_for_preview)
pattern_preview = wash_url_argument(pattern_for_preview, 'str')
if not bibformatadminlib.can_read_format_template(bft): #No read permission
return page(title=_("Restricted Format Template"),
body = """You don't have permission
to view this format template.""",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_CANNOT_READ_TEMPLATE_FILE",
format_template ,
"")],
lastupdated=__lastupdated__,
req=req)
format_name = bibformat_engine.get_format_template_attrs(bft)['name']
if not bibformatadminlib.can_write_format_template(bft) and \
chosen_option == "": #No write permission
return dialog_box(req=req,
ln=ln,
title="File Permission on %s" % format_name,
message="You don't have write permission " \
"on <i>%s</i>.<br/> You can view the template" \
", but not edit it." % format_name,
navtrail=navtrail_previous_links,
options=[ _("Ok")])
if bft.endswith('.xsl'):
format_name += ' (XSL)'
return page(title=_("Format Template %s"%format_name),
body=bibformatadminlib.perform_request_format_template_show(format_template,
code=code,
ln=ln,
ln_for_preview=ln_preview,
pattern_for_preview=pattern_preview,
content_type_for_preview=content_type_for_preview),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def format_template_show_attributes(req, bft, ln=config.CFG_SITE_LANG, new=0):
"""
Page for template name and descrition attributes edition.
This is also the first page shown when a format template
has just been added. In that case new is different from
False and we can offer specific option to user (for ex
let him make a duplicate of existing template).
@param ln language
@param bft the name of the template to show
@param new if "False", the template has not just been added
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Format Templates")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Format Templates")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
format_template = wash_url_argument(bft, 'str')
format_name = bibformat_engine.get_format_template_attrs(bft)['name']
is_new = wash_url_argument(new, 'int')
if not bibformatadminlib.can_read_format_template(bft): #No read permission
return page(title=_("Restricted Format Template"),
body = """You don't have permission
to view this format template.""",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_CANNOT_READ_TEMPLATE_FILE",
format_template ,
"")],
lastupdated=__lastupdated__,
req=req)
return page(title=_("Format Template %s Attributes"%format_name),
body=bibformatadminlib.perform_request_format_template_show_attributes(bft, ln=ln, new=is_new),
uid=uid,
language=ln,
navtrail = navtrail_previous_links ,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg)
def format_template_show_dependencies(req, bft, ln=config.CFG_SITE_LANG):
"""
Show the dependencies (on elements) of the given format.
@param ln language
@param bft the filename of the template to show
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Format Templates")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Format Templates")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
format_template = wash_url_argument(bft, 'str')
format_name = bibformat_engine.get_format_template_attrs(bft)['name']
return page(title=_("Format Template %s Dependencies" % format_name),
body=bibformatadminlib.perform_request_format_template_show_dependencies(bft, ln=ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg)
def format_template_update_attributes(req, bft, ln=config.CFG_SITE_LANG,
name = "", description="",
duplicate=None):
"""
Update the name and description of given format template
@param ln language
@param description the new description
@param name the new name
@param bft the filename of the template to update
@param duplicate the filename of template that we want to copy (the code)
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
if duplicate is not None:
duplicate = wash_url_argument(duplicate, 'str')
name = wash_url_argument(name, 'str')
description = wash_url_argument(description, 'str')
bft = bibformatadminlib.update_format_template_attributes(bft,
name,
description,
duplicate)
redirect_to_url(req, "format_template_show?ln=%(ln)s&bft=%(bft)s" % {'ln':ln, 'bft':bft})
else:
return page_not_authorized(req=req, text=auth_msg)
def format_template_delete(req, bft, ln=config.CFG_SITE_LANG, chosen_option=""):
"""
Delete a format template
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = bibformatadminlib.getnavtrail('''
- &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a> &gt; %s''' % (config.weburl, ln ,_("Manage Format Templates"),_("Delete Format Template")))
+ &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a> &gt; %s''' % (config.CFG_SITE_URL, ln ,_("Manage Format Templates"),_("Delete Format Template")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
#Ask confirmation to user if not already done
chosen_option = wash_url_argument(chosen_option, 'str')
if chosen_option == "":
format_template = wash_url_argument(bft, 'str')
format_name = bibformat_engine.get_format_template_attrs(bft)['name']
return dialog_box(req=req,
ln=ln,
title="Delete %s" % format_name,
message="Are you sure you want to delete" \
"format template <i>%s</i>?" % format_name,
navtrail=navtrail_previous_links,
options=[_("Cancel"), _("Delete")])
elif chosen_option==_("Delete"):
bibformatadminlib.delete_format_template(bft)
redirect_to_url(req, "format_templates_manage?ln=%(ln)s" % {'ln':ln})
else:
return page_not_authorized(req=req, text=auth_msg)
def format_template_add(req, ln=config.CFG_SITE_LANG):
"""
Adds a new format template
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
bft = bibformatadminlib.add_format_template()
redirect_to_url(req, "format_template_show_attributes?ln=%(ln)s&bft=%(bft)s&new=1" % {'ln':ln, 'bft':bft})
else:
return page_not_authorized(req=req, text=auth_msg)
def format_template_show_preview_or_save(req, bft, ln=config.CFG_SITE_LANG, code=None,
ln_for_preview=config.CFG_SITE_LANG,
pattern_for_preview="",
content_type_for_preview='text/html',
save_action=None,
navtrail=""):
"""
Print the preview of a record with a format template. To be included inside Format template
editor. If the save_action has a value, then the code should also be saved at the same time
@param code the code of a template to use for formatting
@param ln_for_preview the language for the preview (for bfo)
@param pattern_for_preview the search pattern to be used for the preview (for bfo)
@param save_action has a value if the code has to be saved
@param bft the filename of the template to save
@param navtrail standard navtrail
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
user_info = collect_user_info(req)
uid = user_info['uid']
bft = wash_url_argument(bft, 'str')
if save_action is not None and code is not None:
#save
bibformatadminlib.update_format_template_code(bft, code=code)
bibformat_engine.clear_caches()
if code is None:
code = bibformat_engine.get_format_template(bft)['code']
ln_for_preview = wash_language(ln_for_preview)
pattern_for_preview = wash_url_argument(pattern_for_preview, 'str')
if pattern_for_preview == "":
try:
recID = search_pattern(p='-collection:DELETED').pop()
except KeyError:
return page(title="No Document Found",
body="",
uid=uid,
language=ln_for_preview,
navtrail = "",
lastupdated=__lastupdated__,
req=req,
navmenuid='search')
pattern_for_preview = "recid:%s" % recID
else:
try:
recID = search_pattern(p=pattern_for_preview + \
' -collection:DELETED').pop()
except KeyError:
return page(title="No Record Found for %s" % pattern_for_preview,
body="",
uid=uid,
language=ln_for_preview,
navtrail = "",
lastupdated=__lastupdated__,
req=req)
units = create_basic_search_units(None, pattern_for_preview, None)
keywords = [unit[1] for unit in units if unit[0] != '-']
bfo = bibformat_engine.BibFormatObject(recID = recID,
ln = ln_for_preview,
search_pattern = keywords,
xml_record = None,
user_info = user_info)
(body, errors) = bibformat_engine.format_with_format_template(bft,
bfo,
verbose=7,
format_template_code=code)
if content_type_for_preview == 'text/html':
#Standard page display with CDS headers, etc.
return page(title="",
body=body,
uid=uid,
language=ln_for_preview,
navtrail = navtrail,
lastupdated=__lastupdated__,
req=req,
navmenuid='search')
else:
#Output with chosen content-type.
req.content_type = content_type_for_preview
req.send_http_header()
req.write(body)
else:
return page_not_authorized(req=req, text=auth_msg)
def format_template_show_short_doc(req, ln=config.CFG_SITE_LANG, search_doc_pattern=""):
"""
Prints the format elements documentation in a brief way. To be included inside Format template
editor.
@param ln: language
@param search_doc_pattern a search pattern that specified which elements to display
@param bft the name of the template to show
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
search_doc_pattern = wash_url_argument(search_doc_pattern, 'str')
return bibformatadminlib.perform_request_format_template_show_short_doc(ln=ln, search_doc_pattern=search_doc_pattern)
else:
return page_not_authorized(req=req, text=auth_msg)
def format_elements_doc(req, ln=config.CFG_SITE_LANG):
"""
Main page for format elements documentation. Check for authentication and print format elements list.
@param ln language
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = bibformatadminlib.getnavtrail()
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
return page(title=_("Format Elements Documentation"),
body=bibformatadminlib.perform_request_format_elements_documentation(ln=ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def format_element_show_dependencies(req, bfe, ln=config.CFG_SITE_LANG):
"""
Shows format element dependencies
@param bfe the name of the bfe to show
@param ln language
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%s">%s</a>''' % (config.weburl, ln , _("Format Elements Documentation")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln , _("Format Elements Documentation")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
bfe = wash_url_argument(bfe, 'str')
return page(title=_("Format Element %s Dependencies" % bfe),
body=bibformatadminlib.perform_request_format_element_show_dependencies(bfe=bfe, ln=ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links)
def format_element_test(req, bfe, ln=config.CFG_SITE_LANG, param_values=None):
"""
Allows user to test element with different parameters and check output
'param_values' is the list of values to pass to 'format'
function of the element as parameters, in the order ...
If params is None, this means that they have not be defined by user yet.
@param bfe the name of the element to test
@param ln language
@param param_values the list of parameters to pass to element format function
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%s">%s</a>''' %( config.weburl, ln , _("Format Elements Documentation")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%s">%s</a>''' %( config.CFG_SITE_URL, ln , _("Format Elements Documentation")))
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
bfe = wash_url_argument(bfe, 'str')
user_info = collect_user_info(req)
uid = user_info['uid']
return page(title=_("Test Format Element %s" % bfe),
body=bibformatadminlib.perform_request_format_element_test(bfe=bfe,
ln=ln,
param_values=param_values,
user_info=user_info),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def kb_manage(req, ln=config.CFG_SITE_LANG):
"""
Main page for knowledge bases management. Check for authentication.
@param ln: language
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = bibformatadminlib.getnavtrail()
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
return page(title=_("Manage Knowledge Bases"),
body=bibformatadminlib.perform_request_knowledge_bases_management(ln=ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def kb_show(req, kb, sortby="to", ln=config.CFG_SITE_LANG):
"""
Shows the content of the given knowledge base id. Check for authentication and kb existence.
Before displaying the content of the knowledge base, check if a form was submitted asking for
adding, editing or removing a value.
@param ln language
@param kb the kb id to show
@param sortby the sorting criteria ('from' or 'to')
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
kb_id = wash_url_argument(kb, 'int')
kb_name = bibformatadminlib.get_kb_name(kb_id)
if kb_name is None:
return page(title=_("Unknown Knowledge Base"),
body = "",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_KB_ID_UNKNOWN", kb)],
lastupdated=__lastupdated__,
req=req)
return page(title=_("Knowledge Base %s" % kb_name),
body=bibformatadminlib.perform_request_knowledge_base_show(ln=ln,
kb_id=kb_id,
sortby=sortby),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def kb_show_attributes(req, kb, ln=config.CFG_SITE_LANG, sortby="to"):
"""
Shows the attributes (name, description) of a given kb
@param ln language
@param kb the kb id to show
@param sortby the sorting criteria ('from' or 'to')
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
kb_id = wash_url_argument(kb, 'int')
kb_name = bibformatadminlib.get_kb_name(kb_id)
if kb_name is None:
return page(title=_("Unknown Knowledge Base"),
body = "",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_KB_ID_UNKNOWN", kb)],
lastupdated=__lastupdated__,
req=req)
return page(title=_("Knowledge Base %s Attributes" % kb_name),
body=bibformatadminlib.perform_request_knowledge_base_show_attributes(ln=ln,
kb_id=kb_id,
sortby=sortby),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links)
def kb_show_dependencies(req, kb, ln=config.CFG_SITE_LANG, sortby="to"):
"""
Shows the dependencies of a given kb
@param ln language
@param kb the kb id to show
@param sortby the sorting criteria ('from' or 'to')
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
kb_id = wash_url_argument(kb, 'int')
kb_name = bibformatadminlib.get_kb_name(kb_id)
if kb_name is None:
return page(title=_("Unknown Knowledge Base"),
body = "",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_KB_ID_UNKNOWN", kb)],
lastupdated=__lastupdated__,
req=req)
return page(title=_("Knowledge Base %s Dependencies" % kb_name),
body=bibformatadminlib.perform_request_knowledge_base_show_dependencies(ln=ln,
kb_id=kb_id,
sortby=sortby),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def kb_add_mapping(req, kb, mapFrom, mapTo, sortby="to", ln=config.CFG_SITE_LANG):
"""
Adds a new mapping to a kb.
@param ln language
@param kb the kb id to show
@param sortby the sorting criteria ('from' or 'to')
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
kb_id = wash_url_argument(kb, 'int')
kb_name = bibformatadminlib.get_kb_name(kb_id)
if kb_name is None:
return page(title=_("Unknown Knowledge Base"),
body = "",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_KB_ID_UNKNOWN", kb)],
lastupdated=__lastupdated__,
req=req)
key = wash_url_argument(mapFrom, 'str')
value = wash_url_argument(mapTo, 'str')
bibformatadminlib.add_kb_mapping(kb_name, key, value)
redirect_to_url(req, "kb_show?ln=%(ln)s&kb=%(kb)s&sortby=%(sortby)s" % {'ln':ln,
'kb':kb_id,
'sortby':sortby})
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def kb_edit_mapping(req, kb, key, mapFrom, mapTo,
update="", delete="", sortby="to", ln=config.CFG_SITE_LANG):
"""
Edit a mapping to in kb. Edit can be "update old value" or "delete existing value"
@param kb the knowledge base id to edit
@param key the key of the mapping that will be modified
@param mapFrom the new key of the mapping
@param mapTo the new value of the mapping
@param update contains a value if the mapping is to be updated
@param delete contains a value if the mapping is to be deleted
@param sortby the sorting criteria ('from' or 'to')
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
kb_id = wash_url_argument(kb, 'int')
kb_name = bibformatadminlib.get_kb_name(kb_id)
if kb_name is None:
return page(title=_("Unknown Knowledge Base"),
body = "",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_KB_ID_UNKNOWN", kb)],
lastupdated=__lastupdated__,
req=req)
key = wash_url_argument(key, 'str')
if delete != "":
#Delete
bibformatadminlib.remove_kb_mapping(kb_name, key)
else:
#Update
new_key = wash_url_argument(mapFrom, 'str')
new_value = wash_url_argument(mapTo, 'str')
bibformatadminlib.update_kb_mapping(kb_name, key, new_key, new_value)
redirect_to_url(req, "kb_show?ln=%(ln)s&kb=%(kb)s&sortby=%(sortby)s" % {'ln':ln, 'kb':kb_id, 'sortby':sortby})
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def kb_update_attributes(req, kb="", name="", description="", sortby="to",
ln=config.CFG_SITE_LANG, chosen_option=None):
"""
Update the attributes of the kb
@param ln language
@param kb the kb id to update
@param sortby the sorting criteria ('from' or 'to')
@param name the new name of the kn
@param description the new description of the kb
@param chosen_option set to dialog box value
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
kb_id = wash_url_argument(kb, 'int')
if chosen_option is not None:
# Update could not be performed.
# Redirect to kb attributes page
redirect_to_url(req, "kb_show_attributes?ln=%(ln)s&kb=%(kb)s&sortby=%(sortby)s" % {'ln':ln, 'kb':kb_id, 'sortby':sortby})
kb_name = bibformatadminlib.get_kb_name(kb_id)
if kb_name is None:
return page(title=_("Unknown Knowledge Base"),
body = "",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_KB_ID_UNKNOWN", kb)],
lastupdated=__lastupdated__,
req=req)
new_name = wash_url_argument(name, 'str')
if kb_name != new_name and bibformatadminlib.kb_exists(new_name):
#A knowledge base with that name already exist
#Do not update
return dialog_box(req=req,
ln=ln,
title="Name already in use",
message="""<i>%s</i> cannot be renamed to %s:
Another knowledge base already has that name.
<br/>Please choose another name.""" % (kb_name,
new_name),
navtrail=navtrail_previous_links,
options=[ _("Ok")])
new_desc = wash_url_argument(description, 'str')
bibformatadminlib.update_kb_attributes(kb_name, new_name, new_desc)
redirect_to_url(req, "kb_show?ln=%(ln)s&kb=%(kb)s&sortby=%(sortby)s" % {'ln':ln, 'kb':kb_id, 'sortby':sortby})
else:
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def kb_add(req, ln=config.CFG_SITE_LANG, sortby="to"):
"""
Adds a new kb
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
kb_id = bibformatadminlib.add_kb()
redirect_to_url(req, "kb_show_attributes?ln=%(ln)s&kb=%(kb)s" % {'ln':ln, 'kb':kb_id, 'sortby':sortby})
else:
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases")))
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def kb_delete(req, kb, ln=config.CFG_SITE_LANG, chosen_option="", sortby="to"):
"""
Deletes an existing kb
@param kb the kb id to delete
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a> &gt; %s''' % (config.weburl, ln, _("Manage Knowledge Bases"), _("Delete Knowledge Base")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage?ln=%s">%s</a> &gt; %s''' % (config.CFG_SITE_URL, ln, _("Manage Knowledge Bases"), _("Delete Knowledge Base")))
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
kb_id = wash_url_argument(kb, 'int')
kb_name = bibformatadminlib.get_kb_name(kb_id)
if kb_name is None:
return page(title=_("Unknown Knowledge Base"),
body = "",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_KB_ID_UNKNOWN", kb)],
lastupdated=__lastupdated__,
req=req)
#Ask confirmation to user if not already done
chosen_option = wash_url_argument(chosen_option, 'str')
if chosen_option == "":
return dialog_box(req=req,
ln=ln,
title="Delete %s" % kb_name,
message="""Are you sure you want to
delete knowledge base <i>%s</i>?""" % kb_name,
navtrail=navtrail_previous_links,
options=[_("Cancel"), _("Delete")])
elif chosen_option==_("Delete"):
bibformatadminlib.delete_kb(kb_name)
redirect_to_url(req, "kb_manage?ln=%(ln)s" % {'ln':ln})
else:
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage">%s</a>'''%(config.weburl, _("Manage Knowledge Bases")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/kb_manage">%s</a>'''%(config.CFG_SITE_URL, _("Manage Knowledge Bases")))
return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links)
def validate_format(req, ln=config.CFG_SITE_LANG, bfo=None, bft=None, bfe=None):
"""
Returns a page showing the status of an output format or format
template or format element. This page is called from output
formats management page or format template management page or
format elements documentation.
The page only shows the status of one of the format, depending on
the specified one. If multiple are specified, shows the first one.
@param ln language
@param bfo an output format 6 chars code
@param bft a format element filename
@param bfe a format element name
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
try:
uid = getUid(req)
except MySQLdb.Error, e:
return error_page(req)
(auth_code, auth_msg) = check_user(req, 'cfgbibformat')
if not auth_code:
if bfo is not None: #Output format validation
bfo = wash_url_argument(bfo, 'str')
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/output_formats_manage?ln=%s">%s</a>'''%(config.weburl, ln, _("Manage Output Formats")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/output_formats_manage?ln=%s">%s</a>'''%(config.CFG_SITE_URL, ln, _("Manage Output Formats")))
if not bibformatadminlib.can_read_output_format(bfo): #No read permission
return page(title=_("Restricted Output Format"),
body = """You don't have permission
to view this output format.""",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_CANNOT_READ_OUTPUT_FILE",
bfo ,
"")],
lastupdated=__lastupdated__,
req=req)
output_format = bibformat_engine.get_output_format(code=bfo,
with_attributes=True)
name = output_format['attrs']['names']['generic']
title = _("Validation of Output Format %s" % name)
elif bft is not None: #Format template validation
bft = wash_url_argument(bft, 'str')
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.weburl, ln, _("Manage Format Templates")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_templates_manage?ln=%s">%s</a>''' % (config.CFG_SITE_URL, ln, _("Manage Format Templates")))
if not bibformatadminlib.can_read_format_template(bft): #No read permission
return page(title=_("Restricted Format Template"),
body = """You don't have permission to
view this format template.""",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_CANNOT_READ_TEMPLATE_FILE",
bft ,
"")],
lastupdated=__lastupdated__,
req=req)
name = bibformat_engine.get_format_template_attrs(bft)['name']
title = _("Validation of Format Template %s" % name)
elif bfe is not None: #Format element validation
bfe = wash_url_argument(bfe, 'str')
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%s#%s">%s</a>''' % (config.weburl, ln , bfe.upper() , _("Format Elements Documentation")))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/format_elements_doc?ln=%s#%s">%s</a>''' % (config.CFG_SITE_URL, ln , bfe.upper() , _("Format Elements Documentation")))
if not bibformatadminlib.can_read_format_element(bfe) and \
not bibformat_dblayer.tag_exists_for_name(bfe): #No read permission
return page(title=_("Restricted Format Element"),
body = """You don't have permission
to view this format element.""",
language=ln,
navtrail = navtrail_previous_links,
errors = [("ERR_BIBFORMAT_CANNOT_READ_ELEMENT_FILE", bfe ,"")],
lastupdated=__lastupdated__,
req=req)
title = _("Validation of Format Element %s" % bfe)
else: #No format specified
return page(title=_("Format Validation"),
uid=uid,
language=ln,
errors = [("ERR_BIBFORMAT_VALIDATE_NO_FORMAT")],
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
return page(title=title,
body=bibformatadminlib.perform_request_format_validate(ln=ln,
bfo=bfo,
bft=bft,
bfe=bfe),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
- navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/?ln=%s'''%(config.weburl, ln))
+ navtrail_previous_links = bibformatadminlib.getnavtrail(''' &gt; <a class="navtrail" href="%s/admin/bibformat/bibformatadmin.py/?ln=%s'''%(config.CFG_SITE_URL, ln))
return page_not_authorized(req=req,
text=auth_msg,
navtrail=navtrail_previous_links)
def download_dreamweaver_floater(req):
"""
Trigger download of a BibFormat palette for Dreamweaver.
"""
#bibformat_templates = invenio.template.load('bibformat')
req.content_type = 'text/html'
req.headers_out["Content-Disposition"] = "attachment; filename=BibFormat_floater.html"
req.send_http_header()
req.write(bibformatadminlib.perform_request_dreamweaver_floater())
def dialog_box(req, url="", ln=config.CFG_SITE_LANG, navtrail="",
title="", message="", options=[]):
"""
Returns a dialog box with a given title, message and options.
Used for asking confirmation on actions.
The page that will receive the result must take 'chosen_option' as parameter.
@param url the url used to submit the options chosen by the user
@param options the list of labels for the buttons given as choice to user
"""
import invenio
bibformat_templates = invenio.template.load('bibformat')
return page(title="",
body = bibformat_templates.tmpl_admin_dialog_box(url,
ln,
title,
message,
options),
language=ln,
lastupdated=__lastupdated__,
navtrail=navtrail,
req=req)
def error_page(req):
"""
Returns a default error page
"""
return page(title="Internal Error",
body = create_error_box(req, ln=config.CFG_SITE_LANG),
description="%s - Internal Error" % config.CFG_SITE_NAME,
keywords="%s, Internal Error" % config.CFG_SITE_NAME,
language=config.CFG_SITE_LANG)
diff --git a/modules/bibharvest/doc/admin/bibharvest-admin-guide.webdoc b/modules/bibharvest/doc/admin/bibharvest-admin-guide.webdoc
index 3473f7896..942f13c7d 100644
--- a/modules/bibharvest/doc/admin/bibharvest-admin-guide.webdoc
+++ b/modules/bibharvest/doc/admin/bibharvest-admin-guide.webdoc
@@ -1,292 +1,292 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibHarvest Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
<strong>2. <a href="#2">OAI Data Harvesting</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.1 <a href="#2.1">One time harvesting</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.2 <a href="#2.2">Periodical harvesting</a><br />
<strong>3. <a href="#3">OAI Repository</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1 <a href="#3.1">Definition of OAI sets</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2 <a href="#3.2">Exposing metadata via OAI Repository Gateway</a><br />
<a name="1"></a><h2>1. Overview</h2>
The BibHarvest module handles metadata gathering and delivery between OAI-PMH v.2.0 compliant repositories. Metadata exchange is performed on top of the OAI-PMH, the Open Archives Initiative's Protocol for Metadata Harvesting.
The BibHarvest Admin Interface can be used to set up a database of OAI sources and determine the periodicity of harvesting.
<br />
<a name="2"></a><h2>2. OAI Data Harvesting</h2>
<a name="2.1"></a><h3>2.1 One-time harvesting</h3>
<p>To harvest records from an OAI compliant repository, run the
<code>bibharvest</code> command-line tool. For example:
<blockquote>
<pre>
$ bibharvest -vListRecords -f2004-04-01 -u2004-04-02 -pmarcxml -o/tmp/z.xml \\
http://cdsweb.cern.ch/oai2d
</pre>
</blockquote>
<p>For further help with the command-line harvesting tool, run
<code>bibharvest --help</code>.
<a name="2.2"></a><h3>2.2 Periodical harvesting</h3>
<p>In order to periodically harvest metadata from one or several repositories, it is possible to organize OAI sources through the BibHarvest Admin Interface.
The interface allows the administrator to add new repositories as well as edit and delete existing ones.
Once the database has been set up thorugh the BibHarvest Admin Interface, run the oaiharvest command-line tool to run periodical harvesting</p>
<a name="2.2.1"></a><h4>2.2.1. Bibharvest Admin Interface</h4>
<b>Add OAI sources</b><br />
<p>The first step requires the administrator to enter the baseURL of the OAI repository. This is done for validation purposes - i.e. to check that the baseURL actually points to an OAI-compliant repository.
(Note: the validation simply performs an 'Identify' query to the baseURL and parses the reply with crucial tags such as <code>OAI-PMH</code> and <code>Identify</code>).
<br />Once the baseURL is validated, the administrator is required to fill into the following fields:
<br /><ul>
<li><strong>Source name</strong>: this will be the (alphanumeric) name used to refer to this OAI repository.</li>
<li><strong>Metadata prefix</strong>: upon validation the baseURL is checked for the available metadata formats. The administator can choose the desired format to harvest metadata in.</li>
<li><strong>Sets</strong>: upon validation the baseURL is checked for the available sets of the repository. The administator can choose the desired sets to harvest. If none is checked, all records are harvested. Otherwise selective harvesting for checked sets is done.</li>
<li><strong>Frequency</strong>: how often do you intend to harvest from this repository? (Note: selecting "never" excludes this source from automatic periodical harvesting)</li>
<li><strong>Starting date</strong>: on your first harvesting session, do you intend to harvest the whole repository (from beginning) or only newly added material (from today)? (WARNING: harvesting large collections of material may take very long!)</li>
<li><strong>Postprocess</strong>: how is the harvested material be dealt with after harvesting?
<ul><li>h: harvest only - metadata will be gathered and saved locally<li>h-u: harvest and upload - metadata will be automatically queued for upload (this is useful when harvested metadata is already in marcxml format)</li><li>h-c: harvest and convert - harvested metadata will be converted and saved locally</li><li>h-c-u: harvest, convert and upload - harvested metadata will be converted and queued for upload - this is useful when harvested metadata is in a format different from marcxml (e.g. oai_dc)</li><li>h-c-f-u: harvest, convert, filter, upload - useful when you want to filter harvested data in some non-trivial way before uploading</li></ul></li>
<li><strong>BibConvert configuration file</strong>: if postprocess involves conversion, the filename of an existing BibConvert configuration file (or its full path if file is not in standard BibConvert config directory) is needed.
This file determines how the metadata conversion will take place (e.g. oai_dc to marcxml).
In order to ensure that the configuration file performs the desired conversion, please test it in advance and follow the instruction contained in the <a href="../bibconvert/guide.html">BibConvert Admin Guide</a>.
Please note that some conversion parameters previously input at the command line can now be handled by the BibConvert Configuration Language (namely record separator, file header and file footer). Please consult the <a href="../bibconvert/guide.html">BibConvert Admin Guide</a> for more information on this.
This allows fully automatised harvesting and conversion (and possibly upload) of records provided a valid, functioning, BibConvert template is provided.</li>
</ul>
<b>Edit and delete OAI sources</b><br />
<p>Once a source has been added to the database, it will be visible in the overview page, as shown below</p>
<div class="pagebody">
<table class="admin_wvar_nomargin" summary="">
<tr>
<th class="adminheader">name</th>
<th class="adminheader">baseURL</th>
<th class="adminheader">metadataprefix</th>
<th class="adminheader">frequency</th>
<th class="adminheader">bibconvertfile</th>
<th class="adminheader">postprocess</th>
<th class="adminheader">actions</th>
</tr>
<tr>
<td class="admintdright">cdsweb</td>
<td class="admintdleft">http://cdsweb.cern.ch/oai2d</td>
<td class="admintdleft">marcxml</td>
<td class="admintdleft">daily</td>
<td class="admintdleft">NULL</td>
<td class="admintdleft">h-u</td>
<td class="admintdleft">edit / delete</td>
<td rowspan="4" style="vertical-align: bottom">
</td>
</tr>
</table>
</div>
<p>At this point it will be possible to edit the definition of this source by clicking on the appropriate action button. All the fields described in 2.2.1 can be modified (except for the Starting date).
There is no more validation at this stage, hence, please take extra care when editing important fields such as baseurl and metadataprefix.
<br />
OAI repositories can be removed from the database by clicking on the appropriate action button in the overview page.
</p>
<a name="2.2.2"></a><h4>2.2.2 oaiharvest commmand-line tool</h4>
<p>Once administrators have set up their desired OAI repositories in the database through the Admin Interface they can invoke <code>oaiharvest</code> to start up periodical harvesting.<br />
<br />
<b>Oaiharvest usage</b>
<blockquote>
<pre>
oaiharvest [options]
Specific options:
-r, --repository=REPOS_ONE,"REPOS TWO" name of the OAI repositories to be harvested (default=all)
-d, --dates=yyyy-mm-dd:yyyy-mm-dd harvest repositories between specified dates (overrides repositories' last updated timestamps)
Scheduling options:
-u, --user=USER user name to store task, password needed
-s, --sleeptime=SLEEP time after which to repeat tasks (no)
e.g.: 1s, 30m, 24h, 7d
-t, --time=TIME moment for the task to be active (now)
e.g.: +15s, 5m, 3h , 2002-10-27 13:57:26
General options:
-h, --help print this help and exit
-V, --version print version and exit
-v, --verbose=LEVEL verbose level (from 0 to 9, default 1)
</pre>
</blockquote>
<code>oaiharvest</code> performs a number of operations on the repositories listed in the database. By default <code>oaiharvest</code> considers all repositories, one by one (this gets overridden when <code>--repository</code> argument is passed).
<br /> For each repository that is considered, <code>oaiharvest</code> behaves according to the arguments passed at the command line:
<ul>
<li> If the <code>--dates</code> argument is not passed, it checks whether an update from the repository is needed (Note: the update status is calculated based on the time of the last harvesting and the frequency chosen by the administrator).
<ul>
<li>If an update is needed, it launches <code>bibharvest</code> and harvests all the metadata that the repository has added since the data of the last update. When the update is finished, the last update value is set to the current time and date.
<li>If an update is not needed, the source is skipped.
</ul>
<li> If the <code>--dates</code> argument is passed, it simply harvests the metadata of the repository from/until the given dates. The last update date is left unchanged.
<li> Finally, it performs the operations indicated in the postprocess mode, i.e. convert and/or upload the harvested metadata.
</ul>
<br />
<b>Oaiharvest usage examples</b><br />
<p>In most cases, administrators will want <code>oaiharvest</code> to run in the background, i.e. run in sleep mode and wake up periodically (e.g. every 24 hours) to check whether updates are needed, e.g. <code>oaiharvest -s 24h</code><br />
<p>In other cases, administrators may want to perform periodical harvesting only on specific sources, e.g. <code>oaiharvest -r cdsweb -s 12h</code><br />
<p>Another option is that administrators may want to harvest from certain repositories within two specific dates. This will be regarded as a one-off operation and will not affect the last update value of the source, e.g. <code>oaiharvest -r cdsweb -d 2005-05-05:2005-05-30</code><br />
<br />
<a name="3"></a><h2>3. OAI Repository</h2>
The OAI Repository corresponds to a set of metadata exposed for periodical harvesting by external OAI service providers. The following steps have to be done in order to expose metadata via OAI:
<ul>
<li>Definition of OAI sets</li>
<li>Exposing metadata via OAI Repository Gateway</li>
</ul>
<a name="3.1"></a><h3>3.1. Definition of OAI sets</h3>
The OAI repository is composed of OAI sets that correspond to a part of your metadata base. OAI sets can be configured via the <a href="oaiarchiveadmin.py">OAI Repository Admin Interface</a>, featuring essential OAI repository maintenance tasks.
<p>Each OAI set is defined by one or a union of several definitions. Each such definition is provided separately and corresponds to one line as displayed in the OAI Repository Admin overview page. The following information displays:
<br /><ul>
<li><strong>id: </strong>OAI set definition identifier (unique).<br /><br />
<li><strong>setSpec: </strong>OAI set identifier; OAI set is defined as a union of definitions with the same setSpec.<br /><br /><li><strong>setName: </strong>OAI set name; a human readable label<br /><br />
<li><strong>setCollection: </strong>Restriction to a particular data collection<br /><br />
<li><strong>p(N): </strong>phrase N; phrases are combined by logical AND<br /><br />
<li><strong>f(N): </strong>field that corresponds to phrase N<br /><br />
<li><strong>m(N): </strong>syntactic mode of phrase N<br /><br />
</ul>
<p>
<a name="3.2"></a><h3>3.2. Exposing metadata via OAI Repository Gateway</h3>
<a name="3.2.1"></a><h4>3.2.1 oaiarchive commmand-line tool</h4>
<p>Once OAI Repository is defined, the next step is to expose corresponding metadata via the OAI Repository Gateway. This is done by launching the <code>oaiarchive</code> script.<br />
<br />
<b>Oaiarchive usage</b>
<blockquote>
<pre>
oaiarchive [options]
Options:
-o --oaiset= Specify setSpec
-h --help Print this help
-V --version Print version information and exit
Modes
-a --add Add records to OAI repository
-d --delete Remove records from OAI repository
-r --report Print OAI repository status
-i --info Give info about OAI set (default)
Additional parameters:
-p --upload Upload records
-u --user=USER User name to submit the task as, password needed.
-v --verbose=LEVEL Verbose level (0=min,1=normal,9=max).
-s --sleeptime=SLEEP Time after which to repeat tasks (no)
-t --time=DATE Moment for the task to be active (now).
</pre>
</blockquote>
To expose set 'setname' via OAI repository gateway:
<blockquote>
<pre>
oaiarchive -apo 'setname' -s24
</pre>
</blockquote>
To remove records defined by set 'setname' from OAI repository:
<blockquote>
<pre>
oaiarchive -dpo 'setname'
</pre>
</blockquote>
To print OAI set status launch:
<blockquote>
<pre>
oaiarchive -io 'setname'
</pre>
</blockquote>
To print out the current status of the OAI repository launch:
<blockquote>
<pre>
oaiarchive -r
</pre>
</blockquote>
<p>
Please note that the <code>oaiarchive</code> script can be scheduled via <code>BibSched</code> in order to periodically update the OAI Repository with respect to database modifications and OAI set definitions modifications.
<p>
Please see also invenio.conf for more fine configurations of the OAI Repository.
<a name="3.2.2"></a><h4>3.2.2 Exposing entire metadata database</h4>
In order to expose all public records (the entire content of the Home
collection) via the OAI Repository gateway, a predefined
<code>global</code> set can be used. To expose the global set with
periodical updates on daily basis launch:
<blockquote>
<pre>
$ oaiarchive -apo global -s24h
</pre>
</blockquote>
To perform a reverse operation, i.e. to remove all records from the global OAI set, remove the oaiarchive task from the <code>BibSched</code> queue and launch:
<blockquote>
<pre>
$ oaiarchive -dpo global
</pre>
</blockquote>
diff --git a/modules/bibharvest/lib/bibharvest_templates.py b/modules/bibharvest/lib/bibharvest_templates.py
index 08a834da2..29bd4e078 100644
--- a/modules/bibharvest/lib/bibharvest_templates.py
+++ b/modules/bibharvest/lib/bibharvest_templates.py
@@ -1,241 +1,238 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import urllib
import time
import cgi
import gettext
import traceback
import urllib
import sys
from invenio.config import \
CFG_VERSION, \
- weburl
+ CFG_SITE_URL
from invenio.messages import gettext_set_language, language_list_long
from invenio.htmlutils import nmtoken_from_string
class Template:
def tmpl_getnavtrail(self, ln, previous):
"""Get the navigation trail
- 'previous' *string* - The previous navtrail"""
_ = gettext_set_language(ln)
- navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (weburl,)
+ navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (CFG_SITE_URL,)
navtrail = navtrail + previous
return navtrail
- def tmpl_draw_titlebar(self, ln, weburl, title, guideurl, extraname="", extraurl=""):
+ def tmpl_draw_titlebar(self, ln, title, guideurl, extraname="", extraurl=""):
"""Draws an html title bar
- 'title' *string* - The name of the titlebar
- - 'weburl' *string* - The general weburl root for this admin section (e.g. help/admin/bibharvest-admin-guide#mi )
- 'guideurl' *string* - The relative url of the guide relative to this section
- 'extraname' *string* - The name of an extra function
- 'extraurl' *string* - The relative url to an extra function
"""
_ = gettext_set_language(ln)
guidetitle = _("See Guide")
titlebar = """ <table class="admin_wvar_nomargin"><tr><th class="adminheader">"""
- titlebar += """%s&nbsp;&nbsp;&nbsp;<small>[<a title="%s" href="%s/%s">?</a>]</small>""" % (title, guidetitle, weburl, guideurl)
+ titlebar += """%s&nbsp;&nbsp;&nbsp;<small>[<a title="%s" href="%s/%s">?</a>]</small>""" % (title, guidetitle, CFG_SITE_URL, guideurl)
if extraname and extraurl:
- titlebar += """&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>[<a href="%s/%s">%s</a>]</small>""" % (weburl, extraurl, extraname)
+ titlebar += """&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>[<a href="%s/%s">%s</a>]</small>""" % (CFG_SITE_URL, extraurl, extraname)
titlebar += """</th></tr></table>"""
return titlebar
- def tmpl_draw_subtitle(self, ln, weburl, title, subtitle, guideurl):
+ def tmpl_draw_subtitle(self, ln, title, subtitle, guideurl):
"""Draws an html title bar
- 'title' *string* - The name of the titlebar
- 'subtitle' *string* - The header name of the subtitle
- - 'weburl' *string* - The general weburl root for this admin section (e.g. help/admin/bibharvest-admin-guide#mi )
- 'guideurl' *string* - The relative url of the guide relative to this section
"""
_ = gettext_set_language(ln)
guidetitle = _("See Guide")
titlebar = """<a name="%s">""" % nmtoken_from_string(title)
titlebar += """ </a>%s&nbsp;&nbsp;&nbsp;<small>""" % subtitle
- titlebar += """ [<a title="%s" href="%s/%s">?</a>]</small>""" % (guidetitle, weburl, guideurl)
+ titlebar += """ [<a title="%s" href="%s/%s">?</a>]</small>""" % (guidetitle, CFG_SITE_URL, guideurl)
return titlebar
- def tmpl_link_with_args(self, ln, weburl, funcurl, title, args):
+ def tmpl_link_with_args(self, ln, funcurl, title, args):
"""Draws an html title bar
- - 'weburl' *string* - The general weburl root for this admin section (e.g. help/admin/bibharvest-admin-guide#mi )
- 'funcurl' *string* - The relative url to this section
- 'title' *string* - The name of the link
- 'args' *list* - The list of arguments to be appended to the url in the form [name, value]
"""
_ = gettext_set_language(ln)
- initurl = '<a href="' + weburl + '/' + funcurl
+ initurl = '<a href="' + CFG_SITE_URL + '/' + funcurl
endurl = '" title="' + title + '">' + title + '</a>'
noargs = len(args)
if noargs==0:
# there are no arguments, close link and return
return initurl + endurl
else:
# we have args. list them in the link, then close it and return it
argsurl = '?'
count = 1
for arg in args:
if count != noargs:
argsurl += arg[0] + '=' + arg[1] + '&amp;'
else:
argsurl += arg[0] + '=' + arg[1]
count = count + 1
return initurl+argsurl+endurl
def tmpl_output_numbersources(self, ln, numbersources):
"""Get the navigation trail
- 'number of sources' *int* - The number of sources in the database"""
_ = gettext_set_language(ln)
present = _("OAI sources currently present in the database")
notpresent = _("No OAI sources currently present in the database")
if (numbersources>0):
output = """&nbsp;&nbsp;&nbsp;&nbsp;<strong><span class="info">%s %s</span></strong><br /><br />""" % (numbersources, present)
return output
else:
output = """&nbsp;&nbsp;&nbsp;&nbsp;<strong><span class="warning">%s</span></strong><br /><br />""" % notpresent
return output
def tmpl_output_schedule(self, ln, schtime, schstatus):
_ = gettext_set_language(ln)
msg_next = _("Next oaiharvest task")
msg_sched = _("scheduled time:")
msg_cur = _("current status:")
msg_notask = _("No oaiharvest task currently scheduled.")
if schtime and schstatus:
output = """&nbsp;&nbsp;&nbsp;&nbsp;<strong>%s<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - %s %s <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - %s %s </strong><br /><br />""" % (msg_next, msg_sched, schtime, msg_cur, schstatus)
return output
else:
output = """&nbsp;&nbsp;&nbsp;&nbsp;<strong><span class="warning">%s</span></strong><br /><br />""" % msg_notask
return output
def tmpl_admin_w200_text(self, ln, title, name, value):
"""Draws an html w200 text box
- 'title' *string* - The name of the textbox
- 'name' *string* - The name of the value in the textbox
- 'value' *string* - The value in the textbox"""
_ = gettext_set_language(ln)
text = """<span class="adminlabel">%s""" % title
text += """</span><input class="admin_w200" type="text" name="%s" value="%s" /><br />""" % (cgi.escape(name,1), cgi.escape(value, 1))
return text
def tmpl_admin_checkboxes(self, ln, title, name, values, labels, states):
"""Draws a list of HTML checkboxes
- 'title' *string* - The name of the list of checkboxes
- 'name' *string* - The name for this group of checkboxes
- 'values' *list* - The values of the checkboxes
- 'labels' *list* - The labels for the checkboxes
- 'states' *list* - The previous state of each checkbox 1|0
len(values) == len(labels) == len(states)
"""
_ = gettext_set_language(ln)
text = """<div><div style="float:left;"><span class="adminlabel">%s</span></div>""" % title
text += """<table><tr><td>"""
text += '&nbsp;&nbsp; <small><i>(Leave all unchecked for non-selective' \
' harvesting)</i></small><br/>'
for i in range(len(values)):
value = values[i]
label = labels[i]
state = states[i]
chk_box_id = value + str(i)
text += '&nbsp;&nbsp; <input type="checkbox"' \
'name="%s" id="%s" value="%s" ' % (name, chk_box_id, value)
if state:
text += 'checked="checked "'
text += "/>"
text += """<label for="%s">%s</label><br/>""" % (chk_box_id, label)
text += "</td></tr></table></div>"
return text
def tmpl_admin_w200_select(self, ln, title, name, valuenil, values, lastval=""):
"""Draws an html w200 drop-down box
- 'title' *string* - The name of the dd box
- 'name' *string* - The name of the value in the dd box
- 'value' *list* - The values in the textbox"""
_ = gettext_set_language(ln)
text = """<span class="adminlabel">%s""" % title
text += """</span><select name="%s" class="admin_w200">""" % name
text += """<option value="">%s</option>""" % valuenil
try:
for val in values:
intval = int(lastval)
if intval==int(val[0]): ## retrieve and display last value inputted into drop-down box
text += """<option value="%s" %s>%s</option>""" % (val[0], 'selected="selected"', str(val[1]))
else:
text += """<option value="%s">%s</option>""" % (val[0], str(val[1]))
text += """</select><br />"""
except StandardError, e:
for val in values:
if lastval==val[0]:
text += """<option value="%s" %s>%s</option>""" % (val[0], 'selected="selected"', str(val[1]))
else:
text += """<option value="%s">%s</option>""" % (val[0], str(val[1]))
text += """</select><br />"""
return text
def tmpl_print_info(self, ln, infotext):
"""Outputs some info"""
_ = gettext_set_language(ln)
text = """<br /><b><span class="info">%s</span></b>""" % infotext
return text
def tmpl_print_warning(self, ln, warntext):
"""Outputs some info"""
_ = gettext_set_language(ln)
text = """<span class="warning">%s</span>""" % warntext
return text
def tmpl_print_brs(self, ln, howmany):
"""Outputs some <br />s"""
_ = gettext_set_language(ln)
text = ""
while howmany>0:
text += """<br />"""
howmany = howmany - 1
return text
def tmpl_output_validate_info(self, ln, outcome, base):
"""Prints a message to say whether source was validated or not
- 'outcome' *int* - 0=success, 1=fail
- 'base' *string* - baseurl"""
_ = gettext_set_language(ln)
msg_success = _("successfully validated")
msg_nosuccess = _("does not seem to be a OAI-compliant baseURL")
if outcome==0:
output = """<br /><span class="info">baseURL <strong>%s</strong> %s</span>""" % (base, msg_success)
return output
else:
output = """<br /><span class="info">baseURL <strong>%s</strong> %s</span>""" % (base, msg_nosuccess)
return output
def tmpl_output_error_info(self, ln, base, error):
"""Prints a http error message"""
_ = gettext_set_language(ln)
msg_error = "returns the following HTTP error: "
output = """<br /><span class="info">baseURL <strong>%s</strong> %s</span><br /><blockquote>%s</blockquote>""" % (base, msg_error, error)
return output
diff --git a/modules/bibharvest/lib/bibharvestadmin_regression_tests.py b/modules/bibharvest/lib/bibharvestadmin_regression_tests.py
index c8edff637..e120b12cc 100644
--- a/modules/bibharvest/lib/bibharvestadmin_regression_tests.py
+++ b/modules/bibharvest/lib/bibharvestadmin_regression_tests.py
@@ -1,69 +1,69 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibHarvest Admin Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class BibHarvestAdminWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibHarvest Admin web pages whether they are up or not."""
def test_bibharvest_admin_interface_pages_availability(self):
"""bibharvestadmin - availability of BibHarvest Admin interface pages"""
- baseurl = weburl + '/admin/bibharvest/bibharvestadmin.py/'
+ baseurl = CFG_SITE_URL + '/admin/bibharvest/bibharvestadmin.py/'
_exports = ['', 'editsource', 'addsource', 'delsource']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_bibharvest_admin_guide_availability(self):
"""bibharvestadmin - availability of BibHarvest Admin guide pages"""
- url = weburl + '/help/admin/bibharvest-admin-guide'
+ url = CFG_SITE_URL + '/help/admin/bibharvest-admin-guide'
error_messages = test_web_page_content(url,
expected_text="BibHarvest Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(BibHarvestAdminWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibharvest/lib/bibharvestadminlib.py b/modules/bibharvest/lib/bibharvestadminlib.py
index 2117cb7ab..5d2e0684f 100644
--- a/modules/bibharvest/lib/bibharvestadminlib.py
+++ b/modules/bibharvest/lib/bibharvestadminlib.py
@@ -1,619 +1,618 @@
## $Id$
## Administrator interface for BibIndex
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio Bibharvest Administrator Interface."""
__revision__ = "$Id$"
import re
import os, sys, string
import ConfigParser
import time
import random
import urllib
from httplib import InvalidURL
from invenio.config import \
CFG_SITE_LANG, \
CFG_TMPDIR, \
CFG_VERSION, \
- weburl,\
+ CFG_SITE_URL,\
CFG_ETCDIR
from invenio.bibrankadminlib import \
write_outcome,modify_translations,\
get_def_name,\
get_i8n_name,\
get_name,\
get_rnk_nametypes,\
get_languages,\
check_user,\
is_adminuser,\
addadminbox,\
tupletotable,\
tupletotable_onlyselected,\
addcheckboxes,\
createhiddenform
from invenio.dbquery import run_sql
from invenio.webpage import page, pageheaderonly, pagefooteronly, adderrorbox
from invenio.webuser import getUid, get_email
import invenio.template
bibharvest_templates = invenio.template.load('bibharvest')
tmppath = CFG_TMPDIR + '/bibharvestadmin.' + str(os.getpid())
guideurl = "help/admin/bibharvest-admin-guide"
freqs = [[0, "never"], [24, "daily"], [168, "weekly"], [720, "monthly"] ]
posts = [["h", "harvest only (h)"], ["h-c", "harvest and convert (h-c)"], ["h-u", "harvest and upload (h-u)"], ["h-c-u", "harvest, convert and upload (h-c-u)"], ["h-c-f-u", "harvest, convert, filter, upload (h-c-f-u)"]]
dates = [[0, "from beginning"], [1, "from today"]]
def getnavtrail(previous = ''):
"""Get the navtrail"""
return bibharvest_templates.tmpl_getnavtrail(previous = previous, ln = CFG_SITE_LANG)
def perform_request_index(ln=CFG_SITE_LANG):
"""start area for administering harvesting from OAI repositories"""
- titlebar = bibharvest_templates.tmpl_draw_titlebar(ln = CFG_SITE_LANG, weburl = weburl, title = "Overview of sources", guideurl = guideurl, extraname = "add new OAI source" , extraurl = "admin/bibharvest/bibharvestadmin.py/addsource" )
- titlebar2 = bibharvest_templates.tmpl_draw_titlebar(ln = CFG_SITE_LANG, weburl = weburl, title = "Harvesting status", guideurl = guideurl)
+ titlebar = bibharvest_templates.tmpl_draw_titlebar(ln = CFG_SITE_LANG, title = "Overview of sources", guideurl = guideurl, extraname = "add new OAI source" , extraurl = "admin/bibharvest/bibharvestadmin.py/addsource" )
+ titlebar2 = bibharvest_templates.tmpl_draw_titlebar(ln = CFG_SITE_LANG, title = "Harvesting status", guideurl = guideurl)
header = ['name', 'baseURL', 'metadataprefix', 'frequency', 'bibconvertfile', 'postprocess', 'actions']
header2 = ['name', 'last update']
oai_src = get_oai_src()
upd_status = get_update_status()
sources = []
for (oai_src_id,oai_src_name,oai_src_baseurl,oai_src_prefix,oai_src_frequency,oai_src_config,oai_src_post,oai_src_bibfilter,oai_src_setspecs) in oai_src:
namelinked_args = []
namelinked_args.append(["oai_src_id", str(oai_src_id)])
namelinked_args.append(["ln", ln])
- namelinked = bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/editsource", title = oai_src_name, args = namelinked_args)
+ namelinked = bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/editsource", title = oai_src_name, args = namelinked_args)
freq = "Not Set"
if oai_src_frequency==0: freq = "never"
elif oai_src_frequency==24: freq = "daily"
elif oai_src_frequency==168: freq = "weekly"
elif oai_src_frequency==720: freq = "monthly"
- editACTION = bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/editsource", title = "edit", args = namelinked_args)
- delACTION = bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/delsource", title = "delete", args = namelinked_args)
+ editACTION = bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/editsource", title = "edit", args = namelinked_args)
+ delACTION = bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/delsource", title = "delete", args = namelinked_args)
action = editACTION + " / " + delACTION
sources.append([namelinked,oai_src_baseurl,oai_src_prefix,freq,oai_src_config,oai_src_post, action])
updates = []
for (upd_name, upd_status) in upd_status:
if not upd_status:
upd_status = bibharvest_templates.tmpl_print_warning(CFG_SITE_LANG, "Never harvested")
else: #cut away leading zeros
upd_status = re.sub(r'\.[0-9]+$', '', str(upd_status))
updates.append([upd_name, upd_status])
(schtime, schstatus) = get_next_schedule()
if schtime:
schtime = re.sub(r'\.[0-9]+$', '', str(schtime))
output = titlebar
output += bibharvest_templates.tmpl_output_numbersources(CFG_SITE_LANG, get_tot_oai_src())
output += tupletotable(header=header, tuple=sources)
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
output += titlebar2
output += bibharvest_templates.tmpl_output_schedule(CFG_SITE_LANG, schtime, str(schstatus))
output += tupletotable(header=header2, tuple=updates)
return output
def perform_request_editsource(oai_src_id=None, oai_src_name='', oai_src_baseurl='', oai_src_prefix='', oai_src_frequency='', oai_src_config='', oai_src_post='',ln=CFG_SITE_LANG, confirm=-1, oai_src_sets=[], oai_src_bibfilter=''):
"""creates html form to edit a OAI source. this method is calling other methods which again is calling this and sending back the output of the method.
confirm - determines the validation status of the data input into the form"""
if oai_src_id is None:
return "No OAI source ID selected."
output = ""
- subtitle = bibharvest_templates.tmpl_draw_subtitle(ln = CFG_SITE_LANG, weburl = weburl, title = "edit source", subtitle = "Edit OAI source", guideurl = guideurl)
+ subtitle = bibharvest_templates.tmpl_draw_subtitle(ln = CFG_SITE_LANG, title = "edit source", subtitle = "Edit OAI source", guideurl = guideurl)
if confirm in [-1, "-1"]:
oai_src = get_oai_src(oai_src_id)
oai_src_name = oai_src[0][1]
oai_src_baseurl = oai_src[0][2]
oai_src_prefix = oai_src[0][3]
oai_src_frequency = oai_src[0][4]
oai_src_config = oai_src[0][5]
oai_src_post = oai_src[0][6]
oai_src_sets = oai_src[0][7].split()
oai_src_bibfilter = oai_src[0][8]
text = bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG, title = "Source name", name = "oai_src_name", value = oai_src_name)
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG, title = "Base URL", name = "oai_src_baseurl", value = oai_src_baseurl)
sets = findSets(oai_src_baseurl)
if sets:
# Show available sets to users
sets_specs = [set[0] for set in sets]
sets_names = [set[1] for set in sets]
sets_labels = [((set[1] and set[0]+' ('+set[1]+')') or set[0]) \
for set in sets]
sets_states = [ ((set[0] in oai_src_sets and 1) or 0) for set in sets]
text += bibharvest_templates.tmpl_admin_checkboxes(ln=CFG_SITE_LANG,
title="Sets",
name="oai_src_sets",
values=sets_specs,
labels=sets_labels,
states=sets_states)
else:
# Let user specify sets in free textbox
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG,
title = "Sets",
name='oai_src_sets',
value=' '.join(oai_src_sets))
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG, title = "Metadata prefix", name = "oai_src_prefix", value = oai_src_prefix)
text += bibharvest_templates.tmpl_admin_w200_select(ln = CFG_SITE_LANG, title = "Frequency", name = "oai_src_frequency", valuenil = "- select frequency -" , values = freqs, lastval = oai_src_frequency)
text += bibharvest_templates.tmpl_admin_w200_select(ln = CFG_SITE_LANG, title = "Postprocess", name = "oai_src_post", valuenil = "- select mode -" , values = posts, lastval = oai_src_post)
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG, title = "BibConvert configuration file (if needed by postprocess)", name = "oai_src_config", value = oai_src_config)
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG, title = "BibFilter program (if needed by postprocess)", name = "oai_src_bibfilter", value = oai_src_bibfilter)
text += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
output += createhiddenform(action="editsource#1",
text=text,
button="Modify",
oai_src_id=oai_src_id,
ln=ln,
confirm=1)
if confirm in [1, "1"] and not oai_src_name:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please enter a name for the source.")
elif confirm in [1, "1"] and not oai_src_prefix:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please enter a metadata prefix.")
elif confirm in [1, "1"] and not oai_src_baseurl:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please enter a base url.")
elif confirm in [1, "1"] and not oai_src_frequency:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please choose a frequency of harvesting")
elif confirm in [1, "1"] and not oai_src_post:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please choose a postprocess mode")
elif confirm in [1, "1"] and oai_src_post.startswith("h-c") and (not oai_src_config or validatefile(oai_src_config)!=0):
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "You selected a postprocess mode which involves conversion.")
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please enter a valid name of or a full path to a BibConvert config file or change postprocess mode.")
elif oai_src_id > -1 and confirm in [1, "1"]:
if not oai_src_frequency:
oai_src_frequency = 0
if not oai_src_config:
oai_src_config = "NULL"
if not oai_src_post:
oai_src_post = "h"
res = modify_oai_src(oai_src_id, oai_src_name, oai_src_baseurl, oai_src_prefix, oai_src_frequency, oai_src_config, oai_src_post, oai_src_sets, oai_src_bibfilter)
output += write_outcome(res)
lnargs = [["ln", ln]]
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs )
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs )
body = [output]
return addadminbox(subtitle, body)
def perform_request_addsource(oai_src_name=None, oai_src_baseurl='', oai_src_prefix='', oai_src_frequency='', oai_src_lastrun='', oai_src_config='', oai_src_post='', ln=CFG_SITE_LANG, confirm=-1, oai_src_sets=[], oai_src_bibfilter=''):
"""creates html form to add a new source"""
if oai_src_name is None:
return "No OAI source name selected."
subtitle = bibharvest_templates.tmpl_draw_subtitle(ln=CFG_SITE_LANG,
- weburl=weburl,
title="add source",
subtitle="Add new OAI source",
guideurl=guideurl)
output = ""
if confirm <= -1:
text = bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
text += bibharvest_templates.tmpl_admin_w200_text(ln=CFG_SITE_LANG,
title="Enter the base url",
name="oai_src_baseurl",
value=oai_src_baseurl+'http://')
output = createhiddenform(action="addsource",
text=text,
ln=ln,
button="Validate",
confirm=0)
if (confirm not in ["-1", -1] and validate(oai_src_baseurl)[0] == 0) or \
confirm in ["1", 1]:
output += bibharvest_templates.tmpl_output_validate_info(CFG_SITE_LANG, 0, str(oai_src_baseurl))
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
text = bibharvest_templates.tmpl_admin_w200_text(ln=CFG_SITE_LANG,
title="Source name",
name="oai_src_name",
value=oai_src_name)
metadatas = findMetadataFormats(oai_src_baseurl)
if metadatas:
# Show available metadata to user
prefixes = []
for value in metadatas:
prefixes.append([value, str(value)])
text += bibharvest_templates.tmpl_admin_w200_select(ln=CFG_SITE_LANG,
title="Metadata prefix",
name="oai_src_prefix",
valuenil="- select prefix -" ,
values=prefixes,
lastval=oai_src_prefix)
else:
# Let user specify prefix in free textbox
text += bibharvest_templates.tmpl_admin_w200_text(ln=CFG_SITE_LANG,
title="Metadata prefix",
name="oai_src_prefix",
value=oai_src_prefix)
sets = findSets(oai_src_baseurl)
if sets:
# Show available sets to users
sets_specs = [set[0] for set in sets]
sets_names = [set[1] for set in sets]
sets_labels = [((set[1] and set[0]+' ('+set[1]+')') or set[0]) \
for set in sets]
sets_states = [ ((set[0] in oai_src_sets and 1) or 0) \
for set in sets]
text += bibharvest_templates.tmpl_admin_checkboxes(ln=CFG_SITE_LANG,
title="Sets",
name="oai_src_sets",
values=sets_specs,
labels=sets_labels,
states=sets_states)
else:
# Let user specify sets in free textbox
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG,
title = "Sets",
name='oai_src_sets',
value=' '.join(oai_src_sets))
text += bibharvest_templates.tmpl_admin_w200_select(ln = CFG_SITE_LANG, title = "Frequency", name = "oai_src_frequency", valuenil = "- select frequency -" , values = freqs, lastval = oai_src_frequency)
text += bibharvest_templates.tmpl_admin_w200_select(ln = CFG_SITE_LANG, title = "Starting date", name = "oai_src_lastrun", valuenil = "- select a date -" , values = dates, lastval = oai_src_lastrun)
text += bibharvest_templates.tmpl_admin_w200_select(ln = CFG_SITE_LANG, title = "Postprocess", name = "oai_src_post", valuenil = "- select mode -" , values = posts, lastval = oai_src_post)
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG, title = "BibConvert configuration file (if needed by postprocess)", name = "oai_src_config", value = oai_src_config)
text += bibharvest_templates.tmpl_admin_w200_text(ln = CFG_SITE_LANG, title = "BibFilter program (if needed by postprocess)", name = "oai_src_bibfilter", value = oai_src_bibfilter)
text += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
output += createhiddenform(action="addsource#1",
text=text,
button="Add OAI Source",
oai_src_baseurl=oai_src_baseurl,
ln=ln,
confirm=1)
elif confirm in ["0", 0] and validate(oai_src_baseurl)[0] > 0:
# Could not perform first url validation
lnargs = [["ln", ln]]
output += bibharvest_templates.tmpl_output_validate_info(CFG_SITE_LANG, 1, str(oai_src_baseurl))
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/addsource", title = "Try again with another url", args = [])
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/addsource", title = "Try again with another url", args = [])
output += """ or """
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/addsource", title = "Continue anyway", args = [['oai_src_baseurl', urllib.urlencode({'':oai_src_baseurl})[1:]], ['confirm', '1']])
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/addsource", title = "Continue anyway", args = [['oai_src_baseurl', urllib.urlencode({'':oai_src_baseurl})[1:]], ['confirm', '1']])
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
output += """or"""
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs)
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs)
elif confirm not in ["-1", -1] and validate(oai_src_baseurl)[0] > 0:
lnargs = [["ln", ln]]
output += bibharvest_templates.tmpl_output_validate_info(CFG_SITE_LANG, 1, str(oai_src_baseurl))
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/addsource", title = "Try again", args = [])
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/addsource", title = "Try again", args = [])
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
output += """or"""
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs)
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs)
elif confirm not in ["-1", -1]:
lnargs = [["ln", ln]]
output += bibharvest_templates.tmpl_output_error_info(CFG_SITE_LANG, str(oai_src_baseurl), validate(oai_src_baseurl)[1])
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/addsource", title = "Try again", args = [])
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/addsource", title = "Try again", args = [])
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
output += """or"""
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs)
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs)
if confirm in [1, "1"] and not oai_src_name:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please enter a name for the source.")
if confirm in [1, "1"] and not oai_src_prefix:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please enter a metadata prefix.")
if confirm in [1, "1"] and not oai_src_frequency:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please choose a frequency of harvesting")
if confirm in [1, "1"] and not oai_src_lastrun:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please choose the harvesting starting date")
if confirm in [1, "1"] and not oai_src_post:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please choose a postprocess mode")
if confirm in [1, "1"] and oai_src_post.startswith("h-c") and (not oai_src_config or validatefile(oai_src_config)!=0):
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "You selected a postprocess mode which involves conversion.")
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Please enter a valid name of or a full path to a BibConvert config file or change postprocess mode.")
elif oai_src_name and confirm in [1, "1"]:
if not oai_src_frequency:
oai_src_frequency = 0
if not oai_src_lastrun:
oai_src_lastrun = 1
if not oai_src_config:
oai_src_config = "NULL"
if not oai_src_post:
oai_src_post = "h"
res = add_oai_src(oai_src_name, oai_src_baseurl, oai_src_prefix, oai_src_frequency, oai_src_lastrun, oai_src_config, oai_src_post, oai_src_sets, oai_src_bibfilter)
output += write_outcome(res)
lnargs = [["ln", ln]]
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs )
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs )
body = [output]
return addadminbox(subtitle, body)
def perform_request_delsource(oai_src_id=None, ln=CFG_SITE_LANG, callback='yes', confirm=0):
"""creates html form to delete a source
"""
output = ""
subtitle = ""
if oai_src_id:
oai_src = get_oai_src(oai_src_id)
namesrc = (oai_src[0][1])
pagetitle = """Delete OAI source: %s""" % namesrc
- subtitle = bibharvest_templates.tmpl_draw_subtitle(ln = CFG_SITE_LANG, weburl = weburl, title = "delete source", subtitle = pagetitle, guideurl = guideurl)
+ subtitle = bibharvest_templates.tmpl_draw_subtitle(ln = CFG_SITE_LANG, title = "delete source", subtitle = pagetitle, guideurl = guideurl)
output = ""
if confirm in ["0", 0]:
if oai_src:
question = """Do you want to delete the OAI source '%s' and all its definitions?""" % namesrc
text = bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, question)
text += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 3)
output += createhiddenform(action="delsource#5",
text=text,
button="Confirm",
oai_src_id=oai_src_id,
confirm=1)
else:
return bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Source specified does not exist.")
elif confirm in ["1", 1]:
res = delete_oai_src(oai_src_id)
if res[0] == 1:
output += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "Source removed.")
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 1)
output += write_outcome(res)
else:
output += write_outcome(res)
lnargs = [["ln", ln]]
output += bibharvest_templates.tmpl_print_brs(CFG_SITE_LANG, 2)
- output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs )
+ output += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/bibharvestadmin.py/index", title = "Go back to the OAI sources overview", args = lnargs )
body = [output]
return addadminbox(subtitle, body)
##################################################################
### Here the functions to retrieve, modify, delete and add sources
##################################################################
def get_oai_src(oai_src_id=''):
"""Returns a row parameters for a given id"""
sql = "SELECT id,name,baseurl,metadataprefix,frequency,bibconvertcfgfile,postprocess,setspecs,bibfilterprogram FROM oaiHARVEST"
try:
if oai_src_id:
sql += " WHERE id=%s" % oai_src_id
sql += " ORDER BY id asc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def modify_oai_src(oai_src_id, oai_src_name, oai_src_baseurl, oai_src_prefix, oai_src_frequency, oai_src_config, oai_src_post, oai_src_sets=[], oai_src_bibfilter=''):
"""Modifies a row's parameters"""
try:
res = run_sql("UPDATE oaiHARVEST SET name=%s WHERE id=%s", (oai_src_name, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET baseurl=%s WHERE id=%s", (oai_src_baseurl, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET metadataprefix=%s WHERE id=%s", (oai_src_prefix, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET frequency=%s WHERE id=%s", (oai_src_frequency, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET bibconvertcfgfile=%s WHERE id=%s", (oai_src_config, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET postprocess=%s WHERE id=%s", (oai_src_post, oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET setspecs=%s WHERE id=%s", (' '.join(oai_src_sets), oai_src_id))
res = run_sql("UPDATE oaiHARVEST SET bibfilterprogram=%s WHERE id=%s", (oai_src_bibfilter, oai_src_id))
return (1, "")
except StandardError, e:
return (0, e)
def add_oai_src(oai_src_name, oai_src_baseurl, oai_src_prefix, oai_src_frequency, oai_src_lastrun, oai_src_config, oai_src_post, oai_src_sets=[], oai_src_bibfilter=''):
"""Adds a new row to the database with the given parameters"""
try:
if oai_src_lastrun in [0, "0"]: lastrun_mode = 'NULL'
else:
lastrun_mode = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
# lastrun_mode = "'"+lastrun_mode+"'"
sql = "INSERT INTO oaiHARVEST (id, baseurl, metadataprefix, arguments, comment, bibconvertcfgfile, name, lastrun, frequency, postprocess, bibfilterprogram, setspecs) VALUES (0, %s, %s, NULL, NULL, %s, %s, %s, %s, %s, %s, %s)", (oai_src_baseurl, oai_src_prefix, oai_src_config, oai_src_name, lastrun_mode, oai_src_frequency, oai_src_post, oai_src_bibfilter, " ".join(oai_src_sets))
res = run_sql(sql)
return (1, "")
except StandardError, e:
return (0, e)
def delete_oai_src(oai_src_id):
"""Deletes a row from the database according to its id"""
try:
res = run_sql("DELETE FROM oaiHARVEST WHERE id=%s" % oai_src_id)
return (1, "")
except StandardError, e:
return (0, e)
def get_tot_oai_src():
"""Returns number of rows in the database"""
try:
sql = "SELECT COUNT(*) FROM oaiHARVEST"
res = run_sql(sql)
return res[0][0]
except StandardError, e:
return ""
def get_update_status():
"""Returns a table showing a list of all rows and their LastUpdate status"""
try:
sql = "SELECT name,lastrun FROM oaiHARVEST ORDER BY lastrun desc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_next_schedule():
"""Returns the next scheduled oaiharvestrun tasks"""
try:
sql = "SELECT runtime,status FROM schTASK WHERE proc='oaiharvest' AND runtime > now() ORDER by runtime LIMIT 1"
res = run_sql(sql)
if len(res)>0:
return res[0]
else:
return ("", "")
except StandardError, e:
return ("","")
def validate(oai_src_baseurl):
"""This function validates a baseURL by opening its URL and 'greping' for the <OAI-PMH> and <Identify> tags:
Codes:
0 = okay
1 = baseURL not valid
2 = baseURL not found/not existing
3 = tmp directoy is not writable
4 = Unknown error
Returns tuple (code, message)
"""
try:
url = oai_src_baseurl + "?verb=Identify"
urllib.urlretrieve(url, tmppath)
# First check if we have xml oai-pmh output
grepOUT1 = os.popen('grep -iwc "<OAI-PMH" '+tmppath).read()
if int(grepOUT1) == 0:
# No.. we have an http error
return (4, os.popen('cat '+tmppath).read())
grepOUT2 = os.popen('grep -iwc "<identify>" '+tmppath).read()
if int(grepOUT2) > 0:
#print "Valid!"
return (0, '')
else:
#print "Not valid!"
return (1, '')
except IOError, (errno, strerror):
# Quick error handling for frequent error codes.
if errno == 13:
return (3, "Please check permission on %s and retry" % CFG_TMPDIR)
elif errno == 2 or errno == 'socket error':
return (2, "Could not connect with URL %s. Check URL or retry when server is available." % url)
return (4, strerror)
except StandardError, e:
return (4, "An unknown error has occured")
except InvalidURL, e:
return (2, "Could not connect with URL %s. Check URL or retry when server is available." % url)
def validatefile(oai_src_config):
"""This function checks whether the given path to text file exists or not
0 = okay
1 = file non existing
"""
CFG_BIBCONVERT_XSL_PATH = "%s%sbibconvert%sconfig" % (CFG_ETCDIR,
os.sep,
os.sep)
path_to_config = (CFG_BIBCONVERT_XSL_PATH + os.sep +
oai_src_config)
if os.path.exists(path_to_config):
# Try to read in config directory
try:
ftmp = open(path_to_config, 'r')
cfgstr= ftmp.read()
ftmp.close()
if cfgstr!="":
#print "Valid!"
return 0
except StandardError, e:
pass
# Try to read as complete path
try:
ftmp = open(oai_src_config, 'r')
cfgstr= ftmp.read()
ftmp.close()
if cfgstr!="":
#print "Valid!"
return 0
else:
#print "Not valid!"
return 1
except StandardError, e:
return 1
return 1
def findMetadataFormats(oai_src_baseurl):
"""This function finds the Metadata formats offered by a OAI repository by analysing the output of verb=ListMetadataFormats"""
formats = []
url = oai_src_baseurl + "?verb=ListMetadataFormats"
try:
urllib.urlretrieve(url, tmppath)
except IOError:
return formats
ftmp = open(tmppath, 'r')
xmlstr= ftmp.read()
ftmp.close()
chunks = xmlstr.split('<metadataPrefix>')
count = 0 # first chunk is invalid
for chunk in chunks:
if count!=0:
formats.append(chunk.split('</metadataPrefix>')[0])
count = count + 1
return formats
def findSets(oai_src_baseurl):
"""This function finds the sets offered by a OAI repository
by analysing the output of verb=ListSets.
Returns list of tuples(SetSpec, SetName)"""
url = oai_src_baseurl + "?verb=ListSets"
sets = {}
try:
urllib.urlretrieve(url, tmppath)
except IOError:
return sets
ftmp = open(tmppath, 'r')
xmlstr= ftmp.read()
ftmp.close()
chunks = xmlstr.split('<set>')
count = 0 # first chunk is invalid
for chunk in chunks:
if count!=0:
chunks_set = chunk.split('<setSpec>')[1].split("</setSpec>")
set_spec = chunks_set[0]
#chunks_set[1].split('<setName>')
check_set_2 = chunks_set[1].split("<setName>")
set_name = None
if len(check_set_2) > 1:
set_name = check_set_2[1].split("</setName>")[0]
sets[set_spec] = [set_spec, set_name]
count = count + 1
return sets.values()
diff --git a/modules/bibharvest/lib/oai_repository.py b/modules/bibharvest/lib/oai_repository.py
index f3d5573fa..779ead452 100644
--- a/modules/bibharvest/lib/oai_repository.py
+++ b/modules/bibharvest/lib/oai_repository.py
@@ -1,987 +1,987 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""OAI interface for CDS Invenio written in Python compliant with OAI-PMH2.0"""
__revision__ = "$Id$"
import cPickle
import os
import re
import urllib
import time
import md5
from invenio.config import \
CFG_OAI_DELETED_POLICY, \
CFG_OAI_EXPIRE, \
CFG_OAI_IDENTIFY_DESCRIPTION, \
CFG_OAI_ID_FIELD, \
CFG_OAI_ID_PREFIX, \
CFG_OAI_LOAD, \
CFG_OAI_SAMPLE_IDENTIFIER, \
CFG_OAI_SET_FIELD, \
CFG_CACHEDIR, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
- weburl
+ CFG_SITE_URL
from invenio.dbquery import run_sql
from invenio.search_engine import record_exists, perform_request_search
from invenio.bibformat_dblayer import get_preformatted_record
from invenio.bibformat import format_record
## verbs = {
## "Identify" : [""],
## "ListSets" : ["resumptionToken"],
## "ListMetadataFormats" : ["resumptionToken"],
## "ListRecords" : ["resumptionToken"],
## "ListIdentifiers" : ["resumptionToken"],
## "GetRecord" : [""]
## }
verbs = {
'GetRecord' : ['identifier', 'metadataPrefix'],
'Identify' : [],
'ListIdentifiers' : ['from', 'until',
'metadataPrefix',
'set',
'resumptionToken'],
'ListMetadataFormats': ['identifier'],
'ListRecords' : ['from', 'until',
'metadataPrefix',
'set',
'resumptionToken'],
'ListSets' : ['resumptionToken']
}
params = {
"verb" : ["Identify","ListIdentifiers","ListSets","ListMetadataFormats","ListRecords","GetRecord"],
"metadataPrefix" : ["oai_dc","marcxml"],
"from" :[""],
"until":[""],
"set" :[""],
"identifier": [""]
}
def encode_for_xml(strxml):
"Encode special chars in string for XML-compliancy."
if strxml is None:
return strxml
else:
strxml = strxml.replace('&', '&amp;')
strxml = strxml.replace('<', '&lt;')
return strxml
def escape_space(strxml):
"Encode special chars in string for URL-compliancy."
strxml = strxml.replace(' ', '%20')
return strxml
def encode_for_url(strxml):
"Encode special chars in string for URL-compliancy."
strxml = strxml.replace('%', '%25')
strxml = strxml.replace(' ', '%20')
strxml = strxml.replace('?', '%3F')
strxml = strxml.replace('#', '%23')
strxml = strxml.replace('=', '%3D')
strxml = strxml.replace('&', '%26')
strxml = strxml.replace('/', '%2F')
strxml = strxml.replace(':', '%3A')
strxml = strxml.replace(';', '%3B')
strxml = strxml.replace('+', '%2B')
return strxml
def oai_header(args, verb):
"Print OAI header"
out = ""
out = out + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n"
out = out + "<OAI-PMH xmlns=\"http://www.openarchives.org/OAI/2.0/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd\">\n"
out = out + " <responseDate>" + oaigetresponsedate() + "</responseDate>\n"
if verb:
out = out + " <request verb=\"%s\">%s</request>\n" % (verb, oaigetrequesturl(args))
out = out + " <%s>\n" % verb
else:
out = out + " <request>%s</request>\n" % (oaigetrequesturl(args))
return out
def oai_footer(verb):
"Print OAI footer"
out = ""
if verb:
out = "%s </%s>\n" % (out, verb)
out = out + "</OAI-PMH>\n"
return out
def oai_error_header(args, verb):
"Print OAI header"
out = ""
### out = "Content-Type: text/xml\n\n"
out = out + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n"
out = out + "<OAI-PMH xmlns=\"http://www.openarchives.org/OAI/2.0/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd\">\n"
out = out + " <responseDate>" + oaigetresponsedate() + "</responseDate>\n"
out = out + " <request verb=\"%s\">%s</request>\n" % (verb, oaigetrequesturl(args))
return out
def oai_error_footer(verb):
"Print OAI footer"
out = verb
out = "</OAI-PMH>\n"
return out
def get_field(sysno, field):
"Gets list of field 'field' for the record with 'sysno' system number."
out = []
digit = field[0:2]
bibbx = "bib%sx" % digit
bibx = "bibrec_bib%sx" % digit
query = "SELECT bx.value FROM %s AS bx, %s AS bibx WHERE bibx.id_bibrec='%s' AND bx.id=bibx.id_bibxxx AND bx.tag='%s'" % (bibbx, bibx, sysno, field)
res = run_sql(query)
for row in res:
out.append(row[0])
return out
def utc_to_localtime(date):
"""
Convert UTC to localtime
Reference:
- (1) http://www.openarchives.org/OAI/openarchivesprotocol.html#Dates
- (2) http://www.w3.org/TR/NOTE-datetime
This function works only with dates complying with the
"Complete date plus hours, minutes and seconds" profile of
ISO 8601 defined by (2), and linked from (1).
Eg: 1994-11-05T13:15:30Z
"""
ldate = date.split("T")[0]
ltime = date.split("T")[1]
lhour = ltime.split(":")[0]
lminute = ltime.split(":")[1]
lsec = ltime.split(":")[2]
lsec = lsec[:-1] # Remove trailing "Z"
lyear = ldate.split("-")[0]
lmonth = ldate.split("-")[1]
lday = ldate.split("-")[2]
# 1: Build a time as UTC. Since time.mktime() expect a local time :
## 1a: build it without knownledge of dst
## 1b: substract timezone to get a local time, with possibly wrong dst
utc_time = time.mktime((int(lyear), int(lmonth), int(lday), int(lhour), int(lminute), int(lsec), 0, 0, -1))
local_time = utc_time - time.timezone
# 2: Fix dst for local_time
# Find out the offset for daily saving time of the local
# timezone at the time of the given 'date'
if time.localtime(local_time)[-1] == 1:
local_time = local_time + 3600
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(local_time))
def localtime_to_utc(date):
"Convert localtime to UTC"
ldate = date.split(" ")[0]
ltime = date.split(" ")[1]
lhour = ltime.split(":")[0]
lminute = ltime.split(":")[1]
lsec = ltime.split(":")[2]
lyear = ldate.split("-")[0]
lmonth = ldate.split("-")[1]
lday = ldate.split("-")[2]
# Find out the offset for daily saving time of the local
# timezone at the time of the given 'date'
#
# 1: build time that correspond to local date, without knowledge of dst
# 2: determine if dst is locally enabled at this time
tmp_date = time.mktime((int(lyear), int(lmonth), int(lday), int(lhour), int(lminute), int(lsec), 0, 0, -1))
if time.localtime(tmp_date)[-1] == 1:
dst = time.localtime(tmp_date)[-1]
else:
dst = 0
# 3: Build a new time with knowledge of the dst
local_time = time.mktime((int(lyear), int(lmonth), int(lday), int(lhour), int(lminute), int(lsec), 0, 0, dst))
# 4: Get the time as UTC
utc_time = time.gmtime(local_time)
return time.strftime("%Y-%m-%dT%H:%M:%SZ", utc_time)
def get_modification_date(sysno):
"Returns the date of last modification for the record 'sysno'."
out = ""
res = run_sql("SELECT DATE_FORMAT(modification_date,'%%Y-%%m-%%d %%H:%%i:%%s') FROM bibrec WHERE id=%s", (sysno,), 1)
if res and res[0][0]:
out = localtime_to_utc(res[0][0])
return out
def get_earliest_datestamp():
"Get earliest datestamp in the database"
out = ""
res = run_sql("SELECT MIN(DATE_FORMAT(creation_date,'%%Y-%%m-%%d %%H:%%i:%%s')) FROM bibrec", (), 1)
if res[0][0]:
out = localtime_to_utc(res[0][0])
return out
def get_latest_datestamp():
"Get latest datestamp in the database"
out = ""
res = run_sql("SELECT MAX(DATE_FORMAT(modification_date,'%%Y-%%m-%%d %%H:%%i:%%s')) FROM bibrec", (), 1)
if res[0][0]:
out = localtime_to_utc(res[0][0])
return out
def check_date(date):
"""Check if given date has a correct format, complying to "Complete date" or
"Complete date plus hours, minutes and seconds" formats defined in ISO8601."""
if(re.match("\d\d\d\d-\d\d-\d\d(T\d\d:\d\d:\d\dZ)?", date) is not None):
return date
else:
return ""
def normalize_date(date, dtime="T00:00:00Z"):
"""
Normalize the given date to the
"Complete date plus hours, minutes and seconds" format defined in ISO8601
(If "hours, minutes and seconds" part is missing, append 'dtime' to date).
'date' must be checked before with check_date(..).
Returns empty string if cannot be normalized
"""
if len(date) == 10:
date = date + dtime
elif len(date) != 20:
date = ""
return date
def print_record(sysno, format='marcxml', record_exists_result=None):
"""Prints record 'sysno' formatted according to 'format'.
- if record does not exist, return nothing.
- if record has been deleted and CFG_OAI_DELETED_POLICY is
'transient' or 'deleted', then return only header, with status
'deleted'.
- if record has been deleted and CFG_OAI_DELETED_POLICY is 'no',
then return nothing.
Optional parameter 'record_exists_result' has the value of the result
of the record_exists(sysno) function (in order not to call that function
again if already done.)
"""
out = ""
# sanity check:
if record_exists_result is not None:
_record_exists = record_exists_result
else:
_record_exists = record_exists(sysno)
if not _record_exists:
return
if (format == "dc") or (format == "oai_dc"):
format = "xd"
# print record opening tags:
out = out + " <record>\n"
if _record_exists == -1: # Deleted?
if CFG_OAI_DELETED_POLICY == "persistent" or \
CFG_OAI_DELETED_POLICY == "transient":
out = out + " <header status=\"deleted\">\n"
else:
return
else:
out = out + " <header>\n"
for ident in get_field(sysno, CFG_OAI_ID_FIELD):
out = "%s <identifier>%s</identifier>\n" % (out, escape_space(ident))
out = "%s <datestamp>%s</datestamp>\n" % (out, get_modification_date(sysno))
for set in get_field(sysno, CFG_OAI_SET_FIELD):
if set:
# Print only if field not empty
out = "%s <setSpec>%s</setSpec>\n" % (out, set)
out = out + " </header>\n"
if _record_exists == -1: # Deleted?
pass
else:
out = out + " <metadata>\n"
if format == "marcxml":
formatted_record = get_preformatted_record(sysno, 'xm')
if formatted_record is not None:
## MARCXML is already preformatted. Adapt it if needed
formatted_record = formatted_record.replace("<record>", "<marc:record xmlns:marc=\"http://www.loc.gov/MARC21/slim\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\" type=\"Bibliographic\">\n <marc:leader>00000coc 2200000uu 4500</marc:leader>")
formatted_record = formatted_record.replace("<record xmlns=\"http://www.loc.gov/MARC21/slim\">", "<marc:record xmlns:marc=\"http://www.loc.gov/MARC21/slim\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\" type=\"Bibliographic\">\n <marc:leader>00000coc 2200000uu 4500</marc:leader>")
formatted_record = formatted_record.replace("</record", "</marc:record")
formatted_record = formatted_record.replace("<controlfield", "<marc:controlfield")
formatted_record = formatted_record.replace("</controlfield", "</marc:controlfield")
formatted_record = formatted_record.replace("<datafield", "<marc:datafield")
formatted_record = formatted_record.replace("</datafield", "</marc:datafield")
formatted_record = formatted_record.replace("<subfield", "<marc:subfield")
formatted_record = formatted_record.replace("</subfield", "</marc:subfield")
out += formatted_record
else:
## MARCXML is not formatted in the database, so produce it.
out = out + " <marc:record xmlns:marc=\"http://www.loc.gov/MARC21/slim\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\" type=\"Bibliographic\">"
out = out + " <marc:leader>00000coc 2200000uu 4500</marc:leader>"
out = "%s <marc:controlfield tag=\"001\">%d</marc:controlfield>\n" % (out, int(sysno))
for digit1 in range(0, 10):
for digit2 in range(0, 10):
bibbx = "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" % (bibbx, bibx, sysno, str(digit1)+str(digit2))
res = run_sql(query)
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 == "_":
ind1 = " "
if ind2 == "_":
ind2 = " "
# print field tag
if field_number != field_number_old or field[:-1] != field_old[:-1]:
if format == "marcxml":
if field_number_old != -999:
if field_old[0:2] == "00":
out = out + " </marc:controlfield>\n"
else:
out = out + " </marc:datafield>\n"
if field[0:2] == "00":
out = "%s <marc:controlfield tag=\"%s\">\n" % (out, encode_for_xml(field[0:3]))
else:
out = "%s <marc:datafield tag=\"%s\" ind1=\"%s\" ind2=\"%s\">\n" % (out, encode_for_xml(field[0:3]), encode_for_xml(ind1).lower(), encode_for_xml(ind2).lower())
field_number_old = field_number
field_old = field
# print subfield value
if format == "marcxml":
value = encode_for_xml(value)
if(field[0:2] == "00"):
out = "%s %s\n" % (out, value)
else:
out = "%s <marc:subfield code=\"%s\">%s</marc:subfield>\n" % (out, encode_for_xml(field[-1:]), value)
# fetch next subfield
# all fields/subfields printed in this run, so close the tag:
if (format == "marcxml") and field_number_old != -999:
if field_old[0:2] == "00":
out = out + " </marc:controlfield>\n"
else:
out = out + " </marc:datafield>\n"
out = out + " </marc:record>\n"
elif format == "xd":
out += format_record(sysno, 'xoaidc')
# print record closing tags:
out = out + " </metadata>\n"
out = out + " </record>\n"
return out
def oailistmetadataformats(args):
"Generates response to oailistmetadataformats verb."
arg = parse_args(args)
out = ""
flag = 1 # list or not depending on identifier
if arg['identifier'] != "":
flag = 0
sysno = oaigetsysno(arg['identifier'])
_record_exists = record_exists(sysno)
if _record_exists == 1 or \
(_record_exists == -1 and CFG_OAI_DELETED_POLICY != "no"):
flag = 1
else:
out = out + oai_error("idDoesNotExist","invalid record Identifier")
out = oai_error_header(args, "ListMetadataFormats") + out + oai_error_footer("ListMetadataFormats")
return out
if flag:
out = out + " <metadataFormat>\n"
out = out + " <metadataPrefix>oai_dc</metadataPrefix>\n"
out = out + " <schema>http://www.openarchives.org/OAI/1.1/dc.xsd</schema>\n"
out = out + " <metadataNamespace>http://purl.org/dc/elements/1.1/</metadataNamespace>\n"
out = out + " </metadataFormat>\n"
out = out + " <metadataFormat>\n"
out = out + " <metadataPrefix>marcxml</metadataPrefix>\n"
out = out + " <schema>http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd</schema>\n"
out = out + " <metadataNamespace>http://www.loc.gov/MARC21/slim</metadataNamespace>\n"
out = out + " </metadataFormat>\n"
out = oai_header(args, "ListMetadataFormats") + out + oai_footer("ListMetadataFormats")
return out
def oailistrecords(args):
"Generates response to oailistrecords verb."
arg = parse_args(args)
out = ""
resumptionToken_printed = False
sysnos = []
sysno = []
# check if the resumptionToken did not expire
if arg['resumptionToken']:
filename = "%s/RTdata/%s" % (CFG_CACHEDIR, arg['resumptionToken'])
if os.path.exists(filename) == 0:
out = oai_error("badResumptionToken", "ResumptionToken expired")
out = oai_error_header(args, "ListRecords") + out + oai_error_footer("ListRecords")
return out
if arg['resumptionToken'] != "":
sysnos = oaicacheout(arg['resumptionToken'])
arg['metadataPrefix'] = sysnos.pop()
else:
sysnos = oaigetsysnolist(arg['set'], arg['from'], arg['until'])
if len(sysnos) == 0: # noRecordsMatch error
out = out + oai_error("noRecordsMatch", "no records correspond to the request")
out = oai_error_header(args, "ListRecords") + out + oai_error_footer("ListRecords")
return out
i = 0
for sysno_ in sysnos:
if sysno_:
if i >= CFG_OAI_LOAD: # cache or write?
if not resumptionToken_printed: # resumptionToken?
arg['resumptionToken'] = oaigenresumptionToken()
extdate = oaigetresponsedate(CFG_OAI_EXPIRE)
if extdate:
out = "%s <resumptionToken expirationDate=\"%s\">%s</resumptionToken>\n" % (out, extdate, arg['resumptionToken'])
else:
out = "%s <resumptionToken>%s</resumptionToken>\n" % (out, arg['resumptionToken'])
resumptionToken_printed = True
sysno.append(sysno_)
else:
_record_exists = record_exists(sysno_)
if not (_record_exists == -1 and CFG_OAI_DELETED_POLICY == "no"):
#Produce output only if record exists and had to be printed
i = i + 1 # Increment limit only if record is returned
res = print_record(sysno_, arg['metadataPrefix'], _record_exists)
if res:
out += res
if resumptionToken_printed:
oaicacheclean()
sysno.append(arg['metadataPrefix'])
oaicachein(arg['resumptionToken'], sysno)
out = oai_header(args, "ListRecords") + out + oai_footer("ListRecords")
return out
def oailistsets(args):
"Lists available sets for OAI metadata harvesting."
out = ""
# note: no flow control in ListSets
sets = get_sets()
for set_ in sets:
out = out + " <set>\n"
out = "%s <setSpec>%s</setSpec>\n" % (out, set_[0])
out = "%s <setName>%s</setName>\n" % (out, set_[1])
if set_[2]:
out = "%s <setDescription>%s</setDescription>\n" % (out, set_[2])
out = out + " </set>\n"
out = oai_header(args, "ListSets") + out + oai_footer("ListSets")
return out
def oaigetrecord(args):
"""Returns record 'identifier' according to 'metadataPrefix' format for OAI metadata harvesting.
- if record does not exist, return oai_error 'idDoesNotExist'.
- if record has been deleted and CFG_OAI_DELETED_POLICY is
'transient' or 'deleted', then return only header, with status
'deleted'.
- if record has been deleted and CFG_OAI_DELETED_POLICY is 'no',
then return oai_error 'idDoesNotExist'.
"""
arg = parse_args(args)
out = ""
sysno = oaigetsysno(arg['identifier'])
_record_exists = record_exists(sysno)
if _record_exists == 1 or \
(_record_exists == -1 and CFG_OAI_DELETED_POLICY != 'no'):
out = print_record(sysno, arg['metadataPrefix'], _record_exists)
out = oai_header(args, "GetRecord") + out + oai_footer("GetRecord")
else:
out = oai_error("idDoesNotExist", "invalid record Identifier")
out = oai_error_header(args, "GetRecord") + out + oai_error_footer("GetRecord")
return out
def oailistidentifiers(args):
"Prints OAI response to the ListIdentifiers verb."
arg = parse_args(args)
out = ""
resumptionToken_printed = False
sysno = []
sysnos = []
if arg['resumptionToken']:
filename = "%s/RTdata/%s" % (CFG_CACHEDIR, arg['resumptionToken'])
if os.path.exists(filename) == 0:
out = out + oai_error("badResumptionToken", "ResumptionToken expired")
out = oai_error_header(args, "ListIdentifiers") + out + oai_error_footer("ListIdentifiers")
return out
if arg['resumptionToken']:
sysnos = oaicacheout(arg['resumptionToken'])
else:
sysnos = oaigetsysnolist(arg['set'], arg['from'], arg['until'])
if len(sysnos) == 0: # noRecordsMatch error
out = out + oai_error("noRecordsMatch", "no records correspond to the request")
out = oai_error_header(args, "ListIdentifiers") + out + oai_error_footer("ListIdentifiers")
return out
i = 0
for sysno_ in sysnos:
if sysno_:
if i >= CFG_OAI_LOAD: # cache or write?
if not resumptionToken_printed: # resumptionToken?
arg['resumptionToken'] = oaigenresumptionToken()
extdate = oaigetresponsedate(CFG_OAI_EXPIRE)
if extdate:
out = "%s <resumptionToken expirationDate=\"%s\">%s</resumptionToken>\n" % (out, extdate, arg['resumptionToken'])
else:
out = "%s <resumptionToken>%s</resumptionToken>\n" % (out, arg['resumptionToken'])
resumptionToken_printed = True
sysno.append(sysno_)
else:
_record_exists = record_exists(sysno_)
if (not _record_exists == -1 and CFG_OAI_DELETED_POLICY == "no"):
i = i + 1 # Increment limit only if record is returned
for ident in get_field(sysno_, CFG_OAI_ID_FIELD):
if ident != '':
if _record_exists == -1: #Deleted?
if CFG_OAI_DELETED_POLICY == "persistent" \
or CFG_OAI_DELETED_POLICY == "transient":
out = out + " <header status=\"deleted\">\n"
else:
# In that case, print nothing (do not go further)
break
else:
out = out + " <header>\n"
out = "%s <identifier>%s</identifier>\n" % (out, escape_space(ident))
out = "%s <datestamp>%s</datestamp>\n" % (out, get_modification_date(oaigetsysno(ident)))
for set in get_field(sysno_, CFG_OAI_SET_FIELD):
if set:
# Print only if field not empty
out = "%s <setSpec>%s</setSpec>\n" % (out, set)
out = out + " </header>\n"
if resumptionToken_printed:
oaicacheclean() # clean cache from expired resumptionTokens
oaicachein(arg['resumptionToken'], sysno)
out = oai_header(args, "ListIdentifiers") + out + oai_footer("ListIdentifiers")
return out
def oaiidentify(args):
"Generates response to oaiidentify verb."
out = ""
repositoryname = " <repositoryName>" + CFG_SITE_NAME + "</repositoryName>\n"
- baseurl = " <baseURL>%s/oai2d/</baseURL>\n" % weburl
+ baseurl = " <baseURL>%s/oai2d/</baseURL>\n" % CFG_SITE_URL
protocolversion = " <protocolVersion>2.0</protocolVersion>\n"
adminemailtxt = " <adminEmail>%s</adminEmail>\n" % CFG_SITE_SUPPORT_EMAIL
earliestdst = " <earliestDatestamp>%s</earliestDatestamp>\n" % get_earliest_datestamp()
deletedrecord = " <deletedRecord>%s</deletedRecord>\n" % CFG_OAI_DELETED_POLICY
repositoryidentifier = "%s" % CFG_OAI_ID_PREFIX
sampleidentifier = CFG_OAI_SAMPLE_IDENTIFIER
identifydescription = CFG_OAI_IDENTIFY_DESCRIPTION + "\n"
out = out + repositoryname
out = out + baseurl
out = out + protocolversion
out = out + adminemailtxt
out = out + earliestdst
out = out + deletedrecord
out = out + " <granularity>YYYY-MM-DDThh:mm:ssZ</granularity>\n"
# print " <compression></compression>\n"
out = out + CFG_OAI_IDENTIFY_DESCRIPTION
out = oai_header(args, "Identify") + out + oai_footer("Identify")
return out
def oaigetrequesturl(args):
"Generates requesturl tag for OAI."
# re_amp = re.compile('&')
- requesturl = weburl + "/" + "oai2d/"# + "?" + re_amp.sub("&amp;", args)
+ requesturl = CFG_SITE_URL + "/" + "oai2d/"# + "?" + re_amp.sub("&amp;", args)
return requesturl
def oaigetresponsedate(delay=0):
"Generates responseDate tag for OAI."
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() + delay))
def oai_error(code, msg):
"OAI error occured"
return "<error code=\"%s\">%s</error>\n" % (code, msg)
def oaigetsysno(identifier):
"Returns the first database BIB ID for the OAI identifier 'identifier', if it exists."
sysno = None
if identifier:
query = "SELECT DISTINCT(bb.id_bibrec) FROM bib%sx AS bx, bibrec_bib%sx AS bb WHERE bx.tag='%s' AND bb.id_bibxxx=bx.id AND bx.value='%s'" % (CFG_OAI_ID_FIELD[0:2], CFG_OAI_ID_FIELD[0:2], CFG_OAI_ID_FIELD, identifier)
res = run_sql(query)
for row in res:
sysno = row[0]
return sysno
def oaigetsysnolist(set="", fromdate="", untildate=""):
"Returns list of system numbers for the OAI set 'set', modified from 'fromdate' until 'untildate'."
if fromdate != "":
fromdate = normalize_date(fromdate, "T00:00:00Z")
else:
fromdate = get_earliest_datestamp()
if untildate != "":
untildate = normalize_date(untildate, "T23:59:59Z")
else:
untildate = get_latest_datestamp()
recids = perform_request_search(f1=CFG_OAI_ID_FIELD, p1="oai:*", m1="e", op1='a',
f2=((set and CFG_OAI_SET_FIELD) or ""), p2=set, m2="e",
d1=utc_to_localtime(fromdate),
d2=utc_to_localtime(untildate),
dt='m',
ap=0)
return recids
def oaigenresumptionToken():
"Generates unique ID for resumption token management."
return md5.new(str(time.time())).hexdigest()
def oaicachein(resumptionToken, sysnos):
"Stores or adds sysnos in cache. Input is a string of sysnos separated by commas."
filename = "%s/RTdata/%s" % (CFG_CACHEDIR, resumptionToken)
fil = open(filename, "w")
cPickle.dump(sysnos, fil)
fil.close()
return 1
def oaicacheout(resumptionToken):
"Restores string of comma-separated system numbers from cache."
sysnos = []
filename = "%s/RTdata/%s" % (CFG_CACHEDIR, resumptionToken)
if oaicachestatus(resumptionToken):
fil = open(filename, "r")
sysnos = cPickle.load(fil)
fil.close()
else:
return 0
return sysnos
def oaicacheclean():
"Removes cached resumptionTokens older than specified"
directory = "%s/RTdata" % CFG_CACHEDIR
files = os.listdir(directory)
for file_ in files:
filename = directory + "/" + file_
# cache entry expires when not modified during a specified period of time
if ((time.time() - os.path.getmtime(filename)) > CFG_OAI_EXPIRE):
os.remove(filename)
return 1
def oaicachestatus(resumptionToken):
"Checks cache status. Returns 0 for empty, 1 for full."
filename = "%s/RTdata/%s" % (CFG_CACHEDIR, resumptionToken)
if os.path.exists(filename):
if os.path.getsize(filename) > 0:
return 1
else:
return 0
else:
return 0
def get_sets():
"Returns list of sets."
# TODO: Try to remove dependency on oaiARCHIVE table, by
# determining available sets from data.
out = {}
row = ['', '']
query = "SELECT setSpec,setName,setDescription FROM oaiARCHIVE"
res = run_sql(query)
for row in res:
row_bis = [row[0], row[1], row[2]]
out[row[0]] = row_bis
return out.values()
def parse_args(args=""):
"Parse input args"
out_args = {
"verb" : "",
"metadataPrefix" : "",
"from" : "",
"until" : "",
"set" : "",
"identifier" : "",
"resumptionToken" : ""
}
if args == "" or args is None:
pass
else:
list_of_arguments = args.split('&')
for item in list_of_arguments:
keyvalue = item.split('=')
if len(keyvalue) == 2:
if (out_args.has_key(keyvalue[0])):
if(out_args[keyvalue[0]] != ""):
out_args[keyvalue[0]] = "Error"
else:
out_args[keyvalue[0]] = urllib.unquote(keyvalue[1])
else:
out_args[keyvalue[0]] = urllib.unquote(keyvalue[1])
else:
out_args['verb'] = ""
return out_args
def check_args(arguments):
"Check OAI arguments"
out = ""
## principal argument required
#
#
if verbs.has_key(arguments['verb']):
pass
else:
out = out + oai_error("badVerb", "Illegal OAI verb")
## defined args
#
#
for param in arguments.keys():
if not param in verbs.get(arguments['verb'], []) and param != 'verb':
out = out + oai_error("badArgument", "The request includes illegal arguments")
break # Indicate only once
## resumptionToken exclusive
#
#
if arguments.get('resumptionToken', '') != "" and \
len(arguments.keys()) != 2:
out = out + oai_error("badArgument",
"The request includes illegal arguments")
## datestamp formats
#
#
if arguments.has_key('from') and \
'from' in verbs.get(arguments['verb'], []):
from_length = len(arguments['from'])
if check_date(arguments['from']) == "":
out = out + oai_error("badArgument",
"Bad datestamp format in from")
else:
from_length = 0
if arguments.has_key('until') and \
'until' in verbs.get(arguments['verb'], []):
until_length = len(arguments['until'])
if check_date(arguments['until']) == "":
out = out + oai_error("badArgument",
"Bad datestamp format in until")
else:
until_length = 0
if from_length != 0:
if until_length != 0:
if from_length != until_length:
out = out + oai_error("badArgument",
"Bad datestamp format")
if arguments.has_key('from') and arguments.has_key('until') \
and arguments['from'] > arguments['until'] and \
'from' in verbs.get(arguments['verb'], []) and \
'until' in verbs.get(arguments['verb'], []):
out = out + oai_error("badArgument", "Wrong date")
## Identify exclusive
#
#
if arguments['verb'] == "Identify" and \
len(arguments.keys()) != 1:
if not 'The request includes illegal arguments' in out: # Do not repeat this error
out = out + oai_error("badArgument",
"The request includes illegal arguments")
## parameters for GetRecord
#
#
if arguments['verb'] == "GetRecord" and \
not arguments.has_key('identifier'):
out = out + oai_error("badArgument",
"Record identifier missing")
if arguments['verb'] == "GetRecord" and \
not arguments.has_key('metadataPrefix'):
out = out + oai_error("badArgument",
"Missing metadataPrefix")
## parameters for ListRecords and ListIdentifiers
#
#
if (arguments['verb'] == "ListRecords" or arguments['verb'] == "ListIdentifiers") and \
(not arguments.has_key('resumptionToken') and not arguments.has_key('metadataPrefix')):
out = out + oai_error("badArgument", "Missing metadataPrefix")
## Metadata prefix defined and valid
#
#
if arguments.has_key('metadataPrefix') and \
not arguments['metadataPrefix'] in params['metadataPrefix']:
out = out + oai_error("cannotDisseminateFormat", "Chosen format is not supported")
return out
def oai_profile():
"""
Runs a benchmark
"""
oailistrecords('set=&from=&metadataPrefix=oai_dc&verb=ListRecords&resumptionToken=&identifier=&until=')
#oailistrecords('set=&from=&metadataPrefix=marcxml&verb=ListRecords&resumptionToken=&identifier=&until=')
#oailistidentifiers('set=&from=&metadataPrefix=oai_dc&verb=ListIdentifiers&resumptionToken=&identifier=&until=')
return
if __name__ == "__main__":
import profile
import pstats
profile.run('oai_profile()', "oai_profile")
p = pstats.Stats("oai_profile")
p.strip_dirs().sort_stats("cumulative").print_stats()
diff --git a/modules/bibharvest/lib/oai_repository_regression_tests.py b/modules/bibharvest/lib/oai_repository_regression_tests.py
index fac3f4f91..57ec510c3 100644
--- a/modules/bibharvest/lib/oai_repository_regression_tests.py
+++ b/modules/bibharvest/lib/oai_repository_regression_tests.py
@@ -1,69 +1,69 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""OAIRepository Regression Test Suite."""
__revision__ = "$Id$"
import unittest
import time
-from invenio.config import weburl, CFG_OAI_SLEEP
+from invenio.config import CFG_SITE_URL, CFG_OAI_SLEEP
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class OAIRepositoryWebPagesAvailabilityTest(unittest.TestCase):
"""Check OAIRepository web pages whether they are up or not."""
def test_your_baskets_pages_availability(self):
"""oairepository - availability of OAI server pages"""
- baseurl = weburl + '/oai2d'
+ baseurl = CFG_SITE_URL + '/oai2d'
_exports = [#fast commands first:
'?verb=Identify',
'?verb=ListMetadataFormats',
# sleepy commands now:
'?verb=ListSets',
'?verb=ListRecords',
'?verb=GetRecord']
error_messages = []
for url in [baseurl + page for page in _exports]:
if url.endswith('Identify') or \
url.endswith('ListMetadataFormats'):
pass
else:
# some sleep required for verbs other than Identify
# and ListMetadataFormats, since oai2d refuses too
# frequent access:
time.sleep(CFG_OAI_SLEEP)
error_messages.extend(test_web_page_content(url,
expected_text=
'</OAI-PMH>'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(OAIRepositoryWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibharvest/lib/oaiarchiveadmin_regression_tests.py b/modules/bibharvest/lib/oaiarchiveadmin_regression_tests.py
index 6ab9566c3..8b8c69531 100644
--- a/modules/bibharvest/lib/oaiarchiveadmin_regression_tests.py
+++ b/modules/bibharvest/lib/oaiarchiveadmin_regression_tests.py
@@ -1,79 +1,79 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""OAIArchive Admin Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class OAIArchiveAdminWebPagesAvailabilityTest(unittest.TestCase):
"""Check OAIArchive Admin web pages whether they are up or not."""
def test_oaiarchiveadmin_interface_pages_availability(self):
"""oaiarchiveadmin - availability of OAIArchive Admin interface pages"""
- baseurl = weburl + '/admin/bibharvest/oaiarchiveadmin.py/'
+ baseurl = CFG_SITE_URL + '/admin/bibharvest/oaiarchiveadmin.py/'
_exports = ['', 'delset', 'editset', 'addset']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_oaiarchiveadmin_edit_set(self):
"""oaiarchiveadmin - edit set page"""
- test_edit_url = weburl + \
+ test_edit_url = CFG_SITE_URL + \
"/admin/bibharvest/oaiarchiveadmin.py/editset?oai_set_id=1"
error_messages = test_web_page_content(test_edit_url,
username='admin')
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_oaiarchiveadmin_delete_set(self):
"""oaiarchiveadmin - delete set page"""
- test_edit_url = weburl + \
+ test_edit_url = CFG_SITE_URL + \
"/admin/bibharvest/oaiarchiveadmin.py/delset?oai_set_id=1"
error_messages = test_web_page_content(test_edit_url,
username='admin')
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(OAIArchiveAdminWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibharvest/lib/oaiarchiveadminlib.py b/modules/bibharvest/lib/oaiarchiveadminlib.py
index 80f8423d3..4d9b6f423 100644
--- a/modules/bibharvest/lib/oaiarchiveadminlib.py
+++ b/modules/bibharvest/lib/oaiarchiveadminlib.py
@@ -1,751 +1,750 @@
## $Id$
## Administrator interface for the OAI repository and archive
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio OAI Repository and Archive Administrator Interface."""
__revision__ = "$Id$"
import sys
import cgi
import re
import os
import string
import ConfigParser
import time
import random
import urllib
from invenio.config import \
CFG_SITE_LANG, \
CFG_TMPDIR, \
CFG_VERSION, \
- weburl
+ CFG_SITE_URL
import invenio.access_control_engine as access_manager
from invenio.dbquery import run_sql
from invenio.webpage import page, pageheaderonly, pagefooteronly
from invenio.webuser import getUid, get_email
from invenio.oaiarchive_engine import parse_set_definition
import invenio.template
bibharvest_templates = invenio.template.load('bibharvest')
tmppath = CFG_TMPDIR + '/oaiarchiveadmin.' + str(os.getpid())
guideurl = "help/admin/bibharvest-admin-guide"
def getnavtrail(previous = ''):
"""Get navtrail"""
return bibharvest_templates.tmpl_getnavtrail(previous = previous, ln = CFG_SITE_LANG)
def perform_request_index(ln=CFG_SITE_LANG):
"""OAI archive admin index"""
out = '''<p>Define below the sets to expose through the OAI harvesting
protocol. <br /> You will have to run the
- <a href="%(weburl)s/help/admin/bibharvest-admin-guide#3.2"><code>oaiarchive</code></a>
- utility to apply the settings you have defined here.</p>''' % {'weburl': weburl}
+ <a href="%(siteurl)s/help/admin/bibharvest-admin-guide#3.2"><code>oaiarchive</code></a>
+ utility to apply the settings you have defined here.</p>''' % {'siteurl': CFG_SITE_URL}
titlebar = bibharvest_templates.tmpl_draw_titlebar(ln = CFG_SITE_LANG,
- weburl = weburl,
title = "OAI repository",
guideurl = guideurl,
extraname = "add new OAI set",
extraurl = "admin/bibharvest/oaiarchiveadmin.py/addset")
header = ['id','setSpec','setName','setCollection','p1','f1','m1', 'op1', 'p2','f2','m2', 'op2','p3','f3','m3','','']
oai_set = get_oai_set()
sets = []
for (id, setSpec, setName, setCollection, setDescription, p1, f1, m1, p2, f2, m2, p3, f3, m3, op1, op2) in oai_set:
- del_request = '<a href="' + weburl + "/" + "admin/bibharvest/oaiarchiveadmin.py/delset?ln=" + ln + "&amp;oai_set_id=" + str(id) + '">delete</a>'
+ del_request = '<a href="' + CFG_SITE_URL + "/" + "admin/bibharvest/oaiarchiveadmin.py/delset?ln=" + ln + "&amp;oai_set_id=" + str(id) + '">delete</a>'
- edit_request = '<a href="' + weburl + "/" + "admin/bibharvest/oaiarchiveadmin.py/editset?ln=" + ln + "&amp;oai_set_id=" + str(id) + '">edit</a>'
+ edit_request = '<a href="' + CFG_SITE_URL + "/" + "admin/bibharvest/oaiarchiveadmin.py/editset?ln=" + ln + "&amp;oai_set_id=" + str(id) + '">edit</a>'
sets.append([id, setSpec, setName, setCollection, p1,f1,m1, op1, p2,f2,m2, op2, p3,f3,m3, del_request, edit_request])
- add_request = '<a href="' + weburl + "/" + "admin/bibharvest/oaiarchiveadmin.py/addset?ln=" + ln + '">Add new OAI set definition</a>'
+ add_request = '<a href="' + CFG_SITE_URL + "/" + "admin/bibharvest/oaiarchiveadmin.py/addset?ln=" + ln + '">Add new OAI set definition</a>'
sets.append(['',add_request,'','','','','','','','','','','','','','',''])
out += transform_tuple(header=header, tuple=sets)
out += "<br /><br />"
return out
def perform_request_addset(oai_set_name='', oai_set_spec='', oai_set_collection='', oai_set_description='', oai_set_definition='', oai_set_reclist='', oai_set_p1='', oai_set_f1='',oai_set_m1='', oai_set_p2='', oai_set_f2='', oai_set_m2='', oai_set_p3='', oai_set_f3='', oai_set_m3='', oai_set_op1='a', oai_set_op2='a', ln=CFG_SITE_LANG, func=0):
"""add a new OAI set"""
out = ""
if func in ["0", 0]:
text = input_form(oai_set_name, oai_set_spec, oai_set_collection, oai_set_description, oai_set_definition, oai_set_reclist, oai_set_p1, oai_set_f1,oai_set_m1, oai_set_p2, oai_set_f2,oai_set_m2, oai_set_p3, oai_set_f3, oai_set_m3, oai_set_op1, oai_set_op2, ln=CFG_SITE_LANG)
out = createform(action="addset",
text=text,
ln=ln,
button="Add new OAI set definition line",
func=1)
lnargs = [["ln", ln]]
if func in ["1", 1]:
out += "<br />"
res = add_oai_set(oai_set_name, oai_set_spec, oai_set_collection, oai_set_description, oai_set_definition, oai_set_reclist, oai_set_p1, oai_set_f1,oai_set_m1, oai_set_p2, oai_set_f2,oai_set_m2, oai_set_p3, oai_set_f3, oai_set_m3, oai_set_op1, oai_set_op2)
if res[0] == 1:
out += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG,
"OAI set definition %s added." % oai_set_name)
out += "<br />"
lnargs = [["ln", ln]]
out += "<br /><br />"
- out += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/oaiarchiveadmin.py/index", title = "Return to main selection", args = lnargs)
+ out += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/oaiarchiveadmin.py/index", title = "Return to main selection", args = lnargs)
body = [out]
return nice_box("", body)
def perform_request_editset(oai_set_id=None, oai_set_name='', oai_set_spec='', oai_set_collection='', oai_set_description='', oai_set_definition='', oai_set_reclist='', oai_set_p1='', oai_set_f1='', oai_set_m1='', oai_set_p2='', oai_set_f2='', oai_set_m2='', oai_set_p3='', oai_set_f3='', oai_set_m3='', oai_set_op1='a', oai_set_op2='a', ln=CFG_SITE_LANG, func=0):
"""creates html form to edit an OAI set."""
if oai_set_id is None:
return "No OAI set ID selected."
out = ""
if func in [0, "0"]:
oai_set = get_oai_set(oai_set_id)
oai_set_spec = oai_set[0][1]
oai_set_name = oai_set[0][2]
oai_set_collection = oai_set[0][3]
oai_set_description = oai_set[0][4]
oai_set_definition = ''
oai_set_reclist = ''
oai_set_p1 = oai_set[0][5]
oai_set_f1 = oai_set[0][6]
oai_set_m1 = oai_set[0][7]
oai_set_p2 = oai_set[0][8]
oai_set_f2 = oai_set[0][9]
oai_set_m2 = oai_set[0][10]
oai_set_p3 = oai_set[0][11]
oai_set_f3 = oai_set[0][12]
oai_set_m3 = oai_set[0][13]
oai_set_op1 = oai_set[0][14]
oai_set_op2 = oai_set[0][15]
text = input_form(oai_set_name,
oai_set_spec,
oai_set_collection,
oai_set_description,
oai_set_definition,
oai_set_reclist,
oai_set_p1,
oai_set_f1,
oai_set_m1,
oai_set_p2,
oai_set_f2,
oai_set_m2,
oai_set_p3,
oai_set_f3,
oai_set_m3,
oai_set_op1,
oai_set_op2,
ln=CFG_SITE_LANG)
out += extended_input_form(action="editset",
text=text,
button="Modify",
oai_set_id=oai_set_id,
ln=ln,
func=1)
if func in [1, "1"]:
res = modify_oai_set(oai_set_id,
oai_set_name,
oai_set_spec,
oai_set_collection,
oai_set_description,
oai_set_p1,
oai_set_f1,
oai_set_m1,
oai_set_p2,
oai_set_f2,
oai_set_m2,
oai_set_p3,
oai_set_f3,
oai_set_m3,
oai_set_op1,
oai_set_op2)
out += "<br />"
if res[0] == 1:
out += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG,
"OAI set definition #%s edited." % oai_set_id)
out += "<br />"
lnargs = [["ln", ln]]
out += "<br />"
- out += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/oaiarchiveadmin.py/index", title = "Return to main selection", args = lnargs)
+ out += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/oaiarchiveadmin.py/index", title = "Return to main selection", args = lnargs)
body = [out]
return nice_box("", body)
def perform_request_delset(oai_set_id=None, ln=CFG_SITE_LANG, callback='yes', func=0):
"""creates html form to delete an OAI set"""
out = ""
if oai_set_id:
oai_set = get_oai_set(oai_set_id)
nameset = (oai_set[0][1])
pagetitle = """Delete OAI set: %s""" % nameset
if func in ["0", 0]:
oai_set = get_oai_set(oai_set_id)
oai_set_spec = oai_set[0][1]
oai_set_name = oai_set[0][2]
oai_set_collection = oai_set[0][3]
oai_set_description = oai_set[0][4]
oai_set_definition = ''
oai_set_reclist = ''
oai_set_p1 = oai_set[0][5]
oai_set_f1 = oai_set[0][6]
oai_set_m1 = oai_set[0][7]
oai_set_p2 = oai_set[0][8]
oai_set_f2 = oai_set[0][9]
oai_set_m2 = oai_set[0][10]
oai_set_p3 = oai_set[0][11]
oai_set_f3 = oai_set[0][12]
oai_set_m3 = oai_set[0][13]
oai_set_op1 = oai_set[0][14]
oai_set_op2 = oai_set[0][15]
if oai_set:
question = """Do you want to delete the OAI definition #%s?""" % oai_set_id
text = bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, question)
text += "<br /><br /><br />"
text += pagebody_text("%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s" % (oai_set_spec,
oai_set_name,
oai_set_collection,
oai_set_p1,
oai_set_f1,
oai_set_m1,
oai_set_op1,
oai_set_p2,
oai_set_f2,
oai_set_m2,
oai_set_op2,
oai_set_p3,
oai_set_f3,
oai_set_m3))
out += createform(action="delset",
text=text,
button="Delete",
oai_set_id=oai_set_id,
func=1)
else:
return bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "OAI set does not exist.")
elif func in ["1", 1]:
res = delete_oai_set(oai_set_id)
if res[0] == 1:
out += bibharvest_templates.tmpl_print_info(CFG_SITE_LANG, "OAI set definition #%s deleted." % oai_set_id)
out += "<br />"
else:
pass
lnargs = [["ln", ln]]
out += "<br /><br />"
- out += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, weburl = weburl, funcurl = "admin/bibharvest/oaiarchiveadmin.py/index", title = "Return to main selection", args = lnargs )
+ out += bibharvest_templates.tmpl_link_with_args(ln = CFG_SITE_LANG, funcurl = "admin/bibharvest/oaiarchiveadmin.py/index", title = "Return to main selection", args = lnargs )
body = [out]
return nice_box("", body)
def get_oai_set(id=''):
"""Returns a row parameters for a given id"""
sets = []
sql = "SELECT id, setSpec, setName, setCollection, setDescription, p1,f1,m1, p2,f2,m2, p3,f3,m3, setDefinition FROM oaiARCHIVE"
try:
if id:
sql += " WHERE id=%s" % id
sql += " ORDER BY setSpec asc"
res = run_sql(sql)
for row in res:
set = ['']*16
set[0] = row[0]
set[1] = row[1]
set[2] = row[2]
params = parse_set_definition(row[14])
set[3] = params.get('c', '')
set[5] = params.get('p1', '')
set[6] = params.get('f1', '')
set[7] = params.get('m1', '')
set[8] = params.get('p2', '')
set[9] = params.get('f2', '')
set[10] = params.get('m2', '')
set[11] = params.get('p3', '')
set[12] = params.get('f3', '')
set[13] = params.get('m3', '')
set[14] = params.get('op1', 'a')
set[15] = params.get('op2', 'a')
sets.append(set)
return sets
except StandardError, e:
return str(e)
def modify_oai_set(oai_set_id, oai_set_name, oai_set_spec, oai_set_collection, oai_set_description, oai_set_p1, oai_set_f1,oai_set_m1, oai_set_p2, oai_set_f2,oai_set_m2, oai_set_p3, oai_set_f3, oai_set_m3, oai_set_op1, oai_set_op2):
"""Modifies a row's parameters"""
try:
set_definition = 'c=' + oai_set_collection + ';' + \
'p1=' + oai_set_p1 + ';' + \
'f1=' + oai_set_f1 + ';' + \
'm1=' + oai_set_m1 + ';' + \
'op1='+ oai_set_op1 + ';' + \
'p2=' + oai_set_p2 + ';' + \
'f2=' + oai_set_f2 + ';' + \
'm2=' + oai_set_m2 + ';' + \
'op2='+ oai_set_op2 + ';' + \
'p3=' + oai_set_p3 + ';' + \
'f3=' + oai_set_f3 + ';' + \
'm3=' + oai_set_m3 + ';'
res = run_sql("""UPDATE oaiARCHIVE SET
setName=%s,
setSpec=%s',
setCollection=%s,
setDescription=%s,
setDefinition=%s,
p1=%s,
f1=%s,
m1=%s,
p2=%s,
f2=%s,
m2=%s,
p3=%s,
f3=%s,
m3=%s
WHERE id=%s""",
(oai_set_name,
oai_set_spec,
oai_set_collection,
oai_set_description,
set_definition,
oai_set_p1,
oai_set_f1,
oai_set_m1,
oai_set_p2,
oai_set_f2,
oai_set_m2,
oai_set_p3,
oai_set_f3,
oai_set_m3,
oai_set_id))
return (1, "")
except StandardError, e:
return (0, e)
def add_oai_set(oai_set_name, oai_set_spec, oai_set_collection, oai_set_description, oai_set_definition, oai_set_reclist, oai_set_p1, oai_set_f1,oai_set_m1, oai_set_p2, oai_set_f2,oai_set_m2, oai_set_p3, oai_set_f3, oai_set_m3, oai_set_op1, oai_set_op2):
"""Add a definition into the OAI archive"""
try:
set_definition = 'c=' + oai_set_collection + ';' + \
'p1=' + oai_set_p1 + ';' + \
'f1=' + oai_set_f1 + ';' + \
'm1=' + oai_set_m1 + ';' + \
'op1='+ oai_set_op1 + ';' + \
'p2=' + oai_set_p2 + ';' + \
'f2=' + oai_set_f2 + ';' + \
'm2=' + oai_set_m2 + ';' + \
'p3=' + oai_set_p3 + ';' + \
'f3=' + oai_set_f3 + ';' + \
'm3=' + oai_set_m3 + ';'
res = run_sql("""INSERT INTO oaiARCHIVE (id, setName, setSpec,
setCollection, setDescription, setDefinition,
setRecList, p1, f1, m1, p2, f2, m2, p3, f3, m3)
VALUES (0, %s, %s, %s, %s, %s, NULL, %s, %s, %s,
%s, %s, %s, %s, %s, %s)""",
(oai_set_name, oai_set_spec, oai_set_collection,
oai_set_description, set_definition, oai_set_p1,
oai_set_f1, oai_set_m1, oai_set_p2, oai_set_f2,
oai_set_m2, oai_set_p3, oai_set_f3, oai_set_m3))
return (1, "")
except StandardError, e:
return (0, e)
def delete_oai_set(oai_set_id):
""""""
try:
res = run_sql("DELETE FROM oaiARCHIVE WHERE id=%s" % oai_set_id)
return (1, "")
except StandardError, e:
return (0, e)
def drop_down_menu(boxname, list=['Select', 'selected', 'select']):
""""""
text = "<select name=\"%s\">" % boxname
for (value, selectedflag, txt) in list:
text += "<option value=\""
text += "%s\"" % value
if selectedflag:
text += ' selected="selected"'
text += ">%s</option>" % txt
text += "</select>"
return text
def create_drop_down_menu(sql):
""""""
list = []
res = run_sql(sql)
for item in res:
tmp_list = []
tmp_list.append(item)
tmp_list.append("")
tmp_list.append(item)
list.append(tmp_list)
return list
def createform(action="", text="", button="func", cnfrm='', **hidden):
""""""
out = '<form action="%s" method="post">\n' % (action, )
out += text
if cnfrm:
out += ' <input type="checkbox" name="func" value="1"/>'
for key in hidden.keys():
if type(hidden[key]) is list:
for value in hidden[key]:
out += ' <input type="hidden" name="%s" value="%s"/>\n' % (key, value)
else:
out += ' <input type="hidden" name="%s" value="%s"/>\n' % (key, hidden[key])
out += ' <input class="adminbutton" type="submit" value="%s"/>\n' % (button, )
out += '</form>\n'
return out
def oai_table(ln=CFG_SITE_LANG):
""""""
- titlebar = bibharvest_templates.tmpl_draw_titlebar(ln = CFG_SITE_LANG, weburl = weburl, title = "OAI repository", guideurl = guideurl, extraname = "add new OAI set" , extraurl = "admin/bibharvest/oaiarchiveadmin.py/addset" )
+ titlebar = bibharvest_templates.tmpl_draw_titlebar(ln = CFG_SITE_LANG, title = "OAI repository", guideurl = guideurl, extraname = "add new OAI set" , extraurl = "admin/bibharvest/oaiarchiveadmin.py/addset" )
header = ['id', 'setSpec', 'setName', 'setCollection', 'p1', 'f1', 'm1', 'op1', 'p2', 'f2', 'm2', 'p3', 'op2', 'f3', 'm3', '', '']
oai_set = get_oai_set()
sets = []
for (id, setSpec, setName, setCollection, setDescription, p1,f1,m1, p2,f2,m2, p3,f3,m3, op1, op2) in oai_set:
- del_request = '<a href="' + weburl + "/" + "admin/bibharvest/oaiarchiveadmin.py/delset?ln=" + ln + "&amp;oai_set_id=" + str(id) + '">delete</a>'
+ del_request = '<a href="' + CFG_SITE_URL + "/" + "admin/bibharvest/oaiarchiveadmin.py/delset?ln=" + ln + "&amp;oai_set_id=" + str(id) + '">delete</a>'
sets.append([id, setSpec, setName, setCollection, p1,f1,m1, op1, p2,f2,m2, op2, p3,f3,m3, del_request])
- add_request = '<a href="' + weburl + "/" + "admin/bibharvest/oaiarchiveadmin.py/addset?ln=" + ln + '">Add new OAI set definition</a>'
+ add_request = '<a href="' + CFG_SITE_URL + "/" + "admin/bibharvest/oaiarchiveadmin.py/addset?ln=" + ln + '">Add new OAI set definition</a>'
sets.append(['',add_request,'','','','','','','','','','','',''])
out = transform_tuple(header=header, tuple=sets)
out += "<br /><br />"
return out
def input_text(ln, title, name, value):
""""""
if name is None:
name = ""
if value is None:
value = ""
text = """<table><tr><td width="100%%"><span class="adminlabel">%s</span></td>""" % title
text += """<td align="left"><input class="admin_w200" type="text" name="%s" value="%s" /></td></tr></table>""" % (cgi.escape(name,1), cgi.escape(value, 1))
return text
def pagebody_text(title):
""""""
text = """<span class="admintd">%s</span>""" % title
return text
def bar_text(title):
""""""
text = """<span class="adminlabel">%s</span>""" % title
return text
def input_form(oai_set_name, oai_set_spec, oai_set_collection, oai_set_description, oai_set_definition, oai_set_reclist, oai_set_p1, oai_set_f1,oai_set_m1, oai_set_p2, oai_set_f2,oai_set_m2, oai_set_p3, oai_set_f3, oai_set_m3, oai_set_op1, oai_set_op2, ln=CFG_SITE_LANG):
""""""
modes = {
'r' : 'Regular Expression',
'a' : 'All of the words',
'y' : 'Any of the words',
'e' : 'Exact phrase',
'p' : 'Partial phrase'
}
mode_dropdown = [['r','',modes['r']],
['e','',modes['e']],
['p','',modes['p']],
['a','',modes['a']],
['y','',modes['y']],
['','','']]
operators = {
'a' : 'AND',
'o' : 'OR',
'n' : 'AND NOT',
}
mode_operators_1 = [['a','',operators['a']],
['o','',operators['o']],
['n','',operators['n']],
['a','','']]
mode_operators_2 = [['a','',operators['a']],
['o','',operators['o']],
['n','',operators['n']],
['a','','']]
text = "<br />"
text += "<table><tr><td>"
text += input_text(ln = CFG_SITE_LANG, title = "OAI Set spec:", name = "oai_set_spec", value = oai_set_spec)
text += "</td></tr><tr><td>"
text += input_text(ln = CFG_SITE_LANG, title = "OAI Set name:", name = "oai_set_name", value = oai_set_name)
text += "</td></tr><tr><td>&nbsp;</td></tr><tr><td>"
# text += input_text(ln = CFG_SITE_LANG, title = "OAI Set description", name = "oai_set_description", value = oai_set_description)
#text += "</td><td colspan=2>"
#menu = create_drop_down_menu("SELECT distinct(name) from collection")
#menu.append(['','',''])
#if (oai_set_collection):
# menu.append([oai_set_collection,'selected',oai_set_collection])
#else:
# menu.append(['','selected','Collection'])
text += input_text(ln = CFG_SITE_LANG, title = "Collection(s):", name="oai_set_collection", value=oai_set_collection)
#text += drop_down_menu("oai_set_collection", menu)
text += "</td></tr><tr><td>"
text += input_text(ln = CFG_SITE_LANG, title = "Phrase:", name = "oai_set_p1", value = oai_set_p1)
text += "</td><td>"
fields = create_drop_down_menu("SELECT distinct(code) from field")
fields.append(['','',''])
if (oai_set_f1):
fields.append([oai_set_f1,'selected',oai_set_f1])
else:
fields.append(['','selected','Field'])
if (oai_set_m1):
mode_dropdown.append([oai_set_m1,'selected',modes[oai_set_m1]])
else:
mode_dropdown.append(['','selected','Mode'])
text += drop_down_menu("oai_set_f1", fields)
text += "</td><td>"
text += drop_down_menu("oai_set_m1", mode_dropdown)
text += "</td><td>"
if (oai_set_op1):
mode_operators_1.append([oai_set_op1,'selected',operators[oai_set_op1]])
else:
mode_operators_1.append(['','selected','Operators'])
text += drop_down_menu("oai_set_op1", mode_operators_1)
text += "</td></tr><tr><td>"
text += input_text(ln = CFG_SITE_LANG, title = "Phrase:", name = "oai_set_p2", value = oai_set_p2)
text += "</td><td>"
fields = create_drop_down_menu("SELECT distinct(code) from field")
fields.append(['','',''])
if (oai_set_f2):
fields.append([oai_set_f2,'selected',oai_set_f2])
else:
fields.append(['','selected','Field'])
if (oai_set_m2):
mode_dropdown.append([oai_set_m2,'selected',modes[oai_set_m2]])
else:
mode_dropdown.append(['','selected','Mode'])
text += drop_down_menu("oai_set_f2", fields)
text += "</td><td>"
text += drop_down_menu("oai_set_m2", mode_dropdown)
text += "</td><td>"
if (oai_set_op2):
mode_operators_2.append([oai_set_op2,'selected',operators[oai_set_op2]])
else:
mode_operators_2.append(['','selected','Operators'])
text += drop_down_menu("oai_set_op2", mode_operators_2)
text += "</td></tr><tr><td>"
text += input_text(ln = CFG_SITE_LANG, title = "Phrase:", name = "oai_set_p3", value = oai_set_p3)
text += "</td><td>"
fields = create_drop_down_menu("SELECT distinct(code) from field")
fields.append(['','',''])
if (oai_set_f3):
fields.append([oai_set_f3,'selected',oai_set_f3])
else:
fields.append(['','selected','Field'])
if (oai_set_m3):
mode_dropdown.append([oai_set_m3,'selected',modes[oai_set_m3]])
else:
mode_dropdown.append(['','selected','Mode'])
text += drop_down_menu("oai_set_f3", fields)
text += "</td><td>"
text += drop_down_menu("oai_set_m3", mode_dropdown)
text += "</td></tr></table>"
return text
def check_user(req, role, adminarea=2, authorized=0):
""""""
(auth_code, auth_message) = access_manager.acc_authorize_action(req, role)
if not authorized and auth_code != 0:
return ("false", auth_message)
return ("", auth_message)
def transform_tuple(header=[], tuple=[], start='', end='', extracolumn=''):
""""""
align = []
try:
firstrow = tuple[0]
if type(firstrow) in [int, long]:
align = ['admintdright']
elif type(firstrow) in [str, dict]:
align = ['admintdleft']
else:
for item in firstrow:
if type(item) is int:
align.append('admintdright')
else:
align.append('admintdleft')
except IndexError:
firstrow = []
tblstr = ''
for h in header:
tblstr += ' <th class="adminheader">%s</th>\n' % (h, )
if tblstr: tblstr = ' <tr>\n%s\n </tr>\n' % (tblstr, )
tblstr = start + '<table class="admin_wvar_nomargin">\n' + tblstr
try:
extra = '<tr>'
if type(firstrow) not in [int, long, str, dict]:
for i in range(len(firstrow)): extra += '<td class="%s">%s</td>\n' % (align[i], firstrow[i])
else:
extra += ' <td class="%s">%s</td>\n' % (align[0], firstrow)
#extra += '<td rowspan="%s" style="vertical-align: top">\n%s\n</td>\n</tr>\n' % (len(tuple), extracolumn)
extra += '</tr>\n'
except IndexError:
extra = ''
tblstr += extra
j = 1
for row in tuple[1:]:
style = ''
if j % 2:
style = ' style="background-color: rgb(235, 247, 255);"'
j += 1
tblstr += ' <tr%s>\n' % style
if type(row) not in [int, long, str, dict]:
for i in range(len(row)): tblstr += '<td class="%s" style="padding:5px 10px;">%s</td>\n' % (align[i], row[i])
else:
tblstr += ' <td class="%s" style="padding:5px 10px;">%s</td>\n' % (align[0], row)
tblstr += ' </tr> \n'
tblstr += '</table> \n '
tblstr += end
return tblstr
def nice_box(header='', datalist=[], cls="admin_wvar"):
""""""
if len(datalist) == 1: per = '100'
else: per = '75'
out = '<table class="%s" ' % (cls, ) + 'width="95%">\n'
out += """
<thead>
<tr>
<th class="adminheaderleft" colspan="%s">%s</th>
</tr>
</thead>
<tbody>
""" % (len(datalist), header)
out += ' <tr>\n'
out += """
<td style="vertical-align: top; margin-top: 5px; width: %s;">
%s
</td>
""" % (per+'%', datalist[0])
if len(datalist) > 1:
out += """
<td style="vertical-align: top; margin-top: 5px; width: %s;">
%s
</td>
""" % ('25%', datalist[1])
out += ' </tr>\n'
out += """
</tbody>
</table>
"""
return out
def extended_input_form(action="", text="", button="func", cnfrm='', **hidden):
""""""
out = '<form action="%s" method="post">\n' % (action, )
out += '<table>\n<tr><td style="vertical-align: top">'
out += text
if cnfrm:
out += ' <input type="checkbox" name="func" value="1"/>'
for key in hidden.keys():
if type(hidden[key]) is list:
for value in hidden[key]:
out += ' <input type="hidden" name="%s" value="%s"/>\n' % (key, value)
else:
out += ' <input type="hidden" name="%s" value="%s"/>\n' % (key, hidden[key])
out += '</td><td style="vertical-align: bottom">'
out += ' <input class="adminbutton" type="submit" value="%s"/>\n' % (button, )
out += '</td></tr></table>'
out += '</form>\n'
return out
diff --git a/modules/bibharvest/web/admin/bibharvestadmin.py b/modules/bibharvest/web/admin/bibharvestadmin.py
index 122ef9240..22842c640 100644
--- a/modules/bibharvest/web/admin/bibharvestadmin.py
+++ b/modules/bibharvest/web/admin/bibharvestadmin.py
@@ -1,161 +1,161 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio BibHarvest Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import invenio.bibharvestadminlib as bhc
from invenio.webpage import page, create_error_box
-from invenio.config import CFG_SITE_NAME, weburl, CFG_SITE_LANG
+from invenio.config import CFG_SITE_NAME, CFG_SITE_URL, CFG_SITE_LANG
from invenio.dbquery import Error
from invenio.webuser import getUid, page_not_authorized
def index(req, ln=CFG_SITE_LANG):
navtrail_previous_links = bhc.getnavtrail()
try:
uid = getUid(req)
except Error, e:
return page(title="BibHarvest Admin Interface - Error",
body=e,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
auth = bhc.check_user(req,'cfgbibharvest')
if not auth[0]:
return page(title="BibHarvest Admin Interface",
body=bhc.perform_request_index(ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def editsource(req, oai_src_id=None, oai_src_name='', oai_src_baseurl='', oai_src_prefix='', oai_src_frequency='', oai_src_config='', oai_src_post='', ln=CFG_SITE_LANG, mtype='', callback='yes', confirm=-1, oai_src_sets=[], oai_src_bibfilter=''):
- navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/bibharvestadmin.py">BibHarvest Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/bibharvestadmin.py">BibHarvest Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return page(title="BibHarvest Admin Interface - Error",
body=e,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
auth = bhc.check_user(req,'cfgbibharvest')
if not auth[0]:
if isinstance(oai_src_sets, str):
oai_src_sets = [oai_src_sets]
return page(title="Edit OAI Source",
body=bhc.perform_request_editsource(oai_src_id=oai_src_id,
oai_src_name=oai_src_name,
oai_src_baseurl=oai_src_baseurl,
oai_src_prefix=oai_src_prefix,
oai_src_frequency=oai_src_frequency,
oai_src_config=oai_src_config,
oai_src_post=oai_src_post,
oai_src_sets=oai_src_sets,
oai_src_bibfilter=oai_src_bibfilter,
ln=ln,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addsource(req, ln=CFG_SITE_LANG, oai_src_name='', oai_src_baseurl ='', oai_src_prefix='', oai_src_frequency='', oai_src_lastrun='', oai_src_config='', oai_src_post='', confirm=-1, oai_src_sets=[], oai_src_bibfilter=''):
- navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/bibharvestadmin.py">BibHarvest Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/bibharvestadmin.py">BibHarvest Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return page(title="BibHarvest Admin Interface - Error",
body=e,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
auth = bhc.check_user(req,'cfgbibharvest')
if not auth[0]:
if isinstance(oai_src_sets, str):
oai_src_sets = [oai_src_sets]
return page(title="Add new OAI Source",
body=bhc.perform_request_addsource(oai_src_name=oai_src_name,
oai_src_baseurl=oai_src_baseurl,
oai_src_prefix=oai_src_prefix,
oai_src_frequency=oai_src_frequency,
oai_src_lastrun=oai_src_lastrun,
oai_src_config=oai_src_config,
oai_src_post=oai_src_post,
oai_src_sets=oai_src_sets,
oai_src_bibfilter=oai_src_bibfilter,
ln=ln,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def delsource(req, oai_src_id=None, ln=CFG_SITE_LANG, confirm=0):
- navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/bibharvestadmin.py">BibHarvest Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/bibharvestadmin.py">BibHarvest Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return page(title="BibHarvest Admin Interface - Error",
body=e,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
auth = bhc.check_user(req,'cfgbibharvest')
if not auth[0]:
return page(title="Delete OAI Source",
body=bhc.perform_request_delsource(oai_src_id=oai_src_id,
ln=ln,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
diff --git a/modules/bibharvest/web/admin/oaiarchiveadmin.py b/modules/bibharvest/web/admin/oaiarchiveadmin.py
index 63db1f158..837939f21 100644
--- a/modules/bibharvest/web/admin/oaiarchiveadmin.py
+++ b/modules/bibharvest/web/admin/oaiarchiveadmin.py
@@ -1,180 +1,180 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio OAI Archive Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import sys
import invenio.oaiarchiveadminlib as bhc
from invenio.webpage import page, create_error_box
-from invenio.config import weburl,CFG_SITE_LANG
+from invenio.config import CFG_SITE_URL,CFG_SITE_LANG
from invenio.dbquery import Error
from invenio.webuser import getUid, page_not_authorized
def index(req, ln=CFG_SITE_LANG):
navtrail_previous_links = bhc.getnavtrail()
try:
uid = getUid(req)
except Error, e:
return page(title="OAI Archive Admin Interface - Error",
body=e,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
auth = bhc.check_user(req,'cfgoairepository')
if not auth[0]:
return page(title="OAI Repository Admin Interface",
body=bhc.perform_request_index(ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addset(req, oai_set_name='', oai_set_spec='', oai_set_collection='', oai_set_description='', oai_set_definition='', oai_set_reclist='', oai_set_p1='', oai_set_f1='',oai_set_m1='', oai_set_p2='', oai_set_f2='', oai_set_m2='', oai_set_p3='', oai_set_f3='', oai_set_m3='', oai_set_op1='a', oai_set_op2='a', ln=CFG_SITE_LANG, func=0):
- navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/oaiarchiveadmin.py">OAI Repository Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/oaiarchiveadmin.py">OAI Repository Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return page(title="OAI Archive Admin Interface - Error",
body=e,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
auth = bhc.check_user(req,'cfgoairepository')
if not auth[0]:
return page(title="Add new OAI Set",
body=bhc.perform_request_addset(oai_set_name=oai_set_name,
oai_set_spec=oai_set_spec,
oai_set_collection=oai_set_collection,
oai_set_description=oai_set_description,
oai_set_definition=oai_set_definition,
oai_set_reclist=oai_set_reclist,
oai_set_p1=oai_set_p1,
oai_set_f1=oai_set_f1,
oai_set_m1=oai_set_m1,
oai_set_p2=oai_set_p2,
oai_set_f2=oai_set_f2,
oai_set_m2=oai_set_m2,
oai_set_p3=oai_set_p3,
oai_set_f3=oai_set_f3,
oai_set_m3=oai_set_m3,
oai_set_op1=oai_set_op1,
oai_set_op2=oai_set_op2,
ln=ln,
func=func),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def delset(req, oai_set_id=None, ln=CFG_SITE_LANG, func=0):
- navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/oaiarchiveadmin.py">OAI Repository Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/oaiarchiveadmin.py">OAI Repository Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return page(title="OAI Archive Admin Interface - Error",
body=e,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
auth = bhc.check_user(req,'cfgoairepository')
if not auth[0]:
return page(title="Delete OAI Set",
body=bhc.perform_request_delset(oai_set_id=oai_set_id,
ln=ln,
func=func),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def editset(req, oai_set_id=None, oai_set_name='', oai_set_spec='', oai_set_collection='', oai_set_description='', oai_set_definition='', oai_set_reclist='', oai_set_p1='', oai_set_f1='', oai_set_m1='', oai_set_p2='', oai_set_f2='', oai_set_m2='', oai_set_p3='', oai_set_f3='', oai_set_m3='', oai_set_op1='a', oai_set_op2='a', ln=CFG_SITE_LANG, func=0):
- navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/oaiarchiveadmin.py">OAI Repository Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = bhc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibharvest/oaiarchiveadmin.py">OAI Repository Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return page(title="OAI Archive Admin Interface - Error",
body=e,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
auth = bhc.check_user(req,'cfgoairepository')
if not auth[0]:
return page(title="Edit OAI Set",
body=bhc.perform_request_editset(oai_set_id=oai_set_id,
oai_set_name=oai_set_name,
oai_set_spec=oai_set_spec,
oai_set_collection=oai_set_collection,
oai_set_description=oai_set_description,
oai_set_definition=oai_set_definition,
oai_set_reclist=oai_set_reclist,
oai_set_p1=oai_set_p1,
oai_set_f1=oai_set_f1,
oai_set_m1=oai_set_m1,
oai_set_p2=oai_set_p2,
oai_set_f2=oai_set_f2,
oai_set_m2=oai_set_m2,
oai_set_p3=oai_set_p3,
oai_set_f3=oai_set_f3,
oai_set_m3=oai_set_m3,
oai_set_op1=oai_set_op1,
oai_set_op2=oai_set_op2,
ln=ln,
func=func),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
diff --git a/modules/bibindex/doc/admin/bibindex-admin-guide.webdoc b/modules/bibindex/doc/admin/bibindex-admin-guide.webdoc
index f659602da..a921b3810 100644
--- a/modules/bibindex/doc/admin/bibindex-admin-guide.webdoc
+++ b/modules/bibindex/doc/admin/bibindex-admin-guide.webdoc
@@ -1,292 +1,292 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibIndex Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p><table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
WARNING: BIBINDEX ADMIN GUIDE IS UNDER DEVELOPMENT
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
BibIndex Admin Guide is not yet completed. Most of admin-level
functionality for BibIndex exists only in commandline mode. We
are in the process of developing both the guide as well as the
web admin interface. If you are interested in seeing some
specific things implemented with high priority, please contact us
at <CFG_SITE_SUPPORT_EMAIL>. Thanks for your interest!
</td>
</tr>
</tbody>
</table></p>
<h2>Contents</h2>
<strong>1.<a href="#1">Overview</a></strong><br/>
<strong>2. <a href="#2">Configure Metadata Tags and Fields</a></strong><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.1 <a href="#2.1">Configure Physical MARC Tags</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.2 <a href="#2.2">Configure Logical Fields</a><br/>
<strong>3. <a href="#3">Configure Word/Phrase Indexes</a></strong><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1 <a href="#3.1">Define New Index</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2 <a href="#3.2">Configure Word-Breaking Procedure</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3 <a href="#3.3">Configure Stopwords List</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4 <a href="#3.4">Configure Stemming</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.5 <a href="#3.5">Configure Word Length</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.6 <a href="#3.6">Configure Removal of HTML Code</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.7 <a href="#3.7">Configure Accent Stripping</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.8 <a href="#3.8">Configure Fulltext Indexing</a><br/>
<strong>4. <a href="#4">Run BibIndex Daemon</a></strong><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.1 <a href="#4.1">Run BibIndex daemon</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.2 <a href="#4.2">Checking and repairing indexes</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.3 <a href="#4.3">Reindexing</a><br/>
<a name="1"></a><h2>1. Overview</h2>
<a name="2"></a><h2>2. Configure Metadata Tags and Fields</h2>
<a name="2.1"></a><h3>2.1 Configure Physical MARC Tags</h3>
<a name="2.2"></a><h3>2.2 Configure Logical Fields</h3>
<a name="3"></a><h2>3. Configure Word/Phrase Indexes</h2>
<a name="3.1"></a><h3>3.1 Define New Index</h3>
<p>To define a new index you must first give the index a internal
name. An empty index is then created by preparing the database tables.</p>
<p>Before the index can be used for searching, the fields that should be
included in the index must be selected.</p>
<p>When desired to fill the index based on the fields selected, you can
schedule the update by running <b>bibindex -w indexname</b> together
with other desired parameters.</p>
<a name="3.2"></a><h3>3.2 Configure Word-Breaking Procedure</h3>
<p>Can be configured by changing
<b>CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS</b> and
<b>CFG_BIBINDEX_CHARS_PUNCTUATION</b> in the general config file.</p>
<p>How the words are broken up defines what is added to the index. Should
only "director-general" be added, or should "director", "general" and
"director-general" be added? The index can vary between 300 000 and 3
000 000 terms based the policy for breaking words.</p>
<a name="3.3"></a><h3>3.3 Configure Stopwords List</h3>
<p>BibIndex supports stopword removal by not adding words which exists in
a given stopword list to the index. Stopword removal makes the index
smaller by removing much used words.</p>
<p>Which stopword list that should be used can be configured in the
general config file file by changing the value of the variable
CFG_BIBINDEX_PATH_TO_STOPWORDS_FILE. If no stopword list should be
used, the value should be 0.</p>
<a name="3.4"></a><h3>3.4 Configure stemming</h3>
<p>The BibIndex indexer supports stemming, removing the ending of
words thus creating a smaller indexer. For example, using English, the
word "information" will be stemmed to "inform"; or "looking", "looks",
and "looked" will be all stemmed to "look", thus giving more hits to each
word.</p>
<p>Currently you can configure the stemming language on a per-index basis. All
searches referring a stemmed index will also be stemmed based on the same
language.</p>
<a name="3.5"></a><h3>3.5 Configure Word Length</h3>
<p>By setting the value of <b>CFG_BIBINDEX_MIN_WORD_LENGTH</b> in the
general config file higher than 0, only words with the number of
characters higher than this will be added to the index.</p>
<a name="3.6"></a><h3>3.6 Configure Removal of HTML and LaTeX Code</h3>
<p>By setting the value of <b>CFG_BIBINDEX_REMOVE_HTML_MARKUP</b> in
the general config file, the indexer may try to remove all HTML code
from documents before indexing, and index only the text left. (HTML
code is defined as everything between '&lt;' and '>' in a text.)</p>
<p>By setting the value of <b>CFG_BIBINDEX_REMOVE_LATEX_MARKUP</b> in
the general config file, the indexer may try to remove all LaTeX code
from documents before indexing, and index only the text left. (LaTeX
code is defined as everything between '\command{' and '}' in a text, or
'{\command ' and '}').</p>
<a name="3.7"></a><h3>3.7 Configure Accent Stripping</h3>
<a name="3.8"></a><h3>3.8 Configure Fulltext Indexing</h3>
<p>The metadata tags are usually indexed by its content. There are
special cases however, such as the fulltext indexing. In this case
the tag contains an URL to the fulltext material and we would like to
fetch this material and index words found in this material rather than
in the metadata itself. This is possible via special tag assignement
via <code>tagToWordsFunctions</code> variable.</p>
<p>The default setup is configured in the way that if the indexer sees
that it has to index tag <code>8564_u</code>, it switches into the
fulltext indexing mode described above. It can index locally stored
files or even fetch them from external URLs, depending on the value of
the <b>CFG_BIBINDEX_FULLTEXT_INDEX_LOCAL_FILES_ONLY</b> configuration
variable. When fetching files from remote URLs, when it ends on a
splash page (an intermediate page before getting to fulltext file
itself), it can find and follow any further links to fulltext files.</p>
<p>The default setup also differentiate between metadata and fulltext
indexing, so that <code>any field</code> index does process only
metadata, not fulltext. If you want to have the fulltext indexed
together with the metadata, so that both are searched by default, you
can go to BibIndex Admin interface and in the Manage Logical Fields
explicitly add the tag <code>8564_u</code> under <code>any
field</code> field.</p>
<a name="4"></a><h2>4. Run BibIndex Daemon</h2>
<a name="4.1"></a><h3>4.1 Run BibIndex daemon</h3>
<p>To index your newly created or modified documents, bibindex must be
run periodically via bibsched. This is achieved by the sleep option
(-s) to bibindex. For more information please see <a
href="../howto/run.html">HOWTO Run</a> admin guide.</p>
<a name="4.2"></a><h3>4.2 Checking and repairing indexes</h3>
<p>Upon each indexing run, bibindex checks and reports any
inconsistencies in the indexes. You can also manually check for the
index corruption yourself by using the check (-k) option to bibindex.</p>
<p>If a problem is found during the check, bibindex hints you to run
repairing (-r). If you run it, then during repair bibindex tries to
correct problems automatically by its own means. Usually it succeeds.</p>
<p>When the automatic repairing does not succeed though, then manual
intervention is required. The easiest thing to get the indexes back
to shape are commands like: (assuming the problem is with the index ID
1):
<blockquote>
<pre>
$ echo "DELETE FROM idxWORD01R WHERE type='TEMPORARY' or type='FUTURE';" | \
/opt/cds-invenio/bin/dbexec
</pre>
</blockquote>
to leave only the 'CURRENT' reverse index. After that you can rerun
the index checking procedure (-k) and, if successful, continue with
the normal web site operation. However, a full reindexing should be
scheduled for the forthcoming night or weekend.</p>
<a name="4.3"></a><h3>4.3 Reindexing</h3>
<p>The procedure of reindexing is taking place into the real indexes that
are also used for searching. Therefore the end users will feel
immediately any change in the indexes. If you need to reindex your
records from scratch, then the best procedure is the following: reindex
the collection index only (fast operation), recreate collection cache,
and only after that reindex all the other indexes (slow operation).
This will ensure that the records in your system will be at least
browsable while the indexes are being rebuilt. The steps to perform are:</p>
<p>First we reindex the collection index:
<blockquote>
<pre>
$ bibindex --reindex -f50000 -wcollection # reindex the collection index (fast)
$ echo "UPDATE collection SET reclist=NULL;" | \
/opt/cds-invenio/bin/dbexec # clean collection cache
$ webcoll -f # recreate the collection cache
$ bibsched # run the two above-submitted tasks
$ sudo apachectl restart
</pre>
</blockquote></p>
<p>Then we launch (slower) reindexing of the remaining indexes:
<blockquote>
<pre>
$ bibindex --reindex -f50000 # reindex other indexes (slow)
$ webcoll -f
$ bibsched # run the two above-submitted tasks, and put the queue back in auto mode
$ sudo apachectl restart
</pre>
</blockquote></p>
<p>You may optionally want to reindex the word ranking tables:
<blockquote>
<pre>
$ bibsched # wait for all active tasks to finish, and put the queue into manual mode
$ cd cds-invenio-0.92.1 # source dir
$ grep rnkWORD ./modules/miscutil/sql/tabbibclean.sql | \
/opt/cds-invenio/bin/dbexec # truncate rank indexes
$ echo "UPDATE rnkMETHOD SET last_updated='0000-00-00 00:00:00';" | \
/opt/cds-invenio/bin/dbexec # rewind the last ranking time
</pre>
</blockquote></p>
<p>Secondly, if you have been using custom ranking methods using new
rnkWORD* tables (most probably you have not), you would have to
truncate them too:
<blockquote>
<pre>
&nbsp; # find out which custom ranking indexes were added:
&nbsp; $ echo "SELECT id FROM rnkMETHOD" | /opt/cds-invenio/bin/dbexec
&nbsp; id
&nbsp; 66
&nbsp; 67
&nbsp; [...]
&nbsp;
&nbsp; # for every ranking index id, truncate corresponding ranking tables:
&nbsp; $ echo "TRUNCATE rnkWORD66F" | /opt/cds-invenio/bin/dbexec
&nbsp; $ echo "TRUNCATE rnkWORD66R" | /opt/cds-invenio/bin/dbexec
&nbsp; $ echo "TRUNCATE rnkWORD67F" | /opt/cds-invenio/bin/dbexec
&nbsp; $ echo "TRUNCATE rnkWORD67R" | /opt/cds-invenio/bin/dbexec
</pre>
</blockquote></p>
<p>At last, we launch reindexing of the ranking indexes:
<blockquote>
<pre>
$ bibrank -f50000
$ bibsched # run the three above-submitted tasks, and put the queue back in auto mode
$ sudo apachectl restart
</pre>
</blockquote>
and we are done.</p>
<p>In the future CDS Invenio should ideally run indexing into
invisible tables that would be switched against the production ones
once the indexing process is successfully over. For the time being,
if reindexing takes several hours in your installation (e.g. if you
have 1,000,000 records), you may want to mysqlhotcopy your tables and
run reindexing on those copies yourself.</p>
diff --git a/modules/bibindex/lib/bibindex_engine.py b/modules/bibindex/lib/bibindex_engine.py
index a2b984185..c98849ac2 100644
--- a/modules/bibindex/lib/bibindex_engine.py
+++ b/modules/bibindex/lib/bibindex_engine.py
@@ -1,1585 +1,1585 @@
# -*- coding: utf-8 -*-
##
## $Id$
## BibIndxes bibliographic data, reference and fulltext indexing utility.
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
BibIndex indexing engine implementation. See bibindex executable for entry point.
"""
__revision__ = "$Id$"
import os
import re
import sys
import time
import urllib2
import tempfile
import traceback
from invenio.config import \
CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS, \
CFG_BIBINDEX_CHARS_PUNCTUATION, \
CFG_BIBINDEX_FULLTEXT_INDEX_LOCAL_FILES_ONLY, \
CFG_BIBINDEX_MIN_WORD_LENGTH, \
CFG_BIBINDEX_REMOVE_HTML_MARKUP, \
CFG_BIBINDEX_REMOVE_LATEX_MARKUP, \
- weburl, CFG_TMPDIR
+ CFG_SITE_URL, CFG_TMPDIR
from invenio.bibindex_engine_config import *
from invenio.bibdocfile import bibdocfile_url_to_fullpath, bibdocfile_url_p, decompose_bibdocfile_url
from invenio.search_engine import perform_request_search, strip_accents, wash_index_term, get_index_stemming_language
from invenio.dbquery import run_sql, DatabaseError, serialize_via_marshal, deserialize_via_marshal
from invenio.bibindex_engine_stopwords import is_stopword
from invenio.bibindex_engine_stemmer import stem
from invenio.bibtask import task_init, write_message, get_datetime, \
task_set_option, task_get_option, task_get_task_param, task_update_status, \
task_update_progress
from invenio.intbitset import intbitset
from invenio.errorlib import register_exception
## import optional modules:
try:
import psyco
psyco.bind(get_words_from_phrase)
psyco.bind(WordTable.merge_with_old_recIDs)
except:
pass
## precompile some often-used regexp for speed reasons:
re_subfields = re.compile('\$\$\w')
re_html = re.compile("(?s)<[^>]*>|&#?\w+;")
re_block_punctuation_begin = re.compile(r"^"+CFG_BIBINDEX_CHARS_PUNCTUATION+"+")
re_block_punctuation_end = re.compile(CFG_BIBINDEX_CHARS_PUNCTUATION+"+$")
re_punctuation = re.compile(CFG_BIBINDEX_CHARS_PUNCTUATION)
re_separators = re.compile(CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS)
re_datetime_shift = re.compile("([-\+]{0,1})([\d]+)([dhms])")
nb_char_in_line = 50 # for verbose pretty printing
chunksize = 1000 # default size of chunks that the records will be treated by
base_process_size = 4500 # process base size
_last_word_table = None
## Dictionary merging functions
def intersection(dict1, dict2):
"Returns intersection of the two dictionaries."
int_dict = {}
if len(dict1) > len(dict2):
for e in dict2:
if dict1.has_key(e):
int_dict[e] = 1
else:
for e in dict1:
if dict2.has_key(e):
int_dict[e] = 1
return int_dict
def union(dict1, dict2):
"Returns union of the two dictionaries."
union_dict = {}
for e in dict1.keys():
union_dict[e] = 1
for e in dict2.keys():
union_dict[e] = 1
return union_dict
def diff(dict1, dict2):
"Returns dict1 - dict2."
diff_dict = {}
for e in dict1.keys():
if not dict2.has_key(e):
diff_dict[e] = 1
return diff_dict
def list_union(list1, list2):
"Returns union of the two lists."
union_dict = {}
for e in list1:
union_dict[e] = 1
for e in list2:
union_dict[e] = 1
return union_dict.keys()
## safety function for killing slow DB threads:
def kill_sleepy_mysql_threads(max_threads=CFG_MAX_MYSQL_THREADS, thread_timeout=CFG_MYSQL_THREAD_TIMEOUT):
"""Check the number of DB threads and if there are more than
MAX_THREADS of them, lill all threads that are in a sleeping
state for more than THREAD_TIMEOUT seconds. (This is useful
for working around the the max_connection problem that appears
during indexation in some not-yet-understood cases.) If some
threads are to be killed, write info into the log file.
"""
res = run_sql("SHOW FULL PROCESSLIST")
if len(res) > max_threads:
for row in res:
r_id, dummy, dummy, dummy, r_command, r_time, dummy, dummy = row
if r_command == "Sleep" and int(r_time) > thread_timeout:
run_sql("KILL %s", (r_id,))
write_message("WARNING: too many DB threads, killing thread %s" % r_id, verbose=1)
return
## MARC-21 tag/field access functions
def get_fieldvalues(recID, tag):
"""Returns list of values of the MARC-21 'tag' fields for the record
'recID'."""
out = []
bibXXx = "bib" + tag[0] + tag[1] + "x"
bibrec_bibXXx = "bibrec_" + bibXXx
query = "SELECT value FROM %s AS b, %s AS bb WHERE bb.id_bibrec=%s AND bb.id_bibxxx=b.id AND tag LIKE '%s'" \
% (bibXXx, bibrec_bibXXx, recID, tag)
res = run_sql(query)
for row in res:
out.append(row[0])
return out
def get_associated_subfield_value(recID, tag, value, associated_subfield_code):
"""Return list of ASSOCIATED_SUBFIELD_CODE, if exists, for record
RECID and TAG of value VALUE. Used by fulltext indexer only.
Note: TAG must be 6 characters long (tag+ind1+ind2+sfcode),
otherwise en empty string is returned.
FIXME: what if many tag values have the same value but different
associated_subfield_code? Better use bibrecord library for this.
"""
out = ""
if len(tag) != 6:
return out
bibXXx = "bib" + tag[0] + tag[1] + "x"
bibrec_bibXXx = "bibrec_" + bibXXx
query = """SELECT bb.field_number, b.tag, b.value FROM %s AS b, %s AS bb
WHERE bb.id_bibrec=%s AND bb.id_bibxxx=b.id AND tag LIKE '%s%%'""" % \
(bibXXx, bibrec_bibXXx, recID, tag[:-1])
res = run_sql(query)
field_number = -1
for row in res:
if row[1] == tag and row[2] == value:
field_number = row[0]
if field_number > 0:
for row in res:
if row[0] == field_number and row[1] == tag[:-1] + associated_subfield_code:
out = row[2]
break
return out
def get_field_tags(field):
"""Returns a list of MARC tags for the field code 'field'.
Returns empty list in case of error.
Example: field='author', output=['100__%','700__%']."""
out = []
query = """SELECT t.value FROM tag AS t, field_tag AS ft, field AS f
WHERE f.code='%s' AND ft.id_field=f.id AND t.id=ft.id_tag
ORDER BY ft.score DESC""" % field
res = run_sql(query)
for row in res:
out.append(row[0])
return out
## Fulltext word extraction functions
def get_fulltext_urls_from_html_page(htmlpagebody):
"""Parses htmlpagebody data (the splash page content) looking for
url_directs referring to probable fulltexts.
Returns an array of (ext,url_direct) to fulltexts.
Note: it looks for file format extensions as defined by global
'CONV_PROGRAMS' structure, minus the HTML ones, because we don't
want to index HTML pages that the splash page might point to.
"""
out = []
for ext in CONV_PROGRAMS.keys():
expr = re.compile( r"\"(http://[\w]+\.+[\w]+[^\"'><]*\." + \
ext + r")\"")
match = expr.search(htmlpagebody)
if match and ext not in ['htm', 'html']:
out.append([ext, match.group(1)])
#else: # FIXME: workaround for getfile, should use bibdoc tables
#expr_getfile = re.compile(r"\"(http://.*getfile\.py\?.*format=" + ext + r"&version=.*)\"")
#match = expr_getfile.search(htmlpagebody)
#if match and ext not in ['htm', 'html']:
#out.append([ext, match.group(1)])
return out
def get_words_from_local_fulltext(path, ext='', stemming_language=None):
# FIXME to be continued
raise NotImplemented
if not ext:
ext = path[len(file_strip_ext(path))+1:].lower()
tmp_name = path.replace(';', '\\;')
tmp_dst_name = tempfile.mkstemp('invenio.tmp.txt', dir=CFG_TMPDIR)[1]
# try all available conversion programs according to their order:
bingo = 0
for conv_program in CONV_PROGRAMS.get(ext, []):
if os.path.exists(conv_program):
# intelligence on how to run various conversion programs:
cmd = "" # wil keep command to run
bingo = 0 # had we success?
if os.path.basename(conv_program) == "pdftotext":
cmd = "%s -enc UTF-8 %s %s" % (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "pstotext":
if ext == "ps.gz":
# is there gzip available?
if os.path.exists(CONV_PROGRAMS_HELPERS["gz"]):
cmd = "%s -cd %s | %s > %s" \
% (CONV_PROGRAMS_HELPERS["gz"], tmp_name, conv_program, tmp_dst_name)
else:
cmd = "%s %s > %s" \
% (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "ps2ascii":
if ext == "ps.gz":
# is there gzip available?
if os.path.exists(CONV_PROGRAMS_HELPERS["gz"]):
cmd = "%s -cd %s | %s > %s"\
% (CONV_PROGRAMS_HELPERS["gz"], tmp_name,
conv_program, tmp_dst_name)
else:
cmd = "%s %s %s" \
% (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "antiword":
cmd = "%s %s > %s" % (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "catdoc":
cmd = "%s %s > %s" % (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "wvText":
cmd = "%s %s %s" % (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "ppthtml":
# is there html2text available?
if os.path.exists(CONV_PROGRAMS_HELPERS["html"]):
cmd = "%s %s | %s > %s"\
% (conv_program, tmp_name,
CONV_PROGRAMS_HELPERS["html"], tmp_dst_name)
else:
cmd = "%s %s > %s" \
% (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "xlhtml":
# is there html2text available?
if os.path.exists(CONV_PROGRAMS_HELPERS["html"]):
cmd = "%s %s | %s > %s" % \
(conv_program, tmp_name,
CONV_PROGRAMS_HELPERS["html"], tmp_dst_name)
else:
cmd = "%s %s > %s" % \
(conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "html2text":
cmd = "%s %s > %s" % \
(conv_program, tmp_name, tmp_dst_name)
else:
sys.stderr.write("Error: Do not know how to handle %s conversion program.\n" % conv_program)
# try to run it:
try:
write_message("..... launching %s" % cmd, verbose=9)
# Note we replace ; in order to make happy internal file names
errcode = os.system(cmd)
if errcode == 0 and os.path.exists(tmp_dst_name):
bingo = 1
break # bingo!
else:
write_message("Error while running %s for %s.\n" % (cmd, path), sys.stderr)
except:
write_message("Error running %s for %s.\n" % (cmd, path), sys.stderr)
# were we successful?
if bingo:
tmp_name_txt_file = open(tmp_dst_name)
for phrase in tmp_name_txt_file.xreadlines():
for word in get_words_from_phrase(phrase, stemming_language):
if not words.has_key(word):
words[word] = 1
tmp_name_txt_file.close()
else:
write_message("No conversion success for %s.\n" % (path), sys.stderr)
# delete temp files (they might not exist):
try:
os.unlink(tmp_dst_name)
except StandardError:
write_message("Error: Could not delete file. It didn't exist", sys.stderr)
write_message("... reading fulltext files from %s ended" % path, verbose=2)
return words.keys()
def get_words_from_fulltext(url_direct_or_indirect, stemming_language=None):
"""Returns all the words contained in the document specified by
URL_DIRECT_OR_INDIRECT with the words being split by various
SRE_SEPARATORS regexp set earlier. If FORCE_FILE_EXTENSION is
set (e.g. to "pdf", then treat URL_DIRECT_OR_INDIRECT as a PDF
file. (This is interesting to index Indico for example.) Note
also that URL_DIRECT_OR_INDIRECT may be either a direct URL to
the fulltext file or an URL to a setlink-like page body that
presents the links to be indexed. In the latter case the
URL_DIRECT_OR_INDIRECT is parsed to extract actual direct URLs
to fulltext documents, for all knows file extensions as
specified by global CONV_PROGRAMS config variable.
"""
if CFG_BIBINDEX_FULLTEXT_INDEX_LOCAL_FILES_ONLY and \
- url_direct_or_indirect.find(weburl) < 0:
+ url_direct_or_indirect.find(CFG_SITE_URL) < 0:
return []
write_message("... reading fulltext files from %s started" % url_direct_or_indirect, verbose=2)
fulltext_urls = []
if bibdocfile_url_p(url_direct_or_indirect):
write_message("... url %s is an internal url" % url_direct_or_indirect, verbose=9)
ext = decompose_bibdocfile_url(url_direct_or_indirect)[2]
if ext[0] == '.':
ext = ext[1:].lower()
fulltext_urls = [(ext, url_direct_or_indirect)]
else:
# check for direct link in url
url_direct_or_indirect_ext = url_direct_or_indirect.split(".")[-1].lower()
if url_direct_or_indirect_ext in CONV_PROGRAMS.keys():
fulltext_urls = [(url_direct_or_indirect_ext, url_direct_or_indirect)]
# Indirect URL. Try to discover the real fulltext(s) from this splash page URL.
if not fulltext_urls:
# read "setlink" data
try:
htmlpagebody = urllib2.urlopen(url_direct_or_indirect).read()
except Exception, e:
register_exception()
sys.stderr.write("Error: Cannot read %s: %s" % (url_direct_or_indirect, e))
return []
fulltext_urls = get_fulltext_urls_from_html_page(htmlpagebody)
write_message("... fulltext_urls = %s" % fulltext_urls, verbose=9)
write_message('... data to elaborate: %s' % fulltext_urls, verbose=9)
words = {}
# process as many urls as they were found:
for (ext, url_direct) in fulltext_urls:
write_message(".... processing %s from %s started" % (ext, url_direct), verbose=2)
# sanity check:
if not url_direct:
break
if bibdocfile_url_p(url_direct):
# Let's manage this with BibRecDocs...
- # We got something like http://$(weburl)/record/xxx/yyy.ext
+ # We got something like http://$(CFG_SITE_URL)/record/xxx/yyy.ext
try:
tmp_name = bibdocfile_url_to_fullpath(url_direct)
write_message("Found internal path %s for url %s" % (tmp_name, url_direct), verbose=2)
no_src_delete = True
except Exception, e:
register_exception()
sys.stderr.write("Error in retrieving fulltext from internal url %s: %s\n" % (url_direct, e))
break # try other fulltext files...
else:
# read fulltext file:
try:
url = urllib2.urlopen(url_direct)
no_src_delete = False
except Exception, e:
register_exception()
sys.stderr.write("Error: Cannot read %s: %s\n" % (url_direct, e))
break # try other fulltext files...
tmp_fd, tmp_name = tempfile.mkstemp('invenio.tmp')
data_chunk = url.read(8*1024)
while data_chunk:
os.write(tmp_fd, data_chunk)
data_chunk = url.read(8*1024)
os.close(tmp_fd)
tmp_dst_name = tempfile.mkstemp('invenio.tmp.txt', dir=CFG_TMPDIR)[1]
bingo = 0
# try all available conversion programs according to their order:
for conv_program in CONV_PROGRAMS.get(ext, []):
if os.path.exists(conv_program):
# intelligence on how to run various conversion programs:
cmd = "" # will keep command to run
bingo = 0 # had we success?
if os.path.basename(conv_program) == "pdftotext":
cmd = "%s -enc UTF-8 %s %s" % (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "pstotext":
if ext == "ps.gz":
# is there gzip available?
if os.path.exists(CONV_PROGRAMS_HELPERS["gz"]):
cmd = "%s -cd %s | %s > %s" \
% (CONV_PROGRAMS_HELPERS["gz"], tmp_name, conv_program, tmp_dst_name)
else:
cmd = "%s %s > %s" \
% (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "ps2ascii":
if ext == "ps.gz":
# is there gzip available?
if os.path.exists(CONV_PROGRAMS_HELPERS["gz"]):
cmd = "%s -cd %s | %s > %s"\
% (CONV_PROGRAMS_HELPERS["gz"], tmp_name,
conv_program, tmp_dst_name)
else:
cmd = "%s %s %s" \
% (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "antiword":
cmd = "%s %s > %s" % (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "catdoc":
cmd = "%s %s > %s" % (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "wvText":
cmd = "%s %s %s" % (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "ppthtml":
# is there html2text available?
if os.path.exists(CONV_PROGRAMS_HELPERS["html"]):
cmd = "%s %s | %s > %s"\
% (conv_program, tmp_name,
CONV_PROGRAMS_HELPERS["html"], tmp_dst_name)
else:
cmd = "%s %s > %s" \
% (conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "xlhtml":
# is there html2text available?
if os.path.exists(CONV_PROGRAMS_HELPERS["html"]):
cmd = "%s %s | %s > %s" % \
(conv_program, tmp_name,
CONV_PROGRAMS_HELPERS["html"], tmp_dst_name)
else:
cmd = "%s %s > %s" % \
(conv_program, tmp_name, tmp_dst_name)
elif os.path.basename(conv_program) == "html2text":
cmd = "%s %s > %s" % \
(conv_program, tmp_name, tmp_dst_name)
else:
sys.stderr.write("Error: Do not know how to handle %s conversion program.\n" % conv_program)
# try to run it:
try:
write_message("..... launching %s" % cmd, verbose=9)
# Note we replace ; in order to make happy internal file names
errcode = os.system(cmd.replace(';', '\\;'))
if errcode == 0 and os.path.exists(tmp_dst_name):
bingo = 1
break # bingo!
else:
write_message("Error while running %s for %s.\n" % (cmd, url_direct), sys.stderr)
except:
write_message("Error running %s for %s.\n" % (cmd, url_direct), sys.stderr)
# were we successful?
if bingo:
tmp_name_txt_file = open(tmp_dst_name)
for phrase in tmp_name_txt_file.xreadlines():
for word in get_words_from_phrase(phrase, stemming_language):
if not words.has_key(word):
words[word] = 1
tmp_name_txt_file.close()
else:
write_message("No conversion success for %s.\n" % (url_direct), sys.stderr)
# delete temp files (they might not exist):
try:
if not no_src_delete:
os.unlink(tmp_name)
os.unlink(tmp_dst_name)
except StandardError:
write_message("Error: Could not delete file. It didn't exist", sys.stderr)
write_message(".... processing %s from %s ended" % (ext, url_direct), verbose=2)
write_message("... reading fulltext files from %s ended" % url_direct_or_indirect, verbose=2)
return words.keys()
latex_markup_re = re.compile(r"\\begin(\[.+?\])?\{.+?\}|\\end\{.+?}|\\\w+(\[.+?\])?\{(?P<inside1>.*?)\}|\{\\\w+ (?P<inside2>.*?)\}")
def remove_latex_markup(phrase):
ret_phrase = ''
index = 0
for match in latex_markup_re.finditer(phrase):
ret_phrase += phrase[index:match.start()]
ret_phrase += match.group('inside1') or match.group('inside2') or ''
index = match.end()
ret_phrase += phrase[index:]
return ret_phrase
def get_nothing_from_phrase(phrase, stemming_language=None):
""" A dump implementation of get_words_from_phrase to be used when
when a tag should not be indexed (such as when trying to extract phrases from
8564_u)."""
return []
latex_formula_re = re.compile(r'\$.*?\$|\\\[.*?\\\]')
def get_words_from_phrase(phrase, stemming_language=None):
"""Return list of words found in PHRASE. Note that the phrase is
split into groups depending on the alphanumeric characters and
punctuation characters definition present in the config file.
"""
words = {}
formulas = []
if CFG_BIBINDEX_REMOVE_HTML_MARKUP and phrase.find("</") > -1:
phrase = re_html.sub(' ', phrase)
if CFG_BIBINDEX_REMOVE_LATEX_MARKUP:
formulas = latex_formula_re.findall(phrase)
phrase = remove_latex_markup(phrase)
phrase = latex_formula_re.sub(' ', phrase)
phrase = phrase.lower()
# 1st split phrase into blocks according to whitespace
for block in strip_accents(phrase).split():
# 2nd remove leading/trailing punctuation and add block:
block = re_block_punctuation_begin.sub("", block)
block = re_block_punctuation_end.sub("", block)
if block:
if stemming_language:
block = apply_stemming_and_stopwords_and_length_check(block, stemming_language)
if block:
words[block] = 1
# 3rd break each block into subblocks according to punctuation and add subblocks:
for subblock in re_punctuation.split(block):
if stemming_language:
subblock = apply_stemming_and_stopwords_and_length_check(subblock, stemming_language)
if subblock:
words[subblock] = 1
# 4th break each subblock into alphanumeric groups and add groups:
for alphanumeric_group in re_separators.split(subblock):
if stemming_language:
alphanumeric_group = apply_stemming_and_stopwords_and_length_check(alphanumeric_group, stemming_language)
if alphanumeric_group:
words[alphanumeric_group] = 1
for block in formulas:
words[block] = 1
return words.keys()
phrase_delimiter_re = re.compile(r'[\.:;\?\!]')
space_cleaner_re = re.compile(r'\s+')
def get_phrases_from_phrase(phrase, stemming_language=None):
"""Return list of phrases found in PHRASE. Note that the phrase is
split into groups depending on the alphanumeric characters and
punctuation characters definition present in the config file.
"""
words = {}
phrase = strip_accents(phrase)
# 1st split phrase into blocks according to whitespace
for block1 in phrase_delimiter_re.split(strip_accents(phrase)):
block1 = block1.strip()
if block1 and stemming_language:
new_words = []
for block2 in re_punctuation.split(block1):
block2 = block2.strip()
if block2:
for block3 in block2.split():
block3 = block3.strip()
if block3:
block3 = apply_stemming_and_stopwords_and_length_check(block3, stemming_language)
if block3:
new_words.append(block3)
block1 = ' '.join(new_words)
if block1:
words[block1] = 1
return words.keys()
def apply_stemming_and_stopwords_and_length_check(word, stemming_language):
"""Return WORD after applying stemming and stopword and length checks.
See the config file in order to influence these.
"""
# now check against stopwords:
if is_stopword(word):
return ""
# finally check the word length:
if len(word) < CFG_BIBINDEX_MIN_WORD_LENGTH:
return ""
# stem word, when configured so:
if stemming_language:
word = stem(word, stemming_language)
return word
def remove_subfields(s):
"Removes subfields from string, e.g. 'foo $$c bar' becomes 'foo bar'."
return re_subfields.sub(' ', s)
def get_index_id_from_index_name(index_name):
"""Returns the words/phrase index id for INDEXNAME.
Returns empty string in case there is no words table for this index.
Example: field='author', output=4."""
out = 0
query = """SELECT w.id FROM idxINDEX AS w
WHERE w.name='%s' LIMIT 1""" % index_name
res = run_sql(query, None, 1)
if res:
out = res[0][0]
return out
def get_index_tags(indexname):
"""Returns the list of tags that are indexed inside INDEXNAME.
Returns empty list in case there are no tags indexed in this index.
Note: uses get_field_tags() defined before.
Example: field='author', output=['100__%', '700__%']."""
out = []
query = """SELECT f.code FROM idxINDEX AS w, idxINDEX_field AS wf,
field AS f WHERE w.name='%s' AND w.id=wf.id_idxINDEX
AND f.id=wf.id_field""" % indexname
res = run_sql(query)
for row in res:
out.extend(get_field_tags(row[0]))
return out
def get_all_indexes():
"""Returns the list of the names of all defined words indexes.
Returns empty list in case there are no tags indexed in this index.
Example: output=['global', 'author']."""
out = []
query = """SELECT name FROM idxINDEX"""
res = run_sql(query)
for row in res:
out.append(row[0])
return out
def split_ranges(parse_string):
"""Parse a string a return the list or ranges."""
recIDs = []
ranges = parse_string.split(",")
for arange in ranges:
tmp_recIDs = arange.split("-")
if len(tmp_recIDs)==1:
recIDs.append([int(tmp_recIDs[0]), int(tmp_recIDs[0])])
else:
if int(tmp_recIDs[0]) > int(tmp_recIDs[1]): # sanity check
tmp = tmp_recIDs[0]
tmp_recIDs[0] = tmp_recIDs[1]
tmp_recIDs[1] = tmp
recIDs.append([int(tmp_recIDs[0]), int(tmp_recIDs[1])])
return recIDs
def get_word_tables(tables):
""" Given a list of table names it return a dictionary of index_id : index_tags.
if tables is empty it returns the whole dictionary."""
wordTables = {}
if tables:
indexes = tables.split(",")
for index in indexes:
index_id = get_index_id_from_index_name(index)
if index_id:
wordTables[index_id] = get_index_tags(index)
else:
write_message("Error: There is no %s words table." % index, sys.stderr)
else:
for index in get_all_indexes():
index_id = get_index_id_from_index_name(index)
wordTables[index_id] = get_index_tags(index)
return wordTables
def get_date_range(var):
"Returns the two dates contained as a low,high tuple"
limits = var.split(",")
if len(limits)==1:
low = get_datetime(limits[0])
return low, None
if len(limits)==2:
low = get_datetime(limits[0])
high = get_datetime(limits[1])
return low, high
return None, None
def create_range_list(res):
"""Creates a range list from a recID select query result contained
in res. The result is expected to have ascending numerical order."""
if not res:
return []
row = res[0]
if not row:
return []
else:
range_list = [[row[0], row[0]]]
for row in res[1:]:
row_id = row[0]
if row_id == range_list[-1][1] + 1:
range_list[-1][1] = row_id
else:
range_list.append([row_id, row_id])
return range_list
def beautify_range_list(range_list):
"""Returns a non overlapping, maximal range list"""
ret_list = []
for new in range_list:
found = 0
for old in ret_list:
if new[0] <= old[0] <= new[1] + 1 or new[0] - 1 <= old[1] <= new[1]:
old[0] = min(old[0], new[0])
old[1] = max(old[1], new[1])
found = 1
break
if not found:
ret_list.append(new)
return ret_list
def truncate_index_table(index_name):
"""Properly truncate the given index."""
index_id = get_index_id_from_index_name(index_name)
if index_id:
write_message('Truncating %s index table in order to reindex.' % index_name, verbose=2)
run_sql("UPDATE idxINDEX SET last_updated='0000-00-00 00:00:00' WHERE id=%s", (index_id,))
run_sql("TRUNCATE idxWORD%02dF" % index_id)
run_sql("TRUNCATE idxWORD%02dR" % index_id)
run_sql("TRUNCATE idxPHRASE%02dF" % index_id)
run_sql("TRUNCATE idxPHRASE%02dR" % index_id)
class WordTable:
"A class to hold the words table."
def __init__(self, index_id, fields_to_index, table_name_pattern, default_get_words_fnc, tag_to_words_fnc_map, wash_index_terms=True):
"""Creates words table instance.
@param index_id the index integer identificator
@param fields_to_index a list of fields to index
@param table_name_pattern i.e. idxWORD%02dF or idxPHRASE%02dF
@parm default_get_words_fnc the default function called to extract
words from a metadata
@param tag_to_words_fnc_map a mapping to specify particular function to
extract words from particular metdata (such as 8564_u)
"""
self.index_id = index_id
self.tablename = table_name_pattern % index_id
self.recIDs_in_mem = []
self.fields_to_index = fields_to_index
self.value = {}
self.stemming_language = get_index_stemming_language(index_id)
self.wash_index_terms = wash_index_terms
# tagToFunctions mapping. It offers an indirection level necessary for
# indexing fulltext. The default is get_words_from_phrase
self.tag_to_words_fnc_map = tag_to_words_fnc_map
self.default_get_words_fnc = default_get_words_fnc
if self.stemming_language:
write_message('Stemming(%s) is enabled for table %s' % (self.stemming_language, self.tablename))
else:
write_message('Stemming is disabled for table %s' % self.tablename)
def get_field(self, recID, tag):
"""Returns list of values of the MARC-21 'tag' fields for the
record 'recID'."""
out = []
bibXXx = "bib" + tag[0] + tag[1] + "x"
bibrec_bibXXx = "bibrec_" + bibXXx
query = """SELECT value FROM %s AS b, %s AS bb
WHERE bb.id_bibrec=%s AND bb.id_bibxxx=b.id
AND tag LIKE '%s'""" % (bibXXx, bibrec_bibXXx, recID, tag);
res = run_sql(query)
for row in res:
out.append(row[0])
return out
def clean(self):
"Cleans the words table."
self.value = {}
def put_into_db(self, mode="normal"):
"""Updates the current words table in the corresponding DB
idxFOO table. Mode 'normal' means normal execution,
mode 'emergency' means words index reverting to old state.
"""
write_message("%s %s wordtable flush started" % (self.tablename, mode))
write_message('...updating %d words into %s started' % \
(len(self.value), self.tablename))
task_update_progress("%s flushed %d/%d words" % (self.tablename, 0, len(self.value)))
self.recIDs_in_mem = beautify_range_list(self.recIDs_in_mem)
if mode == "normal":
for group in self.recIDs_in_mem:
query = """UPDATE %sR SET type='TEMPORARY' WHERE id_bibrec
BETWEEN '%d' AND '%d' AND type='CURRENT'""" % \
(self.tablename[:-1], group[0], group[1])
write_message(query, verbose=9)
run_sql(query)
nb_words_total = len(self.value)
nb_words_report = int(nb_words_total/10.0)
nb_words_done = 0
for word in self.value.keys():
self.put_word_into_db(word)
nb_words_done += 1
if nb_words_report != 0 and ((nb_words_done % nb_words_report) == 0):
write_message('......processed %d/%d words' % (nb_words_done, nb_words_total))
task_update_progress("%s flushed %d/%d words" % (self.tablename, nb_words_done, nb_words_total))
write_message('...updating %d words into %s ended' % \
(nb_words_total, self.tablename))
write_message('...updating reverse table %sR started' % self.tablename[:-1])
if mode == "normal":
for group in self.recIDs_in_mem:
query = """UPDATE %sR SET type='CURRENT' WHERE id_bibrec
BETWEEN '%d' AND '%d' AND type='FUTURE'""" % \
(self.tablename[:-1], group[0], group[1])
write_message(query, verbose=9)
run_sql(query)
query = """DELETE FROM %sR WHERE id_bibrec
BETWEEN '%d' AND '%d' AND type='TEMPORARY'""" % \
(self.tablename[:-1], group[0], group[1])
write_message(query, verbose=9)
run_sql(query)
write_message('End of updating wordTable into %s' % self.tablename, verbose=9)
elif mode == "emergency":
for group in self.recIDs_in_mem:
query = """UPDATE %sR SET type='CURRENT' WHERE id_bibrec
BETWEEN '%d' AND '%d' AND type='TEMPORARY'""" % \
(self.tablename[:-1], group[0], group[1])
write_message(query, verbose=9)
run_sql(query)
query = """DELETE FROM %sR WHERE id_bibrec
BETWEEN '%d' AND '%d' AND type='FUTURE'""" % \
(self.tablename[:-1], group[0], group[1])
write_message(query, verbose=9)
run_sql(query)
write_message('End of emergency flushing wordTable into %s' % self.tablename, verbose=9)
write_message('...updating reverse table %sR ended' % self.tablename[:-1])
self.clean()
self.recIDs_in_mem = []
write_message("%s %s wordtable flush ended" % (self.tablename, mode))
task_update_progress("%s flush ended" % (self.tablename))
def load_old_recIDs(self, word):
"""Load existing hitlist for the word from the database index files."""
query = "SELECT hitlist FROM %s WHERE term=%%s" % self.tablename
res = run_sql(query, (word,))
if res:
return intbitset(res[0][0])
else:
return None
def merge_with_old_recIDs(self,word,set):
"""Merge the system numbers stored in memory (hash of recIDs with value +1 or -1
according to whether to add/delete them) with those stored in the database index
and received in set universe of recIDs for the given word.
Return 0 in case no change was done to SET, return 1 in case SET was changed.
"""
oldset = intbitset(set)
set.update_with_signs(self.value[word])
return set != oldset
def put_word_into_db(self, word):
"""Flush a single word to the database and delete it from memory"""
set = self.load_old_recIDs(word)
if set: # merge the word recIDs found in memory:
if self.merge_with_old_recIDs(word,set) == 0:
# nothing to update:
write_message("......... unchanged hitlist for ``%s''" % word, verbose=9)
pass
else:
# yes there were some new words:
write_message("......... updating hitlist for ``%s''" % word, verbose=9)
run_sql("UPDATE %s SET hitlist=%%s WHERE term=%%s" % self.tablename,
(set.fastdump(), word))
else: # the word is new, will create new set:
write_message("......... inserting hitlist for ``%s''" % word, verbose=9)
set = intbitset(self.value[word].keys())
run_sql("INSERT INTO %s (term, hitlist) VALUES (%%s, %%s)" % self.tablename,
(word, set.fastdump()))
if not set: # never store empty words
run_sql("DELETE from %s WHERE term=%%s" % self.tablename,
(word,))
del self.value[word]
def display(self):
"Displays the word table."
keys = self.value.keys()
keys.sort()
for k in keys:
write_message("%s: %s" % (k, self.value[k]))
def count(self):
"Returns the number of words in the table."
return len(self.value)
def info(self):
"Prints some information on the words table."
write_message("The words table contains %d words." % self.count())
def lookup_words(self, word=""):
"Lookup word from the words table."
if not word:
done = 0
while not done:
try:
word = raw_input("Enter word: ")
done = 1
except (EOFError, KeyboardInterrupt):
return
if self.value.has_key(word):
write_message("The word '%s' is found %d times." \
% (word, len(self.value[word])))
else:
write_message("The word '%s' does not exist in the word file."\
% word)
def update_last_updated(self, starting_time=None):
"""Update last_updated column of the index table in the database.
Puts starting time there so that if the task was interrupted for record download,
the records will be reindexed next time."""
if starting_time is None:
return None
write_message("updating last_updated to %s..." % starting_time, verbose=9)
return run_sql("UPDATE idxINDEX SET last_updated=%s WHERE id=%s",
(starting_time, self.index_id,))
def add_recIDs(self, recIDs, opt_flush):
"""Fetches records which id in the recIDs range list and adds
them to the wordTable. The recIDs range list is of the form:
[[i1_low,i1_high],[i2_low,i2_high], ..., [iN_low,iN_high]].
"""
global chunksize, _last_word_table
flush_count = 0
records_done = 0
records_to_go = 0
for arange in recIDs:
records_to_go = records_to_go + arange[1] - arange[0] + 1
time_started = time.time() # will measure profile time
for arange in recIDs:
i_low = arange[0]
chunksize_count = 0
while i_low <= arange[1]:
# calculate chunk group of recIDs and treat it:
i_high = min(i_low+opt_flush-flush_count-1,arange[1])
i_high = min(i_low+chunksize-chunksize_count-1, i_high)
try:
self.chk_recID_range(i_low, i_high)
except StandardError, e:
write_message("Exception caught: %s" % e, sys.stderr)
if task_get_option('verbose') >= 9:
traceback.print_tb(sys.exc_info()[2])
task_update_status("ERROR")
self.put_into_db()
sys.exit(1)
write_message("%s adding records #%d-#%d started" % \
(self.tablename, i_low, i_high))
if CFG_CHECK_MYSQL_THREADS:
kill_sleepy_mysql_threads()
task_update_progress("%s adding recs %d-%d" % (self.tablename, i_low, i_high))
self.del_recID_range(i_low, i_high)
just_processed = self.add_recID_range(i_low, i_high)
flush_count = flush_count + i_high - i_low + 1
chunksize_count = chunksize_count + i_high - i_low + 1
records_done = records_done + just_processed
write_message("%s adding records #%d-#%d ended " % \
(self.tablename, i_low, i_high))
if chunksize_count >= chunksize:
chunksize_count = 0
# flush if necessary:
if flush_count >= opt_flush:
self.put_into_db()
self.clean()
write_message("%s backing up" % (self.tablename))
flush_count = 0
self.log_progress(time_started,records_done,records_to_go)
# iterate:
i_low = i_high + 1
if flush_count > 0:
self.put_into_db()
self.log_progress(time_started,records_done,records_to_go)
def add_recIDs_by_date(self, dates, opt_flush):
"""Add records that were modified between DATES[0] and DATES[1].
If DATES is not set, then add records that were modified since
the last update of the index.
"""
if not dates:
table_id = self.tablename[-3:-1]
query = """SELECT last_updated FROM idxINDEX WHERE id='%s'
""" % table_id
res = run_sql(query)
if not res:
return
if not res[0][0]:
dates = ("0000-00-00", None)
else:
dates = (res[0][0], None)
if dates[1] is None:
res = run_sql("""SELECT b.id FROM bibrec AS b
WHERE b.modification_date >= %s ORDER BY b.id ASC""",
(dates[0],))
elif dates[0] is None:
res = run_sql("""SELECT b.id FROM bibrec AS b
WHERE b.modification_date <= %s ORDER BY b.id ASC""",
(dates[1],))
else:
res = run_sql("""SELECT b.id FROM bibrec AS b
WHERE b.modification_date >= %s AND
b.modification_date <= %s ORDER BY b.id ASC""",
(dates[0], dates[1]))
alist = create_range_list(res)
if not alist:
write_message( "No new records added. %s is up to date" % self.tablename)
else:
self.add_recIDs(alist, opt_flush)
def add_recID_range(self, recID1, recID2):
"""Add records from RECID1 to RECID2."""
wlist = {}
self.recIDs_in_mem.append([recID1,recID2])
# secondly fetch all needed tags:
for tag in self.fields_to_index:
get_words_function = self.tag_to_words_fnc_map.get(tag, self.default_get_words_fnc)
bibXXx = "bib" + tag[0] + tag[1] + "x"
bibrec_bibXXx = "bibrec_" + bibXXx
query = """SELECT bb.id_bibrec,b.value FROM %s AS b, %s AS bb
WHERE bb.id_bibrec BETWEEN %d AND %d
AND bb.id_bibxxx=b.id AND tag LIKE '%s'""" % (bibXXx, bibrec_bibXXx, recID1, recID2, tag)
res = run_sql(query)
for row in res:
recID,phrase = row
if not wlist.has_key(recID):
wlist[recID] = []
new_words = get_words_function(phrase, stemming_language=self.stemming_language) # ,self.separators
wlist[recID] = list_union(new_words, wlist[recID])
# were there some words for these recIDs found?
if len(wlist) == 0: return 0
recIDs = wlist.keys()
for recID in recIDs:
# was this record marked as deleted?
if "DELETED" in self.get_field(recID, "980__c"):
wlist[recID] = []
write_message("... record %d was declared deleted, removing its word list" % recID, verbose=9)
write_message("... record %d, termlist: %s" % (recID, wlist[recID]), verbose=9)
# put words into reverse index table with FUTURE status:
for recID in recIDs:
run_sql("INSERT INTO %sR (id_bibrec,termlist,type) VALUES (%%s,%%s,'FUTURE')" % self.tablename[:-1],
(recID, serialize_via_marshal(wlist[recID])))
# ... and, for new records, enter the CURRENT status as empty:
try:
run_sql("INSERT INTO %sR (id_bibrec,termlist,type) VALUES (%%s,%%s,'CURRENT')" % self.tablename[:-1],
(recID, serialize_via_marshal([])))
except DatabaseError:
# okay, it's an already existing record, no problem
pass
# put words into memory word list:
put = self.put
for recID in recIDs:
for w in wlist[recID]:
put(recID, w, 1)
return len(recIDs)
def log_progress(self, start, done, todo):
"""Calculate progress and store it.
start: start time,
done: records processed,
todo: total number of records"""
time_elapsed = time.time() - start
# consistency check
if time_elapsed == 0 or done > todo:
return
time_recs_per_min = done/(time_elapsed/60.0)
write_message("%d records took %.1f seconds to complete.(%1.f recs/min)"\
% (done, time_elapsed, time_recs_per_min))
if time_recs_per_min:
write_message("Estimated runtime: %.1f minutes" % \
((todo-done)/time_recs_per_min))
def put(self, recID, word, sign):
"Adds/deletes a word to the word list."
try:
if self.wash_index_terms:
word = wash_index_term(word)
if self.value.has_key(word):
# the word 'word' exist already: update sign
self.value[word][recID] = sign
else:
self.value[word] = {recID: sign}
except:
write_message("Error: Cannot put word %s with sign %d for recID %s." % (word, sign, recID))
def del_recIDs(self, recIDs):
"""Fetches records which id in the recIDs range list and adds
them to the wordTable. The recIDs range list is of the form:
[[i1_low,i1_high],[i2_low,i2_high], ..., [iN_low,iN_high]].
"""
count = 0
for arange in recIDs:
self.del_recID_range(arange[0],arange[1])
count = count + arange[1] - arange[0]
self.put_into_db()
def del_recID_range(self, low, high):
"""Deletes records with 'recID' system number between low
and high from memory words index table."""
write_message("%s fetching existing words for records #%d-#%d started" % \
(self.tablename, low, high), verbose=3)
self.recIDs_in_mem.append([low,high])
query = """SELECT id_bibrec,termlist FROM %sR as bb WHERE bb.id_bibrec
BETWEEN '%d' AND '%d'""" % (self.tablename[:-1], low, high)
recID_rows = run_sql(query)
for recID_row in recID_rows:
recID = recID_row[0]
wlist = deserialize_via_marshal(recID_row[1])
for word in wlist:
self.put(recID, word, -1)
write_message("%s fetching existing words for records #%d-#%d ended" % \
(self.tablename, low, high), verbose=3)
def report_on_table_consistency(self):
"""Check reverse words index tables (e.g. idxWORD01R) for
interesting states such as 'TEMPORARY' state.
Prints small report (no of words, no of bad words).
"""
# find number of words:
query = """SELECT COUNT(*) FROM %s""" % (self.tablename)
res = run_sql(query, None, 1)
if res:
nb_words = res[0][0]
else:
nb_words = 0
# find number of records:
query = """SELECT COUNT(DISTINCT(id_bibrec)) FROM %sR""" % (self.tablename[:-1])
res = run_sql(query, None, 1)
if res:
nb_records = res[0][0]
else:
nb_records = 0
# report stats:
write_message("%s contains %d words from %d records" % (self.tablename, nb_words, nb_records))
# find possible bad states in reverse tables:
query = """SELECT COUNT(DISTINCT(id_bibrec)) FROM %sR WHERE type <> 'CURRENT'""" % (self.tablename[:-1])
res = run_sql(query)
if res:
nb_bad_records = res[0][0]
else:
nb_bad_records = 999999999
if nb_bad_records:
write_message("EMERGENCY: %s needs to repair %d of %d records" % \
(self.tablename, nb_bad_records, nb_records))
else:
write_message("%s is in consistent state" % (self.tablename))
return nb_bad_records
def repair(self, opt_flush):
"""Repair the whole table"""
# find possible bad states in reverse tables:
query = """SELECT COUNT(DISTINCT(id_bibrec)) FROM %sR WHERE type <> 'CURRENT'""" % (self.tablename[:-1])
res = run_sql(query, None, 1)
if res:
nb_bad_records = res[0][0]
else:
nb_bad_records = 0
if nb_bad_records == 0:
return
query = """SELECT id_bibrec FROM %sR WHERE type <> 'CURRENT' ORDER BY id_bibrec""" \
% (self.tablename[:-1])
res = run_sql(query)
recIDs = create_range_list(res)
flush_count = 0
records_done = 0
records_to_go = 0
for arange in recIDs:
records_to_go = records_to_go + arange[1] - arange[0] + 1
time_started = time.time() # will measure profile time
for arange in recIDs:
i_low = arange[0]
chunksize_count = 0
while i_low <= arange[1]:
# calculate chunk group of recIDs and treat it:
i_high = min(i_low+opt_flush-flush_count-1,arange[1])
i_high = min(i_low+chunksize-chunksize_count-1, i_high)
try:
self.fix_recID_range(i_low, i_high)
except StandardError, e:
write_message("Exception caught: %s" % e, sys.stderr)
if task_get_option['verbose'] >= 9:
traceback.print_tb(sys.exc_info()[2])
task_update_status("ERROR")
self.put_into_db()
sys.exit(1)
flush_count = flush_count + i_high - i_low + 1
chunksize_count = chunksize_count + i_high - i_low + 1
records_done = records_done + i_high - i_low + 1
if chunksize_count >= chunksize:
chunksize_count = 0
# flush if necessary:
if flush_count >= opt_flush:
self.put_into_db("emergency")
self.clean()
flush_count = 0
self.log_progress(time_started,records_done,records_to_go)
# iterate:
i_low = i_high + 1
if flush_count > 0:
self.put_into_db("emergency")
self.log_progress(time_started,records_done,records_to_go)
write_message("%s inconsistencies repaired." % self.tablename)
def chk_recID_range(self, low, high):
"""Check if the reverse index table is in proper state"""
## check db
query = """SELECT COUNT(*) FROM %sR WHERE type <> 'CURRENT'
AND id_bibrec BETWEEN '%d' AND '%d'""" % (self.tablename[:-1], low, high)
res = run_sql(query, None, 1)
if res[0][0]==0:
write_message("%s for %d-%d is in consistent state" % (self.tablename,low,high))
return # okay, words table is consistent
## inconsistency detected!
write_message("EMERGENCY: %s inconsistencies detected..." % self.tablename)
write_message("""EMERGENCY: Errors found. You should check consistency of the %s - %sR tables.\nRunning 'bibindex --repair' is recommended.""" \
% (self.tablename, self.tablename[:-1]))
raise StandardError
def fix_recID_range(self, low, high):
"""Try to fix reverse index database consistency (e.g. table idxWORD01R) in the low,high doc-id range.
Possible states for a recID follow:
CUR TMP FUT: very bad things have happened: warn!
CUR TMP : very bad things have happened: warn!
CUR FUT: delete FUT (crash before flushing)
CUR : database is ok
TMP FUT: add TMP to memory and del FUT from memory
flush (revert to old state)
TMP : very bad things have happened: warn!
FUT: very bad things have happended: warn!
"""
state = {}
query = "SELECT id_bibrec,type FROM %sR WHERE id_bibrec BETWEEN '%d' AND '%d'"\
% (self.tablename[:-1], low, high)
res = run_sql(query)
for row in res:
if not state.has_key(row[0]):
state[row[0]]=[]
state[row[0]].append(row[1])
ok = 1 # will hold info on whether we will be able to repair
for recID in state.keys():
if not 'TEMPORARY' in state[recID]:
if 'FUTURE' in state[recID]:
if 'CURRENT' not in state[recID]:
write_message("EMERGENCY: Record %d is in inconsistent state. Can't repair it." % recID)
ok = 0
else:
write_message("EMERGENCY: Inconsistency in record %d detected" % recID)
query = """DELETE FROM %sR
WHERE id_bibrec='%d'""" % (self.tablename[:-1], recID)
run_sql(query)
write_message("EMERGENCY: Inconsistency in record %d repaired." % recID)
else:
if 'FUTURE' in state[recID] and not 'CURRENT' in state[recID]:
self.recIDs_in_mem.append([recID,recID])
# Get the words file
query = """SELECT type,termlist FROM %sR
WHERE id_bibrec='%d'""" % (self.tablename[:-1], recID)
write_message(query, verbose=9)
res = run_sql(query)
for row in res:
wlist = deserialize_via_marshal(row[1])
write_message("Words are %s " % wlist, verbose=9)
if row[0] == 'TEMPORARY':
sign = 1
else:
sign = -1
for word in wlist:
self.put(recID, word, sign)
else:
write_message("EMERGENCY: %s for %d is in inconsistent state. Couldn't repair it." % (self.tablename, recID))
ok = 0
if not ok:
write_message("""EMERGENCY: Unrepairable errors found. You should check consistency
of the %s - %sR tables. Deleting affected entries from these tables
is recommended.""" % (self.tablename, self.tablename[:-1]))
raise StandardError
def test_fulltext_indexing():
"""Tests fulltext indexing programs on PDF, PS, DOC, PPT,
XLS. Prints list of words and word table on the screen. Does not
integrate anything into the database. Useful when debugging
problems with fulltext indexing: call this function instead of main().
"""
print get_words_from_fulltext("http://doc.cern.ch/cgi-bin/setlink?base=atlnot&categ=Communication&id=com-indet-2002-012") # protected URL
print get_words_from_fulltext("http://doc.cern.ch/cgi-bin/setlink?base=agenda&categ=a00388&id=a00388s2t7") # XLS
print get_words_from_fulltext("http://doc.cern.ch/cgi-bin/setlink?base=agenda&categ=a02883&id=a02883s1t6/transparencies") # PPT
print get_words_from_fulltext("http://doc.cern.ch/cgi-bin/setlink?base=agenda&categ=a99149&id=a99149s1t10/transparencies") # DOC
print get_words_from_fulltext("http://doc.cern.ch/cgi-bin/setlink?base=preprint&categ=cern&id=lhc-project-report-601") # PDF
sys.exit(0)
def main():
"""Main that construct all the bibtask."""
task_set_option('cmd', 'add')
task_set_option('id', [])
task_set_option("modified", [])
task_set_option("collection", [])
task_set_option("maxmem", 0)
task_set_option("flush", 10000)
task_set_option("windex", ','.join(get_all_indexes()))
task_set_option("reindex", False)
task_init(authorization_action='runbibindex',
authorization_msg="BibIndex Task Submission",
description="""Examples:
\t%s -a -i 234-250,293,300-500 -u admin@localhost
\t%s -a -w author,fulltext -M 8192 -v3
\t%s -d -m +4d -A on --flush=10000\n""" % ((sys.argv[0],) * 3), help_specific_usage=""" Indexing options:
-a, --add\t\tadd or update words for selected records
-d, --del\t\tdelete words for selected records
-i, --id=low[-high]\t\tselect according to doc recID
-m, --modified=from[,to]\tselect according to modification date
-c, --collection=c1[,c2]\tselect according to collection
-R, --reindex\treindex the selected indexes from scratch
Repairing options:
-k, --check\t\tcheck consistency for all records in the table(s)
-r, --repair\t\ttry to repair all records in the table(s)
Specific options:
-w, --windex=w1[,w2]\tword/phrase indexes to consider (all)
-M, --maxmem=XXX\tmaximum memory usage in kB (no limit)
-f, --flush=NNN\t\tfull consistent table flush after NNN records (10000)
""",
version=__revision__,
specific_params=("adi:m:c:w:krRM:f:", [
"add",
"del",
"id=",
"modified=",
"collection=",
"windex=",
"check",
"repair",
"reindex",
"maxmem=",
"flush=",
]),
task_stop_helper_fnc=task_stop_table_close_fnc,
task_submit_elaborate_specific_parameter_fnc=task_submit_elaborate_specific_parameter,
task_run_fnc=task_run_core)
def task_submit_elaborate_specific_parameter(key, value, opts, args):
""" Given the string key it checks it's meaning, eventually using the
value. Usually it fills some key in the options dict.
It must return True if it has elaborated the key, False, if it doesn't
know that key.
eg:
if key in ['-n', '--number']:
self.options['number'] = value
return True
return False
"""
if key in ("-a", "--add"):
task_set_option("cmd", "add")
if ("-x","") in opts or ("--del","") in opts:
raise StandardError, "Can not have --add and --del at the same time!"
elif key in ("-k", "--check"):
task_set_option("cmd", "check")
elif key in ("-r", "--repair"):
task_set_option("cmd", "repair")
elif key in ("-d", "--del"):
task_set_option("cmd", "del")
elif key in ("-i", "--id"):
task_set_option('id', task_get_option('id') + split_ranges(value))
elif key in ("-m", "--modified"):
task_set_option("modified", get_date_range(value))
elif key in ("-c", "--collection"):
task_set_option("collection", value)
elif key in ("-R", "--reindex"):
task_set_option("reindex", True)
elif key in ("-w", "--windex"):
task_set_option("windex", value)
elif key in ("-M", "--maxmem"):
task_set_option("maxmem", int(value))
if task_get_option("maxmem") < base_process_size + 1000:
raise StandardError, "Memory usage should be higher than %d kB" % \
(base_process_size + 1000)
elif key in ("-f", "--flush"):
task_set_option("flush", int(value))
else:
return False
return True
def task_stop_table_close_fnc():
""" Close tables to STOP. """
global _last_word_table
if _last_word_table:
_last_word_table.put_into_db()
def task_run_core():
"""Runs the task by fetching arguments from the BibSched task queue. This is
what BibSched will be invoking via daemon call.
The task prints Fibonacci numbers for up to NUM on the stdout, and some
messages on stderr.
Return 1 in case of success and 0 in case of failure."""
global _last_word_table
if task_get_option("cmd") == "check":
wordTables = get_word_tables(task_get_option("windex"))
for index_id, index_tags in wordTables.iteritems():
wordTable = WordTable(index_id, index_tags, 'idxWORD%02dF', get_words_from_phrase, {'8564_u': get_words_from_fulltext})
_last_word_table = wordTable
wordTable.report_on_table_consistency()
_last_word_table = None
return True
if False: # FIXME: remove when idxPHRASE will be plugged to search_engine
if task_get_option("cmd") == "check":
wordTables = get_word_tables(task_get_option("windex"))
for index_id, index_tags in wordTables.iteritems():
wordTable = WordTable(index_id, index_tags, 'idxPHRASE%02dF', get_phrases_from_phrase, {'8564_u': get_nothing_from_phrase}, False)
_last_word_table = wordTable
wordTable.report_on_table_consistency()
_last_word_table = None
return True
if task_get_option("reindex"):
for index_name in task_get_option("windex").split(','):
truncate_index_table(index_name)
# Let's work on single words!
wordTables = get_word_tables(task_get_option("windex"))
for index_id, index_tags in wordTables.iteritems():
wordTable = WordTable(index_id, index_tags, 'idxWORD%02dF', get_words_from_phrase, {'8564_u': get_words_from_fulltext})
_last_word_table = wordTable
wordTable.report_on_table_consistency()
try:
if task_get_option("cmd") == "del":
if task_get_option("id"):
wordTable.del_recIDs(task_get_option("id"))
elif task_get_option("collection"):
l_of_colls = task_get_option("collection").split(",")
recIDs = perform_request_search(c=l_of_colls)
recIDs_range = []
for recID in recIDs:
recIDs_range.append([recID,recID])
wordTable.del_recIDs(recIDs_range)
else:
write_message("Missing IDs of records to delete from index %s." % wordTable.tablename,
sys.stderr)
raise StandardError
elif task_get_option("cmd") == "add":
if task_get_option("id"):
wordTable.add_recIDs(task_get_option("id"), task_get_option("flush"))
elif task_get_option("collection"):
l_of_colls = task_get_option("collection").split(",")
recIDs = perform_request_search(c=l_of_colls)
recIDs_range = []
for recID in recIDs:
recIDs_range.append([recID,recID])
wordTable.add_recIDs(recIDs_range, task_get_option("flush"))
else:
wordTable.add_recIDs_by_date(task_get_option("modified"), task_get_option("flush"))
# only update last_updated if run via automatic mode:
wordTable.update_last_updated(task_get_task_param('task_starting_time'))
elif task_get_option("cmd") == "repair":
wordTable.repair(task_get_option("flush"))
else:
write_message("Invalid command found processing %s" % \
wordTable.tablename, sys.stderr)
raise StandardError
except StandardError, e:
write_message("Exception caught: %s" % e, sys.stderr)
if task_get_option("verbose") >= 8:
traceback.print_tb(sys.exc_info()[2])
task_update_status("ERROR")
if _last_word_table:
_last_word_table.put_into_db()
sys.exit(1)
wordTable.report_on_table_consistency()
if False: # FIXME: remove when idxPHRASE will be plugged to search_engine
# Let's work on phrases now
wordTables = get_word_tables(task_get_option("windex"))
for index_id, index_tags in wordTables.iteritems():
wordTable = WordTable(index_id, index_tags, 'idxPHRASE%02dF', get_phrases_from_phrase, {'8564_u': get_nothing_from_phrase}, False)
_last_word_table = wordTable
wordTable.report_on_table_consistency()
try:
if task_get_option("cmd") == "del":
if task_get_option("id"):
wordTable.del_recIDs(task_get_option("id"))
elif task_get_option("collection"):
l_of_colls = task_get_option("collection").split(",")
recIDs = perform_request_search(c=l_of_colls)
recIDs_range = []
for recID in recIDs:
recIDs_range.append([recID,recID])
wordTable.del_recIDs(recIDs_range)
else:
write_message("Missing IDs of records to delete from index %s." % wordTable.tablename,
sys.stderr)
raise StandardError
elif task_get_option("cmd") == "add":
if task_get_option("id"):
wordTable.add_recIDs(task_get_option("id"), task_get_option("flush"))
elif task_get_option("collection"):
l_of_colls = task_get_option("collection").split(",")
recIDs = perform_request_search(c=l_of_colls)
recIDs_range = []
for recID in recIDs:
recIDs_range.append([recID,recID])
wordTable.add_recIDs(recIDs_range, task_get_option("flush"))
else:
wordTable.add_recIDs_by_date(task_get_option("modified"), task_get_option("flush"))
# only update last_updated if run via automatic mode:
wordTable.update_last_updated(task_get_task_param('task_starting_time'))
elif task_get_option("cmd") == "repair":
wordTable.repair(task_get_option("flush"))
else:
write_message("Invalid command found processing %s" % \
wordTable.tablename, sys.stderr)
raise StandardError
except StandardError, e:
write_message("Exception caught: %s" % e, sys.stderr)
if task_get_option("verbose") >= 9:
traceback.print_tb(sys.exc_info()[2])
task_update_status("ERROR")
if _last_word_table:
_last_word_table.put_into_db()
sys.exit(1)
wordTable.report_on_table_consistency()
_last_word_table = None
return True
### okay, here we go:
if __name__ == '__main__':
main()
diff --git a/modules/bibindex/lib/bibindexadmin_regression_tests.py b/modules/bibindex/lib/bibindexadmin_regression_tests.py
index 1fa48a753..b19cb6ac9 100644
--- a/modules/bibindex/lib/bibindexadmin_regression_tests.py
+++ b/modules/bibindex/lib/bibindexadmin_regression_tests.py
@@ -1,78 +1,78 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibIndex Admin Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class BibIndexAdminWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibIndex Admin web pages whether they are up or not."""
def test_bibindex_admin_interface_pages_availability(self):
"""bibindexadmin - availability of BibIndex Admin interface pages"""
- baseurl = weburl + '/admin/bibindex/bibindexadmin.py/'
+ baseurl = CFG_SITE_URL + '/admin/bibindex/bibindexadmin.py/'
_exports = ['',
'index',
'index?mtype=perform_showindexoverview',
'index?mtype=perform_editindexes',
'index?mtype=perform_addindex',
'field',
'field?mtype=perform_showfieldoverview',
'field?mtype=perform_editfields',
'field?mtype=perform_addfield',
]
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_bibindex_admin_guide_availability(self):
"""bibindexadmin - availability of BibIndex Admin guide pages"""
- url = weburl + '/help/admin/bibindex-admin-guide'
+ url = CFG_SITE_URL + '/help/admin/bibindex-admin-guide'
error_messages = test_web_page_content(url,
expected_text="BibIndex Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(BibIndexAdminWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibindex/lib/bibindexadminlib.py b/modules/bibindex/lib/bibindexadminlib.py
index 7a50d2116..0531f8250 100644
--- a/modules/bibindex/lib/bibindexadminlib.py
+++ b/modules/bibindex/lib/bibindexadminlib.py
@@ -1,1701 +1,1701 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio BibIndex Administrator Interface."""
__revision__ = "$Id$"
import cgi
import re
import os
import urllib
import time
import random
from zlib import compress,decompress
from invenio.config import \
CFG_SITE_LANG, \
CFG_VERSION, \
- weburl, \
+ CFG_SITE_URL, \
CFG_BINDIR
from invenio.bibrankadminlib import write_outcome,modify_translations,get_def_name,get_i8n_name,get_name,get_rnk_nametypes,get_languages,check_user,is_adminuser,addadminbox,tupletotable,tupletotable_onlyselected,addcheckboxes,createhiddenform
from invenio.dbquery import run_sql, get_table_status_info
from invenio.webpage import page, pageheaderonly, pagefooteronly, adderrorbox
from invenio.webuser import getUid, get_email
from invenio.bibindex_engine_stemmer import get_stemming_language_map
import invenio.template
websearch_templates = invenio.template.load('websearch')
def getnavtrail(previous = ''):
"""Get the navtrail"""
- navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (weburl,)
+ navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (CFG_SITE_URL,)
navtrail = navtrail + previous
return navtrail
def perform_index(ln=CFG_SITE_LANG, mtype='', content=''):
"""start area for modifying indexes
mtype - the method that called this method.
content - the output from that method."""
fin_output = """
<table>
<tr>
<td>0.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/index?ln=%s">Show all</a></small></td>
<td>1.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/index?ln=%s&amp;mtype=perform_showindexoverview#1">Overview of indexes</a></small></td>
<td>2.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/index?ln=%s&amp;mtype=perform_editindexes#2">Edit index</a></small></td>
<td>3.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/index?ln=%s&amp;mtype=perform_addindex#3">Add new index</a></small></td>
<td>4.&nbsp;<small><a href="%s/help/admin/bibindex-admin-guide">Guide</a></small></td>
</tr>
</table>
- """ % (weburl, ln, weburl, ln, weburl, ln, weburl, ln, weburl)
+ """ % (CFG_SITE_URL, ln, CFG_SITE_URL, ln, CFG_SITE_URL, ln, CFG_SITE_URL, ln, CFG_SITE_URL)
if mtype == "perform_showindexoverview" and content:
fin_output += content
elif mtype == "perform_showindexoverview" or not mtype:
fin_output += perform_showindexoverview(ln, callback='')
if mtype == "perform_editindexes" and content:
fin_output += content
elif mtype == "perform_editindexes" or not mtype:
fin_output += perform_editindexes(ln, callback='')
if mtype == "perform_addindex" and content:
fin_output += content
elif mtype == "perform_addindex" or not mtype:
fin_output += perform_addindex(ln, callback='')
return addadminbox("<b>Menu</b>", [fin_output])
def perform_field(ln=CFG_SITE_LANG, mtype='', content=''):
"""Start area for modifying fields
mtype - the method that called this method.
content - the output from that method."""
fin_output = """
<table>
<tr>
<td>0.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/field?ln=%s">Show all</a></small></td>
<td>1.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/field?ln=%s&amp;mtype=perform_showfieldoverview#1">Overview of logical fields</a></small></td>
<td>2.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/field?ln=%s&amp;mtype=perform_editfields#2">Edit logical field</a></small></td>
<td>3.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/field?ln=%s&amp;mtype=perform_addfield#3">Add new logical field</a></small></td>
<td>4.&nbsp;<small><a href="%s/help/admin/bibindex-admin-guide">Guide</a></small></td>
</tr>
</table>
- """ % (weburl, ln, weburl, ln, weburl, ln, weburl, ln, weburl)
+ """ % (CFG_SITE_URL, ln, CFG_SITE_URL, ln, CFG_SITE_URL, ln, CFG_SITE_URL, ln, CFG_SITE_URL)
if mtype == "perform_editfields" and content:
fin_output += content
elif mtype == "perform_editfields" or not mtype:
fin_output += perform_editfields(ln, callback='')
if mtype == "perform_addfield" and content:
fin_output += content
elif mtype == "perform_addfield" or not mtype:
fin_output += perform_addfield(ln, callback='')
if mtype == "perform_showfieldoverview" and content:
fin_output += content
elif mtype == "perform_showfieldoverview" or not mtype:
fin_output += perform_showfieldoverview(ln, callback='')
return addadminbox("<b>Menu</b>", [fin_output])
def perform_editfield(fldID, ln=CFG_SITE_LANG, mtype='', content='', callback='yes', confirm=-1):
"""form to modify a field. this method is calling other methods which again is calling this and sending back the output of the method.
if callback, the method will call perform_editcollection, if not, it will just return its output.
fldID - id of the field
mtype - the method that called this method.
content - the output from that method."""
fld_dict = dict(get_def_name('', "field"))
if fldID in [-1, "-1"]:
return addadminbox("Edit logical field", ["""<b><span class="info">Please go back and select a logical field</span></b>"""])
fin_output = """
<table>
<tr>
<td><b>Menu</b></td>
</tr>
<tr>
<td>0.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editfield?fldID=%s&amp;ln=%s">Show all</a></small></td>
<td>1.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editfield?fldID=%s&amp;ln=%s&amp;mtype=perform_modifyfield">Modify field code</a></small></td>
<td>2.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editfield?fldID=%s&amp;ln=%s&amp;mtype=perform_modifyfieldtranslations">Modify translations</a></small></td>
<td>3.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editfield?fldID=%s&amp;ln=%s&amp;mtype=perform_modifyfieldtags">Modify MARC tags</a></small></td>
<td>4.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editfield?fldID=%s&amp;ln=%s&amp;mtype=perform_deletefield">Delete field</a></small></td>
</tr><tr>
<td>5.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editfield?fldID=%s&amp;ln=%s&amp;mtype=perform_showdetailsfield">Show field usage</a></small></td>
</tr>
</table>
- """ % (weburl, fldID, ln, weburl, fldID, ln, weburl, fldID, ln, weburl, fldID, ln, weburl, fldID, ln, weburl, fldID, ln)
+ """ % (CFG_SITE_URL, fldID, ln, CFG_SITE_URL, fldID, ln, CFG_SITE_URL, fldID, ln, CFG_SITE_URL, fldID, ln, CFG_SITE_URL, fldID, ln, CFG_SITE_URL, fldID, ln)
if mtype == "perform_modifyfield" and content:
fin_output += content
elif mtype == "perform_modifyfield" or not mtype:
fin_output += perform_modifyfield(fldID, ln, callback='')
if mtype == "perform_modifyfieldtranslations" and content:
fin_output += content
elif mtype == "perform_modifyfieldtranslations" or not mtype:
fin_output += perform_modifyfieldtranslations(fldID, ln, callback='')
if mtype == "perform_modifyfieldtags" and content:
fin_output += content
elif mtype == "perform_modifyfieldtags" or not mtype:
fin_output += perform_modifyfieldtags(fldID, ln, callback='')
if mtype == "perform_deletefield" and content:
fin_output += content
elif mtype == "perform_deletefield" or not mtype:
fin_output += perform_deletefield(fldID, ln, callback='')
return addadminbox("Edit logical field '%s'" % fld_dict[int(fldID)], [fin_output])
def perform_editindex(idxID, ln=CFG_SITE_LANG, mtype='', content='', callback='yes', confirm=-1):
"""form to modify a index. this method is calling other methods which again is calling this and sending back the output of the method.
idxID - id of the index
mtype - the method that called this method.
content - the output from that method."""
if idxID in [-1, "-1"]:
return addadminbox("Edit index", ["""<b><span class="info">Please go back and select a index</span></b>"""])
fin_output = """
<table>
<tr>
<td><b>Menu</b></td>
</tr>
<tr>
<td>0.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s">Show all</a></small></td>
<td>1.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s&amp;mtype=perform_modifyindex">Modify index name / descriptor</a></small></td>
<td>2.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s&amp;mtype=perform_modifyindextranslations">Modify translations</a></small></td>
<td>3.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s&amp;mtype=perform_modifyindexfields">Modify index fields</a></small></td>
<td>4.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s&amp;mtype=perform_modifyindexstemming">Modify index stemming language</a></small></td>
<td>5.&nbsp;<small><a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s&amp;mtype=perform_deleteindex">Delete index</a></small></td>
</tr>
</table>
- """ % (weburl, idxID, ln, weburl, idxID, ln, weburl, idxID, ln, weburl, idxID, ln, weburl, idxID, ln, weburl, idxID, ln)
+ """ % (CFG_SITE_URL, idxID, ln, CFG_SITE_URL, idxID, ln, CFG_SITE_URL, idxID, ln, CFG_SITE_URL, idxID, ln, CFG_SITE_URL, idxID, ln, CFG_SITE_URL, idxID, ln)
if mtype == "perform_modifyindex" and content:
fin_output += content
elif mtype == "perform_modifyindex" or not mtype:
fin_output += perform_modifyindex(idxID, ln, callback='')
if mtype == "perform_modifyindextranslations" and content:
fin_output += content
elif mtype == "perform_modifyindextranslations" or not mtype:
fin_output += perform_modifyindextranslations(idxID, ln, callback='')
if mtype == "perform_modifyindexfields" and content:
fin_output += content
elif mtype == "perform_modifyindexfields" or not mtype:
fin_output += perform_modifyindexfields(idxID, ln, callback='')
if mtype == "perform_modifyindexstemming" and content:
fin_output += content
elif mtype == "perform_modifyindexstemming" or not mtype:
fin_output += perform_modifyindexstemming(idxID, ln, callback='')
if mtype == "perform_deleteindex" and content:
fin_output += content
elif mtype == "perform_deleteindex" or not mtype:
fin_output += perform_deleteindex(idxID, ln, callback='')
return addadminbox("Edit index", [fin_output])
def perform_showindexoverview(ln=CFG_SITE_LANG, callback='', confirm=0):
subtitle = """<a name="1"></a>1. Overview of indexes"""
output = """<table cellpadding="3" border="1">"""
output += """<tr><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td></tr>""" % ("ID", "Name", "Fwd.Idx Size", "Rev.Idx Size", "Fwd.Idx Words", "Rev.Idx Records", "Last updated", "Fields", "Translations", "Stemming Language")
idx = get_idx()
idx_dict = dict(get_def_name('', "idxINDEX"))
stemming_language_map = get_stemming_language_map()
stemming_language_map_reversed = dict([(elem[1], elem[0]) for elem in stemming_language_map.iteritems()])
for idxID, idxNAME, idxDESC, idxUPD, idxSTEM in idx:
forward_table_status_info = get_table_status_info('idxWORD%sF' % (idxID < 10 and '0%s' % idxID or idxID))
reverse_table_status_info = get_table_status_info('idxWORD%sR' % (idxID < 10 and '0%s' % idxID or idxID))
if str(idxUPD)[-3:] == ".00":
idxUPD = str(idxUPD)[0:-3]
lang = get_lang_list("idxINDEXNAME", "id_idxINDEX", idxID)
idx_fld = get_idx_fld(idxID)
fld = ""
for row in idx_fld:
fld += row[3] + ", "
if fld.endswith(", "):
fld = fld[:-2]
if len(fld) == 0:
fld = """<strong><span class="info">None</span></strong>"""
date = (idxUPD and idxUPD or """<strong><span class="info">Not updated</span></strong>""")
stemming_lang = stemming_language_map_reversed.get(idxSTEM, None)
if not stemming_lang:
stemming_lang = """<strong><span class="info">None</span></strong>"""
if forward_table_status_info and reverse_table_status_info:
output += """<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>""" % \
(idxID,
- """<a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s" title="%s">%s</a>""" % (weburl, idxID, ln, idxDESC, idx_dict.get(idxID, idxNAME)),
+ """<a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s" title="%s">%s</a>""" % (CFG_SITE_URL, idxID, ln, idxDESC, idx_dict.get(idxID, idxNAME)),
"%s MB" % websearch_templates.tmpl_nice_number(forward_table_status_info['Data_length'] / 1048576.0, max_ndigits_after_dot=3),
"%s MB" % websearch_templates.tmpl_nice_number(reverse_table_status_info['Data_length'] / 1048576.0, max_ndigits_after_dot=3),
websearch_templates.tmpl_nice_number(forward_table_status_info['Rows']),
websearch_templates.tmpl_nice_number(reverse_table_status_info['Rows'], max_ndigits_after_dot=3),
date,
fld,
lang,
stemming_lang)
elif not forward_table_status_info:
output += """<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>""" % \
(idxID,
- """<a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s">%s</a>""" % (weburl, idxID, ln, idx_dict.get(idxID, idxNAME)),
+ """<a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s">%s</a>""" % (CFG_SITE_URL, idxID, ln, idx_dict.get(idxID, idxNAME)),
"Error", "%s MB" % websearch_templates.tmpl_nice_number(reverse_table_status_info['Data_length'] / 1048576.0, max_ndigits_after_dot=3),
"Error",
websearch_templates.tmpl_nice_number(reverse_table_status_info['Rows'], max_ndigits_after_dot=3),
date,
"",
lang)
elif not reverse_table_status_info:
output += """<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>""" % \
(idxID,
- """<a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s">%s</a>""" % (weburl, idxID, ln, idx_dict.get(idxID, idxNAME)),
+ """<a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&amp;ln=%s">%s</a>""" % (CFG_SITE_URL, idxID, ln, idx_dict.get(idxID, idxNAME)),
"%s MB" % websearch_templates.tmpl_nice_number(forward_table_status_info['Data_length'] / 1048576.0, max_ndigits_after_dot=3),
"Error", websearch_templates.tmpl_nice_number(forward_table_status_info['Rows'], max_ndigits_after_dot=3),
"Error",
date,
"",
lang)
output += "</table>"
body = [output]
if callback:
return perform_index(ln, "perform_showindexoverview", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_editindexes(ln=CFG_SITE_LANG, callback='yes', content='', confirm=-1):
"""show a list of indexes that can be edited."""
- subtitle = """<a name="2"></a>2. Edit index&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (weburl)
+ subtitle = """<a name="2"></a>2. Edit index&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (CFG_SITE_URL)
fin_output = ''
idx = get_idx()
output = ""
if len(idx) > 0:
text = """
<span class="adminlabel">Index name</span>
<select name="idxID" class="admin_w200">
<option value="-1">- Select a index -</option>
"""
for (idxID, idxNAME, idxDESC, idxUPD, idxSTEM) in idx:
text += """<option value="%s">%s</option>""" % (idxID, idxNAME)
text += """</select>"""
- output += createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/editindex" % weburl,
+ output += createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/editindex" % CFG_SITE_URL,
text=text,
button="Edit",
ln=ln,
confirm=1)
else:
output += """No indexes exists"""
body = [output]
if callback:
return perform_index(ln, "perform_editindexes", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_editfields(ln=CFG_SITE_LANG, callback='yes', content='', confirm=-1):
"""show a list of all logical fields that can be edited."""
- subtitle = """<a name="5"></a>5. Edit logical field&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (weburl)
+ subtitle = """<a name="5"></a>5. Edit logical field&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (CFG_SITE_URL)
fin_output = ''
res = get_fld()
output = ""
if len(res) > 0:
text = """
<span class="adminlabel">Field name</span>
<select name="fldID" class="admin_w200">
<option value="-1">- Select a field -</option>
"""
for (fldID, name, code) in res:
text += """<option value="%s">%s</option>""" % (fldID, name)
text += """</select>"""
- output += createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/editfield" % weburl,
+ output += createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/editfield" % CFG_SITE_URL,
text=text,
button="Edit",
ln=ln,
confirm=1)
else:
output += """No logical fields exists"""
body = [output]
if callback:
return perform_field(ln, "perform_editfields", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_addindex(ln=CFG_SITE_LANG, idxNAME='', callback="yes", confirm=-1):
"""form to add a new index.
idxNAME - the name of the new index"""
output = ""
subtitle = """<a name="3"></a>3. Add new index"""
text = """
<span class="adminlabel">Index name</span>
<input class="admin_w200" type="text" name="idxNAME" value="%s" /><br />
""" % idxNAME
- output = createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/addindex" % weburl,
+ output = createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/addindex" % CFG_SITE_URL,
text=text,
ln=ln,
button="Add index",
confirm=1)
if idxNAME and confirm in ["1", 1]:
res = add_idx(idxNAME)
- output += write_outcome(res) + """<br /><a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&ln=%s">Configure this index</a>.""" % (weburl, res[1], ln)
+ output += write_outcome(res) + """<br /><a href="%s/admin/bibindex/bibindexadmin.py/editindex?idxID=%s&ln=%s">Configure this index</a>.""" % (CFG_SITE_URL, res[1], ln)
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please give the index a name.</span></b>
"""
body = [output]
if callback:
return perform_index(ln, "perform_addindex", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyindextranslations(idxID, ln=CFG_SITE_LANG, sel_type='', trans=[], confirm=-1, callback='yes'):
"""Modify the translations of a index
sel_type - the nametype to modify
trans - the translations in the same order as the languages from get_languages()"""
output = ''
subtitle = ''
langs = get_languages()
if confirm in ["2", 2] and idxID:
finresult = modify_translations(idxID, langs, sel_type, trans, "idxINDEX")
idx_dict = dict(get_def_name('', "idxINDEX"))
if idxID and idx_dict.has_key(int(idxID)):
idxID = int(idxID)
- subtitle = """<a name="2"></a>2. Modify translations for index.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % weburl
+ subtitle = """<a name="2"></a>2. Modify translations for index.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % CFG_SITE_URL
if type(trans) is str:
trans = [trans]
if sel_type == '':
sel_type = get_idx_nametypes()[0][0]
header = ['Language', 'Translation']
actions = []
types = get_idx_nametypes()
if len(types) > 1:
text = """
<span class="adminlabel">Name type</span>
<select name="sel_type" class="admin_w200">
"""
for (key, value) in types:
text += """<option value="%s" %s>%s""" % (key, key == sel_type and 'selected="selected"' or '', value)
trans_names = get_name(idxID, ln, key, "field")
if trans_names and trans_names[0][0]:
text += ": %s" % trans_names[0][0]
text += "</option>"
text += """</select>"""
output += createhiddenform(action="modifyindextranslations#2",
text=text,
button="Select",
idxID=idxID,
ln=ln,
confirm=0)
if confirm in [-1, "-1", 0, "0"]:
trans = []
for (key, value) in langs:
try:
trans_names = get_name(idxID, key, sel_type, "idxINDEX")
trans.append(trans_names[0][0])
except StandardError, e:
trans.append('')
for nr in range(0,len(langs)):
actions.append(["%s %s" % (langs[nr][1], (langs[nr][0]==CFG_SITE_LANG and '<small>(def)</small>' or ''))])
actions[-1].append('<input type="text" name="trans" size="30" value="%s"/>' % trans[nr])
text = tupletotable(header=header, tuple=actions)
output += createhiddenform(action="modifyindextranslations#2",
text=text,
button="Modify",
idxID=idxID,
sel_type=sel_type,
ln=ln,
confirm=2)
if sel_type and len(trans):
if confirm in ["2", 2]:
output += write_outcome(finresult)
body = [output]
if callback:
return perform_editindex(idxID, ln, "perform_modifyindextranslations", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyfieldtranslations(fldID, ln=CFG_SITE_LANG, sel_type='', trans=[], confirm=-1, callback='yes'):
"""Modify the translations of a field
sel_type - the nametype to modify
trans - the translations in the same order as the languages from get_languages()"""
output = ''
subtitle = ''
langs = get_languages()
if confirm in ["2", 2] and fldID:
finresult = modify_translations(fldID, langs, sel_type, trans, "field")
fld_dict = dict(get_def_name('', "field"))
if fldID and fld_dict.has_key(int(fldID)):
fldID = int(fldID)
- subtitle = """<a name="3"></a>3. Modify translations for logical field '%s'&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (fld_dict[fldID], weburl)
+ subtitle = """<a name="3"></a>3. Modify translations for logical field '%s'&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (fld_dict[fldID], CFG_SITE_URL)
if type(trans) is str:
trans = [trans]
if sel_type == '':
sel_type = get_fld_nametypes()[0][0]
header = ['Language', 'Translation']
actions = []
types = get_fld_nametypes()
if len(types) > 1:
text = """
<span class="adminlabel">Name type</span>
<select name="sel_type" class="admin_w200">
"""
for (key, value) in types:
text += """<option value="%s" %s>%s""" % (key, key == sel_type and 'selected="selected"' or '', value)
trans_names = get_name(fldID, ln, key, "field")
if trans_names and trans_names[0][0]:
text += ": %s" % trans_names[0][0]
text += "</option>"
text += """</select>"""
output += createhiddenform(action="modifyfieldtranslations#3",
text=text,
button="Select",
fldID=fldID,
ln=ln,
confirm=0)
if confirm in [-1, "-1", 0, "0"]:
trans = []
for (key, value) in langs:
try:
trans_names = get_name(fldID, key, sel_type, "field")
trans.append(trans_names[0][0])
except StandardError, e:
trans.append('')
for nr in range(0,len(langs)):
actions.append(["%s %s" % (langs[nr][1], (langs[nr][0]==CFG_SITE_LANG and '<small>(def)</small>' or ''))])
actions[-1].append('<input type="text" name="trans" size="30" value="%s"/>' % trans[nr])
text = tupletotable(header=header, tuple=actions)
output += createhiddenform(action="modifyfieldtranslations#3",
text=text,
button="Modify",
fldID=fldID,
sel_type=sel_type,
ln=ln,
confirm=2)
if sel_type and len(trans):
if confirm in ["2", 2]:
output += write_outcome(finresult)
body = [output]
if callback:
return perform_editfield(fldID, ln, "perform_modifytranslations", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showdetailsfieldtag(fldID, tagID, ln=CFG_SITE_LANG, callback="yes", confirm=-1):
"""form to add a new field.
fldNAME - the name of the new field
code - the field code"""
fld_dict = dict(get_def_name('', "field"))
fldID = int(fldID)
tagname = run_sql("SELECT name from tag where id=%s" % tagID)[0][0]
output = ""
subtitle = """<a name="4.1"></a>Showing details for MARC tag '%s'""" % tagname
output += "<br /><b>This MARC tag is used directly in these logical fields:</b>&nbsp;"
fld_tag = get_fld_tags('', tagID)
exist = {}
for (id_field,id_tag, tname, tvalue, score) in fld_tag:
output += "%s, " % fld_dict[int(id_field)]
exist[id_field] = 1
output += "<br /><b>This MARC tag is used indirectly in these logical fields:</b>&nbsp;"
tag = run_sql("SELECT value from tag where id=%s" % id_tag)
tag = tag[0][0]
for i in range(0, len(tag) - 1):
res = run_sql("SELECT id_field,id_tag FROM field_tag,tag WHERE tag.id=field_tag.id_tag AND tag.value='%s%%'" % tag[0:i])
for (id_field, id_tag) in res:
output += "%s, " % fld_dict[int(id_field)]
exist[id_field] = 1
res = run_sql("SELECT id_field,id_tag FROM field_tag,tag WHERE tag.id=field_tag.id_tag AND tag.value like '%s'" % tag)
for (id_field, id_tag) in res:
if not exist.has_key(id_field):
output += "%s, " % fld_dict[int(id_field)]
body = [output]
if callback:
return perform_modifyfieldtags(fldID, ln, "perform_showdetailsfieldtag", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showdetailsfield(fldID, ln=CFG_SITE_LANG, callback="yes", confirm=-1):
"""form to add a new field.
fldNAME - the name of the new field
code - the field code"""
fld_dict = dict(get_def_name('', "field"))
col_dict = dict(get_def_name('', "collection"))
fldID = int(fldID)
col_fld = get_col_fld('', '', fldID)
sort_types = dict(get_sort_nametypes())
fin_output = ""
subtitle = """<a name="1"></a>5. Show usage for logical field '%s'""" % fld_dict[fldID]
output = "This logical field is used in these collections:<br />"
ltype = ''
exist = {}
for (id_collection, id_field, id_fieldvalue, ftype, score, score_fieldvalue) in col_fld:
if ltype != ftype:
output += "<br /><b>%s:&nbsp;</b>" % sort_types[ftype]
ltype = ftype
exist = {}
if not exist.has_key(id_collection):
output += "%s, " % col_dict[int(id_collection)]
exist[id_collection] = 1
if not col_fld:
output = "This field is not used by any collections."
fin_output = addadminbox('Collections', [output])
body = [fin_output]
if callback:
return perform_editfield(ln, "perform_showdetailsfield", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_addfield(ln=CFG_SITE_LANG, fldNAME='', code='', callback="yes", confirm=-1):
"""form to add a new field.
fldNAME - the name of the new field
code - the field code"""
output = ""
subtitle = """<a name="6"></a>6. Add new logical field"""
code = str.replace(code,' ', '')
text = """
<span class="adminlabel">Field name</span>
<input class="admin_w200" type="text" name="fldNAME" value="%s" /><br />
<span class="adminlabel">Field code</span>
<input class="admin_w200" type="text" name="code" value="%s" /><br />
""" % (fldNAME, code)
- output = createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/addfield" % weburl,
+ output = createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/addfield" % CFG_SITE_URL,
text=text,
ln=ln,
button="Add field",
confirm=1)
if fldNAME and code and confirm in ["1", 1]:
res = add_fld(fldNAME, code)
output += write_outcome(res)
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please give the logical field a name and code.</span></b>
"""
body = [output]
if callback:
return perform_field(ln, "perform_addfield", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_deletefield(fldID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
"""form to remove a field.
fldID - the field id from table field.
"""
fld_dict = dict(get_def_name('', "field"))
if not fld_dict.has_key(int(fldID)):
return """<b><span class="info">Field does not exist</span></b>"""
- subtitle = """<a name="4"></a>4. Delete the logical field '%s'&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (fld_dict[int(fldID)], weburl)
+ subtitle = """<a name="4"></a>4. Delete the logical field '%s'&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (fld_dict[int(fldID)], CFG_SITE_URL)
output = ""
if fldID:
fldID = int(fldID)
if confirm in ["0", 0]:
check = run_sql("SELECT id_field from idxINDEX_field where id_field=%s" % fldID)
text = ""
if check:
text += """<b><span class="info">This field is used in an index, deletion may cause problems.</span></b><br />"""
text += """Do you want to delete the logical field '%s' and all its relations and definitions.""" % (fld_dict[fldID])
output += createhiddenform(action="deletefield#4",
text=text,
button="Confirm",
fldID=fldID,
confirm=1)
elif confirm in ["1", 1]:
res = delete_fld(fldID)
if res[0] == 1:
return """<br /><b><span class="info">Field deleted.</span></b>""" + write_outcome(res)
else:
output += write_outcome(res)
body = [output]
if callback:
return perform_editfield(fldID, ln, "perform_deletefield", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_deleteindex(idxID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
"""form to delete an index.
idxID - the index id from table idxINDEX.
"""
if idxID:
- subtitle = """<a name="5"></a>5. Delete the index.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % weburl
+ subtitle = """<a name="5"></a>5. Delete the index.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % CFG_SITE_URL
output = ""
if confirm in ["0", 0]:
idx = get_idx(idxID)
if idx:
text = ""
text += """<b><span class="info">By deleting an index, you may also loose any indexed data in the forward and reverse table for this index.</span></b><br />"""
text += """Do you want to delete the index '%s' and all its relations and definitions.""" % (idx[0][1])
output += createhiddenform(action="deleteindex#5",
text=text,
button="Confirm",
idxID=idxID,
confirm=1)
else:
return """<br /><b><span class="info">Index specified does not exist.</span></b>"""
elif confirm in ["1", 1]:
res = delete_idx(idxID)
if res[0] == 1:
return """<br /><b><span class="info">Index deleted.</span></b>""" + write_outcome(res)
else:
output += write_outcome(res)
body = [output]
if callback:
return perform_editindex(idxID, ln, "perform_deleteindex", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showfieldoverview(ln=CFG_SITE_LANG, callback='', confirm=0):
subtitle = """<a name="4"></a>4. Logical fields overview"""
output = """<table cellpadding="3" border="1">"""
output += """<tr><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td></tr>""" % ("Field", "MARC Tags", "Translations")
query = "SELECT id,name FROM field"
res = run_sql(query)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
for field_id,field_name in res:
query = "SELECT tag.value FROM tag, field_tag WHERE tag.id=field_tag.id_tag AND field_tag.id_field=%d ORDER BY field_tag.score DESC,tag.value ASC" % field_id
res = run_sql(query)
field_tags = ""
for row in res:
field_tags = field_tags + row[0] + ", "
if field_tags.endswith(", "):
field_tags = field_tags[:-2]
if not field_tags:
field_tags = """<b><span class="info">None</span></b>"""
lang = get_lang_list("fieldname", "id_field", field_id)
- output += """<tr><td>%s</td><td>%s</td><td>%s</td></tr>""" % ("""<a href="%s/admin/bibindex/bibindexadmin.py/editfield?fldID=%s&ln=%s">%s</a>""" % (weburl, field_id, ln, fld_dict[field_id]), field_tags, lang)
+ output += """<tr><td>%s</td><td>%s</td><td>%s</td></tr>""" % ("""<a href="%s/admin/bibindex/bibindexadmin.py/editfield?fldID=%s&ln=%s">%s</a>""" % (CFG_SITE_URL, field_id, ln, fld_dict[field_id]), field_tags, lang)
output += "</table>"
body = [output]
if callback:
return perform_field(ln, "perform_showfieldoverview", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyindex(idxID, ln=CFG_SITE_LANG, idxNAME='', idxDESC='', callback='yes', confirm=-1):
"""form to modify an index name.
idxID - the index name to change.
idxNAME - new name of index
idxDESC - description of index content"""
subtitle = ""
output = ""
if idxID not in [-1, "-1"]:
- subtitle = """<a name="2"></a>1. Modify index name.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % weburl
+ subtitle = """<a name="2"></a>1. Modify index name.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % CFG_SITE_URL
if confirm in [-1, "-1"]:
idx = get_idx(idxID)
idxNAME = idx[0][1]
idxDESC = idx[0][2]
text = """
<span class="adminlabel">Index name</span>
<input class="admin_w200" type="text" name="idxNAME" value="%s" /><br />
<span class="adminlabel">Index description</span>
<textarea class="admin_w200" name="idxDESC">%s</textarea><br />
""" % (idxNAME, idxDESC)
output += createhiddenform(action="modifyindex#1",
text=text,
button="Modify",
idxID=idxID,
ln=ln,
confirm=1)
if idxID > -1 and idxNAME and confirm in [1, "1"]:
res = modify_idx(idxID, idxNAME, idxDESC)
output += write_outcome(res)
elif confirm in [1, "1"]:
output += """<br /><b><span class="info">Please give a name for the index.</span></b>"""
else:
output = """No index to modify."""
body = [output]
if callback:
return perform_editindex(idxID, ln, "perform_modifyindex", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyindexstemming(idxID, ln=CFG_SITE_LANG, idxSTEM='', callback='yes', confirm=-1):
"""form to modify an index name.
idxID - the index name to change.
idxSTEM - new stemming language code"""
subtitle = ""
output = ""
stemming_language_map = get_stemming_language_map()
stemming_language_map['None'] = ''
if idxID not in [-1, "-1"]:
- subtitle = """<a name="4"></a>4. Modify index stemming language.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % weburl
+ subtitle = """<a name="4"></a>4. Modify index stemming language.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % CFG_SITE_URL
if confirm in [-1, "-1"]:
idx = get_idx(idxID)
idxSTEM = idx[0][4]
if not idxSTEM:
idxSTEM = ''
language_html_element = """<select name="idxSTEM" class="admin_w200">"""
languages = stemming_language_map.keys()
languages.sort()
for language in languages:
if stemming_language_map[language] == idxSTEM:
selected = 'selected="selected"'
else:
selected = ""
language_html_element += """<option value="%s" %s>%s</option>""" % (stemming_language_map[language], selected, language)
language_html_element += """</select>"""
text = """
<span class="adminlabel">Index stemming language</span>
""" + language_html_element
output += createhiddenform(action="modifyindexstemming#4",
text=text,
button="Modify",
idxID=idxID,
ln=ln,
confirm=0)
if confirm in [0, "0"] and get_idx(idxID)[0][4] == idxSTEM:
output += """<span class="info">Stemming language has not been changed</span>"""
elif confirm in [0, "0"]:
text = """
<span class="important">You are going to change the stemming language for this index. Please note you should not enable stemming for structured-data indexes like "report number", "year", "author" or "collection". On the contrary, it is advisable to enable stemming for indexes like "fulltext", "abstract", "title", etc. since this would improve retrieval quality. <br /> Beware that after changing the stemming language of an index you will have to reindex it. It is a good idea to change the stemming language and to reindex during low usage hours of your service, since searching will be not functional until the reindexing will be completed</span>.<br /> <strong>Are you sure you want to change the stemming language of this index?</strong>"""
output += createhiddenform(action="modifyindexstemming#4",
text=text,
button="Modify",
idxID=idxID,
idxSTEM=idxSTEM,
ln=ln,
confirm=1)
elif idxID > -1 and confirm in [1, "1"]:
res = modify_idx_stemming(idxID, idxSTEM)
output += write_outcome(res)
output += """<br /><span class="info">Please note you must run as soon as possible:
<pre>$> %s/bibindex --reindex -w %s</pre></span>
""" % (CFG_BINDIR, get_idx(idxID)[0][1])
elif confirm in [1, "1"]:
output += """<br /><b><span class="info">Please give a name for the index.</span></b>"""
else:
output = """No index to modify."""
body = [output]
if callback:
return perform_editindex(idxID, ln, "perform_modifyindexstemming", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyfield(fldID, ln=CFG_SITE_LANG, code='', callback='yes', confirm=-1):
"""form to modify a field.
fldID - the field to change."""
subtitle = ""
output = ""
fld_dict = dict(get_def_name('', "field"))
if fldID not in [-1, "-1"]:
if confirm in [-1, "-1"]:
res = get_fld(fldID)
code = res[0][2]
else:
code = str.replace("%s" % code, " ", "")
fldID = int(fldID)
- subtitle = """<a name="2"></a>1. Modify field code for logical field '%s'&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (fld_dict[int(fldID)], weburl)
+ subtitle = """<a name="2"></a>1. Modify field code for logical field '%s'&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (fld_dict[int(fldID)], CFG_SITE_URL)
text = """
<span class="adminlabel">Field code</span>
<input class="admin_w200" type="text" name="code" value="%s" /><br />
""" % code
output += createhiddenform(action="modifyfield#2",
text=text,
button="Modify",
fldID=fldID,
ln=ln,
confirm=1)
if fldID > -1 and confirm in [1, "1"]:
fldID = int(fldID)
res = modify_fld(fldID, code)
output += write_outcome(res)
else:
output = """No field to modify.
"""
body = [output]
if callback:
return perform_editfield(fldID, ln, "perform_modifyfield", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyindexfields(idxID, ln=CFG_SITE_LANG, callback='yes', content='', confirm=-1):
"""Modify which logical fields to use in this index.."""
output = ''
- subtitle = """<a name="3"></a>3. Modify index fields.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % weburl
+ subtitle = """<a name="3"></a>3. Modify index fields.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % CFG_SITE_URL
output = """<dl>
<dt>Menu</dt>
<dd><a href="%s/admin/bibindex/bibindexadmin.py/addindexfield?idxID=%s&amp;ln=%s#3.1">Add field to index</a></dd>
<dd><a href="%s/admin/bibindex/bibindexadmin.py/field?ln=%s">Manage fields</a></dd>
</dl>
- """ % (weburl, idxID, ln, weburl, ln)
+ """ % (CFG_SITE_URL, idxID, ln, CFG_SITE_URL, ln)
header = ['Field', '']
actions = []
idx_fld = get_idx_fld(idxID)
if len(idx_fld) > 0:
for (idxID, idxNAME,fldID, fldNAME, regexp_punct, regexp_alpha_sep) in idx_fld:
actions.append([fldNAME])
for col in [(('Remove','removeindexfield'),)]:
- actions[-1].append('<a href="%s/admin/bibindex/bibindexadmin.py/%s?idxID=%s&amp;fldID=%s&amp;ln=%s#3.1">%s</a>' % (weburl, col[0][1], idxID, fldID, ln, col[0][0]))
+ actions[-1].append('<a href="%s/admin/bibindex/bibindexadmin.py/%s?idxID=%s&amp;fldID=%s&amp;ln=%s#3.1">%s</a>' % (CFG_SITE_URL, col[0][1], idxID, fldID, ln, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/bibindex/bibindexadmin.py/%s?fldID=%s&amp;flID=%s&amp;ln=%s#4.1">%s</a>' % (weburl, function, idxID, fldID, ln, str)
+ actions[-1][-1] += ' / <a href="%s/admin/bibindex/bibindexadmin.py/%s?fldID=%s&amp;flID=%s&amp;ln=%s#4.1">%s</a>' % (CFG_SITE_URL, function, idxID, fldID, ln, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No index fields exists"""
output += content
body = [output]
if callback:
return perform_editindex(idxID, ln, "perform_modifyindexfields", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyfieldtags(fldID, ln=CFG_SITE_LANG, callback='yes', content='', confirm=-1):
"""show the sort fields of this collection.."""
output = ''
fld_dict = dict(get_def_name('', "field"))
fld_type = get_fld_nametypes()
fldID = int(fldID)
- subtitle = """<a name="4"></a>3. Modify MARC tags for the logical field '%s'&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (fld_dict[int(fldID)], weburl)
+ subtitle = """<a name="4"></a>3. Modify MARC tags for the logical field '%s'&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibindex-admin-guide">?</a>]</small>""" % (fld_dict[int(fldID)], CFG_SITE_URL)
output = """<dl>
<dt>Menu</dt>
<dd><a href="%s/admin/bibindex/bibindexadmin.py/addtag?fldID=%s&amp;ln=%s#4.1">Add MARC tag</a></dd>
<dd><a href="%s/admin/bibindex/bibindexadmin.py/deletetag?fldID=%s&amp;ln=%s#4.1">Delete unused MARC tags</a></dd>
</dl>
- """ % (weburl, fldID, ln, weburl, fldID, ln)
+ """ % (CFG_SITE_URL, fldID, ln, CFG_SITE_URL, fldID, ln)
header = ['', 'Value', 'Comment', 'Actions']
actions = []
res = get_fld_tags(fldID)
if len(res) > 0:
i = 0
for (fldID, tagID, tname, tvalue, score) in res:
move = ""
if i != 0:
- move += """<a href="%s/admin/bibindex/bibindexadmin.py/switchtagscore?fldID=%s&amp;id_1=%s&amp;id_2=%s&amp;ln=%s&amp=rand=%s#4"><img border="0" src="%s/img/smallup.gif" title="Move tag up"></a>""" % (weburl, fldID, tagID, res[i - 1][1], ln, random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/bibindex/bibindexadmin.py/switchtagscore?fldID=%s&amp;id_1=%s&amp;id_2=%s&amp;ln=%s&amp=rand=%s#4"><img border="0" src="%s/img/smallup.gif" title="Move tag up"></a>""" % (CFG_SITE_URL, fldID, tagID, res[i - 1][1], ln, random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
i += 1
if i != len(res):
- move += '<a href="%s/admin/bibindex/bibindexadmin.py/switchtagscore?fldID=%s&amp;id_1=%s&amp;id_2=%s&amp;ln=%s&amp;rand=%s#4"><img border="0" src="%s/img/smalldown.gif" title="Move tag down"></a>' % (weburl, fldID, tagID, res[i][1], ln, random.randint(0, 1000), weburl)
+ move += '<a href="%s/admin/bibindex/bibindexadmin.py/switchtagscore?fldID=%s&amp;id_1=%s&amp;id_2=%s&amp;ln=%s&amp;rand=%s#4"><img border="0" src="%s/img/smalldown.gif" title="Move tag down"></a>' % (CFG_SITE_URL, fldID, tagID, res[i][1], ln, random.randint(0, 1000), CFG_SITE_URL)
actions.append([move, tvalue, tname])
for col in [(('Details','showdetailsfieldtag'), ('Modify','modifytag'),('Remove','removefieldtag'),)]:
- actions[-1].append('<a href="%s/admin/bibindex/bibindexadmin.py/%s?fldID=%s&amp;tagID=%s&amp;ln=%s#4.1">%s</a>' % (weburl, col[0][1], fldID, tagID, ln, col[0][0]))
+ actions[-1].append('<a href="%s/admin/bibindex/bibindexadmin.py/%s?fldID=%s&amp;tagID=%s&amp;ln=%s#4.1">%s</a>' % (CFG_SITE_URL, col[0][1], fldID, tagID, ln, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/bibindex/bibindexadmin.py/%s?fldID=%s&amp;tagID=%s&amp;ln=%s#4.1">%s</a>' % (weburl, function, fldID, tagID, ln, str)
+ actions[-1][-1] += ' / <a href="%s/admin/bibindex/bibindexadmin.py/%s?fldID=%s&amp;tagID=%s&amp;ln=%s#4.1">%s</a>' % (CFG_SITE_URL, function, fldID, tagID, ln, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No fields exists"""
output += content
body = [output]
if callback:
return perform_editfield(fldID, ln, "perform_modifyfieldtags", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_addtag(fldID, ln=CFG_SITE_LANG, value=['',-1], name='', callback="yes", confirm=-1):
"""form to add a new field.
fldNAME - the name of the new field
code - the field code"""
output = ""
subtitle = """<a name="4.1"></a>Add MARC tag to logical field"""
text = """
Add new tag:<br />
<span class="adminlabel">Tag value</span>
<input class="admin_w200" maxlength="6" type="text" name="value" value="%s" /><br />
<span class="adminlabel">Tag comment</span>
<input class="admin_w200" type="text" name="name" value="%s" /><br />
""" % ((name=='' and value[0] or name), value[0])
text += """Or existing tag:<br />
<span class="adminlabel">Tag</span>
<select name="value" class="admin_w200">
<option value="-1">- Select a tag -</option>
"""
fld_tags = get_fld_tags(fldID)
tags = get_tags()
fld_tags = dict(map(lambda x: (x[1], x[0]), fld_tags))
for (id_tag, tname, tvalue) in tags:
if not fld_tags.has_key(id_tag):
text += """<option value="%s" %s>%s</option>""" % (tvalue, (tvalue==value[1] and 'selected="selected"' or ''), "%s - %s" % (tvalue, tname))
text += """</select>"""
- output = createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/addtag" % weburl,
+ output = createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/addtag" % CFG_SITE_URL,
text=text,
fldID=fldID,
ln=ln,
button="Add tag",
confirm=1)
if (value[0] and value[1] in [-1, "-1"]) or (not value[0] and value[1] not in [-1, "-1"]):
if confirm in ["1", 1]:
res = add_fld_tag(fldID, name, (value[0] !='' and value[0] or value[1]))
output += write_outcome(res)
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please choose to add either a new or an existing MARC tag, but not both.</span></b>
"""
body = [output]
if callback:
return perform_modifyfieldtags(fldID, ln, "perform_addtag", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifytag(fldID, tagID, ln=CFG_SITE_LANG, name='', value='', callback='yes', confirm=-1):
"""form to modify a field.
fldID - the field to change."""
subtitle = ""
output = ""
fld_dict = dict(get_def_name('', "field"))
fldID = int(fldID)
tagID = int(tagID)
tag = get_tags(tagID)
if confirm in [-1, "-1"] and not value and not name:
name = tag[0][1]
value = tag[0][2]
subtitle = """<a name="3.1"></a>Modify MARC tag"""
text = """
Any modifications will apply to all logical fields using this tag.<br />
<span class="adminlabel">Tag value</span>
<input class="admin_w200" type="text" name="value" value="%s" /><br />
<span class="adminlabel">Comment</span>
<input class="admin_w200" type="text" name="name" value="%s" /><br />
""" % (value, name)
output += createhiddenform(action="modifytag#4.1",
text=text,
button="Modify",
fldID=fldID,
tagID=tagID,
ln=ln,
confirm=1)
if name and value and confirm in [1, "1"]:
res = modify_tag(tagID, name, value)
output += write_outcome(res)
body = [output]
if callback:
return perform_modifyfieldtags(fldID, ln, "perform_modifytag", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_removefieldtag(fldID, tagID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
"""form to remove a tag from a field.
fldID - the current field, remove the tag from this field.
tagID - remove the tag with this id"""
subtitle = """<a name="4.1"></a>Remove MARC tag from logical field"""
output = ""
fld_dict = dict(get_def_name('', "field"))
if fldID and tagID:
fldID = int(fldID)
tagID = int(tagID)
tag = get_fld_tags(fldID, tagID)
if confirm not in ["1", 1]:
text = """Do you want to remove the tag '%s - %s ' from the field '%s'.""" % (tag[0][3], tag[0][2], fld_dict[fldID])
output += createhiddenform(action="removefieldtag#4.1",
text=text,
button="Confirm",
fldID=fldID,
tagID=tagID,
confirm=1)
elif confirm in ["1", 1]:
res = remove_fldtag(fldID, tagID)
output += write_outcome(res)
body = [output]
if callback:
return perform_modifyfieldtags(fldID, ln, "perform_removefieldtag", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_addindexfield(idxID, ln=CFG_SITE_LANG, fldID='', callback="yes", confirm=-1):
"""form to add a new field.
fldNAME - the name of the new field
code - the field code"""
output = ""
subtitle = """<a name="4.1"></a>Add logical field to index"""
text = """
<span class="adminlabel">Field name</span>
<select name="fldID" class="admin_w200">
<option value="-1">- Select a field -</option>
"""
fld = get_fld()
for (fldID2, fldNAME, fldCODE) in fld:
text += """<option value="%s" %s>%s</option>""" % (fldID2, (fldID==fldID2 and 'selected="selected"' or ''), fldNAME)
text += """</select>"""
- output = createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/addindexfield" % weburl,
+ output = createhiddenform(action="%s/admin/bibindex/bibindexadmin.py/addindexfield" % CFG_SITE_URL,
text=text,
idxID=idxID,
ln=ln,
button="Add field",
confirm=1)
if fldID and not fldID in [-1, "-1"] and confirm in ["1", 1]:
res = add_idx_fld(idxID, fldID)
output += write_outcome(res)
elif confirm in ["1", 1]:
output += """<b><span class="info">Please select a field to add.</span></b>"""
body = [output]
if callback:
return perform_modifyindexfields(idxID, ln, "perform_addindexfield", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_removeindexfield(idxID, fldID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
"""form to remove a field from an index.
idxID - the current index, remove the field from this index.
fldID - remove the field with this id"""
subtitle = """<a name="3.1"></a>Remove field from index"""
output = ""
if fldID and idxID:
fldID = int(fldID)
idxID = int(idxID)
fld = get_fld(fldID)
idx = get_idx(idxID)
if fld and idx and confirm not in ["1", 1]:
text = """Do you want to remove the field '%s' from the index '%s'.""" % (fld[0][1], idx[0][1])
output += createhiddenform(action="removeindexfield#3.1",
text=text,
button="Confirm",
idxID=idxID,
fldID=fldID,
confirm=1)
elif confirm in ["1", 1]:
res = remove_idxfld(idxID, fldID)
output += write_outcome(res)
body = [output]
if callback:
return perform_modifyindexfields(idxID, ln, "perform_removeindexfield", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_switchtagscore(fldID, id_1, id_2, ln=CFG_SITE_LANG):
"""Switch the score of id_1 and id_2 in the table type.
colID - the current collection
id_1/id_2 - the id's to change the score for.
type - like "format" """
output = ""
name_1 = run_sql("select name from tag where id=%s" % id_1)[0][0]
name_2 = run_sql("select name from tag where id=%s" % id_2)[0][0]
res = switch_score(fldID, id_1, id_2)
output += write_outcome(res)
return perform_modifyfieldtags(fldID, ln, content=output)
def perform_deletetag(fldID, ln=CFG_SITE_LANG, tagID=-1, callback='yes', confirm=-1):
"""form to delete an MARC tag not in use.
fldID - the collection id of the current collection.
fmtID - the format id to delete."""
subtitle = """<a name="10.3"></a>Delete an unused MARC tag"""
output = """
<dl>
<dd>Deleting an MARC tag will also delete the translations associated.</dd>
</dl>
"""
fldID = int(fldID)
if tagID not in [-1," -1"] and confirm in [1, "1"]:
ares = delete_tag(tagID)
fld_tag = get_fld_tags()
fld_tag = dict(map(lambda x: (x[1], x[0]), fld_tag))
tags = get_tags()
text = """
<span class="adminlabel">MARC tag</span>
<select name="tagID" class="admin_w200">
"""
text += """<option value="-1">- Select MARC tag -"""
i = 0
for (id, name, value) in tags:
if not fld_tag.has_key(id):
text += """<option value="%s" %s>%s</option>""" % (id, id == int(tagID) and 'selected="selected"' or '', "%s - %s" % (value, name))
i += 1
text += """</select><br />"""
if i == 0:
output += """<b><span class="info">No unused MARC tags</span></b><br />"""
else:
output += createhiddenform(action="deletetag#4.1",
text=text,
button="Delete",
fldID=fldID,
ln=ln,
confirm=0)
if tagID not in [-1,"-1"]:
tagID = int(tagID)
tags = get_tags(tagID)
if confirm in [0, "0"]:
text = """<b>Do you want to delete the MARC tag '%s'.</b>""" % tags[0][2]
output += createhiddenform(action="deletetag#4.1",
text=text,
button="Confirm",
fldID=fldID,
tagID=tagID,
ln=ln,
confirm=1)
elif confirm in [1, "1"]:
output += write_outcome(ares)
elif confirm not in [-1, "-1"]:
output += """<b><span class="info">Choose a MARC tag to delete.</span></b>"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfieldtags(fldID, ln, content=output)
def compare_on_val(first, second):
"""Compare the two values"""
return cmp(first[1], second[1])
def get_col_fld(colID=-1, type = '', id_field=''):
"""Returns either all portalboxes associated with a collection, or based on either colID or language or both.
colID - collection id
ln - language id"""
sql = "SELECT id_collection,id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue, field WHERE id_field=field.id"
try:
if id_field:
sql += " AND id_field=%s" % id_field
sql += " ORDER BY type, score desc, score_fieldvalue desc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_idx(idxID=''):
sql = "SELECT id,name,description,last_updated,stemming_language FROM idxINDEX"
try:
if idxID:
sql += " WHERE id=%s" % idxID
sql += " ORDER BY id asc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_fld_tags(fldID='', tagID=''):
"""Returns tags associated with a field.
fldID - field id
tagID - tag id"""
sql = "SELECT id_field,id_tag, tag.name, tag.value, score FROM field_tag,tag WHERE tag.id=field_tag.id_tag"
try:
if fldID:
sql += " AND id_field=%s" % fldID
if tagID:
sql += " AND id_tag=%s" % tagID
sql += " ORDER BY score desc, tag.value, tag.name"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_tags(tagID=''):
"""Returns all or a given tag.
tagID - tag id
ln - language id"""
sql = "SELECT id, name, value FROM tag"
try:
if tagID:
sql += " WHERE id=%s" % tagID
sql += " ORDER BY name, value"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_fld(fldID=''):
"""Returns all fields or only the given field"""
try:
if not fldID:
res = run_sql("SELECT id, name, code FROM field ORDER by name, code")
else:
res = run_sql("SELECT id, name, code FROM field WHERE id=%s ORDER by name, code" % fldID)
return res
except StandardError, e:
return ""
def get_fld_value(fldvID = ''):
"""Returns fieldvalue"""
try:
sql = "SELECT id, name, value FROM fieldvalue"
if fldvID:
sql += " WHERE id=%s" % fldvID
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_idx_fld(idxID=''):
"""Return a list of fields associated with one or all indexes"""
try:
sql = "SELECT id_idxINDEX, idxINDEX.name, id_field, field.name, regexp_punctuation, regexp_alphanumeric_separators FROM idxINDEX, field, idxINDEX_field WHERE idxINDEX.id = idxINDEX_field.id_idxINDEX AND field.id = idxINDEX_field.id_field"
if idxID:
sql += " AND id_idxINDEX=%s" % idxID
sql += " ORDER BY id_idxINDEX asc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_col_nametypes():
"""Return a list of the various translationnames for the fields"""
type = []
type.append(('ln', 'Long name'))
return type
def get_fld_nametypes():
"""Return a list of the various translationnames for the fields"""
type = []
type.append(('ln', 'Long name'))
return type
def get_idx_nametypes():
"""Return a list of the various translationnames for the index"""
type = []
type.append(('ln', 'Long name'))
return type
def get_sort_nametypes():
"""Return a list of the various translationnames for the fields"""
type = {}
type['soo'] = 'Sort options'
type['seo'] = 'Search options'
type['sew'] = 'Search within'
return type
def remove_fld(colID,fldID, fldvID=''):
"""Removes a field from the collection given.
colID - the collection the format is connected to
fldID - the field which should be removed from the collection."""
try:
sql = "DELETE FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s" % (colID, fldID)
if fldvID:
sql += " AND id_fieldvalue=%s" % fldvID
res = run_sql(sql)
return (1, "")
except StandardError, e:
return (0, e)
def remove_idxfld(idxID, fldID):
"""Remove a field from a index in table idxINDEX_field
idxID - index id from idxINDEX
fldID - field id from field table"""
try:
sql = "DELETE FROM idxINDEX_field WHERE id_field=%s and id_idxINDEX=%s" % (fldID, idxID)
res = run_sql(sql)
return (1, "")
except StandardError, e:
return (0, e)
def remove_fldtag(fldID,tagID):
"""Removes a tag from the field given.
fldID - the field the tag is connected to
tagID - the tag which should be removed from the field."""
try:
sql = "DELETE FROM field_tag WHERE id_field=%s AND id_tag=%s" % (fldID, tagID)
res = run_sql(sql)
return (1, "")
except StandardError, e:
return (0, e)
def delete_tag(tagID):
"""Deletes all data for the given field
fldID - delete all data in the tables associated with field and this id """
try:
res = run_sql("DELETE FROM tag where id=%s" % tagID)
return (1, "")
except StandardError, e:
return (0, e)
def delete_idx(idxID):
"""Deletes all data for the given index together with the idxWORDXXR and idxWORDXXF tables"""
try:
res = run_sql("DELETE FROM idxINDEX WHERE id=%s" % idxID)
res = run_sql("DELETE FROM idxINDEXNAME WHERE id_idxINDEX=%s" % idxID)
res = run_sql("DELETE FROM idxINDEX_field WHERE id_idxINDEX=%s" % idxID)
res = run_sql("DROP TABLE idxWORD%sF" % (idxID < 10 and "0%s" % idxID or idxID))
res = run_sql("DROP TABLE idxWORD%sR" % (idxID < 10 and "0%s" % idxID or idxID))
res = run_sql("DROP TABLE idxPHRASE%sF" % (idxID < 10 and "0%s" % idxID or idxID))
res = run_sql("DROP TABLE idxPHRASE%sR" % (idxID < 10 and "0%s" % idxID or idxID))
return (1, "")
except StandardError, e:
return (0, e)
def delete_fld(fldID):
"""Deletes all data for the given field
fldID - delete all data in the tables associated with field and this id """
try:
res = run_sql("DELETE FROM collection_field_fieldvalue WHERE id_field=%s" % fldID)
res = run_sql("DELETE FROM field_tag WHERE id_field=%s" % fldID)
res = run_sql("DELETE FROM idxINDEX_field WHERE id_field=%s" % fldID)
res = run_sql("DELETE FROM field WHERE id=%s" % fldID)
return (1, "")
except StandardError, e:
return (0, e)
def add_idx(idxNAME):
"""Add a new index. returns the id of the new index.
idxID - the id for the index, number
idxNAME - the default name for the default language of the format."""
try:
idxID = 0
res = run_sql("SELECT id from idxINDEX WHERE name=%s", (idxNAME,))
if res:
return (0, (0, "A index with the given name already exists."))
for i in range(1, 100):
res = run_sql("SELECT id from idxINDEX WHERE id=%s" % i)
res2 = get_table_status_info("idxWORD%s%%" % (i < 10 and "0%s" % i or i))
if not res and not res2:
idxID = i
break
if idxID == 0:
return (0, (0, "Not possible to create new indexes, delete an index and try again."))
res = run_sql("INSERT INTO idxINDEX (id, name) VALUES (%s,%s)", (idxID, idxNAME))
type = get_idx_nametypes()[0][0]
res = run_sql("INSERT INTO idxINDEXNAME (id_idxINDEX, ln, type, value) VALUES (%s,%s,%s,%s)",
(idxID, CFG_SITE_LANG, type, idxNAME))
res = run_sql("""CREATE TABLE IF NOT EXISTS idxWORD%sF (
id mediumint(9) unsigned NOT NULL auto_increment,
term varchar(50) default NULL,
hitlist longblob,
PRIMARY KEY (id),
UNIQUE KEY term (term)
) TYPE=MyISAM""" % (idxID < 10 and "0%s" % idxID or idxID))
res = run_sql("""CREATE TABLE IF NOT EXISTS idxWORD%sR (
id_bibrec mediumint(9) unsigned NOT NULL,
termlist longblob,
type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT',
PRIMARY KEY (id_bibrec,type)
) TYPE=MyISAM""" % (idxID < 10 and "0%s" % idxID or idxID))
res = run_sql("""CREATE TABLE `idxPHRASE%sF` (
`id` mediumint(9) unsigned NOT NULL auto_increment,
`term` text default NULL,
`hitlist` longblob,
PRIMARY KEY (`id`),
KEY `term` (`term`(50))
) TYPE=MyISAM""" % (idxID < 10 and "0%s" % idxID or idxID))
res = run_sql("""CREATE TABLE `idxPHRASE%sR` (
`id_bibrec` mediumint(9) unsigned NOT NULL default '0',
`termlist` longblob,
`type` enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT',
PRIMARY KEY (`id_bibrec`,`type`)
) TYPE=MyISAM""" % (idxID < 10 and "0%s" % idxID or idxID))
res = run_sql("SELECT id from idxINDEX WHERE id=%s" % idxID)
res2 = get_table_status_info("idxWORD%sF" % (idxID < 10 and "0%s" % idxID or idxID))
res3 = get_table_status_info("idxWORD%sR" % (idxID < 10 and "0%s" % idxID or idxID))
if res and res2 and res3:
return (1, res[0][0])
elif not res:
return (0, (0, "Could not add the new index to idxINDEX"))
elif not res2:
return (0, (0, "Forward table not created for unknown reason."))
elif not res3:
return (0, (0, "Reverse table not created for unknown reason."))
except StandardError, e:
return (0, e)
def add_fld(name, code):
"""Add a new logical field. Returns the id of the field.
code - the code for the field,
name - the default name for the default language of the field."""
try:
type = get_fld_nametypes()[0][0]
res = run_sql("INSERT INTO field (name, code) VALUES (%s,%s)", (name, code))
fldID = run_sql("SELECT id FROM field WHERE code=%s", (code,))
res = run_sql("INSERT INTO fieldname (id_field, type, ln, value) VALUES (%s,%s,%s,%s)", (fldID[0][0], type, CFG_SITE_LANG, name))
if fldID:
return (1, fldID[0][0])
else:
raise StandardError
except StandardError, e:
return (0, e)
def add_fld_tag(fldID, name, value):
"""Add a sort/search/field to the collection.
colID - the id of the collection involved
fmtID - the id of the format.
score - the score of the format, decides sorting, if not given, place the format on top"""
try:
res = run_sql("SELECT score FROM field_tag WHERE id_field=%s ORDER BY score desc" % (fldID))
if res:
score = int(res[0][0]) + 1
else:
score = 0
res = run_sql("SELECT id FROM tag WHERE value=%s", (value,))
if not res:
if name == '':
name = value
res = run_sql("INSERT INTO tag (name, value) VALUES (%s,%s)", (name, value))
res = run_sql("SELECT id FROM tag WHERE value=%s", (value,))
res = run_sql("INSERT INTO field_tag(id_field, id_tag, score) values(%s, %s, %s)" % (fldID, res[0][0], score))
return (1, "")
except StandardError, e:
return (0, e)
def add_idx_fld(idxID, fldID):
"""Add a field to an index"""
try:
sql = "SELECT id_idxINDEX FROM idxINDEX_field WHERE id_idxINDEX=%s and id_field=%s"
res = run_sql(sql, (idxID, fldID))
if res:
return (0, (0, "The field selected already exists for this index"))
sql = "INSERT INTO idxINDEX_field(id_idxINDEX, id_field) values (%s, %s)"
res = run_sql(sql, (idxID, fldID))
return (1, "")
except StandardError, e:
return (0, e)
def modify_idx(idxID, idxNAME, idxDESC):
"""Modify index name or index description in idxINDEX table"""
try:
res = run_sql("UPDATE idxINDEX SET name=%s WHERE id=%s", (idxNAME, idxID))
res = run_sql("UPDATE idxINDEX SET description=%s WHERE ID=%s", (idxDESC, idxID))
return (1, "")
except StandardError, e:
return (0, e)
def modify_idx_stemming(idxID, idxSTEM):
"""Modify the index stemming language in idxINDEX table"""
try:
res = run_sql("UPDATE idxINDEX SET stemming_language=%s WHERE ID=%s", (idxSTEM, idxID))
return (1, "")
except StandardError, e:
return (0, e)
def modify_fld(fldID, code):
"""Modify the code of field
fldID - the id of the field to modify
code - the new code"""
try:
sql = "UPDATE field SET code='%s'" % code
sql += " WHERE id=%s" % fldID
res = run_sql(sql)
return (1, "")
except StandardError, e:
return (0, e)
def modify_tag(tagID, name, value):
"""Modify the name and value of a tag.
tagID - the id of the tag to modify
name - the new name of the tag
value - the new value of the tag"""
try:
sql = "UPDATE tag SET name='%s' WHERE id=%s" % (name, tagID)
res = run_sql(sql)
sql = "UPDATE tag SET value='%s' WHERE id=%s" % (value, tagID)
res = run_sql(sql)
return (1, "")
except StandardError, e:
return (0, e)
def switch_score(fldID, id_1, id_2):
"""Switch the scores of id_1 and id_2 in the table given by the argument.
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM field_tag WHERE id_field=%s and id_tag=%s" % (fldID, id_1))
res2 = run_sql("SELECT score FROM field_tag WHERE id_field=%s and id_tag=%s" % (fldID, id_2))
res = run_sql("UPDATE field_tag SET score=%s WHERE id_field=%s and id_tag=%s" % (res2[0][0], fldID, id_1))
res = run_sql("UPDATE field_tag SET score=%s WHERE id_field=%s and id_tag=%s" % (res1[0][0], fldID, id_2))
return (1, "")
except StandardError, e:
return (0, e)
def get_lang_list(table, field, id):
langs = run_sql("select ln from %s where %s=%s" % (table, field, id))
exists = {}
lang = ''
for lng in langs:
if not exists.has_key(lng[0]):
lang += lng[0] + ", "
exists[lng[0]] = 1
if lang.endswith(", "):
lang = lang [:-2]
if len(exists) == 0:
lang = """<b><span class="info">None</span></b>"""
return lang
diff --git a/modules/bibindex/web/admin/bibindexadmin.py b/modules/bibindex/web/admin/bibindexadmin.py
index 802e9b4a1..a2ebdf4ba 100644
--- a/modules/bibindex/web/admin/bibindexadmin.py
+++ b/modules/bibindex/web/admin/bibindexadmin.py
@@ -1,661 +1,661 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio BibIndex Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import sys
import invenio.bibindexadminlib as bic
from invenio.webpage import page, create_error_box
-from invenio.config import weburl, CFG_SITE_LANG, CFG_SITE_NAME
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG, CFG_SITE_NAME
from invenio.dbquery import Error
from invenio.webuser import getUid, page_not_authorized
def deletetag(req, fldID, ln=CFG_SITE_LANG, tagID=-1, callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_deletetag(fldID=fldID,
ln=ln,
tagID=tagID,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addtag(req, fldID, ln=CFG_SITE_LANG, value=['',-1], name='', callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_addtag(fldID=fldID,
ln=ln,
value=value,
name=name,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyfieldtags(req, fldID, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_modifyfieldtags(fldID=fldID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addindexfield(req, idxID, ln=CFG_SITE_LANG, fldID='', callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Index",
body=bic.perform_addindexfield(idxID=idxID,
ln=ln,
fldID=fldID,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyindexfields(req, idxID, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Index",
body=bic.perform_modifyindexfields(idxID=idxID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showdetailsfieldtag(req, fldID, tagID, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_showdetailsfieldtag(fldID=fldID,
tagID=tagID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showdetailsfield(req, fldID, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_showdetailsfield(fldID=fldID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyfield(req, fldID, ln=CFG_SITE_LANG, code='', callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_modifyfield(fldID=fldID,
ln=ln,
code=code,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyindex(req, idxID, ln=CFG_SITE_LANG, idxNAME='', idxDESC='', callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Index",
body=bic.perform_modifyindex(idxID=idxID,
ln=ln,
idxNAME=idxNAME,
idxDESC=idxDESC,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyindexstemming(req, idxID, ln=CFG_SITE_LANG, idxSTEM='', callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Index",
body=bic.perform_modifyindexstemming(idxID=idxID,
ln=ln,
idxSTEM=idxSTEM,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifytag(req, fldID, tagID, ln=CFG_SITE_LANG, name='', value='', callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_modifytag(fldID=fldID,
tagID=tagID,
ln=ln,
name=name,
value=value,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def deletefield(req, fldID, ln=CFG_SITE_LANG, confirm=0):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_deletefield(fldID=fldID,
ln=ln,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def deleteindex(req, idxID, ln=CFG_SITE_LANG, confirm=0):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Index",
body=bic.perform_deleteindex(idxID=idxID,
ln=ln,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showfieldoverview(req, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Manage logical fields",
body=bic.perform_showfieldoverview(ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def editfields(req, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Manage logical fields",
body=bic.perform_editfields(ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def editfield(req, fldID, ln=CFG_SITE_LANG, mtype='', callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_editfield(fldID=fldID,
ln=ln,
mtype=mtype,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def editindex(req, idxID, ln=CFG_SITE_LANG, mtype='', callback='yes', confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Index",
body=bic.perform_editindex(idxID=idxID,
ln=ln,
mtype=mtype,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyindextranslations(req, idxID, ln=CFG_SITE_LANG, sel_type='', trans = [], confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Index",
body=bic.perform_modifyindextranslations(idxID=idxID,
ln=ln,
sel_type=sel_type,
trans=trans,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyfieldtranslations(req, fldID, ln=CFG_SITE_LANG, sel_type='', trans = [], confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_modifyfieldtranslations(fldID=fldID,
ln=ln,
sel_type=sel_type,
trans=trans,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addfield(req, ln=CFG_SITE_LANG, fldNAME='', code='', callback="yes", confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Manage logical fields",
body=bic.perform_addfield(ln=ln,
fldNAME=fldNAME,
code=code,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addindex(req, ln=CFG_SITE_LANG, idxNAME='', callback="yes", confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Manage Indexes",
body=bic.perform_addindex(ln=ln,
idxNAME=idxNAME,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def switchtagscore(req, fldID, id_1, id_2, ln=CFG_SITE_LANG):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_switchtagscore(fldID=fldID,
id_1=id_1,
id_2=id_2,
ln=ln),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def removeindexfield(req, idxID, fldID, ln=CFG_SITE_LANG, callback="yes", confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/index">Manage Indexes</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Index",
body=bic.perform_removeindexfield(idxID=idxID,
fldID=fldID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def removefieldtag(req, fldID, tagID, ln=CFG_SITE_LANG, callback="yes", confirm=-1):
- navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (weburl)
+ navtrail_previous_links = bic.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Edit Logical Field",
body=bic.perform_removefieldtag(fldID=fldID,
tagID=tagID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def index(req, ln=CFG_SITE_LANG, mtype='', content=''):
navtrail_previous_links = bic.getnavtrail()
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Manage Indexes",
body=bic.perform_index(ln=ln,
mtype=mtype,
content=content),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def field(req, ln=CFG_SITE_LANG, mtype='', content=''):
navtrail_previous_links = bic.getnavtrail()
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = bic.check_user(req,'cfgbibindex')
if not auth[0]:
return page(title="Manage logical fields",
body=bic.perform_field(ln=ln,
mtype=mtype,
content=content),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def error_page(req, ln=CFG_SITE_LANG, verbose=1):
return page(title="Internal Error",
body = create_error_box(req, verbose=verbose, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req)
diff --git a/modules/bibmatch/doc/admin/bibmatch-admin-guide.webdoc b/modules/bibmatch/doc/admin/bibmatch-admin-guide.webdoc
index bd9798f8b..df6b918f1 100644
--- a/modules/bibmatch/doc/admin/bibmatch-admin-guide.webdoc
+++ b/modules/bibmatch/doc/admin/bibmatch-admin-guide.webdoc
@@ -1,86 +1,86 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibMatch Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>BibMatch allows to match bibliographic data in a MARCXML file against database content. Providing an input file in MARCXML the produced output corresponds to a selection of records in MARCXML that do (not) match the database content. This way it is for example possible to identify potential double entries before they get uploaded in a database. </p>
<h4>BibMatch commmand-line tool</h4>
<blockquote>
<pre>
Usage: bibmatch [options] < input.xml > output.xml
Options:
Output:
-0 --print-new (default)
-1 --print-match
-2 --print-ambiguous
-b --batch-output=(filename)
Simple query:
-f --field=(field)
Advanced query:
-c --config=(config-filename)
-q --query-string=(uploader_querystring)
-m --mode=(a|e|o|p|r)[3]
-o --operator=(a|o)[2]
General options:
-h, --help print this help and exit
-v, --verbose=LEVEL verbose level (from 0 to 9, default 1)
</pre>
</blockquote>
<h4>Examples</h4>
To match records on title and print out only new (unmatched) ones:
<blockquote>
<pre>
$ bibmatch [--print-new] --field=\"title\" < input.xml > output.xml
</pre>
</blockquote>
To print potential double entries before upload for manual double check:
<blockquote>
<pre>
$ bibmatch --print-match --field=\"245__a\" --mode=\"a\" < input.xml > output.xml
</pre>
</blockquote>
To print undecided result from a match on multiple fields:
<blockquote>
<pre>
$ bibmatch --print-ambiguous --query-string=\"245__a||100__a\" < input.xml > output.xml
</pre>
</blockquote>
diff --git a/modules/bibrank/doc/admin/bibrank-admin-guide.webdoc b/modules/bibrank/doc/admin/bibrank-admin-guide.webdoc
index 28ff23fb9..47da16973 100644
--- a/modules/bibrank/doc/admin/bibrank-admin-guide.webdoc
+++ b/modules/bibrank/doc/admin/bibrank-admin-guide.webdoc
@@ -1,552 +1,552 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibRank Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1.<a href="#o">Overview</a></strong><br/>
<strong>2.<a href="#c">Configuration Conventions</a></strong><br/>
<strong>3.<a href="#bai">BibRank Admin Interface</a></strong><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1.<a href="#mi">Main interface</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2.<a href="#ar">Add rank method</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3.<a href="#sd">Show details of rank method</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4.<a href="#mr">Modify rank method</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.5.<a href="#dr">Delete rank method</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.6.<a href="#mt">Modify translations</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.7.<a href="#mc">Modify visibility toward collections</a><br/>
<strong>4.<a href="#bd">BibRank Daemon</a></strong><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.1.<a href="#cli1">Command Line Interface</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.2.<a href="#ubd">Using BibRank</a><br/>
<strong>5.<a href="#brm">BibRank Methods</a></strong><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5.1.<a href="#str">Single tag rank method</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5.2.<a href="#wrd">Word Similarity/Similar Records</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5.3.<a href="#cmb">Combined method</a><br/>
<strong>6.<a href="#bt">bibrankgkb Tool</a></strong><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6.1.<a href="#cli2">Command Line Interface</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6.2.<a href="#ubt">Using bibrankgkb</a><br/>
<strong>7.<a href="#ainf">Additional Information</a></strong><br/>
<a name="o"></a><h2>1. Overview</h2>
<p>The bibrank module consist currently of two tools:
<br/><br/>bibrank - Generates ranking data for ranking search results based on methods like:</p>
<blockquote>
<pre>
Journal Impact Factor
Word Similarity/Similar Records
Combined Method
&nbsp;##Number of downloads
&nbsp;##Author Impact
&nbsp;##Citation Impact
</pre>
</blockquote>
<p>bibrankgkb - For generating knowledge base files for use with bibrank
</p>
<p>The bibrankgkb may not be necessary to use, it depends on which ranking methods you are planning
to use, and what data you already got. This guide will take you through the necessary steps in detail in
order to create different kinds of ranking methods for the search engine to use.
</p>
<a name="c"></a><h2>2. Configuration Conventions</h2>
<blockquote>
<pre>
- comment line starts with '#' sign in the first column
- each section in a configuration file is declared inside '[' ']' signs
- values in knowledgebasefiles are separated by '---'
</pre>
</blockquote>
<a name="bai"></a><h2>3. BibRank Admin Interface</h2>
<p>The bibrank web interface enables you to modify the configuration of most aspects of BibRank. For full functionality, it is advised to
let the http-daemon have write/read access to your invenio/etc/bibrank directory. If this is not wanted, you have to edit the configuration files from the console using your favourite text editor.</p>
<a name="mi"></a><h3>3.1 Main interface</h3>
<p>In the main interface screen, you see a list of all rank methods currently added. Each rank method is identified by the rank method code. To find out about the functionality available, check out the topics below.</p>
<b>Explanation of concepts</b>
<blockquote>
<pre>
Rank method:
A method responsible for creating the necessary data to rank a result.
Translations:
Each rank method may have many names in many languages.
Collections:
Which collections the rank method should be visible in.
</pre>
</blockquote>
<a name="ar"></a><h3>3.2 Add rank method</h3>
<p>When pressing the link in the upper right corner from the main interface, you will see the interface for adding a new rank method. The two available options that needs to be decided upon, are the bibrank code and the template to use, both values can be changed later. The bibrank code is used by the bibrank daemon to run the method, and should be fairly short without spaces. Which template you are using, decides how the ranking will be done, and must before used, be changed to suit your CDS Invenio configuration. When confirming to add a new rank method, it will be added to the list of possible rank methods, and a configuration file will be created if the httpd user has proper rights to the 'invenio/etc/bibrank' directory. If not, the file has to manually be created with the name 'bibrankcode.cfg' where bibrankcode is the same as given in the interface.</p>
<a name="sd"></a><h3>3.3 Show details of rank method</h3>
<p>This interface gives you an overview of the current status of the rank method, and gives direct access to the various interfaces for changing the configuration.
In the overview section, you see the bibrank code, for use with the bibrank daemon, and the date for the last run of the rank method.
In the statistics section you see how many records have been added to the rank method and other statistic data. In the collection part, the collections which the rank method is visible to is shown. The translations part shows the various translations in the languages available in CDS Invenio. On the bottom the configuration file is shown, if accessible.</p>
<a name="mr"></a><h3>3.4 Modify rank method</h3>
<p>This interface gives access to modify the bibrank code given when creating the rank method and the configuration file of the rank method, if the file can be accessed. If not, it may not exist, or the httpd user doesn't have enough rights to read the file. On the bottom of the interface, it is possible to choose a template, see it, and copy it over the old rank method configuration if wanted. Remember that the values present in the template is an example, and must be changed where necessary. See this documentation for information about this, and the 'BibRank Internals' link below for additional information.</p>
<a name="dr"></a><h3>3.5 Delete rank method</h3>
<p>If it is necessary to delete a rank method, some precautions must be taken since the configuration of the method will be lost. When deleting a rank method, the configuration file will also be deleted ('invenio/etc/bibrank/bibrankcode.cfg' where bibrankcode is the code of the rank method) if accessible to the httpd user. If not, the file can be deleted manually from console. Any bibrank tasks scheduled to run the deleted rank method must be modified or deleted manually.</p>
<a name="mt"></a><h3>3.6 Modify translations</h3>
<p>If you want to use internalisation of the rank method names, you have to add them using the 'Modify translations' interface. Below a list of all the languages used in the CDS Invenio installation will be shown with the possibility to add the translation for each language.</p>
<a name="mc"></a><h3>3.7 Modify visibility toward collections</h3>
<p>If a rank method should be visible to the users of the CDS Invenio search interface, it must be enabled for one or several collections. A rank method can be visible in the search interface of the whole site, or just one collection. The collections in the upper list box does not show the rank method in the search interface to the user. To change this select the wanted collection and press 'Enable' to enable the rank method for this collection. The collections that the method has been activated for, is shown in the lower list box. To remove a collection, select it and press the 'Disable' button to remove it from the list of collections which the rank method is enabled for.</p>
<a name="bd"></a><h2>4. BibRank Daemon</h2>
<p>The bibrank daemon read the necessary metadata from the CDS Invenio database and combines the read metadata
in different ways to create the ranking data necessary at searchtime to fast be able to rank the results.</p>
<a name="cli1"></a><h3>4.1 Command Line Interface</h3>
<blockquote>
<pre>
Usage bibrank:
bibrank -wjif -a --id=0-30000,30001-860000 --verbose=9
bibrank -wjif -d --modified='2002-10-27 13:57:26'
bibrank -wwrd --recalculate --collection=Articles
bibrank -wwrd -a -i 234-250,293,300-500 -u admin@localhost
Ranking options:
-w, --run=r1[,r2] runs each rank method in the order given
-c, --collection=c1[,c2] select according to collection
-i, --id=low[-high] select according to doc recID
-m, --modified=from[,to] select according to modification date
-l, --lastupdate select according to last update
-a, --add add or update words for selected records
-d, --del delete words for selected records
-S, --stat show statistics for a method
-R, --recalculate recalculate weigth data, used by word frequency method
should be used if ca 1% of the document has been changed
since last time -R was used
Repairing options:
-k, --check check consistency for all records in the table(s)
check if update of ranking data is necessary
-r, --repair try to repair all records in the table(s)
Scheduling options:
-u, --user=USER user name to store task, password needed
-s, --sleeptime=SLEEP time after which to repeat tasks (no)
e.g.: 1s, 30m, 24h, 7d
-t, --time=TIME moment for the task to be active (now)
e.g.: +15s, 5m, 3h , 2002-10-27 13:57:26
General options:
-h, --help print this help and exit
-V, --version print version and exit
-v, --verbose=LEVEL verbose level (from 0 to 9, default 1)
</pre>
</blockquote>
<a name="ubd"></a><h3>4.2 Using BibRank</h3>
<h4>Step 1 - Adding the rank option to the search interface</h4>
<p>To be able to add the needed ranking data to the database, you first have to add the rank method to the database, and
add the wished code you want to use together with it. The name of the configuration file in the next section, needs to
have the same name as the code stored in the database.</p>
<h4>Step 2 - Get necessary external data (ex. jif values)</h4>
<p>Find out what is necessary of data for each method. The bibrankgkb documentation below may be of assistance.</p>
<p><b>Example of necessary data</b> (<code>jif.kb</code> - journal impact factor knowledge base)</p>
<blockquote>
<pre>
Phys. Rev., D---3.838
Phys. Rev. Lett.---6.462
Phys. Lett., B---4.213
Nucl. Instrum. Methods Phys. Res., A---0.964
J. High Energy Phys.---8.664
</pre>
</blockquote>
<h4>Step 3 - Modify the configuration file</h4>
<p>The configuration files for the different rank methods has different option, so verify that you are using the correct
configuration file and rank method. A template for each rank method exists as examples, but may not work on all configurations of CDS Invenio.
For a description of each rank method and the configuration necessary, check section 6 below.</p>
<h4>Step 4 - Add the ranking method as a scheduled task</h4>
<p>When the configuration is okay, you can add the bibrank daemon to the task scheduler using the scheduling options. The daemon can then do a update of the rank method once each day or similar automatically.</p>
<b>Example</b>
<blockquote>
<pre>
$ bibrank -wjif -r
Task #53 was successfully scheduled for execution.
</pre>
</blockquote>
<p>It is adviced to run the BibRank daemon using no parameters, since the default settings then will be used.</p>
<b>Example</b>
<blockquote>
<pre>
$ bibrank
Task #2505 was successfully scheduled for execution.
</pre>
</blockquote>
<h4>Step 5 - Running bibrank manually</h4>
<p>If BibRank is scheduled without any parameters, and no records has been modified, you may get a output like shown below.</p>
<b>Example</b>
<blockquote>
<pre>
$ bibrank 2505
2004-09-07 17:51:46 --> Task #2505 started.
2004-09-07 17:51:46 -->
2004-09-07 17:51:46 --> Running rank method: Number of downloads.
2004-09-07 17:51:47 --> No new records added since last time method was run
2004-09-07 17:52:10 -->
2004-09-07 17:52:10 --> Running rank method: Journal Impact Factor.
2004-09-07 17:52:10 --> No new records added since last time method was run
2004-09-07 17:52:11 --> Reading knowledgebase file: /home/invenio/etc/bibrank/cern_jif.kb
2004-09-07 17:52:11 --> Number of lines read from knowledgebase file: 420
2004-09-07 17:52:11 --> Number of records available in rank method: 0
2004-09-07 17:52:12 -->
2004-09-07 17:52:12 --> Running rank method: Word frequency
2004-09-07 17:52:13 --> rnkWORD01F contains 256842 words from 677912 records
2004-09-07 17:52:14 --> rnkWORD01F is in consistent state
2004-09-07 17:52:14 --> Using the last update time for the rank method
2004-09-07 17:52:14 --> No new records added. rnkWORD01F is up to date
2004-09-07 17:52:14 --> rnkWORD01F contains 256842 words from 677912 records
2004-09-07 17:52:14 --> rnkWORD01F is in consistent state
2004-09-07 17:52:14 --> Task #2505 finished.
</pre>
</blockquote>
<h4>Step 6 - Fast update of modified records</h4>
<p>If you just want to update the latest additions or modified records, you may want to do a faster update by running the daemon without the recalculate option. (the recalculate option is off by default). This may cause lower accurancy when ranking.</p>
<a name="brm"></a><h2>5. BibRank Methods</h2>
<p>Each BibRank method has a configuration file which contains different parameters and sections necessary to do the ranking.</p>
<a name="str"></a><h3>5.1 Single tag rank method</h3>
<p>This method uses one MARC tag together with a file containing possible values for this MARC tag together with a ranking value. This data is used to create a structure containing the record id associated with the ranking value based on the content of the tag. The method can be used for various ways of ranking like ranking by Journal Impact Factor, or use it to let certain authors always appear top of a search.
The parameters needed to be configured for this method is the 'tag','kb_src' and 'check_mandatory_tags'.</p>
<br/><br/><b>Example</b>
<blockquote>
<pre>
[rank_method]
function = single_tag_rank_method
[single_tag_rank]
tag = 909C4p
kb_src = /home/invenio/etc/bibrank/jif.kb
check_mandatory_tags = 909C4c,909C4v,909C4y
</pre>
</blockquote>
<b>Explanation:</b>
<blockquote>
<pre>
[rank_method]
&nbsp; ##The function which is responsible for doing the work. Should not be changed
&nbsp; function = single_tag_rank_method
&nbsp;
&nbsp; ##This section must be available if the single_tag_rank_method is going to be used
&nbsp; [single_tag_kb]
&nbsp;
&nbsp; ##The tag which got the value to be searched for on the left side in the kb file (like the journal name)
&nbsp; tag = 909C4p
&nbsp;
&nbsp; ##The path to the kb file which got the content of the tag above on left side, and value on the left side
&nbsp; kb_src = /home/invenio/etc/bibrank/jif.kb
&nbsp;
&nbsp; ##Tags that must be included for a record to be added to the ranking data, to disable remove tags
&nbsp; check_mandatory_tags = 909C4c,909C4v,909C4y
&nbsp;
</pre>
</blockquote>
<p>The kb_src file must contain data on the form:</p>
<blockquote>
<pre>
Phys. Rev., D---3.838
Phys. Rev. Lett.---6.462
Phys. Lett., B---4.213
Nucl. Instrum. Methods Phys. Res., A---0.964
J. High Energy Phys.---8.664
</pre>
</blockquote>
<p>The left side must match the content of the tag mentioned in the tag variable.
<a name="wrd"></a><h3>5.2 Word Similarity/Similar Records</h3>
<p>The Word Similarity/Similar Records method uses the content of the tags selected to determine which records is most relevant to a query, or most similar to a selected record. This method got a lot of parameters to configure, and it may need some tweaking to get the best result. The BibRank code for this method has to be 'wrd' for it to work. For best result, it is adviced to install the stemming module mentioned in INSTALL, and use a stopword list containing stopwords in the languages the records exists in. The stemmer and stopword list is used to get better results and to limit the size of the index, thus making ranking faster and more accurate. For best result with the stemmer, it is important to mark each tag to be used with the most common language the value of the tag may be in. It is adviced to not change the 'function','table' and the parameters under [find_similar]. If the stemmer is not installed, to assure that no problems exists, the 'stem_if_avail' parameter should be set to 'no'. Each tag to be used by the method has to be given a point. The number of points describes how important one word is in this tag.</p>
<p>When running BibRank to update the index for this rank method, it is not necessary to recalculate each time, but when large number of records has been updated/added, it can be wise to recalculate using the recalculate parameter of BibRank.</p>
<b>Example</b>
<blockquote>
<pre>
[rank_method]
function = word_similarity
[word_similarity]
stemming = en
table = rnkWORD01F
stopword = True
relevance_number_output_prologue = (
relevance_number_output_epilogue = )
&nbsp;#MARC tag,tag points, tag language
tag1 = 6531_a, 2, en
tag2 = 695__a, 1, en
tag3 = 6532_a, 1, en
tag4 = 245__%, 10, en
tag5 = 246_%, 1, fr
tag6 = 250__a, 1, en
tag7 = 711__a, 1, en
tag8 = 210__a, 1, en
tag9 = 222__a, 1, en
tag10 = 520__%, 1, en
tag11 = 590__%, 1, fr
tag12 = 111__a, 1, en
tag13 = 100__%, 2, none
tag14 = 700__%, 1, none
tag15 = 721__a, 1, none
[find_similar]
max_word_occurence = 0.05
min_word_occurence = 0.00
min_word_length = 3
min_nr_words_docs = 3
max_nr_words_upper = 20
max_nr_words_lower = 10
default_min_relevance = 75
</pre>
</blockquote>
<b>Explanation:</b>
<blockquote>
<pre>
[rank_method]
&nbsp;#internal name for the bibrank program, do not modify
function = word_similarity
[word_similarity]
&nbsp;#if stemmer is available, default stemminglanguage should be given here. Adviced to turn off if not installed
stemming = en
&nbsp;#the internal table to load the index tables from.
table = rnkWORD01F
&nbsp;#remove stopwords?
stopword = True
&nbsp;#text to show before the rank value when the search result is presented. &lt;-- to hide result
relevance_number_output_prologue = (
&nbsp;#text to show after the rank value when the search result is presented. --> to hide result
relevance_number_output_epilogue = )
&nbsp;#MARC tag,tag points, tag language
&nbsp;#a list of the tags to be used, together with a number describing the importance of the tag, and the
&nbsp;#most common language for the content. Not all languages are supported. Among the supported ones are:
&nbsp;#fr/french, en/english, no/norwegian, se/swedish, de/german, it/italian, pt/portugese
&nbsp;#keyword
tag1 = 6531_a, 1, en #keyword
tag2 = 695__a, 1, en #keyword
tag3 = 6532_a, 1, en #keyword
tag4 = 245__%, 10, en #title, the words in the title is usually describing a record very good.
tag5 = 246_% , 1, fr #french title
tag6 = 250__a, 1, en #title
tag7 = 711__a, 1, en #title
tag8 = 210__a, 1, en #abbreviated
tag9 = 222__a, 1, en #key title
[find_similar]
&nbsp;#term should exist in maximum X/100% of documents
max_word_occurence = 0.05
&nbsp;#term should exist in minimum X/100% of documents
min_word_occurence = 0.00
&nbsp;#term should be atleast 3 characters long
min_word_length = 3
&nbsp;#term should be in atleast 3 documents or more
min_nr_words_docs = 3
&nbsp;#do not use more than 20 terms for "find similar"
max_nr_words_upper = 20
&nbsp;#if a document contains less than 10 terms, use much used terms too, if not ignore them
max_nr_words_lower = 10
&nbsp;#default minimum relevance value to use for find similar
default_min_relevance = 75
</pre>
</blockquote>
<p>Tip: When executing a search using a ranking method, you can add "verbose=1" to the list of parameteres
in the URL to see which terms have been used in the ranking.</p>
<a name="cmb"></a><h3>5.3 Combine method</h3>
<p>The 'Combine method' is running each method mentioned in the config file and adding the score together
based on the importance of the method given by the percentage.</p>
<b>Example</b>
<blockquote>
<pre>
[rank_method]
function = combine_method
[combine_method]
method1 = cern_jif,33
method2 = cern_acc,33
method3 = wrd,33
relevance_number_output_prologue = (
relevance_number_output_epilogue = )
</pre>
</blockquote>
<b>Explanation:</b>
<blockquote>
<pre>
[rank_method]
&nbsp;#tells which method to use, do not change
function = combine_method
[combine_method]
&nbsp;#each line tells which method to use, the code is the same as in the BibRank interface, the number describes how
&nbsp;#much of the total score the method should count.
method1 = jif,50
method2 = wrd,50
&nbsp;#text to be shown before the rank value on the search result screen.
relevance_number_output_prologue = (
&nbsp;#text to be shown after the rank value on the search result screen.
relevance_number_output_epilogue = )
</pre>
</blockquote>
<a name="bt"></a><h2>6. bibrankgkb Tool</h2>
<p>For some ranking methods, like the single_tag_rank method, a knowledge base file (kb) with the needed data in the correct format is necessary. This file can be created using the bibrankgkb tool which can read the data either from
the CDS Invenio database, from several web pages using regular expressions, or from another file. In case one source
has another naming convention, bibrank can convert between them using a convert file.</p>
<a name="cli2"></a><h3>6.1 Command Line Interface</h3>
<blockquote>
<pre>
Usage: bibrankgkb %s [options]
Examples:
bibrankgkb --input=bibrankgkb.cfg --output=test.kb
bibrankgkb -otest.cfg -v9
bibrankgkb
Generate options:
-i, --input=file input file, default from /etc/bibrank/bibrankgkb.cfg
-o, --output=file output file, will be placed in current folder
General options:
-h, --help print this help and exit
-V, --version print version and exit
-v, --verbose=LEVEL verbose level (from 0 to 9, default 1)
</pre>
</blockquote>
<a name="ubt"></a><h3>6.2 Using bibrankgkb</h3>
<h4>Step 1 - Find sources</h4>
<p>Since some of the data used for ranking purposes is not freely available, it cannot be bundled with CDS Invenio. To get hold of the necessary data,
you may find it useful to ask your library if they have a copy of the data that can be used (like the Journal Impact Factors from the Science Citation Index), or use google to search the web for any public source.</p>
<h4>Step 2 - Create configuration file</h4>
<p>The default configuration file is shown below.</p>
<blockquote>
<pre>
&nbsp;##The main section
[bibrankgkb]
&nbsp;##The url to a web page with the data to be read, does not need to have the same name as this one, but if there
are several links, the url parameter should end with _0->
url_0 = http://www.taelinke.land.ru/impact_A.html
url_1 = http://www.taelinke.land.ru/impact_B.html
url_2 = http://www.taelinke.land.ru/impact_C.html
url_3 = http://www.taelinke.land.ru/impact_DE.html
url_4 = http://www.taelinke.land.ru/impact_FH.html
url_5 = http://www.taelinke.land.ru/impact_I.html
url_6 = http://www.taelinke.land.ru/impact_J.html
url_7 = http://www.taelinke.land.ru/impact_KN.html
url_8 = http://www.taelinke.land.ru/impact_QQ.html
url_9 = http://www.taelinke.land.ru/impact_RZ.html
&nbsp;##The regular expression for the url mentioned should be given here
url_regexp =
&nbsp;##The various sources that can be read in, can either be a file, web page or from the database
kb_1 = /home/invenio/modules/bibrank/etc/cern_jif.kb
kb_2 = /home/invenio/modules/bibrank/etc/demo_jif.kb
kb_2_filter = /home/invenio/modules/bibrank/etc/convert.kb
kb_3 = SELECT id_bibrec,value FROM bib93x,bibrec_bib93x WHERE tag='938__f' AND id_bibxxx=id
kb_4 = SELECT id_bibrec,value FROM bib21x,bibrec_bib21x WHERE tag='210__a' AND id_bibxxx=id
&nbsp;##This points to the url above (the common part of the url is 'url_' followed by a number
kb_5 = url_%s
&nbsp;##This is the part that will be read by the bibrankgkb tool to determine what to read.
&nbsp;##The first two part (separated by ,,) gives where to look for the conversion file (which convert
&nbsp;##the names between to formats), and the second part is the data source. A conversion file is not
&nbsp;##needed, as shown in create_0. If the source is from a file, url or the database, it must be
&nbsp;##given with file,www or db. If several create lines exists, each will be read in turn, and added
&nbsp;##to a common kb file.
&nbsp;##So this means that:
&nbsp;##create_0: Load from file in variable kb_1 without converting
&nbsp;##create_1: Load from file in variable kb_2 using convertion from file kb_2_filter
&nbsp;##create_3: Load from www using url in variable kb_5 and regular expression in url_regexp
&nbsp;##create_4: Load from database using sql statements in kb_4 and kb_5
create_0 = ,, ,,file,,%(kb_1)s
create_1 = file,,%(kb_2_filter)s,,file,,%(kb_2)s
&nbsp;#create_2 = ,, ,,www,,%(kb_5)s,,%(url_regexp)s
&nbsp;#create_3 = ,, ,,db,,%(kb_4)s,,%(kb_4)s
</pre>
</blockquote>
<p>When you have found a source for the data, created the configuration file, it may be necessary to
create an convertion file, but this depends on the conversions used in the available data versus
the convertion used in your CDS Invenio installation.</p>
<p>The available data may look like this:</p>
<blockquote>
<pre>
COLLOID SURFACE A---1.98
</pre>
</blockquote>
<p>But in CDS Invenio you are using:</p>
<blockquote>
<pre>
Colloids Surf., A---1.98
</pre>
</blockquote>
<p>By using a convertion file like:</p>
<blockquote>
<pre>
COLLOID SURFACE A---Colloids Surf., A
</pre>
</blockquote>
<p>You can convert the source to the correct naming convention.</p>
<blockquote>
<pre>
Colloids Surf., A---1.98
</pre>
</blockquote>
<h4>Step 3 - Run tool</h4>
<p>When ready to run the tool, you may either use the default file (/etc/bibrank/bibrankgkb.cfg), or use another one by giving it using the input variable '--input'.
If you want to test the configuration, you can use '--verbose=9' to output on screen, or if you want to save it to a file, use
'--output=filename', but remember that the file will be saved in the program directory.</p>
<p>The output may look like this:</p>
<blockquote>
<pre>
$ ./bibrankgkb -v9
2004-03-11 17:30:17 --> Running: Generate Knowledge base.
2004-03-11 17:30:17 --> Reading data from file: /home/invenio/etc/bibrank/jif.kb
2004-03-11 17:30:17 --> Reading data from file: /home/invenio/etc/bibrank/conv.kb
2004-03-11 17:30:17 --> Using last resource for converting values.
2004-03-11 17:30:17 --> Reading data from file: /home/invenio/etc/bibrank/jif2.kb
2004-03-11 17:30:17 --> Converting between naming conventions given.
2004-03-11 17:30:17 --> Colloids Surf., A---1.98
2004-03-11 17:30:17 --> Phys. Rev. Lett.---6.462
2004-03-11 17:30:17 --> J. High Energy Phys.---8.664
2004-03-11 17:30:17 --> Nucl. Instrum. Methods Phys. Res., A---0.964
2004-03-11 17:30:17 --> Phys. Lett., B---4.213
2004-03-11 17:30:17 --> Phys. Rev., D---3.838
2004-03-11 17:30:17 --> Total nr of lines: 6
2004-03-11 17:30:17 --> Time used: 0 second(s).
</blockquote>
</pre>
<a name="ainf"></a><h2>7. Additional Information</h2>
-<a href="<WEBURL>/help/hacking/bibrank-internals">BibRank Internals</a>
+<a href="<CFG_SITE_URL>/help/hacking/bibrank-internals">BibRank Internals</a>
diff --git a/modules/bibrank/doc/hacking/bibrank-api.webdoc b/modules/bibrank/doc/hacking/bibrank-api.webdoc
index 31a9a7946..0badb85e4 100644
--- a/modules/bibrank/doc/hacking/bibrank-api.webdoc
+++ b/modules/bibrank/doc/hacking/bibrank-api.webdoc
@@ -1,138 +1,138 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibRank Record Sorter API -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibrank-internals">BibRank Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibrank-internals">BibRank Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<protect>
<pre>
CDS Invenio Bibrank Record Sorter can be called from within your Python
programs via a high-level API and a mid-mid level API.
1. High-level API
Description:
The high-level access to the BibRank Record Sorter is provided
by exactly the same function as called from the web interface
when users submit their queries, if a rank method has been
selected. This should guarantee exactly the same behaviour if
the same parameters are given.
There are three thing to note: (i) When a search is done, the
search engine is sending a HitSet containing all the records that
matches the query. Since only records in the HitSet are ranked, a
HitSet must be created containing wished records to rank and be sent
as a parameter to the function. (ii) Some rank methods may choose to
ignore the HitSet, like the "Similar Records" function. (iii) In case
of an error ranking the records, the returned data is different.
Signature:
def rank_records(rank_method_code, rank_limit_relevance,
hitset_global, pattern, verbose=0):
"""
rank_method_code - 'jif', 'wrd' or other methods
rank_limit_relevance - a number defining the threshold of which
to rank records, may be ignored by rank method.
hitset_global, search engine hits, if all records should be
ranked, fill the HitSet with ones.
pattern, search engine query or record ID, must be a
list. ['CERN', 'fermilab'] or ['recid:12345']
verbose, verbose level - 0-9 defines how much debug information
should be shown
output if successfull:
list of records - [123, 321, 12451, 123, 12, 4]
list of rank values - ascending, same length as the list of
records [0, 10, 20, 30, 40, 100]
prefix - text to show before the rank value, '<--' hides rank
value, defined in config file.
postfix - text to show after the rank value, '-!>' hides rank
value, defined in config file.
verbose_output - the debug text depending on the verbose level.
output if error:
list of records - is None
list of rank values - is None
prefix - Contains headline of error
postfix - Error message or error box if exception.
verbose_output - the debug text depending on the verbose level.
"""
Examples:
>>> # import the function:
>>> from invenio.bibrank_record_sorter import rank_records
>>> # rank all records with the words 'higgs boson' according to the method "wrd"
>>> rank_records('wrd', 0, a_hitset, ['higgs', 'boson'], 0)
>>> # find similar records to the record 12345, hitset is here ignored because of 'similar records'
>>> rank_records('wrd', 0, a_hitset, ['recid:12345'], 0)
>>> # rank all records based on jif value
>>> rank_records('jif', 0, a_hitset, [], 0)
2. Mid-level API
Description:
Using the mid-level API, you can call directly the various methods
for ranking. The functions will not return data in a way the search
engine understands. They will neither find out if it is the correct
function that is called, but return an error if wrong code/function
is used.
Signatures:
def combine_method(rank_method_code, pattern, hitset, rank_limit_relevance,verbose):
-This method calls each method mentioned in the config file and add the results together
def find_similar(rank_method_code, recID, hitset, rank_limit_relevance,
-This method finds similar records based on the one given in the recID field. The recID field
must be a integer value. hitset is ignored. rank_method_code has to be 'wrd'.
def word_frequency(rank_method_code, lwords, hitset, rank_limit_relevance,verbose):
-This method ranks records based on the list of words in lwords field. rank_method_code has to be
'wrd'. Only records in hitset is ranked.
def rank_by_method(rank_method_code, lwords, hitset, rank_limit_relevance,verbose):
-All other rank methods uses this function together with data from the rnkMETHODDATA table
(a dictionary of {recid: (text, value)} to rank the data. Only records in the hitset is ranked.
These mid-level API functions demands that the function create_rnkmethod_cache() has been called,
since it loads the config options needed.
The rank methods returns all the same data:
([[recid, value],[recid, value]], prefix, postfix, verbose_output)
Examples:
>>> # import the function:
>>> from invenio.bibrank_record_sorter import find_similar
>>> # find records similar to 12345, hitset must be full
>>> find_similar('wrd', 12345, hitset, 0, 0)
>>> # rank records according to a method called jif, using the single_tag...based method.
>>> # the list of words is here ignored, only the records in the hitset are used.
>>> rank_by_method('jif',['higgs'], hitset, 0, 0)
>>> # rank records containing ['higgs', 'boson'] using word similarity ('wrd')
>>> word_similarity('wrd',['higgs', 'boson'], hitset, 0, 0)
>>> # rank records using various methods, which methods to use is read from the config file.
>>> combine_method('cmb', ['higgs','boson'], hitset, 0, 0)
</pre>
</protect>
diff --git a/modules/bibrank/doc/hacking/bibrank-bibrankgkb.webdoc b/modules/bibrank/doc/hacking/bibrank-bibrankgkb.webdoc
index e2a2cb06a..d667fea34 100644
--- a/modules/bibrank/doc/hacking/bibrank-bibrankgkb.webdoc
+++ b/modules/bibrank/doc/hacking/bibrank-bibrankgkb.webdoc
@@ -1,91 +1,91 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Create knowledgebase files (bibrankgkb) -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibrank-internals">BibRank Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibrank-internals">BibRank Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
<blockquotes>
1. Read default configuration file or the one specified by the user
2. Read each create_ line from the cfg file, for each line, read the
source(s) from either database, file or www by calling get_from_source().
Convert between naming conventions if source for convertion data is given.
3. Merge into one file, repeat 2. until last source is read.
4. Save file if requested with --output
Configuration:
How to spesify a source:
-create_x = filter, source
Where x is a number from 0 and up. The source and the filter is read,
and each line in the source is checked against the filter to be converted
into the correct naming standard. If no filter is given, the source is
directly translated into a .kb file.
Read filter from:
File:
[bibrankgkb]
<protect>#give path to file containing lines like: COLLOID SURFACE A---Colloids Surf., A</protect>
kb_1_filter = <ETCDIR>/bibrank/bibrankgkb_jif_conv.kb
<protect>#replace filter with the line below (switch kb_1_filter with the variable names you used)</protect>
create_0 = file,,%(kb_1_filter)s
Read source from:
Database:
[bibrankgkb]
<protect>#Specify sql statements
kb_2 = SELECT id_bibrec,value FROM bib93x,bibrec_bib93x WHERE tag='938__f' AND id_bibxxx=id
kb_3 = SELECT id_bibrec,value FROM bib21x,bibrec_bib21x WHERE tag='210__a' AND id_bibxxx=id
<protect>#replace source with the line below (switch kb_2 and kb_3 with the variable names you used)</protect>
db,,%(kb_2)s,,%(kb_3)s
File:
[bibrankgkb]
<protect>#give path to file containing lines like: COLLOID SURFACE A---1.98</protect>
kb_1 = <ETCDIR>/bibrank/bibrankgkb_jif_example.kb
<protect>#replace source with the line below (switch kb_1 with the variable names you used)</protect>
create_0 = file,,%(kb_1)s
Internet:
[bibrankgkb]
<protect> #specify the urls to the file containing JIF data</protect>
url_0 = http://www.sciencegateway.org/impact/if03a.htm
url_1 = http://www.sciencegateway.org/impact/if03bc.htm
url_2 = http://www.sciencegateway.org/impact/if03df.htm
url_3 = http://www.sciencegateway.org/impact/if03gi.htm
url_4 = http://www.sciencegateway.org/impact/if03j.htm
url_5 = http://www.sciencegateway.org/impact/if03ko.htm
url_6 = http://www.sciencegateway.org/impact/if03pr.htm
url_7 = http://www.sciencegateway.org/impact/if03sz.htm
<protect>#give the regular expression necessary to extract the key and value from the file</protect>
url_regexp = (TR bgColor=\#ffffff>\s*?\n\s*?<TD>(?P<key>.*?)</TD>\s*?\n\s*?<TD>.*?</TD>\s*?\n\s*?<TD .*?>.*?</TD>\s*?\n\s*?<TD .*?><A.*?\n.*?</TD>\s*?\n\s*?<TD.*?>(?P<value>[\w|,]+)</TD></TR>)
<protect>#replace source with the line below (switch kb_4 and url_regexp with the variable names you used)</protect>
create_0 = www,,%(kb_4)s,,%(url_regexp)s
</pre>
</blockquotes>
diff --git a/modules/bibrank/doc/hacking/bibrank-internals.webdoc b/modules/bibrank/doc/hacking/bibrank-internals.webdoc
index d79b08fa3..d09e83cc4 100644
--- a/modules/bibrank/doc/hacking/bibrank-internals.webdoc
+++ b/modules/bibrank/doc/hacking/bibrank-internals.webdoc
@@ -1,44 +1,44 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibRank Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>This page summarizes all the information suitable to dig inside
BibRank and it's different ranking methods.</p>
<blockquote>
<dl>
<dt><a href="bibrank-single-tag-rank">Single tag rank method</a>
<dd>Ex. of use: Journal Impact Factor. Explains what this ranking method<br />
does to create the ranking data for use when ranking based on<br />
Journal Impact Factor or similar methods.</dd>
</dt>
<dt><a href="bibrank-word-similarity">Word Similarity/Similar Records methods</a>
<dd>Ex. of use: Ranking based on the importance of words in the whole<br />
collection and in a single record. Explains how the ranking data is calculated.</dd>
</dt>
<dt><a href="bibrank-api">Bibrank Record Sorter API</a>
<dd>How to call the Bibrank Record Sorter library from your python programs if necessary.</dd></dt>
<dt><a href="bibrank-bibrankgkb">Create knowledgebase files (bibrankgkb)</a> <dd>Explains how to create knowledgebase files using the bibrankgkb tool</dd></dt>
</dl>
</blockquote>
diff --git a/modules/bibrank/doc/hacking/bibrank-single-tag-rank.webdoc b/modules/bibrank/doc/hacking/bibrank-single-tag-rank.webdoc
index e608a786e..376f51b47 100644
--- a/modules/bibrank/doc/hacking/bibrank-single-tag-rank.webdoc
+++ b/modules/bibrank/doc/hacking/bibrank-single-tag-rank.webdoc
@@ -1,42 +1,42 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Single Tag Rank Method -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibrank-internals">BibRank Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibrank-internals">BibRank Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
<blockquotes>
Process:
1. Any records that is going to be added is found.
2. If existing, and if a update is going to be done, existing data is
loaded from database.
3. The configuration file is loaded.
4. Loading knowledgebase file.
5. Getting records from the database containing the MARC21 tag from the
configfile, and which are among the ones to be updated.
6. The function will then go through all records, and if we have the
value for the record as a key in the kb file, the value from the kb
file will be associated to this record id. If the record id already has
a value, this will be replaced if the newest is higher than the old one,
and if there is no match in kb_data it will get the value -1.
7. The database is updated.
</pre>
</blockquotes>
diff --git a/modules/bibrank/doc/hacking/bibrank-word-similarity.webdoc b/modules/bibrank/doc/hacking/bibrank-word-similarity.webdoc
index ca6747bfb..c9af9c9a5 100644
--- a/modules/bibrank/doc/hacking/bibrank-word-similarity.webdoc
+++ b/modules/bibrank/doc/hacking/bibrank-word-similarity.webdoc
@@ -1,114 +1,114 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Word Similarity/Similar Records Methods -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibrank-internals">BibRank Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="bibrank-internals">BibRank Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
<blockquote>
<b>Important regarding 'Word Similarity/Similar Records' method:</b>
Because of tight connection with the search_engine and database, there
can only be one method using the "word_similarity" template, and the
code for this method as designated in the BibRank admin interface has
to be 'wrd'.
<b>Preparing word similarity rank indexes:</b>
The 'Word similarity' method works by generating an index over terms in
the tags specified in the configuration file for the given records. The
data is stored in two tables, on forward and one reverse. The forward
index has a list of terms, where each term as a dictionary containing
records using this term. The reverse index contains the opposite, a list
of records, where each record contains a dictionary of the terms it
contains (for the selected tags). This means that the forward/reverse
index is to some degree similar to the tables created by BibIndex.
The main difference is that the rank method stores more information in
the table, based on how important the terms are, based on how many
times they have been used, and how important one term is in one
record. To minimize the number of terms to process, a few techniques
are used. Among these are stemming and stopword removal. Stemming
removes the end of a term, so that only the stem is left, this means
that 'looking' becomes 'look' and minimizes the size of the
database. Stopword removal removes very common words without meaning,
like 'the', 'one', 'me' in english. Terms that consists of numbers or
are below a certain limit is also ignored.
Since automatic language recognisition is not supported, each tag must
therefore be given a language for stemming to work. This means that in
the perfect world one tag should contain text in one language or
mostly in one language. If stemming is not wanted, the module can be
turned off, though lower rank quality may be expected.
Stopword removal works by checking if a term exists in a file, which
can contain any language necessary. Together with the default CDS Invenio
installation, the file contains stopwords in french and english.
For a large CDS Invenio installation (700 000 records), indexing takes
around 2 hours, including calculating the data needed for the
weighting scheme.
How the term importance is computed:
The method used is a variation of the well-known weighting scheme, the
vector model [1], in document retrieval. The method is described in
[2] and called 'Log-entropy' weigthing scheme. For more detailed
explanation of the scheme, the paper should be consulted. Since the
calculations necessary to calculate the number needed by the method is
too demanding, most of the numbers are calculated after the index over
term is created and stored in the database for later use.
<b>Step by step at index time:</b>(Using rebalance)
1. Load configuration for method
2. Begin a loop which loops through all records that should be added
3. Load content of tags in current record range
4. For each tag in all records, check each term if it should be used,
check against stopword list, use stemming, if accepted, add to a structure
the points from configfile for current tag.
5. Add to database the new values
6. Go back to 3 until no more records to be added.
7. Go through list of added terms, get list of all records containing these terms
8. Find all terms in records from last point.
9. For each record, calculate Fi, for each term calculate Gi
10.For each record, calculate normalization value Nj, add Gi value for each
term to the structure in reverse index.
11.Adding the Gi value to each term in forward index, and adding the
normalization value Nj to each term in each document
<b>Word Similarity at search/rank time:</b>
1. For each term, check if it can be used (like check agains stopword list),
use stemming on the term if possible.
2. For each term, get dictionary from forward index, calculate rank values
for each term.
3. Add any records not ranked to end of list.
4. Sort records.
5. Return sorted records
<b>Similar records at search/rank time:</b>
1. Get terms from reverse index which exists in record.
2. Sort terms and use only the most important ones for finding similar records.
3. For selected terms, rank the associated records
4. Sort records
5. Return sorted records
<b>References:</b>
[1] Modern Information Retrieval. Baeza-Yates/Ribeiro-Neto
[2] New term weighting formulas for the vector space method in information retrieval.
ORNL/TM-13756.E.Chisholm/T.G.Kolda
</pre>
diff --git a/modules/bibrank/lib/bibrank_citation_grapher.py b/modules/bibrank/lib/bibrank_citation_grapher.py
index 4a7afa06c..9b07bd377 100644
--- a/modules/bibrank/lib/bibrank_citation_grapher.py
+++ b/modules/bibrank/lib/bibrank_citation_grapher.py
@@ -1,137 +1,137 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import os
import time
import tempfile
from marshal import loads
from zlib import decompress
-from invenio.config import weburl, CFG_SITE_LANG
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG
from invenio.dbquery import run_sql
from invenio.messages import gettext_set_language
from invenio.bibrank_grapher import create_temporary_image, write_coordinates_in_tmp_file, remove_old_img
from invenio.bibrank_citation_searcher import calculate_cited_by_list
cfg_bibrank_print_citation_history = 1
color_line_list = ['9', '19', '10', '15', '21', '18']
def get_field_values(recID, tag):
"""Return list of field values for field tag inside record RECID."""
out = []
if tag == "001___":
out.append(str(recID))
else:
digit = tag[0:2]
bx = "bib%sx" % digit
bibx = "bibrec_bib%sx" % digit
query = "SELECT bx.value FROM %s AS bx, %s AS bibx WHERE bibx.id_bibrec='%s' AND bx.id=bibx.id_bibxxx AND bx.tag LIKE '%s'" "ORDER BY bibx.field_number, bx.tag ASC" % (bx, bibx, recID, tag)
res = run_sql(query)
for row in res:
out.append(row[0])
return out
def calculate_citation_history_coordinates(recid):
"""Return a list of citation graph coordinates for RECID, sorted by year."""
result = []
dbg = ""
initial_result= get_initial_result(calculate_citation_graphe_x_coordinates(recid))
citlist = calculate_cited_by_list(recid)
for rec_id, cit_weight in citlist:
cit_year = get_field_values(rec_id,'773__y')
if not cit_year: cit_year = get_field_values(rec_id, '260__c')
#some records simlpy do not have these fields
if cit_year:
if initial_result.has_key(int(cit_year[0][0:4])):
initial_result[int(cit_year[0][0:4])] += 1
for key, value in initial_result.items():
result.append((key, value))
result.sort()
return result
def calculate_citation_graphe_x_coordinates(recid):
"""Return a range of year from the publication year of record RECID
until the current year."""
rec_years = []
recordyear = get_field_values(recid, '773__y')
if not recordyear: recordyear = get_field_values(recid, '260__c')
currentyear = time.localtime()[0]
if recordyear == []:
recordyear = currentyear
else:
recordyear = find_year(recordyear[0])
interval = range(int(recordyear), currentyear+1)
return interval
def find_year(recordyear):
"""find the year in the string as a suite of 4 int"""
s = ""
for i in range(len(recordyear)-3):
s = recordyear[i:i+4]
if s.isalnum():
print s
break
return s
def get_initial_result(rec_years):
"""return an initial dictionary with year of record publication as key
and zero as value
"""
result = {}
for year in rec_years :
result[year] = 0
return result
def html_command(file):
- t = """<img src='%s/img/%s' align="center" alt="">""" % (weburl, file)
+ t = """<img src='%s/img/%s' align="center" alt="">""" % (CFG_SITE_URL, file)
#t += "</table></td></tr></table>"
return t
def create_citation_history_graph_and_box(recid, ln=CFG_SITE_LANG):
"""Create graph with citation history for record RECID (into a
temporary file) and return HTML box refering to that image.
Called by Detailed record pages.
"""
_ = gettext_set_language(ln)
html_result = ""
if cfg_bibrank_print_citation_history:
coordinates = calculate_citation_history_coordinates(recid)
if coordinates:
html_head = """<br /><table><tr><td class="blocknote">%s</td></tr></table>""" % _("Citation history:")
graphe_file_name = 'citation_%s_stats.png' % str(recid)
remove_old_img(graphe_file_name)
years = calculate_citation_graphe_x_coordinates(recid)
years.sort()
datas_info = write_coordinates_in_tmp_file([coordinates])
graphe = create_temporary_image(recid, 'citation', datas_info[0], 'Year', 'Times cited', [0,0], datas_info[1], [], ' ', years)
graphe_image = graphe[0]
graphe_source_file = graphe[1]
if graphe_image and graphe_source_file:
if os.path.exists(graphe_source_file):
os.unlink(datas_info[0])
html_graphe_code = """<p>%s"""% html_command(graphe_image)
html_result = html_head + html_graphe_code
return html_result
diff --git a/modules/bibrank/lib/bibrank_downloads_grapher.py b/modules/bibrank/lib/bibrank_downloads_grapher.py
index 4e0fbc033..6f9e3cfa5 100644
--- a/modules/bibrank/lib/bibrank_downloads_grapher.py
+++ b/modules/bibrank/lib/bibrank_downloads_grapher.py
@@ -1,295 +1,295 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import string
import os
import sys
import time
import tempfile
import calendar
-from invenio.config import weburl, CFG_SITE_LANG, CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS, CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS_CLIENT_IP_DISTRIBUTION
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG, CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS, CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS_CLIENT_IP_DISTRIBUTION
from invenio.messages import gettext_set_language
from invenio.dbquery import run_sql
from invenio.bibrank_downloads_indexer import database_tuples_to_single_list
from invenio.bibrank_grapher import *
color_line_list = ['9', '19', '10', '15', '21', '18']
cfg_id_bibdoc_id_bibrec = 5
def create_download_history_graph_and_box(id_bibrec, ln=CFG_SITE_LANG):
"""Create graph with citation history for record ID_BIBREC (into a
temporary file) and return HTML box refering to that image.
Called by Detailed record pages.
Notes:
if id_bibdoc=0 : its an oustide-stored document and it has no id_bibdoc --> only one line
if len(id_bibdocs) <= cfg_id_bibdoc_id_bibrec draw one line per id_bibdoc
if len(id_bibdocs) > cfg_id_bibdoc_id_bibrec draw only one line which hold simultaneously the downloads for all id_bibdoc
Each time this function is called, all the images older than 10 minutes are deleted.
"""
_ = gettext_set_language(ln)
out = ""
# Prepare downloads history graph:
if CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS:
html_content = ""
# remove images older than 10 minutes
remove_old_img("download")
# download count graph
id_bibdocs = run_sql("select distinct id_bibdoc from rnkDOWNLOADS where id_bibrec=%s;" % id_bibrec)
history_analysis_results = ()
if id_bibdocs == ():
pass
elif len(id_bibdocs) <= cfg_id_bibdoc_id_bibrec and (0, ) not in id_bibdocs:
history_analysis_results = draw_downloads_statistics(id_bibrec, list(id_bibdocs))
else:
history_analysis_results = draw_downloads_statistics(id_bibrec, [])
if history_analysis_results and history_analysis_results[0]:
- graph_file_history = weburl + "/img/" + history_analysis_results[0]
+ graph_file_history = CFG_SITE_URL + "/img/" + history_analysis_results[0]
file_to_close_history = history_analysis_results[1]
html_content += """<tr><td valign=center align=center><img src='%s'/></td>""" % graph_file_history
if file_to_close_history :
if os.path.exists(file_to_close_history):
os.unlink(file_to_close_history)
if html_content != "":
out += """<br/><br/><table><tr><td class="blocknote">
%s</td></tr><tr><td>
<table border="0" cellspacing="1" cellpadding="1">""" % _("Download history:")
out += html_content
out += "</table></td></tr></table>"
if CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS_CLIENT_IP_DISTRIBUTION:
# do we show also user IP repartition?
html_content = ""
remove_old_img("download")
#Users analysis graph
ips = database_tuples_to_single_list(run_sql("select client_host from rnkDOWNLOADS where id_bibrec=%s;" % id_bibrec))
if ips:
users_analysis_results = create_users_analysis_graph(id_bibrec, ips)
if users_analysis_results[0]:
- graph_file_users = weburl + "/img/" + users_analysis_results[0]
+ graph_file_users = CFG_SITE_URL + "/img/" + users_analysis_results[0]
file_to_close_users = users_analysis_results[1]
html_content += """<tr><td valign=center align=center><img src='%s'/></td>""" % graph_file_users
if file_to_close_users:
if os.path.exists(file_to_close_users):
os.unlink(file_to_close_users)
if html_content != "":
out += """<br/><br/><table><tr><td class="blocknote">
%s</td></tr><tr><td>
<table border="0" cellspacing="1" cellpadding="1">""" % _("Download user distribution:")
out += html_content
out += "</table></td></tr></table>"
# return html code used by get_file or search_engine
return out
def draw_downloads_statistics(id_bibrec, id_bibdoc_list):
"""Create a graph of download history using a temporary file to store datas
and a new png file for each id_bibrec to store the image of the graph which will
be referenced by html code."""
intervals = []
#used to name the different curves when len(id_bibdoc_list)>1
docfile_name_list = []
#used to name the uniquecurve when len(id_bibdoc_list)=0 or > cfg_id_bibdoc_id_bibrec
record_name = ""
record_name_query = run_sql("select value from bibrec_bib24x,bib24x where id_bibrec=%s and id_bibxxx=id;" % id_bibrec)
if record_name_query:
record_name = record_name_query[0][0]
#list of lists of tuples: [[("09/2004",4),..],[(..,..)]..]
#Each list element of the list is represented by a curve
#each elem of each list is a point on the graph
coordinates_list = []
#If the document is not stored in internally it has id_bibrec 0 and no creation date
#In this case the beginning date is the first time the document has been downloaded
local_time = time.localtime()
local_month = local_time.tm_mon
local_year = local_time.tm_year
creation_date_res = run_sql("""SELECT DATE_FORMAT(creation_date,"%%Y-%%m") FROM bibrec WHERE id=%s;""" % id_bibrec)
if creation_date_res == ():
creation_date_res = run_sql("""SELECT DATE_FORMAT(MIN(download_time),"%%Y-%%m") FROM rnkDOWNLOADS where id_bibrec=%s;""" % id_bibrec)
if creation_date_res == (('0000-00',),):
creation_date_year = local_year - 1
creation_date_month = local_month
else :
creation_date_year, creation_date_month = string.split(creation_date_res[0][0], "-")
creation_date_year = int(creation_date_year)
creation_date_month = int(creation_date_month)
#create intervals and corresponding values
res = create_tic_intervals(local_year, local_month, creation_date_year, creation_date_month)
intervals = res[1]
tic_list = res[0]
if id_bibdoc_list == []:
coordinates_list.append(create_list_tuple_data(intervals, id_bibrec))
docfile_name_list = record_name
else :
for i in range(len(id_bibdoc_list)):
datas = create_list_tuple_data(intervals, id_bibrec, id_bibdoc_query_addition="and id_bibdoc=%s" % id_bibdoc_list[i][0])
coordinates_list.append(datas)
docname = run_sql("select docname from bibdoc where id=%s;" % id_bibdoc_list[i][0])
docfile_name_list.append(docname[0][0])
#In case of multiple id_bibdocs datas_max will be used to draw a line which is the total of the others lines
if not (len(intervals)==1 or len(id_bibdoc_list)==1):
datas_max = create_list_tuple_total(intervals, coordinates_list)
coordinates_list.append(datas_max)
#write coordinates_list in a temporary file
result2 = write_coordinates_in_tmp_file(coordinates_list)
fname = result2[0]
y_max = result2[1]
#Use create the graph from the temporary file
return create_temporary_image(id_bibrec, 'download_history', fname, ' ', 'Times downloaded', [0, 0], y_max, id_bibdoc_list, docfile_name_list, tic_list)
def create_list_tuple_data(intervals, id_bibrec, id_bibdoc_query_addition=""):
"""-Return a list of tuple of the form [('10/2004',3),(..)] used to plot graph
Where 3 is the number of downloads between 01/10/2004 and 31/10/2004"""
list_tuple = []
for elem in intervals:
main_date_end = string.split(elem[1], '/')
end_of_month_end = calendar.monthrange(int(main_date_end[1]), int(main_date_end[0]))[1]
s0 = string.split(elem[0], "/")
s1 = string.split(elem[1], "/")
elem0 = s0[1] + "-" + s0[0]
elem1 = s1[1] + "-" + s1[0]
date1 = "%s%s" % (elem0, "-01 00:00:00")
date2 = "%s%s" % (elem1, "-%s 00:00:00" % str(end_of_month_end))
sql_query = "select count(*) from rnkDOWNLOADS where id_bibrec=%s %s and download_time>='%s' and download_time<'%s';" % (id_bibrec, id_bibdoc_query_addition, date1, date2)
res = run_sql(sql_query)[0][0]
list_tuple.append((elem[0], res))
#list_tuple = sort_list_tuple_by_date(list_tuple)
return (list_tuple)
def sort_list_tuple_by_date(list_tuple):
"""Sort a list of tuple of the forme ("09/2004", 3)according to the
year of the first element of the tuple"""
list_tuple.sort(lambda x, y: (cmp(string.split(x[0], '/')[1],
string.split(y[0], '/')[1])))
return list_tuple
def create_list_tuple_total(intervals, list_data):
"""In the case of multiple id_bibdocs, a last paragraph is added
at the end to show the global evolution of the record"""
list_tuple = []
if len(intervals)==1:
res = 0
for j in range(len(list_data)):
res += list_data[j][1]
list_tuple.append((intervals[0][0], res))
else :
for i in range(len(intervals)):
res = 0
for j in range(len(list_data)):
res += list_data[j][i][1]
list_tuple.append((intervals[i][0], res))
#list_tuple = sort_list_tuple_by_date(list_tuple)
return list_tuple
def create_tic_intervals(local_year, local_month, creation_date_year, creation_date_month):
"""Create intervals since document creation date until now
Return a list of the tics for the graph of the form ["04/2004","05/2004"), ...]
And a list of tuple(each tuple stands for a period) of the form [("04/2004", "04/2004"),.]
to compute the number of downloads in each period
For the very short periods some tics and tuples are added to make sure that
at least two dates are returned. Useful for drawing graphs.
"""
# okay, off we go
tic_list = []
interval_list = []
original_date = (creation_date_month, creation_date_year)
while (creation_date_year, creation_date_month) <= (local_year, local_month) and creation_date_month <= 12:
date_elem = "%s/%s" % (creation_date_month, creation_date_year)
tic_list.append(date_elem)
interval_list.append((date_elem, date_elem))
if creation_date_month != 12:
creation_date_month = creation_date_month+1
else :
creation_date_year = creation_date_year+1
creation_date_month = 1
next_period = (creation_date_month, creation_date_year)
#additional periods for the short period
if len(interval_list) <= 2:
period_before = "%s/%s" % (sub_month(original_date[0], original_date[1]))
period_after = "%s/%s" % next_period
interval_list.insert(0, (period_before, period_before))
interval_list.append((period_after, period_after))
tic_list.insert(0, period_before)
tic_list.append(period_after)
return (tic_list, interval_list)
def add_month(month, year):
"""Add a month and increment the year if necessary"""
if month == 12:
month = 1
year += 1
else :
month += 1
return (month, year)
def sub_month(month, year):
"""Add a month and decrease the year if necessary"""
if month == 1:
month = 12
year = year -1
else :
month -= 1
return (month, year)
def create_users_analysis_graph(id_bibrec, ips):
"""For a given id_bibrec, classify cern users and other users
Draw a percentage graphic reprentation"""
cern_users = 0
other_users = 0
coordinates_list = []
#compute users repartition
for i in range(len(ips)):
if 2307522817 <= ips[i] <= 2307588095 or 2156724481 <= ips[i] <= 2156789759:
cern_users += 1
else :
other_users += 1
tot = float(cern_users+other_users)
#prepare coordinates list
coordinates_list.append((1, str(float(cern_users)/tot*100)))
coordinates_list.append((3, str(float(other_users)/tot*100)))
#write coordinates in a temporary file
result2 = write_coordinates_in_tmp_file([coordinates_list])
#result2 example: [/path/to-invenio/var/www/img/tmpeC9GP5,'100.0']
#the file contains e.g.
#1 100.0
#3 0.0
#plot the graph
return create_temporary_image(id_bibrec, 'download_users', result2[0], ' ', 'User distribution', (0, 0), result2[1], [], [], [1, 3])
diff --git a/modules/bibrank/lib/bibrank_regression_tests.py b/modules/bibrank/lib/bibrank_regression_tests.py
index 1bcde9af4..0db90556f 100644
--- a/modules/bibrank/lib/bibrank_regression_tests.py
+++ b/modules/bibrank/lib/bibrank_regression_tests.py
@@ -1,98 +1,98 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibRank Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class BibRankWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibRank web pages whether they are up or not."""
def test_rank_by_word_similarity_pages_availability(self):
"""bibrank - availability of ranking search results pages"""
- baseurl = weburl + '/search'
+ baseurl = CFG_SITE_URL + '/search'
_exports = ['?p=ellis&r=wrd']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_similar_records_pages_availability(self):
"""bibrank - availability of similar records results pages"""
- baseurl = weburl + '/search'
+ baseurl = CFG_SITE_URL + '/search'
_exports = ['?p=recid%3A18&rm=wrd']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
class BibRankWordSimilarityRankingTest(unittest.TestCase):
"""Check BibRank word similarity ranking tools."""
def test_search_results_ranked_by_similarity(self):
"""bibrank - search results ranked by word similarity"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=ellis&rm=wrd&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?p=ellis&rm=wrd&of=id',
expected_text="[8, 10, 11, 12, 47, 17, 13, 16, 18, 9, 14, 15]"))
def test_similar_records_link(self):
"""bibrank - 'Similar records' link"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=recid%3A77&rm=wrd&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A77&rm=wrd&of=id',
expected_text="[84, 95, 85, 77]"))
class BibRankCitationRankingTest(unittest.TestCase):
"""Check BibRank citation ranking tools."""
def test_search_results_ranked_by_citations(self):
"""bibrank - search results ranked by number of citations"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?cc=Articles+%26+Preprints&p=Klebanov&rm=citation&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?cc=Articles+%26+Preprints&p=Klebanov&rm=citation&of=id',
expected_text="[85, 77, 84]"))
def test_search_results_ranked_by_citations_verbose(self):
"""bibrank - search results ranked by number of citations, verbose output"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?cc=Articles+%26+Preprints&p=Klebanov&rm=citation&verbose=2',
+ test_web_page_content(CFG_SITE_URL + '/search?cc=Articles+%26+Preprints&p=Klebanov&rm=citation&verbose=2',
expected_text="find_citations retlist [[85, 0], [77, 2], [84, 3]]"))
test_suite = make_test_suite(BibRankWebPagesAvailabilityTest,
BibRankWordSimilarityRankingTest,
BibRankCitationRankingTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibrank/lib/bibrankadmin_regression_tests.py b/modules/bibrank/lib/bibrankadmin_regression_tests.py
index 67fcd8cdc..cde5ceef9 100644
--- a/modules/bibrank/lib/bibrankadmin_regression_tests.py
+++ b/modules/bibrank/lib/bibrankadmin_regression_tests.py
@@ -1,71 +1,71 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""BibRank Admin Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class BibRankAdminWebPagesAvailabilityTest(unittest.TestCase):
"""Check BibRank Admin web pages whether they are up or not."""
def test_bibrank_admin_interface_pages_availability(self):
"""bibrankadmin - availability of BibRank Admin interface pages"""
- baseurl = weburl + '/admin/bibrank/bibrankadmin.py/'
+ baseurl = CFG_SITE_URL + '/admin/bibrank/bibrankadmin.py/'
_exports = ['', 'addrankarea', 'modifytranslations',
'modifycollection', 'showrankdetails', 'modifyrank',
'deleterank']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_bibrank_admin_guide_availability(self):
"""bibrankadmin - availability of BibRank Admin guide pages"""
- url = weburl + '/help/admin/bibrank-admin-guide'
+ url = CFG_SITE_URL + '/help/admin/bibrank-admin-guide'
error_messages = test_web_page_content(url,
expected_text="BibRank Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(BibRankAdminWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/bibrank/lib/bibrankadminlib.py b/modules/bibrank/lib/bibrankadminlib.py
index 8bfa8308c..cf95d16e4 100644
--- a/modules/bibrank/lib/bibrankadminlib.py
+++ b/modules/bibrank/lib/bibrankadminlib.py
@@ -1,1038 +1,1038 @@
## $Id$
## Administrator interface for BibRank
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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.
##
## Youshould have received a copy of the GNU General Public License
## along with CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio BibRank Administrator Interface."""
__revision__ = "$Id$"
import cgi
import re
import os
import ConfigParser
from zlib import compress,decompress
import marshal
try:
from mod_python import apache
except ImportError:
pass
from invenio.config import \
CFG_SITE_LANG, \
CFG_ETCDIR, \
CFG_VERSION, \
- weburl
+ CFG_SITE_URL
import invenio.access_control_engine as acce
from invenio.messages import language_list_long
from invenio.dbquery import run_sql
from invenio.webpage import page, pageheaderonly, pagefooteronly
from invenio.webuser import getUid, get_email
def getnavtrail(previous = ''):
- navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (weburl,)
+ navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (CFG_SITE_URL,)
navtrail = navtrail + previous
return navtrail
def check_user(req, role, adminarea=2, authorized=0):
(auth_code, auth_message) = is_adminuser(req, role)
if not authorized and auth_code != 0:
return ("false", auth_message)
return ("", auth_message)
def is_adminuser(req, role):
"""check if user is a registered administrator. """
return acce.acc_authorize_action(req, role)
def perform_index(ln=CFG_SITE_LANG):
"""create the bibrank main area menu page."""
header = ['Code', 'Translations', 'Collections', 'Rank method']
rnk_list = get_def_name('', "rnkMETHOD")
actions = []
for (rnkID, name) in rnk_list:
actions.append([name])
for col in [(('Modify', 'modifytranslations'),),
(('Modify', 'modifycollection'),),
(('Show Details', 'showrankdetails'),
('Modify', 'modifyrank'),
('Delete', 'deleterank'))]:
- actions[-1].append('<a href="%s/admin/bibrank/bibrankadmin.py/%s?rnkID=%s&amp;ln=%s">%s</a>' % (weburl, col[0][1], rnkID, ln, col[0][0]))
+ actions[-1].append('<a href="%s/admin/bibrank/bibrankadmin.py/%s?rnkID=%s&amp;ln=%s">%s</a>' % (CFG_SITE_URL, col[0][1], rnkID, ln, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/bibrank/bibrankadmin.py/%s?rnkID=%s&amp;ln=%s">%s</a>' % (weburl, function, rnkID, ln, str)
+ actions[-1][-1] += ' / <a href="%s/admin/bibrank/bibrankadmin.py/%s?rnkID=%s&amp;ln=%s">%s</a>' % (CFG_SITE_URL, function, rnkID, ln, str)
output = """
<a href="%s/admin/bibrank/bibrankadmin.py/addrankarea?ln=%s">Add new rank method</a><br /><br />
- """ % (weburl, ln)
+ """ % (CFG_SITE_URL, ln)
output += tupletotable(header=header, tuple=actions)
- return addadminbox("""Overview of rank methods&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#mi">?</a>]</small>""" % weburl, datalist=[output, ''])
+ return addadminbox("""Overview of rank methods&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#mi">?</a>]</small>""" % CFG_SITE_URL, datalist=[output, ''])
def perform_modifycollection(rnkID='', ln=CFG_SITE_LANG, func='', colID='', confirm=0):
"""Modify which collections the rank method is visible to"""
output = ""
subtitle = ""
if rnkID:
rnkNAME = get_def_name(rnkID, "rnkMETHOD")[0][1]
if func in ["0", 0] and confirm in ["1", 1]:
finresult = attach_col_rnk(rnkID, colID)
elif func in ["1", 1] and confirm in ["1", 1]:
finresult = detach_col_rnk(rnkID, colID)
if colID:
colNAME = get_def_name(colID, "collection")[0][1]
subtitle = """Step 1 - Select collection to enable/disable rank method '%s' for""" % rnkNAME
output = """
<dl>
<dt>The rank method is currently enabled for these collections:</dt>
<dd>
"""
col_list = get_rnk_col(rnkID, ln)
if not col_list:
output += """No collections"""
else:
for (id, name) in col_list:
output += """%s, """ % name
output += """</dd>
</dl>
"""
col_list = get_def_name('', "collection")
col_rnk = dict(get_rnk_col(rnkID))
col_list = filter(lambda x: not col_rnk.has_key(x[0]), col_list)
if col_list:
text = """
<span class="adminlabel">Enable for:</span>
<select name="colID" class="admin_w200">
<option value="">- select collection -</option>
"""
for (id, name) in col_list:
text += """<option value="%s" %s>%s</option>""" % (id, (func in ["0", 0] and confirm in ["0", 0] and colID and int(colID) == int(id)) and 'selected="selected"' or '' , name)
text += """</select>"""
output += createhiddenform(action="modifycollection",
text=text,
button="Enable",
rnkID=rnkID,
ln=ln,
func=0,
confirm=1)
if confirm in ["0", 0] and func in ["0", 0] and colID:
subtitle = "Step 2 - Confirm to enable rank method for the chosen collection"
text = "<b><p>Please confirm to enable rank method '%s' for the collection '%s'</p></b>" % (rnkNAME, colNAME)
output += createhiddenform(action="modifycollection",
text=text,
button="Confirm",
rnkID=rnkID,
ln=ln,
colID=colID,
func=0,
confirm=1)
elif confirm in ["1", 1] and func in ["0", 0] and colID:
subtitle = "Step 3 - Result"
output += write_outcome(finresult)
elif confirm not in ["0", 0] and func in ["0", 0]:
output += """<b><span class="info">Please select a collection.</span></b>"""
col_list = get_rnk_col(rnkID, ln)
if col_list:
text = """
<span class="adminlabel">Disable for:</span>
<select name="colID" class="admin_w200">
<option value="">- select collection -</option>
"""
for (id, name) in col_list:
text += """<option value="%s" %s>%s</option>""" % (id, (func in ["1", 1] and confirm in ["0", 0] and colID and int(colID) == int(id)) and 'selected="selected"' or '' , name)
text += """</select>"""
output += createhiddenform(action="modifycollection",
text=text,
button="Disable",
rnkID=rnkID,
ln=ln,
func=1,
confirm=1)
if confirm in ["1", 1] and func in ["1", 1] and colID:
subtitle = "Step 3 - Result"
output += write_outcome(finresult)
elif confirm not in ["0", 0] and func in ["1", 1]:
output += """<b><span class="info">Please select a collection.</span></b>"""
body = [output]
- return addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#mc">?</a>]</small>""" % weburl, body)
+ return addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#mc">?</a>]</small>""" % CFG_SITE_URL, body)
def perform_modifytranslations(rnkID, ln, sel_type, trans, confirm, callback='yes'):
"""Modify the translations of a rank method"""
output = ''
subtitle = ''
langs = get_languages()
langs.sort()
if confirm in ["2", 2] and rnkID:
finresult = modify_translations(rnkID, langs, sel_type, trans, "rnkMETHOD")
rnk_name = get_def_name(rnkID, "rnkMETHOD")[0][1]
rnk_dict = dict(get_i8n_name('', ln, get_rnk_nametypes()[0][0], "rnkMETHOD"))
if rnkID and rnk_dict.has_key(int(rnkID)):
rnkID = int(rnkID)
subtitle = """<a name="3">3. Modify translations for rank method '%s'</a>""" % rnk_name
if type(trans) is str:
trans = [trans]
if sel_type == '':
sel_type = get_rnk_nametypes()[0][0]
header = ['Language', 'Translation']
actions = []
text = """
<span class="adminlabel">Name type</span>
<select name="sel_type" class="admin_w200">
"""
types = get_rnk_nametypes()
if len(types) > 1:
for (key, value) in types:
text += """<option value="%s" %s>%s""" % (key, key == sel_type and 'selected="selected"' or '', value)
trans_names = get_name(rnkID, ln, key, "rnkMETHOD")
if trans_names and trans_names[0][0]:
text += ": %s" % trans_names[0][0]
text += "</option>"
text += """</select>"""
output += createhiddenform(action="modifytranslations",
text=text,
button="Select",
rnkID=rnkID,
ln=ln,
confirm=0)
if confirm in [-1, "-1", 0, "0"]:
trans = []
for key, value in langs:
try:
trans_names = get_name(rnkID, key, sel_type, "rnkMETHOD")
trans.append(trans_names[0][0])
except StandardError, e:
trans.append('')
for nr in range(0,len(langs)):
actions.append(["%s %s" % (langs[nr][1], (langs[nr][0]==CFG_SITE_LANG and '<small>(def)</small>' or ''))])
actions[-1].append('<input type="text" name="trans" size="30" value="%s"/>' % trans[nr])
text = tupletotable(header=header, tuple=actions)
output += createhiddenform(action="modifytranslations",
text=text,
button="Modify",
rnkID=rnkID,
sel_type=sel_type,
ln=ln,
confirm=2)
if sel_type and len(trans) and confirm in ["2", 2]:
output += write_outcome(finresult)
body = [output]
- return addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#mt">?</a>]</small>""" % weburl, body)
+ return addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#mt">?</a>]</small>""" % CFG_SITE_URL, body)
def perform_addrankarea(rnkcode='', ln=CFG_SITE_LANG, template='', confirm=-1):
"""form to add a new rank method with these values:"""
subtitle = 'Step 1 - Create new rank method'
output = """
<dl>
<dt>BibRank code:</dt>
<dd>A unique code that identifies a rank method, is used when running the bibrank daemon and used to name the configuration file for the method.
<br />The template files includes the necessary parameters for the chosen rank method, and only needs to be edited with the correct tags and paths.
<br />For more information, please go to the <a title="See guide" href="%s/help/admin/bibrank-admin-guide">BibRank guide</a> and read the section about adding a rank method</dd>
</dl>
- """ % weburl
+ """ % CFG_SITE_URL
text = """
<span class="adminlabel">BibRank code</span>
<input class="admin_wvar" type="text" name="rnkcode" value="%s" />
""" % (rnkcode)
text += """<br />
<span class="adminlabel">Cfg template</span>
<select name="template" class="admin_w200">
<option value="">No template</option>
"""
templates = get_templates()
for templ in templates:
text += """<option value="%s" %s>%s</option>""" % (templ, template == templ and 'selected="selected"' or '', templ[9:len(templ)-4])
text += """</select>"""
output += createhiddenform(action="addrankarea",
text=text,
button="Add rank method",
ln=ln,
confirm=1)
if rnkcode:
if confirm in ["0", 0]:
subtitle = 'Step 2 - Confirm addition of rank method'
text = """<b>Add rank method with BibRank code: '%s'.</b>""" % (rnkcode)
if template:
text += """<br /><b>Using configuration template: '%s'.</b>""" % (template)
else:
text += """<br /><b>Create empty configuration file.</b>"""
output += createhiddenform(action="addrankarea",
text=text,
rnkcode=rnkcode,
button="Confirm",
template=template,
confirm=1)
elif confirm in ["1", 1]:
rnkID = add_rnk(rnkcode)
subtitle = "Step 3 - Result"
if rnkID[0] == 1:
rnkID = rnkID[1]
text = """<b><span class="info">Added new rank method with BibRank code '%s'</span></b>""" % rnkcode
try:
if template:
infile = open("%s/bibrank/%s" % (CFG_ETCDIR, template), 'r')
indata = infile.readlines()
infile.close()
else:
indata = ()
file = open("%s/bibrank/%s.cfg" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0]), 'w')
for line in indata:
file.write(line)
file.close()
if template:
text += """<b><span class="info"><br />Configuration file created using '%s' as template.</span></b>""" % template
else:
text += """<b><span class="info"><br />Empty configuration file created.</span></b>"""
except StandardError, e:
text += """<b><span class="info"><br />Sorry, could not create configuration file: '%s/bibrank/%s.cfg', either because it already exists, or not enough rights to create file. <br />Please create the file in the path given.</span></b>""" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0])
else:
text = """<b><span class="info">Sorry, could not add rank method, rank method with the same BibRank code probably exists.</span></b>"""
output += text
elif not rnkcode and confirm not in [-1, "-1"]:
output += """<b><span class="info">Sorry, could not add rank method, not enough data submitted.</span></b>"""
body = [output]
- return addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#ar">?</a>]</small>""" % weburl, body)
+ return addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#ar">?</a>]</small>""" % CFG_SITE_URL, body)
def perform_modifyrank(rnkID, rnkcode='', ln=CFG_SITE_LANG, template='', cfgfile='', confirm=0):
"""form to modify a rank method
rnkID - id of the rank method
"""
if not rnkID:
return "No ranking method selected."
if not get_rnk_code(rnkID):
return "Ranking method %s does not seem to exist." % str(rnkID)
subtitle = 'Step 1 - Please modify the wanted values below'
if not rnkcode:
oldcode = get_rnk_code(rnkID)[0]
else:
oldcode = rnkcode
output = """
<dl>
<dd>When changing the BibRank code of a rank method, you must also change any scheduled tasks using the old value.
<br />For more information, please go to the <a title="See guide" href="%s/help/admin/bibrank-admin-guide">BibRank guide</a> and read the section about modifying a rank method's BibRank code.</dd>
</dl>
- """ % weburl
+ """ % CFG_SITE_URL
text = """
<span class="adminlabel">BibRank code</span>
<input class="admin_wvar" type="text" name="rnkcode" value="%s" />
<br />
""" % (oldcode)
try:
text += """<span class="adminlabel">Cfg file</span>"""
textarea = ""
if cfgfile:
textarea +=cfgfile
else:
file = open("%s/bibrank/%s.cfg" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0]))
for line in file.readlines():
textarea += line
text += """<textarea class="admin_wvar" name="cfgfile" rows="15" cols="70">""" + textarea + """</textarea>"""
except StandardError, e:
text += """<b><span class="info">Cannot load file, either it does not exist, or not enough rights to read it: '%s/bibrank/%s.cfg'<br />Please create the file in the path given.</span></b>""" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0])
output += createhiddenform(action="modifyrank",
text=text,
rnkID=rnkID,
button="Modify",
confirm=1)
if rnkcode and confirm in ["1", 1] and get_rnk_code(rnkID)[0][0] != rnkcode:
oldcode = get_rnk_code(rnkID)[0][0]
result = modify_rnk(rnkID, rnkcode)
subtitle = "Step 3 - Result"
if result:
text = """<b><span class="info">Rank method modified.</span></b>"""
try:
file = open("%s/bibrank/%s.cfg" % (CFG_ETCDIR, oldcode), 'r')
file2 = open("%s/bibrank/%s.cfg" % (CFG_ETCDIR, rnkcode), 'w')
lines = file.readlines()
for line in lines:
file2.write(line)
file.close()
file2.close()
os.remove("%s/bibrank/%s.cfg" % (CFG_ETCDIR, oldcode))
except StandardError, e:
text = """<b><span class="info">Sorry, could not change name of cfg file, must be done manually: '%s/bibrank/%s.cfg'</span></b>""" % (CFG_ETCDIR, oldcode)
else:
text = """<b><span class="info">Sorry, could not modify rank method.</span></b>"""
output += text
if cfgfile and confirm in ["1", 1]:
try:
file = open("%s/bibrank/%s.cfg" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0]), 'w')
file.write(cfgfile)
file.close()
text = """<b><span class="info"><br />Configuration file modified: '%s/bibrank/%s.cfg'</span></b>""" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0])
except StandardError, e:
text = """<b><span class="info"><br />Sorry, could not modify configuration file, please check for rights to do so: '%s/bibrank/%s.cfg'<br />Please modify the file manually.</span></b>""" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0])
output += text
- finoutput = addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#mr">?</a>]</small>""" % weburl, [output])
+ finoutput = addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#mr">?</a>]</small>""" % CFG_SITE_URL, [output])
output = ""
text = """
<span class="adminlabel">Select</span>
<select name="template" class="admin_w200">
<option value="">- select template -</option>
"""
templates = get_templates()
for templ in templates:
text += """<option value="%s" %s>%s</option>""" % (templ, template == templ and 'selected="selected"' or '', templ[9:len(templ)-4])
text += """</select><br />"""
output += createhiddenform(action="modifyrank",
text=text,
rnkID=rnkID,
button="Show template",
confirm=0)
try:
if template:
textarea = ""
text = """<span class="adminlabel">Content:</span>"""
file = open("%s/bibrank/%s" % (CFG_ETCDIR, template), 'r')
lines = file.readlines()
for line in lines:
textarea += line
file.close()
text += """<textarea class="admin_wvar" readonly="true" rows="15" cols="70">""" + textarea + """</textarea>"""
output += text
except StandardError, e:
output += """Cannot load file, either it does not exist, or not enough rights to read it: '%s/bibrank/%s'""" % (CFG_ETCDIR, template)
finoutput += addadminbox("View templates", [output])
return finoutput
def perform_deleterank(rnkID, ln=CFG_SITE_LANG, confirm=0):
"""form to delete a rank method
"""
subtitle =''
output = """
<span class="warning">
<dl>
<dt><strong>WARNING:</strong></dt>
<dd><strong>When deleting a rank method, you also deletes all data related to the rank method, like translations, which collections
it was attached to and the data necessary to rank the searchresults. Any scheduled tasks using the deleted rank method will also stop working.
<br /><br />For more information, please go to the <a title="See guide" href="%s/help/admin/bibrank-admin-guide">BibRank guide</a> and read the section regarding deleting a rank method.</strong></dd>
</dl>
</span>
- """ % weburl
+ """ % CFG_SITE_URL
if rnkID:
if confirm in ["0", 0]:
rnkNAME = get_def_name(rnkID, "rnkMETHOD")[0][1]
subtitle = 'Step 1 - Confirm deletion'
text = """Delete rank method '%s'.""" % (rnkNAME)
output += createhiddenform(action="deleterank",
text=text,
button="Confirm",
rnkID=rnkID,
confirm=1)
elif confirm in ["1", 1]:
try:
rnkNAME = get_def_name(rnkID, "rnkMETHOD")[0][1]
rnkcode = get_rnk_code(rnkID)[0][0]
table = ""
try:
config = ConfigParser.ConfigParser()
config.readfp(open("%s/bibrank/%s.cfg" % (CFG_ETCDIR, rnkcode), 'r'))
table = config.get(config.get('rank_method', "function"), "table")
except Exception:
pass
result = delete_rnk(rnkID, table)
subtitle = "Step 2 - Result"
if result:
text = """<b><span class="info">Rank method deleted</span></b>"""
try:
os.remove("%s/bibrank/%s.cfg" % (CFG_ETCDIR, rnkcode))
text += """<br /><b><span class="info">Configuration file deleted: '%s/bibrank/%s.cfg'.</span></b>""" % (CFG_ETCDIR, rnkcode)
except StandardError, e:
text += """<br /><b><span class="info">Sorry, could not delete configuration file: '%s/bibrank/%s.cfg'.</span><br />Please delete the file manually.</span></b>""" % (CFG_ETCDIR, rnkcode)
else:
text = """<b><span class="info">Sorry, could not delete rank method</span></b>"""
except StandardError, e:
text = """<b><span class="info">Sorry, could not delete rank method, most likely already deleted</span></b>"""
output = text
body = [output]
- return addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#dr">?</a>]</small>""" % weburl, body)
+ return addadminbox(subtitle + """&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/bibrank-admin-guide#dr">?</a>]</small>""" % CFG_SITE_URL, body)
def perform_showrankdetails(rnkID, ln=CFG_SITE_LANG):
"""Returns details about the rank method given by rnkID"""
if not rnkID:
return "No ranking method selected."
if not get_rnk_code(rnkID):
return "Ranking method %s does not seem to exist." % str(rnkID)
- subtitle = """Overview <a href="%s/admin/bibrank/bibrankadmin.py/modifyrank?rnkID=%s&amp;ln=%s">[Modify]</a>""" % (weburl, rnkID, ln)
+ subtitle = """Overview <a href="%s/admin/bibrank/bibrankadmin.py/modifyrank?rnkID=%s&amp;ln=%s">[Modify]</a>""" % (CFG_SITE_URL, rnkID, ln)
text = """
BibRank code: %s<br />
Last updated by BibRank:
""" % (get_rnk_code(rnkID)[0][0])
if get_rnk(rnkID)[0][2]:
text += "%s<br />" % get_rnk(rnkID)[0][2]
else:
text += "Not yet run.<br />"
output = addadminbox(subtitle, [text])
subtitle = """Rank method statistics"""
text = ""
try:
text = "Not yet implemented"
except StandardError, e:
text = "BibRank not yet run, cannot show statistics for method"
output += addadminbox(subtitle, [text])
- subtitle = """Attached to collections <a href="%s/admin/bibrank/bibrankadmin.py/modifycollection?rnkID=%s&amp;ln=%s">[Modify]</a>""" % (weburl, rnkID, ln)
+ subtitle = """Attached to collections <a href="%s/admin/bibrank/bibrankadmin.py/modifycollection?rnkID=%s&amp;ln=%s">[Modify]</a>""" % (CFG_SITE_URL, rnkID, ln)
text = ""
col = get_rnk_col(rnkID, ln)
for key, value in col:
text+= "%s<br />" % value
if not col:
text +="No collections"
output += addadminbox(subtitle, [text])
- subtitle = """Translations <a href="%s/admin/bibrank/bibrankadmin.py/modifytranslations?rnkID=%s&amp;ln=%s">[Modify]</a>""" % (weburl, rnkID, ln)
+ subtitle = """Translations <a href="%s/admin/bibrank/bibrankadmin.py/modifytranslations?rnkID=%s&amp;ln=%s">[Modify]</a>""" % (CFG_SITE_URL, rnkID, ln)
prev_lang = ''
trans = get_translations(rnkID)
types = get_rnk_nametypes()
types = dict(map(lambda x: (x[0], x[1]), types))
text = ""
languages = dict(get_languages())
if trans:
for lang, type, name in trans:
if lang and languages.has_key(lang) and type and name:
if prev_lang != lang:
prev_lang = lang
text += """%s: <br />""" % (languages[lang])
if types.has_key(type):
text+= """<span style="margin-left: 10px">'%s'</span><span class="note">(%s)</span><br />""" % (name, types[type])
else:
text = """No translations exists"""
output += addadminbox(subtitle, [text])
- subtitle = """Configuration file: '%s/bibrank/%s.cfg' <a href="%s/admin/bibrank/bibrankadmin.py/modifyrank?rnkID=%s&amp;ln=%s">[Modify]</a>""" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0], weburl, rnkID, ln)
+ subtitle = """Configuration file: '%s/bibrank/%s.cfg' <a href="%s/admin/bibrank/bibrankadmin.py/modifyrank?rnkID=%s&amp;ln=%s">[Modify]</a>""" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0], CFG_SITE_URL, rnkID, ln)
text = ""
try:
file = open("%s/bibrank/%s.cfg" % (CFG_ETCDIR, get_rnk_code(rnkID)[0][0]))
text += """<pre>"""
for line in file.readlines():
text += line
text += """</pre>"""
except StandardError, e:
text = """Cannot load file, either it does not exist, or not enough rights to read it."""
output += addadminbox(subtitle, [text])
return output
def compare_on_val(second, first):
return cmp(second[1], first[1])
def get_rnk_code(rnkID):
"""Returns the name from rnkMETHOD based on argument
rnkID - id from rnkMETHOD"""
try:
res = run_sql("SELECT name FROM rnkMETHOD where id=%s" % (rnkID))
return res
except StandardError, e:
return ()
def get_rnk(rnkID=''):
"""Return one or all rank methods
rnkID - return the rank method given, or all if not given"""
try:
if rnkID:
res = run_sql("SELECT id,name,DATE_FORMAT(last_updated, '%%Y-%%m-%%d %%H:%%i:%%s') from rnkMETHOD WHERE id=%s" % rnkID)
else:
res = run_sql("SELECT id,name,DATE_FORMAT(last_updated, '%%Y-%%m-%%d %%H:%%i:%%s') from rnkMETHOD")
return res
except StandardError, e:
return ()
def get_translations(rnkID):
"""Returns the translations in rnkMETHODNAME for a rankmethod
rnkID - the id of the rankmethod from rnkMETHOD """
try:
res = run_sql("SELECT ln, type, value FROM rnkMETHODNAME where id_rnkMETHOD=%s ORDER BY ln,type" % (rnkID))
return res
except StandardError, e:
return ()
def get_rnk_nametypes():
"""Return a list of the various translationnames for the rank methods"""
type = []
type.append(('ln', 'Long name'))
#type.append(('sn', 'Short name'))
return type
def get_col_nametypes():
"""Return a list of the various translationnames for the rank methods"""
type = []
type.append(('ln', 'Long name'))
return type
def get_rnk_col(rnkID, ln=CFG_SITE_LANG):
""" Returns a list of the collections the given rank method is attached to
rnkID - id from rnkMETHOD"""
try:
res1 = dict(run_sql("SELECT id_collection, '' FROM collection_rnkMETHOD WHERE id_rnkMETHOD=%s" % rnkID))
res2 = get_def_name('', "collection")
result = filter(lambda x: res1.has_key(x[0]), res2)
return result
except StandardError, e:
return ()
def get_templates():
"""Read CFG_ETCDIR/bibrank and returns a list of all files with 'template' """
templates = []
files = os.listdir(CFG_ETCDIR + "/bibrank/")
for file in files:
if str.find(file,"template_") != -1:
templates.append(file)
return templates
def attach_col_rnk(rnkID, colID):
"""attach rank method to collection
rnkID - id from rnkMETHOD table
colID - id of collection, as in collection table """
try:
res = run_sql("INSERT INTO collection_rnkMETHOD(id_collection, id_rnkMETHOD) values (%s,%s)" % (colID, rnkID))
return (1, "")
except StandardError, e:
return (0, e)
def detach_col_rnk(rnkID, colID):
"""detach rank method from collection
rnkID - id from rnkMETHOD table
colID - id of collection, as in collection table """
try:
res = run_sql("DELETE FROM collection_rnkMETHOD WHERE id_collection=%s AND id_rnkMETHOD=%s" % (colID, rnkID))
return (1, "")
except StandardError, e:
return (0, e)
def delete_rnk(rnkID, table=""):
"""Deletes all data for the given rank method
rnkID - delete all data in the tables associated with ranking and this id """
try:
res = run_sql("DELETE FROM rnkMETHOD WHERE id=%s" % rnkID)
res = run_sql("DELETE FROM rnkMETHODNAME WHERE id_rnkMETHOD=%s" % rnkID)
res = run_sql("DELETE FROM collection_rnkMETHOD WHERE id_rnkMETHOD=%s" % rnkID)
res = run_sql("DELETE FROM rnkMETHODDATA WHERE id_rnkMETHOD=%s" % rnkID)
if table:
res = run_sql("truncate %s" % table)
res = run_sql("truncate %sR" % table[:-1])
return (1, "")
except StandardError, e:
return (0, e)
def modify_rnk(rnkID, rnkcode):
"""change the code for the rank method given
rnkID - change in rnkMETHOD where id is like this
rnkcode - new value for field 'name' in rnkMETHOD """
try:
res = run_sql("UPDATE rnkMETHOD set name=%s WHERE id=%s", (rnkcode, rnkID))
return (1, "")
except StandardError, e:
return (0, e)
def add_rnk(rnkcode):
"""Adds a new rank method to rnkMETHOD
rnkcode - the "code" for the rank method, to be used by bibrank daemon """
try:
res = run_sql("INSERT INTO rnkMETHOD (name) VALUES (%s)", (rnkcode,))
res = run_sql("SELECT id FROM rnkMETHOD WHERE name=%s", (rnkcode,))
if res:
return (1, res[0][0])
else:
raise StandardError
except StandardError, e:
return (0, e)
def addadminbox(header='', datalist=[], cls="admin_wvar"):
"""used to create table around main data on a page, row based.
header - header on top of the table
datalist - list of the data to be added row by row
cls - possible to select wich css-class to format the look of the table."""
if len(datalist) == 1: per = '100'
else: per = '75'
output = '<table class="%s" ' % (cls, ) + 'width="95%">\n'
output += """
<thead>
<tr>
<th class="adminheaderleft" colspan="%s">%s</th>
</tr>
</thead>
<tbody>
""" % (len(datalist), header)
output += ' <tr>\n'
output += """
<td style="vertical-align: top; margin-top: 5px; width: %s;">
%s
</td>
""" % (per+'%', datalist[0])
if len(datalist) > 1:
output += """
<td style="vertical-align: top; margin-top: 5px; width: %s;">
%s
</td>
""" % ('25%', datalist[1])
output += ' </tr>\n'
output += """
</tbody>
</table>
"""
return output
def tupletotable(header=[], tuple=[], start='', end='', extracolumn=''):
"""create html table for a tuple.
header - optional header for the columns
tuple - create table of this
start - text to be added in the beginning, most likely beginning of a form
end - text to be added in the end, mot likely end of a form.
extracolumn - mainly used to put in a button. """
# study first row in tuple for alignment
align = []
try:
firstrow = tuple[0]
if type(firstrow) in [int, long]:
align = ['admintdright']
elif type(firstrow) in [str, dict]:
align = ['admintdleft']
else:
for item in firstrow:
if type(item) is int:
align.append('admintdright')
else:
align.append('admintdleft')
except IndexError:
firstrow = []
tblstr = ''
for h in header + ['']:
tblstr += ' <th class="adminheader">%s</th>\n' % (h, )
if tblstr: tblstr = ' <tr>\n%s\n </tr>\n' % (tblstr, )
tblstr = start + '<table class="admin_wvar_nomargin">\n' + tblstr
# extra column
try:
extra = '<tr>'
if type(firstrow) not in [int, long, str, dict]:
# for data in firstrow: extra += '<td class="%s">%s</td>\n' % ('admintd', data)
for i in range(len(firstrow)): extra += '<td class="%s">%s</td>\n' % (align[i], firstrow[i])
else:
extra += ' <td class="%s">%s</td>\n' % (align[0], firstrow)
extra += '<td rowspan="%s" style="vertical-align: top">\n%s\n</td>\n</tr>\n' % (len(tuple), extracolumn)
except IndexError:
extra = ''
tblstr += extra
# for i in range(1, len(tuple)):
for row in tuple[1:]:
tblstr += ' <tr>\n'
# row = tuple[i]
if type(row) not in [int, long, str, dict]:
# for data in row: tblstr += '<td class="admintd">%s</td>\n' % (data,)
for i in range(len(row)): tblstr += '<td class="%s">%s</td>\n' % (align[i], row[i])
else:
tblstr += ' <td class="%s">%s</td>\n' % (align[0], row)
tblstr += ' </tr> \n'
tblstr += '</table> \n '
tblstr += end
return tblstr
def tupletotable_onlyselected(header=[], tuple=[], selected=[], start='', end='', extracolumn=''):
"""create html table for a tuple.
header - optional header for the columns
tuple - create table of this
selected - indexes of selected rows in the tuple
start - put this in the beginning
end - put this in the beginning
extracolumn - mainly used to put in a button"""
tuple2 = []
for index in selected:
tuple2.append(tuple[int(index)-1])
return tupletotable(header=header,
tuple=tuple2,
start=start,
end=end,
extracolumn=extracolumn)
def addcheckboxes(datalist=[], name='authids', startindex=1, checked=[]):
"""adds checkboxes in front of the listdata.
datalist - add checkboxes in front of this list
name - name of all the checkboxes, values will be associated with this name
startindex - usually 1 because of the header
checked - values of checkboxes to be pre-checked """
if not type(checked) is list: checked = [checked]
for row in datalist:
if 1 or row[0] not in [-1, "-1", 0, "0"]: # always box, check another place
chkstr = str(startindex) in checked and 'checked="checked"' or ''
row.insert(0, '<input type="checkbox" name="%s" value="%s" %s />' % (name, startindex, chkstr))
else:
row.insert(0, '')
startindex += 1
return datalist
def createhiddenform(action="", text="", button="confirm", cnfrm='', **hidden):
"""create select with hidden values and submit button
action - name of the action to perform on submit
text - additional text, can also be used to add non hidden input
button - value/caption on the submit button
cnfrm - if given, must check checkbox to confirm
**hidden - dictionary with name=value pairs for hidden input """
output = '<form action="%s" method="post">\n' % (action, )
output += '<table>\n<tr><td style="vertical-align: top">'
output += text
if cnfrm:
output += ' <input type="checkbox" name="confirm" value="1"/>'
for key in hidden.keys():
if type(hidden[key]) is list:
for value in hidden[key]:
output += ' <input type="hidden" name="%s" value="%s"/>\n' % (key, value)
else:
output += ' <input type="hidden" name="%s" value="%s"/>\n' % (key, hidden[key])
output += '</td><td style="vertical-align: bottom">'
output += ' <input class="adminbutton" type="submit" value="%s"/>\n' % (button, )
output += '</td></tr></table>'
output += '</form>\n'
return output
def get_languages():
languages = []
for (lang, lang_namelong) in language_list_long():
languages.append((lang, lang_namelong))
languages.sort()
return languages
def get_def_name(ID, table):
"""Returns a list of the names, either with the name in the current language, the default language, or just the name from the given table
ln - a language supported by CDS Invenio
type - the type of value wanted, like 'ln', 'sn'"""
name = "name"
if table[-1:].isupper():
name = "NAME"
try:
if ID:
res = run_sql("SELECT id,name FROM %s where id=%s" % (table, ID))
else:
res = run_sql("SELECT id,name FROM %s" % table)
res = list(res)
res.sort(compare_on_val)
return res
except StandardError, e:
return []
def get_i8n_name(ID, ln, rtype, table):
"""Returns a list of the names, either with the name in the current language, the default language, or just the name from the given table
ln - a language supported by CDS Invenio
type - the type of value wanted, like 'ln', 'sn'"""
name = "name"
if table[-1:].isupper():
name = "NAME"
try:
res = ""
if ID:
res = run_sql("SELECT id_%s,value FROM %s%s where type='%s' and ln='%s' and id_%s=%s" % (table, table, name, rtype,ln, table, ID))
else:
res = run_sql("SELECT id_%s,value FROM %s%s where type='%s' and ln='%s'" % (table, table, name, rtype,ln))
if ln != CFG_SITE_LANG:
if ID:
res1 = run_sql("SELECT id_%s,value FROM %s%s WHERE ln='%s' and type='%s' and id_%s=%s" % (table, table, name, CFG_SITE_LANG, rtype, table, ID))
else:
res1 = run_sql("SELECT id_%s,value FROM %s%s WHERE ln='%s' and type='%s'" % (table, table, name, CFG_SITE_LANG, rtype))
res2 = dict(res)
result = filter(lambda x: not res2.has_key(x[0]), res1)
res = res + result
if ID:
res1 = run_sql("SELECT id,name FROM %s where id=%s" % (table, ID))
else:
res1 = run_sql("SELECT id,name FROM %s" % table)
res2 = dict(res)
result = filter(lambda x: not res2.has_key(x[0]), res1)
res = res + result
res = list(res)
res.sort(compare_on_val)
return res
except StandardError, e:
raise StandardError
def get_name(ID, ln, rtype, table):
"""Returns the value from the table name based on arguments
ID - id
ln - a language supported by CDS Invenio
type - the type of value wanted, like 'ln', 'sn'
table - tablename"""
name = "name"
if table[-1:].isupper():
name = "NAME"
try:
res = run_sql("SELECT value FROM %s%s WHERE type='%s' and ln='%s' and id_%s=%s" % (table, name, rtype, ln, table, ID))
return res
except StandardError, e:
return ()
def modify_translations(ID, langs, sel_type, trans, table):
"""add or modify translations in tables given by table
frmID - the id of the format from the format table
sel_type - the name type
langs - the languages
trans - the translations, in same order as in langs
table - the table"""
name = "name"
if table[-1:].isupper():
name = "NAME"
try:
for nr in range(0,len(langs)):
res = run_sql("SELECT value FROM %s%s WHERE id_%s=%%s AND type=%%s AND ln=%%s" % (table, name, table),
(ID, sel_type, langs[nr][0]))
if res:
if trans[nr]:
res = run_sql("UPDATE %s%s SET value=%%s WHERE id_%s=%%s AND type=%%s AND ln=%%s" % (table, name, table),
(trans[nr], ID, sel_type, langs[nr][0]))
else:
res = run_sql("DELETE FROM %s%s WHERE id_%s=%%s AND type=%%s AND ln=%%s" % (table, name, table),
(ID, sel_type, langs[nr][0]))
else:
if trans[nr]:
res = run_sql("INSERT INTO %s%s (id_%s, type, ln, value) VALUES (%%s,%%s,%%s,%%s)" % (table, name, table),
(ID, sel_type, langs[nr][0], trans[nr]))
return (1, "")
except StandardError, e:
return (0, e)
def write_outcome(res):
try:
if res and res[0] == 1:
return """<b><span class="info">Operation successfully completed.</span></b>"""
elif res:
return """<b><span class="info">Operation failed. Reason:</span></b><br />%s""" % res[1][1]
except Exception, e:
return """<b><span class="info">Operation failed. Reason unknown</span></b><br />"""
diff --git a/modules/bibrank/web/admin/bibrankadmin.py b/modules/bibrank/web/admin/bibrankadmin.py
index d8d4c41d4..fc99f5666 100644
--- a/modules/bibrank/web/admin/bibrankadmin.py
+++ b/modules/bibrank/web/admin/bibrankadmin.py
@@ -1,199 +1,199 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio BibRank Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import sys
import invenio.bibrankadminlib as brc
#reload(brc)
from invenio.webpage import page, create_error_box
-from invenio.config import weburl, CFG_SITE_LANG, CFG_SITE_NAME
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG, CFG_SITE_NAME
from invenio.dbquery import Error
from invenio.webuser import getUid, page_not_authorized
def index(req, ln=CFG_SITE_LANG):
- navtrail_previous_links = brc.getnavtrail() # + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py">BibRank Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = brc.getnavtrail() # + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py">BibRank Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = brc.check_user(req,'cfgbibrank')
if not auth[0]:
return page(title="BibRank Admin Interface",
body=brc.perform_index(ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addrankarea(req, ln=CFG_SITE_LANG, rnkcode='', template='', confirm=-1):
- navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = brc.check_user(req,'cfgbibrank')
if not auth[0]:
return page(title="Add new rank method",
body=brc.perform_addrankarea(rnkcode=rnkcode,
ln=ln,
template=template,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifytranslations(req, rnkID='', ln=CFG_SITE_LANG, sel_type='', trans = [], confirm=0):
- navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = brc.check_user(req,'cfgbibrank')
if not auth[0]:
return page(title="Modify translations",
body=brc.perform_modifytranslations(rnkID=rnkID,
ln=ln,
sel_type=sel_type,
trans=trans,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifycollection(req, ln=CFG_SITE_LANG, rnkID='', func='', colID='', confirm=0):
- navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = brc.check_user(req,'cfgbibrank')
if not auth[0]:
return page(title="Modify visibility toward collections",
body=brc.perform_modifycollection(rnkID=rnkID,
ln=ln,
func=func,
colID=colID,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def deleterank(req, ln=CFG_SITE_LANG, rnkID='', confirm=0):
- navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = brc.check_user(req,'cfgbibrank')
if not auth[0]:
return page(title="Delete rank method",
body=brc.perform_deleterank(rnkID=rnkID,
ln=ln,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyrank(req, ln=CFG_SITE_LANG, rnkID='', rnkcode='', template='', cfgfile='', confirm=0):
- navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = brc.check_user(req,'cfgbibrank')
if not auth[0]:
return page(title="Modify rank method",
body=brc.perform_modifyrank(rnkID=rnkID,
ln=ln,
rnkcode=rnkcode,
cfgfile=cfgfile,
template=template,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showrankdetails(req, ln=CFG_SITE_LANG, rnkID=''):
- navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (weburl)
+ navtrail_previous_links = brc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/bibrank/bibrankadmin.py/">BibRank Admin Interface</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = brc.check_user(req,'cfgbibrank')
if not auth[0]:
return page(title="Rank method details",
body=brc.perform_showrankdetails(rnkID=rnkID,
ln=ln),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def error_page(req, ln=CFG_SITE_LANG, verbose=1):
return page(title="Internal Error",
body = create_error_box(req, verbose=verbose, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req)
diff --git a/modules/bibsched/doc/admin/bibsched-admin-guide.webdoc b/modules/bibsched/doc/admin/bibsched-admin-guide.webdoc
index f02560438..c88aba0b3 100644
--- a/modules/bibsched/doc/admin/bibsched-admin-guide.webdoc
+++ b/modules/bibsched/doc/admin/bibsched-admin-guide.webdoc
@@ -1,66 +1,66 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibSched Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p><table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
WARNING: THIS ADMIN GUIDE IS NOT FULLY COMPLETED
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
This Admin Guide is not yet completed. If you are interested
in seeing some specific things implemented with high priority,
please contact us at <CFG_SITE_SUPPORT_EMAIL>. Thanks for your interest!
</td>
</tr>
</tbody>
</table>
<h2>Overview</h2>
<p>BibSched -- the bibliographic task scheduler -- is central unit of the
system that allows all other modules to access the bibliographic
database in a controlled manner, preventing sharing violation threats
and assuring the coherent execution of the database update tasks. The
module comes with an administrative interface that allows to monitor
the task queue including various possibilities of a manual
intervention, for example to re-schedule queued tasks, change the task
order, etc.</p>
<p>You can run the administrative interface by doing:</p>
<blockquote>
<pre>
$ bibsched
</pre>
</blockquote>
<p>The <code>bibsched</code> can run in two modes: auto and manual. In
the auto mode, it will execute tasks automatically as they arrive in
the waiting queue. In the manual mode, the administrator has to
launch the tasks manually.</p>
diff --git a/modules/bibupload/doc/admin/bibupload-admin-guide.webdoc b/modules/bibupload/doc/admin/bibupload-admin-guide.webdoc
index 428fbf21b..5e8320537 100644
--- a/modules/bibupload/doc/admin/bibupload-admin-guide.webdoc
+++ b/modules/bibupload/doc/admin/bibupload-admin-guide.webdoc
@@ -1,308 +1,308 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: BibUpload Admin Guide -->
-<!-- WebDoc-Page-Navtrail:<a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail:<a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
<strong>2. <a href="#2">Configuring BibUpload</a></strong><br />
<strong>3. <a href="#3">Running BibUpload</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1. <a href="#3.1">Inserting new records</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2. <a href="#3.2">Updating existing records</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3. <a href="#3.3">Inserting and updating at the same time</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4. <a href="#3.4">Updating preformatted output formats</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.5. <a href="#3.5">Uploading fulltext files</a><br />
<a name="1"></a><h2>1. Overview</h2>
<p>BibUpload enables you to upload bibliographic data in MARCXML
format into CDS Invenio bibliographic database. It is also used
internally by other CDS Invenio modules as the sole entrance of
metadata into the bibliographic databases.</p>
<p>Note that before uploading a MARCXML file, you may want to run
provided <code>/opt/cds-invenio/bin/xmlmarclint</code> on it in order
to verify its correctness.</p>
<a name="2"></a><h2>2. Configuring BibUpload</h2>
<p>BibUpload takes a MARCXML file as its input. There is nothing to
be configured for these files. If the files have to be coverted into
MARCXML from some other format, structured or not, this is usually
done beforehand via <a href="bibconvert-admin">BibConvert</a> module.
</p>
<p>Note that if you are using external system numbers for your
records, such as when your records are being synchronized from an
external system, then BibUpload knows about the tag 970 as the one
containing external system number. (To change this 970 tag into
something else, you would have to edit BibUpload config source file.)
</p>
<p>Note also that in the similar way BibUpload knows about OAI
identifiers, so that it will refuse to insert the same OAI harvested
record twice, for example.
</p>
<a name="3"></a><h2>3. Running BibUpload</h2>
<a name="3.1"></a><h3>3.1 Inserting new records</h3>
<p>Consider that you have an MARCXML file containing new records that
is to be uploaded into the CDS Invenio. (For example, it might have
been produced by <a href="bibconvert-admin">BibConvert</a>.) To finish
the upload, you would call the BibUpload script in the insert mode as
follows:
<blockquote>
<pre>
$ bibupload -i file.xml
<pre>
</blockquote>
In the insert mode, all the records from the file will be treated as
new. This means that they should not contain neither 001 tags
(holding record IDs) nor 970 tags (holding external system numbers).
BibUpload would refuse to upload records having these tags, in order
to prevent potential double uploading. If your file does contain 001
or 970, then chances are that you want to update existing records, not
re-upload them as new, and so BibUpload will warn you about this and
will refuse to continue.
</p>
<p>For example, to insert a new record, your file should look like this:
<pre>
&lt;record&gt;
&lt;datafield tag="100" ind1=" " ind2=" "&gt;
&lt;subfield code="a"&gt;Doe, John&lt;/subfield&gt;
&lt;/datafield&gt;
&lt;datafield tag="245" ind1=" " ind2=" "&gt;
&lt;subfield code="a"&gt;On The Foo And Bar&lt;/subfield&gt;
&lt;/datafield&gt;
&lt;/record&gt;
</pre>
</p>
<a name="3.2"></a><h3>3.2 Updating existing records</h3>
<p>When you want to update existing records, with the new content from
your input MARCXML file, then your input file should contain either
tags 001 (holding record IDs) or tag 970 (holding external system
numbers). BibUpload will try to match existing records via 001 and
970 and if it finds a record in the database that corresponds to a
record from the file, it will update its content. Otherwise it will
signal an error saying that it could not find the
record-to-be-updated.
</p>
<p>For example, to update a title of record #123 via correct mode, your
input file should contain record ID in the 001 tag and the title in
245 tag as follows:
<pre>
&lt;record&gt;
&lt;controlfield tag="001"&gt;123&lt;/controlfield&gt;
&lt;datafield tag="245" ind1=" " ind2=" "&gt;
&lt;subfield code="a"&gt;My Newly Updated Title&lt;/subfield&gt;
&lt;/datafield&gt;
&lt;/record&gt;
</pre>
</p>
<p>There are several updating modes:
<pre>
-r, --replacerecord Replace existing records by those from the XML
MARC file. The original content is wiped out
and fully replaced. Signals error if record
is not found via matching record IDs or system
numbers.
Note also that `-r' can be combined with `-i'
into an `-ir' option that would automatically
either insert records as new if they are not
found in the system, or correct existing
records if they are found to exist.
-a, --appendfield Append fields from XML MARC file at the end of
existing records. The original content is
enriched only. Signals error if record is not
found via matching record IDs or system
numbers.
-c, --correctfield Correct fields of existing records by those
from XML MARC file. The original record
content is modified only in the fields from
the XML MARC file: the original fields are
removed and replaced by those from the XML
MARC file. Fields not present in XML MARC
file are not changed (unlike the -r option).
Signals error if record is not found via
matching record IDs or system numbers.
</pre>
</p>
<a name="3.3"></a><h3>3.3 Inserting and updating at the same time</h3>
<p>Note that the insert/update modes can be combined together. For
example, if you have a file that contains a mixture of new records
with possibly some records to be updated, then you can run:
<blockquote>
<pre>
$ bibupload -i -r file.xml
<pre>
</blockquote>
In this case BibUpload will try to do an update (for records having
either 001 or 970 identifiers), or an insert (for the other ones).
</p>
<a name="3.4"></a><h3>3.4 Updating preformatted output formats</h3>
<p>BibFormat can use this special upload mode during which metadata will
not be updated, only the preformatted output formats for records:
<pre>
-f, --format Upload only the format (FMT) fields.
The original content is not changed, and neither its modification date.
</pre>
This is useful for <code>bibreformat</code> daemon only; human
administrators don't need to explicitly know about this mode.
</p>
<a name="3.5"></a><h3>3.5 Uploading fulltext files</h3>
<p>The fulltext files can be uploaded and revised via a special FFT
("fulltext file transfer") tag with the following
semantic:
<pre>
FFT $a ... location of the docfile to upload (a filesystem path or a URL)
$n ... docfile name (optional; if not set, deduced from $a)
$m ... new desired docfile name (optional; used for renaming files)
$t ... docfile type (e.g. Main, Additional)
$d ... docfile description (optional)
$f ... format (optional; if not set, deduced from $a)
$z ... comment (optional)
$r ... restriction (optional, see below)
$v ... version (used only with REVERT, see below)
$x ... url/path for an icon (optional)
</pre>
</p>
<p>For example, to upload a new fulltext file <code>thesis.pdf</code>
associated to record ID 123:
<pre>
&lt;record&gt;
&lt;controlfield tag="001"&gt;123&lt;/controlfield&gt;
&lt;datafield tag="FFT" ind1=" " ind2=" "&gt;
&lt;subfield code="a"&gt;/tmp/thesis.pdf&lt;/subfield&gt;
&lt;subfield code="t"&gt;Main&lt;/subfield&gt;
&lt;subfield code="d"&gt;
This is the fulltext version of my thesis in the PDF format.
Chapter 5 still needs some revision.
&lt;/subfield&gt;
&lt;/datafield&gt;
&lt;/record&gt;
</pre>
</p>
<p>The FFT tag can be repetitive, so one can pass along another FFT
tag instance containing a pointer to e.g. the thesis defence slides.
The subfields of an FFT tag are non-repetitive.
</p>
<p>The bibupload process, when it encounters FFT tags, will
automatically populate fulltext storage space
(<code>/opt/cds-invenio/var/data/files</code>) and metadata record
associated tables (<code>bibrec_bibdoc</code>, <code>bibdoc</code>) as
appropriate. It will also enrich the 856 tags (URL tags) of the MARC
metadata of the record in question with references to the latest
versions of each file.
</p>
<p>The bibupload process supports the usual modes correct, append,
replace, insert with a semantic that is somewhat similar to the
semantic of the metadata upload:
<blockquote>
<table border="1">
<thead>
<tr>
<th></th><th>Metadata</th> <th>Fulltext</th>
</tr>
</thead>
<tbody>
<tr>
<td>objects being uploaded</td><td> MARC field instances characterized by tags (010-999) </td> <td> fulltext files characterized by unique file names (FFT $n)</td>
</tr>
<tr>
<td> insert </td><td> insert new record; must not exist </td><td> insert new files; must not exist </td>
</tr>
<tr>
<td> append </td><td> append new tag instances for the given tag XXX, regardless of existing tag instances</td><td> append new files, if filename (i.e. new format) not already present </td>
</tr>
<tr>
<td> correct </td><td> correct tag instances for the given tag XXX; delete existing ones and replace with given ones </td><td> correct files with the given filename; add new revision or delete file</td>
</tr>
<tr>
<td> replace </td><td> replace all tags, whatever XXX are</td><td> replace all files, whatever filenames are </td>
</tr>
</tbody>
</table>
</blockquote>
</p>
<p>Note, in append and insert mode, <pre>$m</pre> is ignored.
<p>In order to purge previous file revisions (i.e. in order to keep
only the latest file version), please use the correct mode with $n
docname and $t PURGE as the special keyword.
</p>
<p>In order to delete all existing versions of a file, making it
effectively hidden, please use the correct mode with $n docname
and $t DELETE as the special keyword.
</p>
<p>In order to expunge (i.e. remove completely, also from the filesystem) all existing versions of a file, making it
effectively disappear, please use the correct mode with $n docname
and $t EXPUNGE as the special keyword.
</p>
<p>In order to revert to a previous file revision (i.e. to create a new revision with
the same content as some previous revision had), please use the correct mode with
$n docname, $t REVERT as the special keyword and $v the number corresponding
to the desired version.</p>
<p>In order to preserve previous comments and descriptions when correcting, please use the KEEP-OLD-VALUE special keyword with the desired $d and $z subfield.
</p>
<p>In order to add an icon representing a document, you must use a $x subfields. All the FFT for different format of the document must have then the same $x subfield. You can use KEEP-OLD-VALUE in order to keep the previous icon when correcting.
</p>
<p>The $r subfield can contain a keyword that can be use to restrict the given document. The same keyword must be specified for all the format of a given document. The keyword will be used as the status parameter for the "viewrestrdoc" action, which can be used to give access right/restriction to desired user. e.g. if you set the keyword "thesis", you can the connect the "thesisviewer" to the action "viewrestrdoc" with parameter "status" set to "thesis". Then all the user which are linked with the "thesisviewer" role will be able to download the document. Instead any other user will not be allowed. Note, if you use the keyword "KEEP-OLD-VALUE" the previous restrictions if applicable will be kept.
</p>
diff --git a/modules/bibupload/lib/bibupload.py b/modules/bibupload/lib/bibupload.py
index 0ac5d3700..c304ea2fe 100644
--- a/modules/bibupload/lib/bibupload.py
+++ b/modules/bibupload/lib/bibupload.py
@@ -1,1597 +1,1597 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
BibUpload: Receive MARC XML file and update the appropriate database
tables according to options.
Usage: bibupload [options] input.xml
Examples:
$ bibupload -i input.xml
Options:
-a, --append new fields are appended to the existing record
-c, --correct fields are replaced by the new ones in the
existing record
-f, --format takes only the FMT fields into account.
Does not update
-i, --insert insert the new record in the database
-r, --replace the existing record is entirely replaced
by the new one
-z, --reference update references (update only 999 fields)
-s, --stage=STAGE stage to start from in the algorithm
(0: always done; 1: FMT tags;
2: FFT tags; 3: BibFmt; 4: Metadata update; 5: time update)
-n, --notimechange do not change record last modification date
when updating
Scheduling options:
-u, --user=USER user name to store task, password needed
General options:
-h, --help print this help and exit
-v, --verbose=LEVEL verbose level (from 0 to 9, default 1)
-V --version print the script version
"""
__revision__ = "$Id$"
import os
import sys
import time
from zlib import compress
import re
import urllib2
import tempfile
-from invenio.config import CFG_OAI_ID_FIELD, weburl
+from invenio.config import CFG_OAI_ID_FIELD, CFG_SITE_URL
from invenio.bibupload_config import *
from invenio.dbquery import run_sql, \
Error
from invenio.bibrecord import create_records, \
create_record, \
record_add_field, \
record_delete_field, \
record_xml_output, \
record_get_field_instances, \
record_get_field_values, \
field_get_subfield_values
from invenio.dateutils import convert_datestruct_to_datetext
from invenio.bibformat import format_record
from invenio.config import CFG_WEBSUBMIT_FILEDIR, \
CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT, \
CFG_TMPDIR, \
CFG_PREFIX
from invenio.bibtask import task_init, write_message, get_datetime, \
task_set_option, task_get_option, task_get_task_param, task_update_status, \
task_update_progress
from invenio.bibdocfile import BibRecDocs, file_strip_ext, normalize_format
#Statistic variables
stat = {}
stat['nb_records_to_upload'] = 0
stat['nb_records_updated'] = 0
stat['nb_records_inserted'] = 0
stat['nb_errors'] = 0
stat['exectime'] = time.localtime()
### bibupload engine functions:
def bibupload(record, opt_tag=None, opt_mode=None,
opt_stage_to_start_from=1, opt_notimechange=0):
"""Main function: process a record and fit it in the tables
bibfmt, bibrec, bibrec_bibxxx, bibxxx with proper record
metadata.
Return (error_code, recID) of the processed record.
"""
assert(opt_mode in ('insert', 'replace', 'replace_or_insert', 'reference',
'correct', 'append', 'format'))
error = None
# If there are special tags to proceed check if it exists in the record
if opt_tag is not None and not(record.has_key(opt_tag)):
write_message(" Failed: Tag not found, enter a valid tag to update.",
verbose=1, stream=sys.stderr)
return (1, -1)
# Extraction of the Record Id from 001, SYSNO or OAIID tags:
rec_id = retrieve_rec_id(record, opt_mode)
if rec_id == -1:
return (1, -1)
elif rec_id > 0:
write_message(" -Retrieve record ID (found %s): DONE." % rec_id, verbose=2)
if not record.has_key('001'):
# Found record ID by means of SYSNO or OAIID, and the
# input MARCXML buffer does not have this 001 tag, so we
# should add it now:
error = record_add_field(record, '001', '', '', rec_id, [], 0)
if error is None:
write_message(" Failed: " \
"Error during adding the 001 controlfield " \
"to the record", verbose=1, stream=sys.stderr)
return (1, int(rec_id))
else:
error = None
write_message(" -Added tag 001: DONE.", verbose=2)
write_message(" -Check if the xml marc file is already in the database: DONE" , verbose=2)
# Reference mode check if there are reference tag
if opt_mode == 'reference':
error = extract_tag_from_record(record, CFG_BIBUPLOAD_REFERENCE_TAG)
if error is None:
write_message(" Failed: No reference tags has been found...",
verbose=1, stream=sys.stderr)
return (1, -1)
else:
error = None
write_message(" -Check if reference tags exist: DONE", verbose=2)
if opt_mode == 'insert' or \
(opt_mode == 'replace_or_insert' and rec_id is None):
insert_mode_p = True
# Insert the record into the bibrec databases to have a recordId
rec_id = create_new_record()
write_message(" -Creation of a new record id (%d): DONE" % rec_id, verbose=2)
# we add the record Id control field to the record
error = record_add_field(record, '001', '', '', rec_id, [], 0)
if error is None:
write_message(" Failed: " \
"Error during adding the 001 controlfield " \
"to the record", verbose=1, stream=sys.stderr)
return (1, int(rec_id))
else:
error = None
elif opt_mode != 'insert' and opt_mode != 'format' and \
opt_stage_to_start_from != 5:
insert_mode_p = False
# Update Mode
# Retrieve the old record to update
rec_old = create_record(format_record(int(rec_id), 'xm'), 2)[0]
if rec_old is None:
write_message(" Failed during the creation of the old record!",
verbose=1, stream=sys.stderr)
return (1, int(rec_id))
else:
write_message(" -Retrieve the old record to update: DONE", verbose=2)
# In Replace mode, take over old strong tags if applicable:
if opt_mode == 'replace' or \
opt_mode == 'replace_or_insert':
copy_strong_tags_from_old_record(record, rec_old)
# Delete tags to correct in the record
if opt_mode == 'correct' or opt_mode == 'reference':
delete_tags_to_correct(record, rec_old, opt_tag)
write_message(" -Delete the old tags to correct in the old record: DONE",
verbose=2)
# Append new tag to the old record and update the new record with the old_record modified
if opt_mode == 'append' or opt_mode == 'correct' or \
opt_mode == 'reference':
record = append_new_tag_to_old_record(record, rec_old,
opt_tag, opt_mode)
write_message(" -Append new tags to the old record: DONE", verbose=2)
# now we clear all the rows from bibrec_bibxxx from the old
# record (they will be populated later (if needed) during
# stage 4 below):
delete_bibrec_bibxxx(rec_old, rec_id)
write_message(" -Clean bibrec_bibxxx: DONE", verbose=2)
write_message(" -Stage COMPLETED", verbose=2)
# Have a look if we have FMT tags
write_message("Stage 1: Start (Insert of FMT tags if exist).", verbose=2)
if opt_stage_to_start_from <= 1 and \
extract_tag_from_record(record, 'FMT') is not None:
record = insert_fmt_tags(record, rec_id, opt_mode)
if record is None:
write_message(" Stage 1 failed: Error while inserting FMT tags",
verbose=1, stream=sys.stderr)
return (1, int(rec_id))
elif record == 0:
# Mode format finished
stat['nb_records_updated'] += 1
return (0, int(rec_id))
write_message(" -Stage COMPLETED", verbose=2)
else:
write_message(" -Stage NOT NEEDED", verbose=2)
# Have a look if we have FFT tags
write_message("Stage 2: Start (Process FFT tags if exist).", verbose=2)
if opt_stage_to_start_from <= 2 and \
extract_tag_from_record(record, 'FFT') is not None:
record = elaborate_fft_tags(record, rec_id, opt_mode)
write_message(" -Stage COMPLETED", verbose=2)
else:
write_message(" -Stage NOT NEEDED", verbose=2)
# Update of the BibFmt
write_message("Stage 3: Start (Update bibfmt).", verbose=2)
if opt_stage_to_start_from <= 3:
# format the single record as xml
rec_xml_new = record_xml_output(record)
# Update bibfmt with the format xm of this record
if opt_mode != 'format':
error = update_bibfmt_format(rec_id, rec_xml_new, 'xm')
if error == 1:
write_message(" Failed: error during update_bibfmt_format",
verbose=1, stream=sys.stderr)
return (1, int(rec_id))
# archive MARCXML format of this record for version history purposes:
if opt_mode != 'format':
error = archive_marcxml_for_history(rec_id)
if error == 1:
write_message(" Failed to archive MARCXML for history",
verbose=1, stream=sys.stderr)
return (1, int(rec_id))
else:
write_message(" -Archived MARCXML for history : DONE", verbose=2)
write_message(" -Stage COMPLETED", verbose=2)
# Update the database MetaData
write_message("Stage 4: Start (Update the database with the metadata).",
verbose=2)
if opt_stage_to_start_from <= 4:
if opt_mode == 'insert' or \
opt_mode == 'replace' or \
opt_mode == 'replace_or_insert' or \
opt_mode == 'append' or \
opt_mode == 'correct' or \
opt_mode == 'reference':
update_database_with_metadata(record, rec_id)
else:
write_message(" -Stage NOT NEEDED in mode %s" % opt_mode,
verbose=2)
write_message(" -Stage COMPLETED", verbose=2)
else:
write_message(" -Stage NOT NEEDED", verbose=2)
# Finally we update the bibrec table with the current date
write_message("Stage 5: Start (Update bibrec table with current date).",
verbose=2)
if opt_stage_to_start_from <= 5 and \
opt_notimechange == 0 and \
not insert_mode_p:
now = convert_datestruct_to_datetext(time.localtime())
write_message(" -Retrieved current localtime: DONE", verbose=2)
update_bibrec_modif_date(now, rec_id)
write_message(" -Stage COMPLETED", verbose=2)
else:
write_message(" -Stage NOT NEEDED", verbose=2)
# Increase statistics
if insert_mode_p:
stat['nb_records_inserted'] += 1
else:
stat['nb_records_updated'] += 1
# Upload of this record finish
write_message("Record "+str(rec_id)+" DONE", verbose=1)
return (0, int(rec_id))
def print_out_bibupload_statistics():
"""Print the statistics of the process"""
out = "Task stats: %(nb_input)d input records, %(nb_updated)d updated, " \
"%(nb_inserted)d inserted, %(nb_errors)d errors. " \
"Time %(nb_sec).2f sec." % { \
'nb_input': stat['nb_records_to_upload'],
'nb_updated': stat['nb_records_updated'],
'nb_inserted': stat['nb_records_inserted'],
'nb_errors': stat['nb_errors'],
'nb_sec': time.time() - time.mktime(stat['exectime']) }
write_message(out)
def open_marc_file(path):
"""Open a file and return the data"""
try:
# open the file containing the marc document
marc_file = open(path,'r')
marc = marc_file.read()
marc_file.close()
except IOError, erro:
write_message("Error: %s" % erro, verbose=1, stream=sys.stderr)
write_message("Exiting.", sys.stderr)
task_update_status("ERROR")
sys.exit(1)
return marc
def xml_marc_to_records(xml_marc):
"""create the records"""
# Creation of the records from the xml Marc in argument
recs = create_records(xml_marc, 1, 1)
if recs == []:
write_message("Error: Cannot parse MARCXML file.", verbose=1, stream=sys.stderr)
write_message("Exiting.", sys.stderr)
task_update_status("ERROR")
sys.exit(1)
elif recs[0][0] is None:
write_message("Error: MARCXML file has wrong format: %s" % recs,
verbose=1, stream=sys.stderr)
write_message("Exiting.", sys.stderr)
task_update_status("ERROR")
sys.exit(1)
else:
recs = map((lambda x:x[0]), recs)
return recs
def find_record_format(rec_id, format):
"""Look whether record REC_ID is formatted in FORMAT,
i.e. whether FORMAT exists in the bibfmt table for this record.
Return the number of times it is formatted: 0 if not, 1 if yes,
2 if found more than once (should never occur).
"""
out = 0
query = """SELECT COUNT(id) FROM bibfmt WHERE id_bibrec=%s AND format=%s"""
params = (rec_id, format)
res = []
try:
res = run_sql(query, params)
out = res[0][0]
except Error, error:
write_message(" Error during find_record_format() : %s " % error, verbose=1, stream=sys.stderr)
return out
def find_record_from_recid(rec_id):
"""
Try to find record in the database from the REC_ID number.
Return record ID if found, None otherwise.
"""
try:
res = run_sql("SELECT id FROM bibrec WHERE id=%s",
(rec_id,))
except Error, error:
write_message(" Error during find_record_bibrec() : %s "
% error, verbose=1, stream=sys.stderr)
if res:
return res[0][0]
else:
return None
def find_record_from_sysno(sysno):
"""
Try to find record in the database from the external SYSNO number.
Return record ID if found, None otherwise.
"""
bibxxx = 'bib'+CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:2]+'x'
bibrec_bibxxx = 'bibrec_' + bibxxx
try:
res = run_sql("""SELECT bb.id_bibrec FROM %(bibrec_bibxxx)s AS bb,
%(bibxxx)s AS b WHERE b.tag=%%s AND b.value=%%s
AND bb.id_bibxxx=b.id""" % \
{'bibxxx': bibxxx,
'bibrec_bibxxx': bibrec_bibxxx},
(CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG, sysno,))
except Error, error:
write_message(" Error during find_record_from_sysno(): %s " % error,
verbose=1, stream=sys.stderr)
if res:
return res[0][0]
else:
return None
def find_record_from_extoaiid(extoaiid):
"""
Try to find record in the database from the external EXTOAIID number.
Return record ID if found, None otherwise.
"""
bibxxx = 'bib'+CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:2]+'x'
bibrec_bibxxx = 'bibrec_' + bibxxx
try:
res = run_sql("""SELECT bb.id_bibrec FROM %(bibrec_bibxxx)s AS bb,
%(bibxxx)s AS b WHERE b.tag=%%s AND b.value=%%s
AND bb.id_bibxxx=b.id""" % \
{'bibxxx': bibxxx,
'bibrec_bibxxx': bibrec_bibxxx},
(CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG, extoaiid,))
except Error, error:
write_message(" Error during find_record_from_extoaiid(): %s "
% error, verbose=1, stream=sys.stderr)
if res:
return res[0][0]
else:
return None
def find_record_from_oaiid(oaiid):
"""
Try to find record in the database from the OAI ID number.
Return record ID if found, None otherwise.
"""
bibxxx = 'bib'+CFG_OAI_ID_FIELD[0:2]+'x'
bibrec_bibxxx = 'bibrec_' + bibxxx
try:
res = run_sql("""SELECT bb.id_bibrec FROM %(bibrec_bibxxx)s AS bb,
%(bibxxx)s AS b WHERE b.tag=%%s AND b.value=%%s
AND bb.id_bibxxx=b.id""" % \
{'bibxxx': bibxxx,
'bibrec_bibxxx': bibrec_bibxxx},
(CFG_OAI_ID_FIELD, oaiid,))
except Error, error:
write_message(" Error during find_record_from_oaiid(): %s " % error,
verbose=1, stream=sys.stderr)
if res:
return res[0][0]
else:
return None
def extract_tag_from_record(record, tag_number):
""" Extract the tag_number for record."""
# first step verify if the record is not already in the database
if record:
return record.get(tag_number, None)
return None
def retrieve_rec_id(record, opt_mode):
"""Retrieve the record Id from a record by using tag 001 or SYSNO or OAI ID
tag. opt_mod is the desired mode."""
rec_id = None
# 1st step: we look for the tag 001
tag_001 = extract_tag_from_record(record, '001')
if tag_001 is not None:
# We extract the record ID from the tag
rec_id = tag_001[0][3]
# if we are in insert mode => error
if opt_mode == 'insert':
write_message(" Failed : Error tag 001 found in the xml" \
" submitted, you should use the option replace," \
" correct or append to replace an existing" \
" record. (-h for help)",
verbose=1, stream=sys.stderr)
return -1
else:
# we found the rec id and we are not in insert mode => continue
# we try to match rec_id against the database:
if find_record_from_recid(rec_id) is not None:
# okay, 001 corresponds to some known record
return rec_id
else:
# The record doesn't exist yet. We shall have try to check
# the SYSNO or OAI id later.
write_message(" -Tag 001 value not found in database.",
verbose=9)
rec_id = None
else:
write_message(" -Tag 001 not found in the xml marc file.", verbose=9)
if rec_id is None:
# 2nd step we look for the SYSNO
sysnos = record_get_field_values(record,
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or "",
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or "",
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6])
if sysnos:
sysno = sysnos[0] # there should be only one external SYSNO
write_message(" -Checking if SYSNO " + sysno + \
" exists in the database", verbose=9)
# try to find the corresponding rec id from the database
rec_id = find_record_from_sysno(sysno)
if rec_id is not None:
# rec_id found
pass
else:
# The record doesn't exist yet. We will try to check
# external and internal OAI ids later.
write_message(" -Tag SYSNO value not found in database.",
verbose=9)
rec_id = None
else:
write_message(" -Tag SYSNO not found in the xml marc file.",
verbose=9)
if rec_id is None:
# 2nd step we look for the external OAIID
extoaiids = record_get_field_values(record,
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or "",
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or "",
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6])
if extoaiids:
extoaiid = extoaiids[0] # there should be only one external OAI ID
write_message(" -Checking if EXTOAIID " + extoaiid + \
" exists in the database", verbose=9)
# try to find the corresponding rec id from the database
rec_id = find_record_from_extoaiid(extoaiid)
if rec_id is not None:
# rec_id found
pass
else:
# The record doesn't exist yet. We will try to check
# OAI id later.
write_message(" -Tag EXTOAIID value not found in database.",
verbose=9)
rec_id = None
else:
write_message(" -Tag EXTOAIID not found in the xml marc file.", verbose=9)
if rec_id is None:
# 4th step we look for the OAI ID
oaiidvalues = record_get_field_values(record,
CFG_OAI_ID_FIELD[0:3],
CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or "",
CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or "",
CFG_OAI_ID_FIELD[5:6])
if oaiidvalues:
oaiid = oaiidvalues[0] # there should be only one OAI ID
write_message(" -Check if local OAI ID " + oaiid + \
" exist in the database", verbose=9)
# try to find the corresponding rec id from the database
rec_id = find_record_from_oaiid(oaiid)
if rec_id is not None:
# rec_id found
pass
else:
write_message(" -Tag OAI ID value not found in database.",
verbose=9)
rec_id = None
else:
write_message(" -Tag SYSNO not found in the xml marc file.",
verbose=9)
# Now we should have detected rec_id from SYSNO or OAIID
# tags. (None otherwise.)
if rec_id:
if opt_mode == 'insert':
write_message(" Failed : Record found in the database," \
" you should use the option replace," \
" correct or append to replace an existing" \
" record. (-h for help)",
verbose=1, stream=sys.stderr)
return -1
else:
if opt_mode != 'insert' and \
opt_mode != 'replace_or_insert':
write_message(" Failed : Record not found in the database."\
" Please insert the file before updating it."\
" (-h for help)", verbose=1, stream=sys.stderr)
return -1
return rec_id
### Insert functions
def create_new_record():
"""Create new record in the database"""
now = convert_datestruct_to_datetext(time.localtime())
query = """INSERT INTO bibrec (creation_date, modification_date)
VALUES (%s, %s)"""
params = (now, now)
try:
rec_id = run_sql(query, params)
return rec_id
except Error, error:
write_message(" Error during the creation_new_record function : %s "
% error, verbose=1, stream=sys.stderr)
return None
def insert_bibfmt(id_bibrec, marc, format):
"""Insert the format in the table bibfmt"""
# compress the marc value
pickled_marc = compress(marc)
# get the current time
now = convert_datestruct_to_datetext(time.localtime())
query = """INSERT INTO bibfmt (id_bibrec, format, last_updated, value)
VALUES (%s, %s, %s, %s)"""
try:
row_id = run_sql(query, (id_bibrec, format, now, pickled_marc))
return row_id
except Error, error:
write_message(" Error during the insert_bibfmt function : %s "
% error, verbose=1, stream=sys.stderr)
return None
def insert_record_bibxxx(tag, value):
"""Insert the record into bibxxx"""
# determine into which table one should insert the record
table_name = 'bib'+tag[0:2]+'x'
# check if the tag, value combination exists in the table
query = """SELECT id,value FROM %s """ % table_name
query += """ WHERE tag=%s AND value=%s"""
params = (tag, value)
try:
res = run_sql(query, params)
except Error, error:
write_message(" Error during the insert_record_bibxxx function : %s "
% error, verbose=1, stream=sys.stderr)
# Note: compare now the found values one by one and look for
# string binary equality (e.g. to respect lowercase/uppercase
# match), regardless of the charset etc settings. Ideally we
# could use a BINARY operator in the above SELECT statement, but
# we would have to check compatibility on various MySQLdb versions
# etc; this approach checks all matched values in Python, not in
# MySQL, which is less cool, but more conservative, so it should
# work better on most setups.
for row in res:
row_id = row[0]
row_value = row[1]
if row_value == value:
return (table_name, row_id)
# We got here only when the tag,value combination was not found,
# so it is now necessary to insert the tag,value combination into
# bibxxx table as new.
query = """INSERT INTO %s """ % table_name
query += """ (tag, value) values (%s , %s)"""
params = (tag, value)
try:
row_id = run_sql(query, params)
except Error, error:
write_message(" Error during the insert_record_bibxxx function : %s "
% error, verbose=1, stream=sys.stderr)
return (table_name, row_id)
def insert_record_bibrec_bibxxx(table_name, id_bibxxx,
field_number, id_bibrec):
"""Insert the record into bibrec_bibxxx"""
# determine into which table one should insert the record
full_table_name = 'bibrec_'+ table_name
# insert the proper row into the table
query = """INSERT INTO %s """ % full_table_name
query += """(id_bibrec,id_bibxxx, field_number) values (%s , %s, %s)"""
params = (id_bibrec, id_bibxxx, field_number)
try:
res = run_sql(query, params)
except Error, error:
write_message(" Error during the insert_record_bibrec_bibxxx"
" function 2nd query : %s " % error, verbose=1, stream=sys.stderr)
return res
def download_url(url, format):
"""Download a url (if it corresponds to a remote file) and return a local url
to it."""
protocol = urllib2.urlparse.urlsplit(url)[0]
(tmp, tmppath) = tempfile.mkstemp(suffix=format, dir=CFG_TMPDIR)
tmp = os.fdopen(tmp, 'w')
try:
if protocol in ('', 'file'):
path = urllib2.urlparse.urlsplit(url)[2]
for allowed_path in CFG_BIBUPLOAD_FFT_ALLOWED_LOCAL_PATHS:
if path.startswith(allowed_path):
tmp.write(open(path).read())
return tmppath
raise StandardError, "%s is not in one of the allowed paths." % path
else:
tmp.write(urllib2.urlopen(url).read())
return tmppath
except Exception, e:
tmp.close()
os.remove(tmppath)
raise e
def make_mark_url(recid, docname, format):
"""Return a url valid to be used for MARC."""
- return '%s/record/%s/files/%s%s' % (weburl, recid, docname, format)
+ return '%s/record/%s/files/%s%s' % (CFG_SITE_URL, recid, docname, format)
def get_docname_from_url(url):
"""Return a potential docname given a url"""
path = urllib2.urlparse.urlsplit(url)[2]
filename = os.path.split(path)[-1]
return file_strip_ext(filename)
def elaborate_fft_tags(record, rec_id, mode):
"""
Process FFT tags that should contain $a with file pathes or URLs
to get the fulltext from. This function enriches record with
proper 8564 URL tags, downloads fulltext files and stores them
into var/data structure where appropriate.
CFG_BIBUPLOAD_WGET_SLEEP_TIME defines time to sleep in seconds in
between URL downloads.
Note: if an FFT tag contains multiple $a subfields, we upload them
into different 856 URL tags in the metadata. See regression test
case test_multiple_fft_insert_via_http().
"""
# Let's define some handy sub procedure.
def _synchronize_8564(rec_id, record, descriptions, comments, changed):
"""Sinchronize the 8564 tags for record with actual files. descriptions
should be a dictionary docname:description for the new description to be
inserted."""
write_message("Synchronizing MARC of recid '%s' with:\n%s\nwith descriptions: %s\nand comments: %s\nand changed urls: %s" % (rec_id, record, descriptions, comments, changed), verbose=9, stream=sys.stderr)
tags8564s = record_get_field_instances(record, '856', '4', ' ')
filtered_tags8564s = []
old_comments = {}
old_descriptions = {}
# Let's discover all the previous internal urls, in order to rebuild them!
for field in tags8564s:
to_be_removed = False
for value in field_get_subfield_values(field, 'u'):
- if value.startswith('%s/record/%s/files/' % (weburl, rec_id)):
+ if value.startswith('%s/record/%s/files/' % (CFG_SITE_URL, rec_id)):
to_be_removed = True
description = field_get_subfield_values(field, 'y')
if description:
old_descriptions[value] = description[0]
comment = field_get_subfield_values(field, 'z')
if comment:
old_comments[value] = comment[0]
if not to_be_removed:
filtered_tags8564s.append(field)
# Let's keep in the record only external 8564
record_delete_field(record, '856', '4', ' ') # First we delete 8564
for field in filtered_tags8564s: # Then we readd external ones
record_add_field(record, '856', '4', ' ', '', field[0])
# Now we refresh with existing internal 8564
bibrecdocs = BibRecDocs(rec_id)
latest_files = bibrecdocs.list_latest_files()
for file in latest_files:
- new_url = '%s/record/%s/files/%s' % (weburl, rec_id, file.get_full_name())
+ new_url = '%s/record/%s/files/%s' % (CFG_SITE_URL, rec_id, file.get_full_name())
new_subfield = [('u', new_url)]
description = descriptions.get(new_url, '')
if description == 'KEEP-OLD-VALUE':
description = old_descriptions.get(changed.get(new_url, new_url), '')
if description:
new_subfield.append(('y', description))
comment = comments.get(new_url, '')
if comment == 'KEEP-OLD-VALUE':
comment = old_comments.get(changed.get(new_url, new_url), '')
if comment:
new_subfield.append(('z', comment))
record_add_field(record, '856', '4', ' ', '', new_subfield)
# Let'handle all the icons
for bibdoc in bibrecdocs.list_bibdocs():
icon = bibdoc.get_icon()
if icon:
icon = icon.list_all_files()
if icon:
icon = icon[0].get_full_name()
- new_subfield = [('q', '%s/record/%s/files/%s' % (weburl, rec_id, icon))]
+ new_subfield = [('q', '%s/record/%s/files/%s' % (CFG_SITE_URL, rec_id, icon))]
new_subfield.append(('x', 'icon'))
record_add_field(record, '856', '4', ' ', '', new_subfield)
return record
def _add_new_format(bibdoc, url, format, docname, doctype, newname):
"""Adds a new format for a given bibdoc. Returns True when everything's fine."""
try:
tmpurl = download_url(url, format)
try:
bibdoc.add_file_new_format(tmpurl)
except StandardError, e:
write_message("('%s', '%s', '%s', '%s', '%s') not inserted because format already exists (%s)." % (url, format, docname, doctype, newname, e), stream=sys.stderr)
os.remove(tmpurl)
return False
except Exception, e:
write_message("Error in downloading '%s' because of: %s" % (url, e))
return False
return True
def _add_new_version(bibdoc, url, format, docname, doctype, newname):
"""Adds a new version for a given bibdoc. Returns True when everything's fine."""
try:
tmpurl = download_url(url, format)
try:
bibdoc.add_file_new_version(tmpurl)
except StandardError, e:
write_message("('%s', '%s', '%s', '%s', '%s') not inserted because '%s'." % (url, format, docname, doctype, newname, e), stream=sys.stderr)
os.remove(tmpurl)
return False
except Exception, e:
write_message("Error in downloading '%s' because of: %s" % (url, e))
return False
return True
def _add_new_icon(bibdoc, url, restriction):
"""Adds a new icon to an existing bibdoc, replacing the previous one if it exists. If url is empty, just remove the current icon."""
if not url:
bibdoc.delete_icon()
else:
try:
path = urllib2.urlparse.urlsplit(url)[2]
filename = os.path.split(path)[-1]
format = filename[len(file_strip_ext(filename)):].lower()
tmpurl = download_url(url, format)
try:
icondoc = bibdoc.add_icon(tmpurl, 'icon-%s' % bibdoc.get_docname())
if restriction and restriction != 'KEEP-OLD-VALUE':
icondoc.set_status(restriction)
except StandardError, e:
write_message("('%s', '%s') icon not added because '%s'." % (url, format, e), stream=sys.stderr)
os.remove(tmpurl)
return False
except Exception, e:
write_message("Error in downloading '%s' because of: %s" % (url, e))
return False
return True
tuple_list = extract_tag_from_record(record, 'FFT')
if tuple_list: # FFT Tags analysis
write_message("FFTs: "+str(tuple_list), verbose=9)
docs = {} # docnames and their data
comments = {} # files and their comments
descriptions = {} # docnames and their descriptions
changed = {} # local urls changed because of docname->newname
for fft in record_get_field_instances(record, 'FFT', ' ', ' '):
# Let's discover the type of the document
doctype = field_get_subfield_values(fft, 't')
if doctype:
doctype = doctype[0]
else: # Default is Main
doctype = 'Main'
# Let's discover the url.
url = field_get_subfield_values(fft, 'a')
if url:
url = url[0]
else:
url = ''
# Let's discover the description
description = field_get_subfield_values(fft, 'd')
if description:
description = description[0]
else:
description = ''
# Let's discover the desired docname to be created/altered
name = field_get_subfield_values(fft, 'n')
if name:
name = file_strip_ext(name[0])
else:
if url:
name = get_docname_from_url(url)
else:
write_message("fft '%s' doesn't specifies neither a url nor a name" % str(fft), stream=sys.stderr)
break
# Let's discover the desired new docname in case we want to change it
newname = field_get_subfield_values(fft, 'm')
if newname:
newname = file_strip_ext(newname[0])
else:
newname = name
# Let's discover the desired format
format = field_get_subfield_values(fft, 'f')
if format:
format = format[0]
else:
if url:
path = urllib2.urlparse.urlsplit(url)[2]
filename = os.path.split(path)[-1]
format = filename[len(file_strip_ext(filename)):]
else:
format = ''
format = normalize_format(format)
# Let's discover the icon
icon = field_get_subfield_values(fft, 'x')
if icon:
icon = icon[0]
else:
icon = ''
# Let's discover the comment
comment = field_get_subfield_values(fft, 'z')
if comment:
comment = comment[0]
else:
comment = ''
# Let's discover the restriction
restriction = field_get_subfield_values(fft, 'r')
if restriction:
restriction = restriction[0]
else:
restriction = ''
version = field_get_subfield_values(fft, 'v')
if version:
version = version[0]
else:
version = ''
if docs.has_key(name): # new format considered
(doctype2, newname2, restriction2, icon2, version2, urls) = docs[name]
if doctype2 != doctype:
write_message("fft '%s' specifies a different doctype from previous fft with docname '%s'" % (str(fft), name), stream=sys.stderr)
break
if newname2 != newname:
write_message("fft '%s' specifies a different newname from previous fft with docname '%s'" % (str(fft), name), stream=sys.stderr)
break
if restriction2 != restriction:
write_message("fft '%s' specifies a different restriction from previous fft with docname '%s'" % (str(fft), name), stream=sys.stderr)
break
if icon2 != icon:
write_message("fft '%x' specifies a different icon than the previous fft with docname '%s'" % (str(fft), name), stream=sys.stderr)
break
if version2 != version:
write_message("fft '%x' specifies a different version than the previous fft with docname '%s'" % (str(fft), name), stream=sys.stderr)
break
for (url2, format2) in urls:
if format == format2:
write_message("fft '%s' specifies a second file '%s' with the same format '%s' from previous fft with docname '%s'" % (str(fft), url, format, name), stream=sys.stderr)
if url:
urls.append((url, format))
else:
if url:
docs[name] = (doctype, newname, restriction, icon, version, [(url, format)])
else:
docs[name] = (doctype, newname, restriction, icon, version, [])
- comments['%s/record/%s/files/%s%s' % (weburl, rec_id, newname, format)] = comment
- descriptions['%s/record/%s/files/%s%s' % (weburl, rec_id, newname, format)] = description
+ comments['%s/record/%s/files/%s%s' % (CFG_SITE_URL, rec_id, newname, format)] = comment
+ descriptions['%s/record/%s/files/%s%s' % (CFG_SITE_URL, rec_id, newname, format)] = description
if newname != name:
- changed['%s/record/%s/files/%s%s' % (weburl, rec_id, newname, format)] = '%s/record/%s/files/%s%s' % (weburl, rec_id, name, format)
+ changed['%s/record/%s/files/%s%s' % (CFG_SITE_URL, rec_id, newname, format)] = '%s/record/%s/files/%s%s' % (CFG_SITE_URL, rec_id, name, format)
write_message('Result of FFT analysis:\n\tDocs: %s\n\tComments: %s\n\tDescriptions: %s' % (docs, comments, descriptions), verbose=9, stream=sys.stderr)
# Let's remove all FFT tags
record_delete_field(record, 'FFT', ' ', ' ')
# Preprocessed data elaboration
bibrecdocs = BibRecDocs(rec_id)
if mode == 'replace': # First we erase previous bibdocs
for bibdoc in bibrecdocs.list_bibdocs():
bibdoc.delete()
bibrecdocs.build_bibdoc_list()
for docname, (doctype, newname, restriction, icon, version, urls) in docs.iteritems():
write_message("Elaborating olddocname: '%s', newdocname: '%s', doctype: '%s', restriction: '%s', icon: '%s', urls: '%s', mode: '%s'" % (docname, newname, doctype, restriction, icon, urls, mode), verbose=9, stream=sys.stderr)
if mode in ('insert', 'replace'): # new bibdocs, new docnames, new marc
if newname in bibrecdocs.get_bibdoc_names(doctype):
write_message("('%s', '%s', '%s') not inserted because docname already exists." % (doctype, newname, urls), stream=sys.stderr)
break
try:
bibdoc = bibrecdocs.add_bibdoc(doctype, newname)
bibdoc.set_status(restriction)
except Exception, e:
write_message("('%s', '%s', '%s') not inserted because: '%s'." % (doctype, newname, urls, e), stream=sys.stderr)
break
for (url, format) in urls:
_add_new_format(bibdoc, url, format, docname, doctype, newname)
if icon and not icon == 'KEEP-OLD-VALUE':
_add_new_icon(bibdoc, icon, restriction)
elif mode == 'replace_or_insert': # to be thought as correct_or_insert
for bibdoc in bibrecdocs.list_bibdocs():
if bibdoc.get_docname() == docname:
if doctype not in ('PURGE', 'DELETE', 'EXPUNGE', 'REVERT'):
if newname != docname:
try:
bibdoc.change_name(newname)
except StandardError, e:
write_message(e, stream=sys.stderr)
break
found_bibdoc = False
for bibdoc in bibrecdocs.list_bibdocs():
if bibdoc.get_docname() == docname:
found_bibdoc = True
if doctype == 'PURGE':
bibdoc.purge()
elif doctype == 'DELETE':
bibdoc.delete()
elif doctype == 'EXPUNGE':
bibdoc.expunge()
elif doctype == 'REVERT':
try:
bibdoc.revert(version)
except Exception, e:
write_message('(%s, %s) not correctly reverted: %s' % (newname, version, e))
else:
if restriction != 'KEEP-OLD-VALUE':
bibdoc.set_status(restriction)
# Since the docname already existed we have to first
# bump the version by pushing the first new file
# then pushing the other files.
if urls:
(first_url, first_format) = urls[0]
other_urls = urls[1:]
if not _add_new_version(bibdoc, first_url, first_format, docname, doctype, newname):
continue
for (url, format) in other_urls:
_add_new_format(bibdoc, url, format, docname, doctype, newname)
if icon != 'KEEP-OLD-VALUE':
_add_new_icon(bibdoc, icon, restriction)
if not found_bibdoc:
bibdoc = bibrecdocs.add_bibdoc(doctype, newname)
for (url, format) in urls:
_add_new_format(bibdoc, url, format, docname, doctype, newname)
if icon and not icon == 'KEEP-OLD-VALUE':
_add_new_icon(bibdoc, icon, restriction)
elif mode == 'correct':
for bibdoc in bibrecdocs.list_bibdocs():
if bibdoc.get_docname() == docname:
if doctype not in ('PURGE', 'DELETE', 'EXPUNGE', 'REVERT'):
if newname != docname:
try:
bibdoc.change_name(newname)
except StandardError, e:
write_message(e, stream=sys.stderr)
break
found_bibdoc = False
for bibdoc in bibrecdocs.list_bibdocs():
if bibdoc.get_docname() == newname:
found_bibdoc = True
if doctype == 'PURGE':
bibdoc.purge()
elif doctype == 'DELETE':
bibdoc.delete()
elif doctype == 'EXPUNGE':
bibdoc.expunge()
elif doctype == 'REVERT':
try:
bibdoc.revert(version)
except Exception, e:
write_message('(%s, %s) not correctly reverted: %s' % (newname, version, e))
else:
if restriction != 'KEEP-OLD-VALUE':
bibdoc.set_status(restriction)
if urls:
(first_url, first_format) = urls[0]
other_urls = urls[1:]
if not _add_new_version(bibdoc, first_url, first_format, docname, doctype, newname):
continue
for (url, format) in other_urls:
_add_new_format(bibdoc, url, format, docname, description, doctype, newname)
if icon != 'KEEP-OLD-VALUE':
_add_new_icon(bibdoc, icon, restriction)
if not found_bibdoc:
write_message("('%s', '%s', '%s') not added because '%s' docname didn't existed." % (doctype, newname, urls, docname), stream=sys.stderr)
elif mode == 'append':
found_bibdoc = False
for bibdoc in bibrecdocs.list_bibdocs():
if bibdoc.get_docname() == docname:
found_bibdoc = True
for (url, format) in urls:
_add_new_format(bibdoc, url, format, docname, doctype, newname)
if icon not in ('', 'KEEP-OLD-VALUE'):
_add_new_icon(bibdoc, icon, restriction)
if not found_bibdoc:
try:
bibdoc = bibrecdocs.add_bibdoc(doctype, docname)
bibdoc.set_status(restriction)
for (url, format) in urls:
_add_new_format(bibdoc, url, format, docname, doctype, newname)
if icon and not icon == 'KEEP-OLD-VALUE':
_add_new_icon(bibdoc, icon, restriction)
except Exception, e:
write_message("('%s', '%s', '%s') not appended because: '%s'." % (doctype, newname, urls, e), stream=sys.stderr)
write_message('Changed urls: %s' % str(changed), verbose=9, stream=sys.stderr)
return _synchronize_8564(rec_id, record, descriptions, comments, changed)
else:
return record
def insert_fmt_tags(record, rec_id, opt_mode):
"""Process and insert FMT tags"""
fmt_fields = record_get_field_instances(record, 'FMT')
if fmt_fields:
for fmt_field in fmt_fields:
# Get the f, g subfields of the FMT tag
try:
f_value = field_get_subfield_values(fmt_field, "f")[0]
except IndexError:
f_value = ""
try:
g_value = field_get_subfield_values(fmt_field, "g")[0]
except IndexError:
g_value = ""
# Update the format
res = update_bibfmt_format(rec_id, g_value, f_value)
if res == 1:
write_message(" Failed: Error during update_bibfmt", verbose=1, stream=sys.stderr)
# If we are in format mode, we only care about the FMT tag
if opt_mode == 'format':
return 0
# We delete the FMT Tag of the record
record_delete_field(record, 'FMT')
write_message(" -Delete field FMT from record : DONE", verbose=2)
return record
elif opt_mode == 'format':
write_message(" Failed: Format updated failed : No tag FMT found", verbose=1, stream=sys.stderr)
return None
else:
return record
### Update functions
def update_bibrec_modif_date(now, bibrec_id):
"""Update the date of the record in bibrec table """
query = """UPDATE bibrec SET modification_date=%s WHERE id=%s"""
params = (now, bibrec_id)
try:
run_sql(query, params)
write_message(" -Update record modification date : DONE" , verbose=2)
except Error, error:
write_message(" Error during update_bibrec_modif_date function : %s" % error,
verbose=1, stream=sys.stderr)
def update_bibfmt_format(id_bibrec, format_value, format_name):
"""Update the format in the table bibfmt"""
# We check if the format is already in bibFmt
nb_found = find_record_format(id_bibrec, format_name)
if nb_found == 1:
# we are going to update the format
# get the current time
now = convert_datestruct_to_datetext(time.localtime())
# compress the format_value value
pickled_format_value = compress(format_value)
# update the format:
query = """UPDATE bibfmt SET last_updated=%s, value=%s WHERE id_bibrec=%s AND format=%s"""
params = (now, pickled_format_value, id_bibrec, format_name)
try:
row_id = run_sql(query, params)
if row_id is None:
write_message(" Failed: Error during update_bibfmt_format function", verbose=1, stream=sys.stderr)
return 1
else:
write_message(" -Update the format %s in bibfmt : DONE" % format_name , verbose=2)
return 0
except Error, error:
write_message(" Error during the update_bibfmt_format function : %s " % error, verbose=1, stream=sys.stderr)
elif nb_found > 1:
write_message(" Failed: Same format %s found several time in bibfmt for the same record." % format_name, verbose=1, stream=sys.stderr)
return 1
else:
# Insert the format information in BibFMT
res = insert_bibfmt(id_bibrec, format_value, format_name)
if res is None:
write_message(" Failed: Error during insert_bibfmt", verbose=1, stream=sys.stderr)
return 1
else:
write_message(" -Insert the format %s in bibfmt : DONE" % format_name , verbose=2)
return 0
def archive_marcxml_for_history(recID):
"""
Archive current MARCXML format of record RECID from BIBFMT table
into hstRECORD table. Useful to keep MARCXML history of records.
Return 0 if everything went fine. Return 1 otherwise.
"""
try:
res = run_sql("SELECT id_bibrec, value, last_updated FROM bibfmt WHERE format='xm' AND id_bibrec=%s",
(recID,))
if res:
run_sql("""INSERT INTO hstRECORD (id_bibrec, marcxml, job_id, job_name, job_person, job_date, job_details)
VALUES (%s,%s,%s,%s,%s,%s,%s)""",
(res[0][0], res[0][1], task_get_option('task', 0), 'bibupload', task_get_option('user','UNKNOWN'), res[0][2],
'mode: ' + task_get_option('mode','UNKNOWN') + '; file: ' + task_get_option('file_path','UNKNOWN') + '.'))
except Error, error:
write_message(" Error during archive_marcxml_for_history: %s " % error,
verbose=1, stream=sys.stderr)
return 1
return 0
def update_database_with_metadata(record, rec_id):
"""Update the database tables with the record and the record id given in parameter"""
for tag in record.keys():
# check if tag is not a special one:
if tag not in CFG_BIBUPLOAD_SPECIAL_TAGS:
# for each tag there is a list of tuples representing datafields
tuple_list = record[tag]
# this list should contain the elements of a full tag [tag, ind1, ind2, subfield_code]
tag_list = []
tag_list.append(tag)
for single_tuple in tuple_list:
# these are the contents of a single tuple
subfield_list = single_tuple[0]
ind1 = single_tuple[1]
ind2 = single_tuple[2]
# append the ind's to the full tag
if ind1 == '' or ind1 == ' ':
tag_list.append('_')
else:
tag_list.append(ind1)
if ind2 == '' or ind2 == ' ':
tag_list.append('_')
else:
tag_list.append(ind2)
datafield_number = single_tuple[4]
if tag in CFG_BIBUPLOAD_SPECIAL_TAGS:
# nothing to do for special tags (FFT, FMT)
pass
elif tag in CFG_BIBUPLOAD_CONTROLFIELD_TAGS and tag != "001":
value = single_tuple[3]
# get the full tag
full_tag = ''.join(tag_list)
# update the tables
write_message(" insertion of the tag "+full_tag+" with the value "+value, verbose=9)
# insert the tag and value into into bibxxx
(table_name, bibxxx_row_id) = insert_record_bibxxx(full_tag, value)
#print 'tname, bibrow', table_name, bibxxx_row_id;
if table_name is None or bibxxx_row_id is None:
write_message(" Failed : during insert_record_bibxxx", verbose=1, stream=sys.stderr)
# connect bibxxx and bibrec with the table bibrec_bibxxx
res = insert_record_bibrec_bibxxx(table_name, bibxxx_row_id, datafield_number, rec_id)
if res is None:
write_message(" Failed : during insert_record_bibrec_bibxxx", verbose=1, stream=sys.stderr)
else:
# get the tag and value from the content of each subfield
for subfield in subfield_list:
subtag = subfield[0]
value = subfield[1]
tag_list.append(subtag)
# get the full tag
full_tag = ''.join(tag_list)
# update the tables
write_message(" insertion of the tag "+full_tag+" with the value "+value, verbose=9)
# insert the tag and value into into bibxxx
(table_name, bibxxx_row_id) = insert_record_bibxxx(full_tag, value)
if table_name is None or bibxxx_row_id is None:
write_message(" Failed : during insert_record_bibxxx", verbose=1, stream=sys.stderr)
# connect bibxxx and bibrec with the table bibrec_bibxxx
res = insert_record_bibrec_bibxxx(table_name, bibxxx_row_id, datafield_number, rec_id)
if res is None:
write_message(" Failed : during insert_record_bibrec_bibxxx", verbose=1, stream=sys.stderr)
# remove the subtag from the list
tag_list.pop()
tag_list.pop()
tag_list.pop()
tag_list.pop()
write_message(" -Update the database with metadata : DONE", verbose=2)
def append_new_tag_to_old_record(record, rec_old, opt_tag, opt_mode):
"""Append new tags to a old record"""
if opt_tag is not None:
tag = opt_tag
if tag in CFG_BIBUPLOAD_CONTROLFIELD_TAGS:
if tag == '001':
pass
else:
# if it is a controlfield,just access the value
for single_tuple in record[tag]:
controlfield_value = single_tuple[3]
# add the field to the old record
newfield_number = record_add_field(rec_old, tag, "", "", controlfield_value)
if newfield_number is None:
write_message(" Error when adding the field"+tag, verbose=1, stream=sys.stderr)
else:
# For each tag there is a list of tuples representing datafields
for single_tuple in record[tag]:
# We retrieve the information of the tag
subfield_list = single_tuple[0]
ind1 = single_tuple[1]
ind2 = single_tuple[2]
# We add the datafield to the old record
write_message(" Adding tag: %s ind1=%s ind2=%s code=%s" % (tag, ind1, ind2, subfield_list), verbose=9)
newfield_number = record_add_field(rec_old, tag, ind1, ind2, "", subfield_list)
if newfield_number is None:
write_message("Error when adding the field"+tag, verbose=1, stream=sys.stderr)
else:
# Go through each tag in the appended record
for tag in record.keys():
# Reference mode append only reference tag
if opt_mode == 'reference':
if tag == CFG_BIBUPLOAD_REFERENCE_TAG:
for single_tuple in record[tag]:
# We retrieve the information of the tag
subfield_list = single_tuple[0]
ind1 = single_tuple[1]
ind2 = single_tuple[2]
# We add the datafield to the old record
write_message(" Adding tag: %s ind1=%s ind2=%s code=%s" % (tag, ind1, ind2, subfield_list), verbose=9)
newfield_number = record_add_field(rec_old, tag, ind1, ind2, "", subfield_list)
if newfield_number is None:
write_message(" Error when adding the field"+tag, verbose=1, stream=sys.stderr)
else:
if tag in CFG_BIBUPLOAD_CONTROLFIELD_TAGS:
if tag == '001':
pass
else:
# if it is a controlfield,just access the value
for single_tuple in record[tag]:
controlfield_value = single_tuple[3]
# add the field to the old record
newfield_number = record_add_field(rec_old, tag, "", "", controlfield_value)
if newfield_number is None:
write_message(" Error when adding the field"+tag, verbose=1, stream=sys.stderr)
else:
# For each tag there is a list of tuples representing datafields
for single_tuple in record[tag]:
# We retrieve the information of the tag
subfield_list = single_tuple[0]
ind1 = single_tuple[1]
ind2 = single_tuple[2]
# We add the datafield to the old record
write_message(" Adding tag: %s ind1=%s ind2=%s code=%s" % (tag, ind1, ind2, subfield_list), verbose=9)
newfield_number = record_add_field(rec_old, tag, ind1, ind2, "", subfield_list)
if newfield_number is None:
write_message(" Error when adding the field"+tag, verbose=1, stream=sys.stderr)
return rec_old
def copy_strong_tags_from_old_record(record, rec_old):
"""
Look for strong tags in RECORD and REC_OLD. If no strong tags are
found in RECORD, then copy them over from REC_OLD. This function
modifies RECORD structure on the spot.
"""
for strong_tag in CFG_BIBUPLOAD_STRONG_TAGS:
if not record_get_field_instances(record, strong_tag):
strong_tag_old_field_instances = record_get_field_instances(rec_old, strong_tag)
if strong_tag_old_field_instances:
for strong_tag_old_field_instance in strong_tag_old_field_instances:
sf_vals, fi_ind1, fi_ind2, controlfield, dummy = strong_tag_old_field_instance
record_add_field(record, strong_tag, fi_ind1, fi_ind2, controlfield, sf_vals)
return
### Delete functions
def delete_tags_to_correct(record, rec_old, opt_tag):
"""
Delete tags from REC_OLD which are also existing in RECORD. When
deleting, pay attention not only to tags, but also to indicators,
so that fields with the same tags but different indicators are not
deleted.
"""
# browse through all the tags from the MARCXML file:
for tag in record.keys():
# do we have to delete only a special tag or any tag?
if opt_tag is None or opt_tag == tag:
# check if the tag exists in the old record too:
if rec_old.has_key(tag) and tag != '001':
# the tag does exist, so delete all record's tag+ind1+ind2 combinations from rec_old
for dummy_sf_vals, ind1, ind2, dummy_cf, dummy_field_number in record[tag]:
write_message(" Delete tag: " + tag + " ind1=" + ind1 + " ind2=" + ind2, verbose=9)
record_delete_field(rec_old, tag, ind1, ind2)
def delete_bibrec_bibxxx(record, id_bibrec):
"""Delete the database record from the table bibxxx given in parameters"""
# we clear all the rows from bibrec_bibxxx from the old record
for tag in record.keys():
if tag not in CFG_BIBUPLOAD_SPECIAL_TAGS:
# for each name construct the bibrec_bibxxx table name
table_name = 'bibrec_bib'+tag[0:2]+'x'
# delete all the records with proper id_bibrec
query = """DELETE FROM `%s` where id_bibrec = %s"""
params = (table_name, id_bibrec)
try:
run_sql(query % params)
except Error, error:
write_message(" Error during the delete_bibrec_bibxxx function : %s " % error, verbose=1, stream=sys.stderr)
def wipe_out_record_from_all_tables(recid):
"""
Wipe out completely the record and all its traces of RECID from
the database (bibrec, bibrec_bibxxx, bibxxx, bibfmt). Useful for
the time being for test cases.
"""
# delete all the linked bibdocs
for bibdoc in BibRecDocs(recid).list_bibdocs():
bibdoc.expunge()
# delete from bibrec:
run_sql("DELETE FROM bibrec WHERE id=%s", (recid,))
# delete from bibrec_bibxxx:
for i in range(0, 10):
for j in range(0, 10):
run_sql("DELETE FROM %(bibrec_bibxxx)s WHERE id_bibrec=%%s" % \
{'bibrec_bibxxx': "bibrec_bib%i%ix" % (i, j)},
(recid,))
# delete all unused bibxxx values:
for i in range(0, 10):
for j in range(0, 10):
run_sql("DELETE %(bibxxx)s FROM %(bibxxx)s " \
" LEFT JOIN %(bibrec_bibxxx)s " \
" ON %(bibxxx)s.id=%(bibrec_bibxxx)s.id_bibxxx " \
" WHERE %(bibrec_bibxxx)s.id_bibrec IS NULL" % \
{'bibxxx': "bib%i%ix" % (i, j),
'bibrec_bibxxx': "bibrec_bib%i%ix" % (i, j)})
# delete from bibfmt:
run_sql("DELETE FROM bibfmt WHERE id_bibrec=%s", (recid,))
# delete from bibrec_bibdoc:
run_sql("DELETE FROM bibrec_bibdoc WHERE id_bibrec=%s", (recid,))
return
def delete_bibdoc(id_bibrec):
"""Delete document from bibdoc which correspond to the bibrec id given in parameter"""
query = """UPDATE bibdoc SET status='DELETED'
WHERE id IN (SELECT id_bibdoc FROM bibrec_bibdoc
WHERE id_bibrec=%s)"""
params = (id_bibrec,)
try:
run_sql(query, params)
except Error, error:
write_message(" Error during the delete_bibdoc function : %s " % error,
verbose=1, stream=sys.stderr)
def delete_bibrec_bibdoc(id_bibrec):
"""Delete the bibrec record from the table bibrec_bibdoc given in parameter"""
# delete all the records with proper id_bibrec
query = """DELETE FROM bibrec_bibdoc WHERE id_bibrec=%s"""
params = (id_bibrec,)
try:
run_sql(query, params)
except Error, error:
write_message(" Error during the delete_bibrec_bibdoc function : %s " % error,
verbose=1, stream=sys.stderr)
def main():
"""Main that construct all the bibtask."""
task_set_option('mode', None)
task_set_option('verbose', 1)
task_set_option("tag", None)
task_set_option("file_path", None)
task_set_option("notimechange", 0)
task_set_option("stage_to_start_from", 1)
task_init(authorization_action='runbibupload',
authorization_msg="BibUpload Task Submission",
description="""Receive MARC XML file and update appropriate database
tables according to options.
Examples:
$ bibupload -i input.xml
""",
help_specific_usage=""" -a, --append\t\tnew fields are appended to the existing record
-c, --correct\t\tfields are replaced by the new ones in the existing record
-f, --format\t\ttakes only the FMT fields into account. Does not update
-i, --insert\t\tinsert the new record in the database
-r, --replace\t\tthe existing record is entirely replaced by the new one
-z, --reference\tupdate references (update only 999 fields)
-s, --stage=STAGE\tstage to start from in the algorithm (0: always done; 1: FMT tags;
\t\t\t2: FFT tags; 3: BibFmt; 4: Metadata update; 5: time update)
-n, --notimechange\tdo not change record last modification date when updating
""",
version=__revision__,
specific_params=("ircazs:fn",
[
"insert",
"replace",
"correct",
"append",
"reference",
"stage=",
"format",
"notimechange",
]),
task_submit_elaborate_specific_parameter_fnc=task_submit_elaborate_specific_parameter,
task_run_fnc=task_run_core)
def task_submit_elaborate_specific_parameter(key, value, opts, args):
""" Given the string key it checks it's meaning, eventually using the
value. Usually it fills some key in the options dict.
It must return True if it has elaborated the key, False, if it doesn't
know that key.
eg:
if key in ['-n', '--number']:
task_get_option(\1) = value
return True
return False
"""
# No time change option
if key in ("-n", "--notimechange"):
task_set_option('notimechange', 1)
# Insert mode option
elif key in ("-i", "--insert"):
if task_get_option('mode') == 'replace':
# if also replace found, then set to replace_or_insert
task_set_option('mode', 'replace_or_insert')
else:
task_set_option('mode', 'insert')
task_set_option('file_path', os.path.abspath(args[0]))
# Replace mode option
elif key in ("-r", "--replace"):
if task_get_option('mode') == 'insert':
# if also insert found, then set to replace_or_insert
task_set_option('mode', 'replace_or_insert')
else:
task_set_option('mode', 'replace')
task_set_option('file_path', os.path.abspath(args[0]))
# Correct mode option
elif key in ("-c", "--correct"):
task_set_option('mode', 'correct')
task_set_option('file_path', os.path.abspath(args[0]))
# Append mode option
elif key in ("-a", "--append"):
task_set_option('mode', 'append')
task_set_option('file_path', os.path.abspath(args[0]))
# Reference mode option
elif key in ("-z", "--reference"):
task_set_option('mode', 'reference')
task_set_option('file_path', os.path.abspath(args[0]))
# Format mode option
elif key in ("-f", "--format"):
task_set_option('mode', 'format')
task_set_option('file_path', os.path.abspath(args[0]))
else:
return False
return True
def task_submit_check_options():
""" Reimplement this method for having the possibility to check options
before submitting the task, in order for example to provide default
values. It must return False if there are errors in the options.
"""
if task_get_option('mode') is None:
write_message("Please specify at least one update/insert mode!")
return False
if task_get_option('file_path') is None:
write_message("Missing filename! -h for help.")
return False
return True
def task_run_core():
""" Reimplement to add the body of the task."""
error = 0
write_message("Input file '%s', input mode '%s'." %
(task_get_option('file_path'), task_get_option('mode')))
write_message("STAGE 0:", verbose=2)
if task_get_option('file_path') is not None:
recs = xml_marc_to_records(open_marc_file(task_get_option('file_path')))
stat['nb_records_to_upload'] = len(recs)
write_message(" -Open XML marc: DONE", verbose=2)
if recs is not None:
# We proceed each record by record
for record in recs:
error = bibupload(
record,
opt_tag=task_get_option('tag'),
opt_mode=task_get_option('mode'),
opt_stage_to_start_from=task_get_option('stage_to_start_from'),
opt_notimechange=task_get_option('notimechange'))
if error[0] == 1:
if record:
write_message(record_xml_output(record),
stream=sys.stderr)
else:
write_message("Record could not have been parsed",
stream=sys.stderr)
stat['nb_errors'] += 1
task_update_progress("Done %d out of %d." % \
(stat['nb_records_inserted'] + \
stat['nb_records_updated'],
stat['nb_records_to_upload']))
else:
write_message(" Error bibupload failed: No record found",
verbose=1, stream=sys.stderr)
if task_get_option('verbose') >= 1:
# Print out the statistics
print_out_bibupload_statistics()
# Check if they were errors
return not stat['nb_errors'] >= 1
if __name__ == "__main__":
main()
diff --git a/modules/bibupload/lib/bibupload_regression_tests.py b/modules/bibupload/lib/bibupload_regression_tests.py
index c1188956b..a99b880e3 100644
--- a/modules/bibupload/lib/bibupload_regression_tests.py
+++ b/modules/bibupload/lib/bibupload_regression_tests.py
@@ -1,3093 +1,3093 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable-msg=C0301
"""Regression tests for the BibUpload."""
__revision__ = "$Id$"
import re
import unittest
import os
import time
from urllib2 import urlopen
from md5 import md5
-from invenio.config import CFG_OAI_ID_FIELD, CFG_PREFIX, weburl, CFG_TMPDIR
+from invenio.config import CFG_OAI_ID_FIELD, CFG_PREFIX, CFG_SITE_URL, CFG_TMPDIR
from invenio import bibupload
from invenio.bibupload_config import CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG, \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG, \
CFG_BIBUPLOAD_STRONG_TAGS
from invenio.search_engine import print_record
from invenio.dbquery import run_sql
from invenio.dateutils import convert_datestruct_to_datetext
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run
from invenio.bibtask import task_set_option
from invenio.bibdocfile import BibRecDocs
# helper functions:
def remove_tag_001_from_xmbuffer(xmbuffer):
"""Remove tag 001 from MARCXML buffer. Useful for testing two
MARCXML buffers without paying attention to recIDs attributed
during the bibupload.
"""
return re.sub(r'<controlfield tag="001">.*</controlfield>', '', xmbuffer)
def compare_xmbuffers(xmbuffer1, xmbuffer2):
"""Compare two XM (XML MARC) buffers by removing whitespaces
before testing.
"""
def remove_blanks_from_xmbuffer(xmbuffer):
"""Remove \n and blanks from XMBUFFER."""
out = xmbuffer.replace("\n", "")
out = out.replace(" ", "")
return out
# remove whitespace:
xmbuffer1 = remove_blanks_from_xmbuffer(xmbuffer1)
xmbuffer2 = remove_blanks_from_xmbuffer(xmbuffer2)
if xmbuffer1 != xmbuffer2:
print "\n=" + xmbuffer1 + "=\n"
print "\n=" + xmbuffer2 + "=\n"
return (xmbuffer1 == xmbuffer2)
def remove_tag_001_from_hmbuffer(hmbuffer):
"""Remove tag 001 from HTML MARC buffer. Useful for testing two
HTML MARC buffers without paying attention to recIDs attributed
during the bibupload.
"""
return re.sub(r'(^|\n)(<pre>)?[0-9]{9}\s001__\s\d+($|\n)', '', hmbuffer)
def compare_hmbuffers(hmbuffer1, hmbuffer2):
"""Compare two HM (HTML MARC) buffers by removing whitespaces
before testing.
"""
# remove eventual <pre>...</pre> formatting:
hmbuffer1 = re.sub(r'^<pre>', '', hmbuffer1)
hmbuffer2 = re.sub(r'^<pre>', '', hmbuffer2)
hmbuffer1 = re.sub(r'</pre>$', '', hmbuffer1)
hmbuffer2 = re.sub(r'</pre>$', '', hmbuffer2)
# remove leading recid, leaving only field values:
hmbuffer1 = re.sub(r'(^|\n)[0-9]{9}\s', '', hmbuffer1)
hmbuffer2 = re.sub(r'(^|\n)[0-9]{9}\s', '', hmbuffer2)
# remove leading whitespace:
hmbuffer1 = re.sub(r'(^|\n)\s+', '', hmbuffer1)
hmbuffer2 = re.sub(r'(^|\n)\s+', '', hmbuffer2)
compare_hmbuffers = hmbuffer1 == hmbuffer2
if not compare_hmbuffers:
print "\n=" + hmbuffer1 + "=\n"
print "\n=" + hmbuffer2 + "=\n"
return compare_hmbuffers
def try_url_download(url):
"""Try to download a given URL"""
try:
open_url = urlopen(url)
open_url.read()
except Exception, e:
raise StandardError, "Downloading %s is impossible because of %s" \
% (url, str(e))
return True
class BibUploadInsertModeTest(unittest.TestCase):
"""Testing insert mode."""
def setUp(self):
# pylint: disable-msg=C0103
"""Initialise the MARCXML variable"""
self.test = """<record>
<datafield tag ="245" ind1=" " ind2=" ">
<subfield code="a">something</subfield>
</datafield>
<datafield tag ="700" ind1=" " ind2=" ">
<subfield code="a">Tester, J Y</subfield>
<subfield code="u">MIT</subfield>
</datafield>
<datafield tag ="700" ind1=" " ind2=" ">
<subfield code="a">Tester, K J</subfield>
<subfield code="u">CERN2</subfield>
</datafield>
<datafield tag ="700" ind1=" " ind2=" ">
<subfield code="a">Tester, G</subfield>
<subfield code="u">CERN3</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="a">test11</subfield>
<subfield code="c">test31</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="a">test12</subfield>
<subfield code="c">test32</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="a">test13</subfield>
<subfield code="c">test33</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="b">test21</subfield>
<subfield code="d">test41</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="b">test22</subfield>
<subfield code="d">test42</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="a">test14</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="e">test51</subfield>
</datafield>
<datafield tag ="111" ind1=" " ind2=" ">
<subfield code="e">test52</subfield>
</datafield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">CERN</subfield>
</datafield>
</record>"""
self.test_hm = """
100__ $$aTester, T$$uCERN
111__ $$atest11$$ctest31
111__ $$atest12$$ctest32
111__ $$atest13$$ctest33
111__ $$btest21$$dtest41
111__ $$btest22$$dtest42
111__ $$atest14
111__ $$etest51
111__ $$etest52
245__ $$asomething
700__ $$aTester, J Y$$uMIT
700__ $$aTester, K J$$uCERN2
700__ $$aTester, G$$uCERN3
"""
def test_create_record_id(self):
"""bibupload - insert mode, trying to create a new record ID in the database"""
rec_id = bibupload.create_new_record()
self.assertNotEqual(-1, rec_id)
def test_no_retrieve_record_id(self):
"""bibupload - insert mode, detection of record ID in the input file"""
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test)
# We call the function which should retrieve the record id
rec_id = bibupload.retrieve_rec_id(recs[0], 'insert')
# We compare the value found with None
self.assertEqual(None, rec_id)
def test_insert_complete_xmlmarc(self):
"""bibupload - insert mode, trying to insert complete MARCXML file"""
# Initialize the global variable
task_set_option('verbose', 0)
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test)
# We call the main function with the record as a parameter
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# We retrieve the inserted xml
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
# Compare if the two MARCXML are the same
self.failUnless(compare_xmbuffers(remove_tag_001_from_xmbuffer(inserted_xm),
self.test))
self.failUnless(compare_hmbuffers(remove_tag_001_from_hmbuffer(inserted_hm),
self.test_hm))
class BibUploadAppendModeTest(unittest.TestCase):
"""Testing append mode."""
def setUp(self):
# pylint: disable-msg=C0103
"""Initialize the MARCXML variable"""
self.test_existing = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">DESY</subfield>
</datafield>
</record>"""
self.test_to_append = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, U</subfield>
<subfield code="u">CERN</subfield>
</datafield>
</record>"""
self.test_expected_xm = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">DESY</subfield>
</datafield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, U</subfield>
<subfield code="u">CERN</subfield>
</datafield>
</record>"""
self.test_expected_hm = """
001__ 123456789
100__ $$aTester, T$$uDESY
100__ $$aTester, U$$uCERN
"""
# insert test record:
test_to_upload = self.test_existing.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
self.test_recid = recid
# replace test buffers with real recid of inserted test record:
self.test_existing = self.test_existing.replace('123456789',
str(self.test_recid))
self.test_to_append = self.test_to_append.replace('123456789',
str(self.test_recid))
self.test_expected_xm = self.test_expected_xm.replace('123456789',
str(self.test_recid))
self.test_expected_hm = self.test_expected_hm.replace('123456789',
str(self.test_recid))
def test_retrieve_record_id(self):
"""bibupload - append mode, the input file should contain a record ID"""
task_set_option('verbose', 0)
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test_to_append)
# We call the function which should retrieve the record id
rec_id = bibupload.retrieve_rec_id(recs[0], 'append')
# We compare the value found with None
self.assertEqual(str(self.test_recid), rec_id)
# clean up after ourselves:
bibupload.wipe_out_record_from_all_tables(self.test_recid)
return
def test_update_modification_record_date(self):
"""bibupload - append mode, checking the update of the modification date"""
# Initialize the global variable
task_set_option('verbose', 0)
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test_existing)
# We call the function which should retrieve the record id
rec_id = bibupload.retrieve_rec_id(recs[0], opt_mode='append')
# Retrieve current localtime
now = time.localtime()
# We update the modification date
bibupload.update_bibrec_modif_date(convert_datestruct_to_datetext(now), rec_id)
# We retrieve the modification date from the database
query = """SELECT DATE_FORMAT(modification_date,'%%Y-%%m-%%d %%H:%%i:%%s') FROM bibrec where id = %s"""
res = run_sql(query % rec_id)
# We compare the two results
self.assertEqual(res[0][0], convert_datestruct_to_datetext(now))
# clean up after ourselves:
bibupload.wipe_out_record_from_all_tables(self.test_recid)
return
def test_append_complete_xml_marc(self):
"""bibupload - append mode, appending complete MARCXML file"""
# Now we append a datafield
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test_to_append)
# We call the main function with the record as a parameter
err, recid = bibupload.bibupload(recs[0], opt_mode='append')
# We retrieve the inserted xm
after_append_xm = print_record(recid, 'xm')
after_append_hm = print_record(recid, 'hm')
# Compare if the two MARCXML are the same
self.failUnless(compare_xmbuffers(after_append_xm, self.test_expected_xm))
self.failUnless(compare_hmbuffers(after_append_hm, self.test_expected_hm))
# clean up after ourselves:
bibupload.wipe_out_record_from_all_tables(self.test_recid)
return
class BibUploadCorrectModeTest(unittest.TestCase):
"""
Testing correcting a record containing similar tags (identical
tag, different indicators). Currently CDS Invenio replaces only
those tags that have matching indicators too, unlike ALEPH500 that
does not pay attention to indicators, it corrects all fields with
the same tag, regardless of the indicator values.
"""
def setUp(self):
"""Initialize the MARCXML test record."""
self.testrec1_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="8">
<subfield code="a">Cool</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Jim</subfield>
<subfield code="u">Test Laboratory</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
10047 $$aTest, John$$uTest University
10048 $$aCool
10047 $$aTest, Jim$$uTest Laboratory
"""
self.testrec1_xm_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test2, Joseph</subfield>
<subfield code="u">Test2 Academy</subfield>
</datafield>
</record>
"""
self.testrec1_corrected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="8">
<subfield code="a">Cool</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test2, Joseph</subfield>
<subfield code="u">Test2 Academy</subfield>
</datafield>
</record>
"""
self.testrec1_corrected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
10048 $$aCool
10047 $$aTest, Joseph$$uTest Academy
10047 $$aTest2, Joseph$$uTest2 Academy
"""
# insert test record:
task_set_option('verbose', 0)
test_record_xm = self.testrec1_xm.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_record_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.testrec1_xm = self.testrec1_xm.replace('123456789', str(recid))
self.testrec1_hm = self.testrec1_hm.replace('123456789', str(recid))
self.testrec1_xm_to_correct = self.testrec1_xm_to_correct.replace('123456789', str(recid))
self.testrec1_corrected_xm = self.testrec1_corrected_xm.replace('123456789', str(recid))
self.testrec1_corrected_hm = self.testrec1_corrected_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm, self.testrec1_xm))
self.failUnless(compare_hmbuffers(inserted_hm, self.testrec1_hm))
def test_record_correction(self):
"""bibupload - correct mode, similar MARCXML tags/indicators"""
# correct some tags:
recs = bibupload.xml_marc_to_records(self.testrec1_xm_to_correct)
err, recid = bibupload.bibupload(recs[0], opt_mode='correct')
corrected_xm = print_record(recid, 'xm')
corrected_hm = print_record(recid, 'hm')
# did it work?
self.failUnless(compare_xmbuffers(corrected_xm, self.testrec1_corrected_xm))
self.failUnless(compare_hmbuffers(corrected_hm, self.testrec1_corrected_hm))
# clean up after ourselves:
bibupload.wipe_out_record_from_all_tables(recid)
return
class BibUploadReplaceModeTest(unittest.TestCase):
"""Testing replace mode."""
def setUp(self):
"""Initialize the MARCXML test record."""
self.testrec1_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="8">
<subfield code="a">Cool</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Jim</subfield>
<subfield code="u">Test Laboratory</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
10047 $$aTest, John$$uTest University
10048 $$aCool
10047 $$aTest, Jim$$uTest Laboratory
"""
self.testrec1_xm_to_replace = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test2, Joseph</subfield>
<subfield code="u">Test2 Academy</subfield>
</datafield>
</record>
"""
self.testrec1_replaced_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="100" ind1="4" ind2="7">
<subfield code="a">Test2, Joseph</subfield>
<subfield code="u">Test2 Academy</subfield>
</datafield>
</record>
"""
self.testrec1_replaced_hm = """
001__ 123456789
10047 $$aTest, Joseph$$uTest Academy
10047 $$aTest2, Joseph$$uTest2 Academy
"""
# insert test record:
task_set_option('verbose', 0)
test_record_xm = self.testrec1_xm.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_record_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.testrec1_xm = self.testrec1_xm.replace('123456789', str(recid))
self.testrec1_hm = self.testrec1_hm.replace('123456789', str(recid))
self.testrec1_xm_to_replace = self.testrec1_xm_to_replace.replace('123456789', str(recid))
self.testrec1_replaced_xm = self.testrec1_replaced_xm.replace('123456789', str(recid))
self.testrec1_replaced_hm = self.testrec1_replaced_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm, self.testrec1_xm))
self.failUnless(compare_hmbuffers(inserted_hm, self.testrec1_hm))
def test_record_replace(self):
"""bibupload - replace mode, similar MARCXML tags/indicators"""
# replace some tags:
recs = bibupload.xml_marc_to_records(self.testrec1_xm_to_replace)
err, recid = bibupload.bibupload(recs[0], opt_mode='replace')
replaced_xm = print_record(recid, 'xm')
replaced_hm = print_record(recid, 'hm')
# did it work?
self.failUnless(compare_xmbuffers(replaced_xm, self.testrec1_replaced_xm))
self.failUnless(compare_hmbuffers(replaced_hm, self.testrec1_replaced_hm))
# clean up after ourselves:
bibupload.wipe_out_record_from_all_tables(recid)
return
class BibUploadReferencesModeTest(unittest.TestCase):
"""Testing references mode."""
def setUp(self):
# pylint: disable-msg=C0103
"""Initialize the MARCXML variable"""
self.test_insert = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">CERN</subfield>
</datafield>
</record>"""
self.test_reference = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag =\"""" + bibupload.CFG_BIBUPLOAD_REFERENCE_TAG + """\" ind1="C" ind2="5">
<subfield code="m">M. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,</subfield>
<subfield code="s">J. High Energy Phys. 07 (2004) 014</subfield>
</datafield>
</record>"""
self.test_reference_expected_xm = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag =\"""" + bibupload.CFG_BIBUPLOAD_REFERENCE_TAG + """\" ind1="C" ind2="5">
<subfield code="m">M. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,</subfield>
<subfield code="s">J. High Energy Phys. 07 (2004) 014</subfield>
</datafield>
</record>"""
self.test_insert_hm = """
001__ 123456789
100__ $$aTester, T$$uCERN
"""
self.test_reference_expected_hm = """
001__ 123456789
100__ $$aTester, T$$uCERN
%(reference_tag)sC5 $$mM. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,$$sJ. High Energy Phys. 07 (2004) 014
""" % {'reference_tag': bibupload.CFG_BIBUPLOAD_REFERENCE_TAG}
# insert test record:
task_set_option('verbose', 0)
test_insert = self.test_insert.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_insert)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.test_insert = self.test_insert.replace('123456789', str(recid))
self.test_insert_hm = self.test_insert_hm.replace('123456789', str(recid))
self.test_reference = self.test_reference.replace('123456789', str(recid))
self.test_reference_expected_xm = self.test_reference_expected_xm.replace('123456789', str(recid))
self.test_reference_expected_hm = self.test_reference_expected_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm, self.test_insert))
self.failUnless(compare_hmbuffers(inserted_hm, self.test_insert_hm))
self.test_recid = recid
def test_reference_complete_xml_marc(self):
"""bibupload - reference mode, inserting references MARCXML file"""
# We create create the record out of the xml marc
recs = bibupload.xml_marc_to_records(self.test_reference)
# We call the main function with the record as a parameter
err, recid = bibupload.bibupload(recs[0], opt_mode='reference')
# We retrieve the inserted xml
reference_xm = print_record(recid, 'xm')
reference_hm = print_record(recid, 'hm')
# Compare if the two MARCXML are the same
self.failUnless(compare_xmbuffers(reference_xm, self.test_reference_expected_xm))
self.failUnless(compare_hmbuffers(reference_hm, self.test_reference_expected_hm))
# clean up after ourselves:
bibupload.wipe_out_record_from_all_tables(self.test_recid)
return
class BibUploadFMTModeTest(unittest.TestCase):
"""Testing FMT mode."""
def setUp(self):
# pylint: disable-msg=C0103
"""Initialize the MARCXML variable"""
self.new_xm_with_fmt = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HB</subfield>
<subfield code="g">Test. Okay.</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux</subfield>
</datafield>
</record>
"""
self.expected_xm_after_inserting_new_xm_with_fmt = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux</subfield>
</datafield>
</record>
"""
self.expected_hm_after_inserting_new_xm_with_fmt = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux
"""
self.recid3_xm_before_all_the_tests = print_record(3, 'xm')
self.recid3_hm_before_all_the_tests = print_record(3, 'hm')
self.recid3_xm_with_fmt = """
<record>
<controlfield tag="001">3</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HB</subfield>
<subfield code="g">Test. Here is some format value.</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Doe, John</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the foos and bars</subfield>
</datafield>
</record>
"""
self.recid3_xm_with_fmt_only_first = """
<record>
<controlfield tag="001">3</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HB</subfield>
<subfield code="g">Test. Let us see if this gets inserted well.</subfield>
</datafield>
</record>
"""
self.recid3_xm_with_fmt_only_second = """
<record>
<controlfield tag="001">3</controlfield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HB</subfield>
<subfield code="g">Test. Yet another test, to be run after the first one.</subfield>
</datafield>
<datafield tag="FMT" ind1=" " ind2=" ">
<subfield code="f">HD</subfield>
<subfield code="g">Test. Let's see what will be stored in the detailed format field.</subfield>
</datafield>
</record>
"""
def restore_recid3(self):
"""Helper function that restores recID 3 MARCXML, using the
value saved before all the tests started to execute.
(see self.recid3_xm_before_all_the_tests).
Does not restore HB and HD formats.
"""
recs = bibupload.xml_marc_to_records(self.recid3_xm_before_all_the_tests)
err, recid = bibupload.bibupload(recs[0], opt_mode='replace')
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm, self.recid3_xm_before_all_the_tests))
self.failUnless(compare_hmbuffers(inserted_hm, self.recid3_hm_before_all_the_tests))
def test_inserting_new_record_containing_fmt_tag(self):
"""bibupload - FMT tag, inserting new record containing FMT tag"""
recs = bibupload.xml_marc_to_records(self.new_xm_with_fmt)
(dummy, new_recid) = bibupload.bibupload(recs[0], opt_mode='insert')
xm_after = print_record(new_recid, 'xm')
hm_after = print_record(new_recid, 'hm')
hb_after = print_record(new_recid, 'hb')
self.failUnless(compare_xmbuffers(xm_after,
self.expected_xm_after_inserting_new_xm_with_fmt.replace('123456789', str(new_recid))))
self.failUnless(compare_hmbuffers(hm_after,
self.expected_hm_after_inserting_new_xm_with_fmt.replace('123456789', str(new_recid))))
self.failUnless(hb_after.startswith("Test. Okay."))
def test_updating_existing_record_formats_in_format_mode(self):
"""bibupload - FMT tag, updating existing record via format mode"""
xm_before = print_record(3, 'xm')
hm_before = print_record(3, 'hm')
# insert first format value:
recs = bibupload.xml_marc_to_records(self.recid3_xm_with_fmt_only_first)
bibupload.bibupload(recs[0], opt_mode='format')
xm_after = print_record(3, 'xm')
hm_after = print_record(3, 'hm')
hb_after = print_record(3, 'hb')
self.assertEqual(xm_after, xm_before)
self.assertEqual(hm_after, hm_before)
self.failUnless(hb_after.startswith("Test. Let us see if this gets inserted well."))
# now insert another format value and recheck:
recs = bibupload.xml_marc_to_records(self.recid3_xm_with_fmt_only_second)
bibupload.bibupload(recs[0], opt_mode='format')
xm_after = print_record(3, 'xm')
hm_after = print_record(3, 'hm')
hb_after = print_record(3, 'hb')
hd_after = print_record(3, 'hd')
self.assertEqual(xm_after, xm_before)
self.assertEqual(hm_after, hm_before)
self.failUnless(hb_after.startswith("Test. Yet another test, to be run after the first one."))
self.failUnless(hd_after.startswith("Test. Let's see what will be stored in the detailed format field."))
# restore original record 3:
self.restore_recid3()
def test_updating_existing_record_formats_in_correct_mode(self):
"""bibupload - FMT tag, updating existing record via correct mode"""
xm_before = print_record(3, 'xm')
hm_before = print_record(3, 'hm')
# insert first format value:
recs = bibupload.xml_marc_to_records(self.recid3_xm_with_fmt_only_first)
bibupload.bibupload(recs[0], opt_mode='correct')
xm_after = print_record(3, 'xm')
hm_after = print_record(3, 'hm')
hb_after = print_record(3, 'hb')
self.assertEqual(xm_after, xm_before)
self.assertEqual(hm_after, hm_before)
self.failUnless(hb_after.startswith("Test. Let us see if this gets inserted well."))
# now insert another format value and recheck:
recs = bibupload.xml_marc_to_records(self.recid3_xm_with_fmt_only_second)
bibupload.bibupload(recs[0], opt_mode='correct')
xm_after = print_record(3, 'xm')
hm_after = print_record(3, 'hm')
hb_after = print_record(3, 'hb')
hd_after = print_record(3, 'hd')
self.assertEqual(xm_after, xm_before)
self.assertEqual(hm_after, hm_before)
self.failUnless(hb_after.startswith("Test. Yet another test, to be run after the first one."))
self.failUnless(hd_after.startswith("Test. Let's see what will be stored in the detailed format field."))
# restore original record 3:
self.restore_recid3()
def test_updating_existing_record_formats_in_replace_mode(self):
"""bibupload - FMT tag, updating existing record via replace mode"""
# insert first format value:
recs = bibupload.xml_marc_to_records(self.recid3_xm_with_fmt_only_first)
bibupload.bibupload(recs[0], opt_mode='replace')
xm_after = print_record(3, 'xm')
hm_after = print_record(3, 'hm')
hb_after = print_record(3, 'hb')
self.failUnless(compare_xmbuffers(xm_after,
'<record><controlfield tag="001">3</controlfield></record>'), 0)
self.failUnless(compare_hmbuffers(hm_after,
'000000003 001__ 3'), 0)
self.failUnless(hb_after.startswith("Test. Let us see if this gets inserted well."))
# now insert another format value and recheck:
recs = bibupload.xml_marc_to_records(self.recid3_xm_with_fmt_only_second)
bibupload.bibupload(recs[0], opt_mode='replace')
xm_after = print_record(3, 'xm')
hm_after = print_record(3, 'hm')
hb_after = print_record(3, 'hb')
hd_after = print_record(3, 'hd')
self.failUnless(compare_xmbuffers(xm_after, """
<record>
<controlfield tag="001">3</controlfield>
</record>"""))
self.failUnless(compare_hmbuffers(hm_after, '000000003 001__ 3'))
self.failUnless(hb_after.startswith("Test. Yet another test, to be run after the first one."))
self.failUnless(hd_after.startswith("Test. Let's see what will be stored in the detailed format field."))
# final insertion and recheck:
recs = bibupload.xml_marc_to_records(self.recid3_xm_with_fmt)
bibupload.bibupload(recs[0], opt_mode='replace')
xm_after = print_record(3, 'xm')
hm_after = print_record(3, 'hm')
hb_after = print_record(3, 'hb')
hd_after = print_record(3, 'hd')
self.failUnless(compare_xmbuffers(xm_after, """
<record>
<controlfield tag="001">3</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Doe, John</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the foos and bars</subfield>
</datafield>
</record>
"""))
self.failUnless(compare_hmbuffers(hm_after, """
001__ 3
003__ SzGeCERN
100__ $$aDoe, John$$uCERN
245__ $$aOn the foos and bars
"""))
self.failUnless(hb_after.startswith("Test. Here is some format value."))
self.failUnless(hd_after.startswith("Test. Let's see what will be stored in the detailed format field."))
# restore original record 3:
self.restore_recid3()
class BibUploadRecordsWithSYSNOTest(unittest.TestCase):
"""Testing uploading of records that have external SYSNO present."""
def setUp(self):
# pylint: disable-msg=C0103
"""Initialize the MARCXML test records."""
self.verbose = 0
# Note that SYSNO fields are repeated but with different
# subfields, this is to test whether bibupload would not
# mistakenly pick up wrong values.
self.xm_testrec1 = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="%(sysnosubfieldcode)s">sysno1</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="0">sysno2</subfield>
</datafield>
</record>
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or " ",
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or " ",
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.hm_testrec1 = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$%(sysnosubfieldcode)ssysno1
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$0sysno2
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4],
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5],
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.xm_testrec1_to_update = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="%(sysnosubfieldcode)s">sysno1</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="0">sysno2</subfield>
</datafield>
</record>
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or " ",
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or " ",
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.xm_testrec1_updated = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="%(sysnosubfieldcode)s">sysno1</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="0">sysno2</subfield>
</datafield>
</record>
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or " ",
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or " ",
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.hm_testrec1_updated = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1 Updated
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$%(sysnosubfieldcode)ssysno1
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$0sysno2
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4],
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5],
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.xm_testrec2 = """
<record>
<controlfield tag="001">987654321</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 2</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="%(sysnosubfieldcode)s">sysno2</subfield>
</datafield>
<datafield tag="%(sysnotag)s" ind1="%(sysnoind1)s" ind2="%(sysnoind2)s">
<subfield code="0">sysno1</subfield>
</datafield>
</record>
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4] or " ",
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5] or " ",
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
self.hm_testrec2 = """
001__ 987654321
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 2
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$%(sysnosubfieldcode)ssysno2
%(sysnotag)s%(sysnoind1)s%(sysnoind2)s $$0sysno1
""" % {'sysnotag': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[0:3],
'sysnoind1': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[3:4],
'sysnoind2': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[4:5],
'sysnosubfieldcode': CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG[5:6],
}
def test_insert_the_same_sysno_record(self):
"""bibupload - SYSNO tag, refuse to insert the same SYSNO record"""
# initialize bibupload mode:
if self.verbose:
print "test_insert_the_same_sysno_record() started"
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
task_set_option('verbose', 0)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# insert record 2 first time:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
task_set_option('verbose', 0)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid2, 'xm')
inserted_hm = print_record(recid2, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec2 = self.xm_testrec2.replace('987654321', str(recid2))
self.hm_testrec2 = self.hm_testrec2.replace('987654321', str(recid2))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec2))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec2))
# try to insert updated record 1, it should fail:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
task_set_option('verbose', 0)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='insert')
self.assertEqual(-1, recid1_updated)
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid2)
bibupload.wipe_out_record_from_all_tables(recid1_updated)
if self.verbose:
print "test_insert_the_same_sysno_record() finished"
def test_insert_or_replace_the_same_sysno_record(self):
"""bibupload - SYSNO tag, allow to insert or replace the same SYSNO record"""
# initialize bibupload mode:
task_set_option('verbose', self.verbose)
if self.verbose:
print "test_insert_or_replace_the_same_sysno_record() started"
# insert/replace record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# try to insert/replace updated record 1, it should be okay:
task_set_option('verbose', self.verbose)
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0],
opt_mode='replace_or_insert')
inserted_xm = print_record(recid1_updated, 'xm')
inserted_hm = print_record(recid1_updated, 'hm')
self.assertEqual(recid1, recid1_updated)
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1_updated = self.xm_testrec1_updated.replace('123456789', str(recid1))
self.hm_testrec1_updated = self.hm_testrec1_updated.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1_updated))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1_updated))
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid1_updated)
if self.verbose:
print "test_insert_or_replace_the_same_sysno_record() finished"
def test_replace_nonexisting_sysno_record(self):
"""bibupload - SYSNO tag, refuse to replace non-existing SYSNO record"""
# initialize bibupload mode:
task_set_option('verbose', self.verbose)
if self.verbose:
print "test_replace_nonexisting_sysno_record() started"
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# try to replace record 2 it should fail:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='replace')
self.assertEqual(-1, recid2)
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid2)
if self.verbose:
print "test_replace_nonexisting_sysno_record() finished"
class BibUploadRecordsWithEXTOAIIDTest(unittest.TestCase):
"""Testing uploading of records that have external EXTOAIID present."""
def setUp(self):
# pylint: disable-msg=C0103
"""Initialize the MARCXML test records."""
self.verbose = 0
# Note that EXTOAIID fields are repeated but with different
# subfields, this is to test whether bibupload would not
# mistakenly pick up wrong values.
self.xm_testrec1 = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">extoaiid1</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="0">extoaiid2</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1</subfield>
</datafield>
</record>
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
}
self.hm_testrec1 = """
001__ 123456789
003__ SzGeCERN
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$%(extoaiidsubfieldcode)sextoaiid1
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$0extoaiid2
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4],
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5],
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
}
self.xm_testrec1_to_update = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">extoaiid1</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="0">extoaiid2</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
</record>
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
}
self.xm_testrec1_updated = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">extoaiid1</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="0">extoaiid2</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
</record>
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
}
self.hm_testrec1_updated = """
001__ 123456789
003__ SzGeCERN
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$%(extoaiidsubfieldcode)sextoaiid1
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$0extoaiid2
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1 Updated
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4],
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5],
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
}
self.xm_testrec2 = """
<record>
<controlfield tag="001">987654321</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="%(extoaiidsubfieldcode)s">extoaiid2</subfield>
</datafield>
<datafield tag="%(extoaiidtag)s" ind1="%(extoaiidind1)s" ind2="%(extoaiidind2)s">
<subfield code="0">extoaiid1</subfield>
</datafield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 2</subfield>
</datafield>
</record>
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4] or " ",
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] != "_" and \
CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5] or " ",
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
}
self.hm_testrec2 = """
001__ 987654321
003__ SzGeCERN
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$%(extoaiidsubfieldcode)sextoaiid2
%(extoaiidtag)s%(extoaiidind1)s%(extoaiidind2)s $$0extoaiid1
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 2
""" % {'extoaiidtag': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[0:3],
'extoaiidind1': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[3:4],
'extoaiidind2': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[4:5],
'extoaiidsubfieldcode': CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG[5:6],
}
def test_insert_the_same_extoaiid_record(self):
"""bibupload - EXTOAIID tag, refuse to insert the same EXTOAIID record"""
# initialize bibupload mode:
task_set_option('verbose', self.verbose)
if self.verbose:
print "test_insert_the_same_extoaiid_record() started"
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# insert record 2 first time:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid2, 'xm')
inserted_hm = print_record(recid2, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec2 = self.xm_testrec2.replace('987654321', str(recid2))
self.hm_testrec2 = self.hm_testrec2.replace('987654321', str(recid2))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec2))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec2))
# try to insert updated record 1, it should fail:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='insert')
self.assertEqual(-1, recid1_updated)
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid2)
bibupload.wipe_out_record_from_all_tables(recid1_updated)
if self.verbose:
print "test_insert_the_same_extoaiid_record() finished"
def test_insert_or_replace_the_same_extoaiid_record(self):
"""bibupload - EXTOAIID tag, allow to insert or replace the same EXTOAIID record"""
# initialize bibupload mode:
task_set_option('verbose', self.verbose)
if self.verbose:
print "test_insert_or_replace_the_same_extoaiid_record() started"
# insert/replace record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# try to insert/replace updated record 1, it should be okay:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1_updated, 'xm')
inserted_hm = print_record(recid1_updated, 'hm')
self.assertEqual(recid1, recid1_updated)
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1_updated = self.xm_testrec1_updated.replace('123456789', str(recid1))
self.hm_testrec1_updated = self.hm_testrec1_updated.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1_updated))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1_updated))
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid1_updated)
if self.verbose:
print "test_insert_or_replace_the_same_extoaiid_record() finished"
def test_replace_nonexisting_extoaiid_record(self):
"""bibupload - EXTOAIID tag, refuse to replace non-existing EXTOAIID record"""
# initialize bibupload mode:
task_set_option('verbose', self.verbose)
if self.verbose:
print "test_replace_nonexisting_extoaiid_record() started"
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# try to replace record 2 it should fail:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='replace')
self.assertEqual(-1, recid2)
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid2)
if self.verbose:
print "test_replace_nonexisting_extoaiid_record() finished"
class BibUploadRecordsWithOAIIDTest(unittest.TestCase):
"""Testing uploading of records that have OAI ID present."""
def setUp(self):
# pylint: disable-msg=C0103
"""Initialize the MARCXML test records."""
self.verbose = 0
# Note that OAI fields are repeated but with different
# subfields, this is to test whether bibupload would not
# mistakenly pick up wrong values.
self.xm_testrec1 = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="%(oaisubfieldcode)s">oai:foo:1</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="0">oai:foo:2</subfield>
</datafield>
</record>
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or " ",
'oaiind2': CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or " ",
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.hm_testrec1 = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1
%(oaitag)s%(oaiind1)s%(oaiind2)s $$%(oaisubfieldcode)soai:foo:1
%(oaitag)s%(oaiind1)s%(oaiind2)s $$0oai:foo:2
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4],
'oaiind2': CFG_OAI_ID_FIELD[4:5],
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.xm_testrec1_to_update = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="%(oaisubfieldcode)s">oai:foo:1</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="0">oai:foo:2</subfield>
</datafield>
</record>
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or " ",
'oaiind2': CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or " ",
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.xm_testrec1_updated = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 1 Updated</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="%(oaisubfieldcode)s">oai:foo:1</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="0">oai:foo:2</subfield>
</datafield>
</record>
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or " ",
'oaiind2': CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or " ",
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.hm_testrec1_updated = """
001__ 123456789
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 1 Updated
%(oaitag)s%(oaiind1)s%(oaiind2)s $$%(oaisubfieldcode)soai:foo:1
%(oaitag)s%(oaiind1)s%(oaiind2)s $$0oai:foo:2
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4],
'oaiind2': CFG_OAI_ID_FIELD[4:5],
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.xm_testrec2 = """
<record>
<controlfield tag="001">987654321</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Bar, Baz</subfield>
<subfield code="u">Foo</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">On the quux and huux 2</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="%(oaisubfieldcode)s">oai:foo:2</subfield>
</datafield>
<datafield tag="%(oaitag)s" ind1="%(oaiind1)s" ind2="%(oaiind2)s">
<subfield code="0">oai:foo:1</subfield>
</datafield>
</record>
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4] != "_" and \
CFG_OAI_ID_FIELD[3:4] or " ",
'oaiind2': CFG_OAI_ID_FIELD[4:5] != "_" and \
CFG_OAI_ID_FIELD[4:5] or " ",
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
self.hm_testrec2 = """
001__ 987654321
003__ SzGeCERN
100__ $$aBar, Baz$$uFoo
245__ $$aOn the quux and huux 2
%(oaitag)s%(oaiind1)s%(oaiind2)s $$%(oaisubfieldcode)soai:foo:2
%(oaitag)s%(oaiind1)s%(oaiind2)s $$0oai:foo:1
""" % {'oaitag': CFG_OAI_ID_FIELD[0:3],
'oaiind1': CFG_OAI_ID_FIELD[3:4],
'oaiind2': CFG_OAI_ID_FIELD[4:5],
'oaisubfieldcode': CFG_OAI_ID_FIELD[5:6],
}
def test_insert_the_same_oai_record(self):
"""bibupload - OAIID tag, refuse to insert the same OAI record"""
task_set_option('verbose', self.verbose)
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# insert record 2 first time:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid2, 'xm')
inserted_hm = print_record(recid2, 'hm')
# use real recID when comparing whether it worked:
self.xm_testrec2 = self.xm_testrec2.replace('987654321', str(recid2))
self.hm_testrec2 = self.hm_testrec2.replace('987654321', str(recid2))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec2))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec2))
# try to insert updated record 1, it should fail:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='insert')
self.assertEqual(-1, recid1_updated)
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid2)
bibupload.wipe_out_record_from_all_tables(recid1_updated)
def test_insert_or_replace_the_same_oai_record(self):
"""bibupload - OAIID tag, allow to insert or replace the same OAI record"""
# initialize bibupload mode:
task_set_option('verbose', self.verbose)
# insert/replace record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# try to insert/replace updated record 1, it should be okay:
recs = bibupload.xml_marc_to_records(self.xm_testrec1_to_update)
err1_updated, recid1_updated = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1_updated, 'xm')
inserted_hm = print_record(recid1_updated, 'hm')
self.assertEqual(recid1, recid1_updated)
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1_updated = self.xm_testrec1_updated.replace('123456789', str(recid1))
self.hm_testrec1_updated = self.hm_testrec1_updated.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1_updated))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1_updated))
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid1_updated)
def test_replace_nonexisting_oai_record(self):
"""bibupload - OAIID tag, refuse to replace non-existing OAI record"""
task_set_option('verbose', self.verbose)
# insert record 1 first time:
testrec_to_insert_first = self.xm_testrec1.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='replace_or_insert')
inserted_xm = print_record(recid1, 'xm')
inserted_hm = print_record(recid1, 'hm')
# use real recID in test buffers when comparing whether it worked:
self.xm_testrec1 = self.xm_testrec1.replace('123456789', str(recid1))
self.hm_testrec1 = self.hm_testrec1.replace('123456789', str(recid1))
self.failUnless(compare_xmbuffers(inserted_xm,
self.xm_testrec1))
self.failUnless(compare_hmbuffers(inserted_hm,
self.hm_testrec1))
# try to replace record 2 it should fail:
testrec_to_insert_first = self.xm_testrec2.replace('<controlfield tag="001">987654321</controlfield>',
'')
recs = bibupload.xml_marc_to_records(testrec_to_insert_first)
err2, recid2 = bibupload.bibupload(recs[0], opt_mode='replace')
self.assertEqual(-1, recid2)
# delete test records
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid2)
class BibUploadIndicatorsTest(unittest.TestCase):
"""
Testing uploading of a MARCXML record with indicators having
either blank space (as per MARC schema) or empty string value (old
behaviour).
"""
def setUp(self):
"""Initialize the MARCXML test record."""
self.testrec1_xm = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
003__ SzGeCERN
100__ $$aTest, John$$uTest University
"""
self.testrec2_xm = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1="" ind2="">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
self.testrec2_hm = """
003__ SzGeCERN
100__ $$aTest, John$$uTest University
"""
def test_record_with_spaces_in_indicators(self):
"""bibupload - inserting MARCXML with spaces in indicators"""
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(self.testrec1_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(remove_tag_001_from_xmbuffer(inserted_xm),
self.testrec1_xm))
self.failUnless(compare_hmbuffers(remove_tag_001_from_hmbuffer(inserted_hm),
self.testrec1_hm))
bibupload.wipe_out_record_from_all_tables(recid)
def test_record_with_no_spaces_in_indicators(self):
"""bibupload - inserting MARCXML with no spaces in indicators"""
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(self.testrec2_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(remove_tag_001_from_xmbuffer(inserted_xm),
self.testrec2_xm))
self.failUnless(compare_hmbuffers(remove_tag_001_from_hmbuffer(inserted_hm),
self.testrec2_hm))
bibupload.wipe_out_record_from_all_tables(recid)
class BibUploadUpperLowerCaseTest(unittest.TestCase):
"""
Testing treatment of similar records with only upper and lower
case value differences in the bibxxx table.
"""
def setUp(self):
"""Initialize the MARCXML test records."""
self.testrec1_xm = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
self.testrec1_hm = """
003__ SzGeCERN
100__ $$aTest, John$$uTest University
"""
self.testrec2_xm = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1="" ind2="">
<subfield code="a">TeSt, JoHn</subfield>
<subfield code="u">Test UniVeRsity</subfield>
</datafield>
</record>
"""
self.testrec2_hm = """
003__ SzGeCERN
100__ $$aTeSt, JoHn$$uTest UniVeRsity
"""
def test_record_with_upper_lower_case_letters(self):
"""bibupload - inserting similar MARCXML records with upper/lower case"""
task_set_option('verbose', 0)
# insert test record #1:
recs = bibupload.xml_marc_to_records(self.testrec1_xm)
err1, recid1 = bibupload.bibupload(recs[0], opt_mode='insert')
recid1_inserted_xm = print_record(recid1, 'xm')
recid1_inserted_hm = print_record(recid1, 'hm')
# insert test record #2:
recs = bibupload.xml_marc_to_records(self.testrec2_xm)
err1, recid2 = bibupload.bibupload(recs[0], opt_mode='insert')
recid2_inserted_xm = print_record(recid2, 'xm')
recid2_inserted_hm = print_record(recid2, 'hm')
# let us compare stuff now:
self.failUnless(compare_xmbuffers(remove_tag_001_from_xmbuffer(recid1_inserted_xm),
self.testrec1_xm))
self.failUnless(compare_hmbuffers(remove_tag_001_from_hmbuffer(recid1_inserted_hm),
self.testrec1_hm))
self.failUnless(compare_xmbuffers(remove_tag_001_from_xmbuffer(recid2_inserted_xm),
self.testrec2_xm))
self.failUnless(compare_hmbuffers(remove_tag_001_from_hmbuffer(recid2_inserted_hm),
self.testrec2_hm))
# clean up after ourselves:
bibupload.wipe_out_record_from_all_tables(recid1)
bibupload.wipe_out_record_from_all_tables(recid2)
class BibUploadStrongTagsTest(unittest.TestCase):
"""Testing treatment of strong tags and the replace mode."""
def setUp(self):
"""Initialize the MARCXML test record."""
self.testrec1_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Jane</subfield>
<subfield code="u">Test Institute</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Test title</subfield>
</datafield>
<datafield tag="%(strong_tag)s" ind1=" " ind2=" ">
<subfield code="a">A value</subfield>
<subfield code="b">Another value</subfield>
</datafield>
</record>
""" % {'strong_tag': bibupload.CFG_BIBUPLOAD_STRONG_TAGS[0]}
self.testrec1_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, Jane$$uTest Institute
245__ $$aTest title
%(strong_tag)s__ $$aA value$$bAnother value
""" % {'strong_tag': bibupload.CFG_BIBUPLOAD_STRONG_TAGS[0]}
self.testrec1_xm_to_replace = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
</record>
"""
self.testrec1_replaced_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, Joseph</subfield>
<subfield code="u">Test Academy</subfield>
</datafield>
<datafield tag="%(strong_tag)s" ind1=" " ind2=" ">
<subfield code="a">A value</subfield>
<subfield code="b">Another value</subfield>
</datafield>
</record>
""" % {'strong_tag': bibupload.CFG_BIBUPLOAD_STRONG_TAGS[0]}
self.testrec1_replaced_hm = """
001__ 123456789
100__ $$aTest, Joseph$$uTest Academy
%(strong_tag)s__ $$aA value$$bAnother value
""" % {'strong_tag': bibupload.CFG_BIBUPLOAD_STRONG_TAGS[0]}
# insert test record:
task_set_option('verbose', 0)
test_record_xm = self.testrec1_xm.replace('<controlfield tag="001">123456789</controlfield>',
'')
recs = bibupload.xml_marc_to_records(test_record_xm)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recID:
self.testrec1_xm = self.testrec1_xm.replace('123456789', str(recid))
self.testrec1_hm = self.testrec1_hm.replace('123456789', str(recid))
self.testrec1_xm_to_replace = self.testrec1_xm_to_replace.replace('123456789', str(recid))
self.testrec1_replaced_xm = self.testrec1_replaced_xm.replace('123456789', str(recid))
self.testrec1_replaced_hm = self.testrec1_replaced_hm.replace('123456789', str(recid))
# test of the inserted record:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm, self.testrec1_xm))
self.failUnless(compare_hmbuffers(inserted_hm, self.testrec1_hm))
def test_strong_tags_persistence(self):
"""bibupload - strong tags, persistence in replace mode"""
# replace all metadata tags; will the strong tags be kept?
recs = bibupload.xml_marc_to_records(self.testrec1_xm_to_replace)
err, recid = bibupload.bibupload(recs[0], opt_mode='replace')
replaced_xm = print_record(recid, 'xm')
replaced_hm = print_record(recid, 'hm')
# did it work?
self.failUnless(compare_xmbuffers(replaced_xm, self.testrec1_replaced_xm))
self.failUnless(compare_hmbuffers(replaced_hm, self.testrec1_replaced_hm))
# clean up after ourselves:
bibupload.wipe_out_record_from_all_tables(recid)
return
class BibUploadFFTModeTest(unittest.TestCase):
"""
Testing treatment of fulltext file transfer import mode.
"""
def _test_bibdoc_status(self, recid, docname, status):
res = run_sql('SELECT bd.status FROM bibrec_bibdoc as bb JOIN bibdoc as bd ON bb.id_bibdoc = bd.id WHERE bb.id_bibrec = %s AND bd.docname = %s', (recid, docname))
self.failUnless(res)
self.assertEqual(status, res[0][0])
def test_simple_fft_insert(self):
"""bibupload - simple FFT insert"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/cds.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/cds.gif</subfield>
</datafield>
</record>
- """ % {'weburl': weburl}
+ """ % {'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/cds.gif
- """ % {'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/cds.gif" \
- % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/cds.gif
+ """ % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/cds.gif" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self.failUnless(try_url_download(testrec_expected_url))
bibupload.wipe_out_record_from_all_tables(recid)
def test_exotic_format_fft_append(self):
"""bibupload - exotic format FFT append"""
# define the test case:
testfile = os.path.join(CFG_TMPDIR, 'test.ps.Z')
open(testfile, 'w').write('TEST')
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
testrec_to_append = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s</subfield>
</datafield>
</record>
""" % testfile
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/test.ps.Z</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/test.ps.Z</subfield>
</datafield>
</record>
- """ % {'weburl': weburl}
+ """ % {'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/test.ps.Z
- """ % {'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/test.ps.Z" \
- % {'weburl': weburl}
- testrec_expected_url2 = "%(weburl)s/record/123456789/files/test?format=ps.Z" \
- % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/test.ps.Z
+ """ % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/test.ps.Z" \
+ % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url2 = "%(siteurl)s/record/123456789/files/test?format=ps.Z" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_to_append = testrec_to_append.replace('123456789',
str(recid))
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
testrec_expected_url2 = testrec_expected_url.replace('123456789',
str(recid))
recs = bibupload.xml_marc_to_records(testrec_to_append)
err, recid = bibupload.bibupload(recs[0], opt_mode='append')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self.assertEqual(urlopen(testrec_expected_url).read(), 'TEST')
self.assertEqual(urlopen(testrec_expected_url2).read(), 'TEST')
bibupload.wipe_out_record_from_all_tables(recid)
def test_fft_check_md5_through_bibrecdoc_str(self):
"""bibupload - simple FFT insert, check md5 through BibRecDocs.str()"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s/img/head.gif</subfield>
</datafield>
</record>
- """ % weburl
+ """ % CFG_SITE_URL
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
- original_md5 = md5(urlopen('%s/img/head.gif' % weburl).read()).hexdigest()
+ original_md5 = md5(urlopen('%s/img/head.gif' % CFG_SITE_URL).read()).hexdigest()
bibrec_str = str(BibRecDocs(int(recid)))
md5_found = False
for row in bibrec_str.split('\n'):
if 'checksum' in row:
if original_md5 in row:
md5_found = True
self.failUnless(md5_found)
bibupload.wipe_out_record_from_all_tables(recid)
def test_detailed_fft_insert(self):
"""bibupload - detailed FFT insert"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="t">SuperMain</subfield>
<subfield code="d">This is a description</subfield>
<subfield code="z">This is a comment</subfield>
<subfield code="n">CIDIESSE</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="t">SuperMain</subfield>
<subfield code="f">.jpeg</subfield>
<subfield code="d">This is a description</subfield>
<subfield code="z">This is a second comment</subfield>
<subfield code="n">CIDIESSE</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/CIDIESSE.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/CIDIESSE.gif</subfield>
<subfield code="y">This is a description</subfield>
<subfield code="z">This is a comment</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/CIDIESSE.jpeg</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/CIDIESSE.jpeg</subfield>
<subfield code="y">This is a description</subfield>
<subfield code="z">This is a second comment</subfield>
</datafield>
</record>
- """ % {'weburl': weburl}
+ """ % {'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/CIDIESSE.gif$$yThis is a description$$zThis is a comment
- 8564_ $$u%(weburl)s/record/123456789/files/CIDIESSE.jpeg$$yThis is a description$$zThis is a second comment
- """ % {'weburl': weburl}
- testrec_expected_url1 = "%(weburl)s/record/123456789/files/CIDIESSE.gif" % {'weburl': weburl}
- testrec_expected_url2 = "%(weburl)s/record/123456789/files/CIDIESSE.jpeg" % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/CIDIESSE.gif$$yThis is a description$$zThis is a comment
+ 8564_ $$u%(siteurl)s/record/123456789/files/CIDIESSE.jpeg$$yThis is a description$$zThis is a second comment
+ """ % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url1 = "%(siteurl)s/record/123456789/files/CIDIESSE.gif" % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url2 = "%(siteurl)s/record/123456789/files/CIDIESSE.jpeg" % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url1 = testrec_expected_url1.replace('123456789',
str(recid))
testrec_expected_url2 = testrec_expected_url1.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self.failUnless(try_url_download(testrec_expected_url1))
self.failUnless(try_url_download(testrec_expected_url2))
bibupload.wipe_out_record_from_all_tables(recid)
def test_simple_fft_insert_with_restriction(self):
"""bibupload - simple FFT insert with restriction"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="r">thesis</subfield>
<subfield code="x">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/cds.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/cds.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="q">%(weburl)s/record/123456789/files/icon-cds.gif</subfield>
+ <subfield code="q">%(siteurl)s/record/123456789/files/icon-cds.gif</subfield>
<subfield code="x">icon</subfield>
</datafield>
</record>
- """ % {'weburl': weburl}
+ """ % {'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/cds.gif
- 8564_ $$q%(weburl)s/record/123456789/files/icon-cds.gif$$xicon
- """ % {'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/cds.gif" \
- % {'weburl': weburl}
- testrec_expected_icon = "%(weburl)s/record/123456789/files/icon-cds.gif" \
- % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/cds.gif
+ 8564_ $$q%(siteurl)s/record/123456789/files/icon-cds.gif$$xicon
+ """ % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/cds.gif" \
+ % {'siteurl': CFG_SITE_URL}
+ testrec_expected_icon = "%(siteurl)s/record/123456789/files/icon-cds.gif" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
testrec_expected_icon = testrec_expected_icon.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
open_url = urlopen(testrec_expected_url)
self.failUnless("This file is restricted" in open_url.read())
open_icon = urlopen(testrec_expected_icon)
- restricted_icon = urlopen("%s/img/restricted.gif" % weburl)
+ restricted_icon = urlopen("%s/img/restricted.gif" % CFG_SITE_URL)
self.failUnless(open_icon.read() == restricted_icon.read())
bibupload.wipe_out_record_from_all_tables(recid)
def test_simple_fft_insert_with_icon(self):
"""bibupload - simple FFT insert with icon"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="x">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/cds.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/cds.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="q">%(weburl)s/record/123456789/files/icon-cds.gif</subfield>
+ <subfield code="q">%(siteurl)s/record/123456789/files/icon-cds.gif</subfield>
<subfield code="x">icon</subfield>
</datafield>
</record>
- """ % {'weburl': weburl}
+ """ % {'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/cds.gif
- 8564_ $$q%(weburl)s/record/123456789/files/icon-cds.gif$$xicon
- """ % {'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/cds.gif" \
- % {'weburl': weburl}
- testrec_expected_icon = "%(weburl)s/record/123456789/files/icon-cds.gif" \
- % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/cds.gif
+ 8564_ $$q%(siteurl)s/record/123456789/files/icon-cds.gif$$xicon
+ """ % {'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/cds.gif" \
+ % {'siteurl': CFG_SITE_URL}
+ testrec_expected_icon = "%(siteurl)s/record/123456789/files/icon-cds.gif" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
testrec_expected_icon = testrec_expected_icon.replace('123456789',
str(recid))
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(try_url_download(testrec_expected_icon))
bibupload.wipe_out_record_from_all_tables(recid)
def test_multiple_fft_insert(self):
"""bibupload - multiple FFT insert"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cdsweb.cern.ch/img/head.gif</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://doc.cern.ch/archive/electronic/hep-th/0101/0101001.pdf</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%(prefix)s/var/tmp/demobibdata.xml</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">/etc/passwd</subfield>
</datafield>
</record>
""" % { 'prefix': CFG_PREFIX }
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/0101001.pdf</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/0101001.pdf</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/cds.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/cds.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/demobibdata.xml</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/demobibdata.xml</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/head.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/head.gif</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/0101001.pdf
- 8564_ $$u%(weburl)s/record/123456789/files/cds.gif
- 8564_ $$u%(weburl)s/record/123456789/files/demobibdata.xml
- 8564_ $$u%(weburl)s/record/123456789/files/head.gif
- """ % { 'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/0101001.pdf
+ 8564_ $$u%(siteurl)s/record/123456789/files/cds.gif
+ 8564_ $$u%(siteurl)s/record/123456789/files/demobibdata.xml
+ 8564_ $$u%(siteurl)s/record/123456789/files/head.gif
+ """ % { 'siteurl': CFG_SITE_URL}
# insert test record:
testrec_expected_urls = []
for files in ('cds.gif', 'head.gif', '0101001.pdf', 'demobibdata.xml'):
- testrec_expected_urls.append('%(weburl)s/record/123456789/files/%(files)s' % {'weburl' : weburl, 'files' : files})
+ testrec_expected_urls.append('%(siteurl)s/record/123456789/files/%(files)s' % {'siteurl' : CFG_SITE_URL, 'files' : files})
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_urls = []
for files in ('cds.gif', 'head.gif', '0101001.pdf', 'demobibdata.xml'):
- testrec_expected_urls.append('%(weburl)s/record/%(recid)s/files/%(files)s' % {'weburl' : weburl, 'files' : files, 'recid' : recid})
+ testrec_expected_urls.append('%(siteurl)s/record/%(recid)s/files/%(files)s' % {'siteurl' : CFG_SITE_URL, 'files' : files, 'recid' : recid})
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
# FIXME: Next test has been commented out since, appearently, the
# returned xml can have non predictable row order (but still correct)
# Using only html marc output is fine because a value is represented
# by a single row, so a row to row comparison can be employed.
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
for url in testrec_expected_urls:
self.failUnless(try_url_download(url))
self._test_bibdoc_status(recid, 'head', '')
self._test_bibdoc_status(recid, '0101001', '')
self._test_bibdoc_status(recid, 'cds', '')
self._test_bibdoc_status(recid, 'demobibdata', '')
bibupload.wipe_out_record_from_all_tables(recid)
def test_simple_fft_correct(self):
"""bibupload - simple FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
</record>
"""
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/cds.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/cds.gif</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/cds.gif
- """ % { 'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/patata.gif" \
- % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/cds.gif
+ """ % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/patata.gif" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self._test_bibdoc_status(recid, 'cds', '')
#print "\nRecid: " + str(recid) + "\n"
#print testrec_expected_hm + "\n"
#print print_record(recid, 'hm') + "\n"
bibupload.wipe_out_record_from_all_tables(recid)
def test_detailed_fft_correct(self):
"""bibupload - detailed FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="d">Try</subfield>
<subfield code="z">Comment</subfield>
</datafield>
</record>
"""
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cdsweb.cern.ch/img/head.gif</subfield>
<subfield code="n">cds</subfield>
<subfield code="m">patata</subfield>
<subfield code="d">Next Try</subfield>
<subfield code="z">KEEP-OLD-VALUE</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/patata.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/patata.gif</subfield>
<subfield code="y">Next Try</subfield>
<subfield code="z">Comment</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/patata.gif$$yNext Try$$zComment
- """ % { 'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/patata.gif" \
- % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/patata.gif$$yNext Try$$zComment
+ """ % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/patata.gif" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self._test_bibdoc_status(recid, 'patata', '')
#print "\nRecid: " + str(recid) + "\n"
#print testrec_expected_hm + "\n"
#print print_record(recid, 'hm') + "\n"
bibupload.wipe_out_record_from_all_tables(recid)
def test_no_url_fft_correct(self):
"""bibupload - no_url FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="d">Try</subfield>
<subfield code="z">Comment</subfield>
</datafield>
</record>
"""
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="n">cds</subfield>
<subfield code="m">patata</subfield>
<subfield code="f">.gif</subfield>
<subfield code="d">KEEP-OLD-VALUE</subfield>
<subfield code="z">Next Comment</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/patata.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/patata.gif</subfield>
<subfield code="y">Try</subfield>
<subfield code="z">Next Comment</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/patata.gif$$yTry$$zNext Comment
- """ % { 'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/patata.gif" \
- % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/patata.gif$$yTry$$zNext Comment
+ """ % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/patata.gif" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self._test_bibdoc_status(recid, 'patata', '')
#print "\nRecid: " + str(recid) + "\n"
#print testrec_expected_hm + "\n"
#print print_record(recid, 'hm') + "\n"
bibupload.wipe_out_record_from_all_tables(recid)
def test_new_icon_fft_append(self):
"""bibupload - new icon FFT append"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
</record>
"""
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="n">cds</subfield>
<subfield code="x">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="q">%(weburl)s/record/123456789/files/icon-cds.gif</subfield>
+ <subfield code="q">%(siteurl)s/record/123456789/files/icon-cds.gif</subfield>
<subfield code="x">icon</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$q%(weburl)s/record/123456789/files/icon-cds.gif$$xicon
- """ % { 'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/icon-cds.gif" \
- % {'weburl': weburl}
+ 8564_ $$q%(siteurl)s/record/123456789/files/icon-cds.gif$$xicon
+ """ % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/icon-cds.gif" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='append')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self._test_bibdoc_status(recid, 'cds', '')
#print "\nRecid: " + str(recid) + "\n"
#print testrec_expected_hm + "\n"
#print print_record(recid, 'hm') + "\n"
bibupload.wipe_out_record_from_all_tables(recid)
def test_multiple_fft_correct(self):
"""bibupload - multiple FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="d">Try</subfield>
<subfield code="z">Comment</subfield>
<subfield code="r">Restricted</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="f">.jpeg</subfield>
<subfield code="d">Try jpeg</subfield>
<subfield code="z">Comment jpeg</subfield>
<subfield code="r">Restricted</subfield>
</datafield>
</record>
"""
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="m">patata</subfield>
<subfield code="f">.gif</subfield>
<subfield code="r">New restricted</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/patata.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/patata.gif</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/patata.gif
- """ % { 'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/patata.gif" \
- % {'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/patata.gif
+ """ % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/patata.gif" \
+ % {'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
# correct test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self._test_bibdoc_status(recid, 'patata', 'New restricted')
#print "\nRecid: " + str(recid) + "\n"
#print testrec_expected_hm + "\n"
#print print_record(recid, 'hm') + "\n"
bibupload.wipe_out_record_from_all_tables(recid)
def test_purge_fft_correct(self):
"""bibupload - purge FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cdsweb.cern.ch/img/head.gif</subfield>
</datafield>
</record>
"""
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
</datafield>
</record>
"""
test_to_purge = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">http://cds.cern.ch/img/cds.gif</subfield>
<subfield code="t">PURGE</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/cds.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/cds.gif</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/head.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/head.gif</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/cds.gif
- 8564_ $$u%(weburl)s/record/123456789/files/head.gif
- """ % { 'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/cds.gif" % { 'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/cds.gif
+ 8564_ $$u%(siteurl)s/record/123456789/files/head.gif
+ """ % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/cds.gif" % { 'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
test_to_purge = test_to_purge.replace('123456789',
str(recid))
# correct test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# purge test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_purge)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self._test_bibdoc_status(recid, 'cds', '')
self._test_bibdoc_status(recid, 'head', '')
#print "\nRecid: " + str(recid) + "\n"
#print testrec_expected_hm + "\n"
#print print_record(recid, 'hm') + "\n"
bibupload.wipe_out_record_from_all_tables(recid)
def test_revert_fft_correct(self):
"""bibupload - revert FFT correct"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s/img/iconpen.gif</subfield>
<subfield code="n">cds</subfield>
</datafield>
</record>
- """ % weburl
+ """ % CFG_SITE_URL
test_to_correct = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s/img/head.gif</subfield>
<subfield code="n">cds</subfield>
</datafield>
</record>
- """ % weburl
+ """ % CFG_SITE_URL
test_to_revert = """
<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="n">cds</subfield>
<subfield code="t">REVERT</subfield>
<subfield code="v">1</subfield>
</datafield>
</record>
"""
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/cds.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/cds.gif</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/cds.gif
- """ % { 'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/cds.gif" % { 'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/cds.gif
+ """ % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/cds.gif" % { 'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_correct = test_to_correct.replace('123456789',
str(recid))
test_to_revert = test_to_revert.replace('123456789',
str(recid))
# correct test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_correct)
bibupload.bibupload(recs[0], opt_mode='correct')
# revert test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_revert)
bibupload.bibupload(recs[0], opt_mode='correct')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
self._test_bibdoc_status(recid, 'cds', '')
- expected_content_version1 = urlopen('%s/img/iconpen.gif' % weburl).read()
- expected_content_version2 = urlopen('%s/img/head.gif' % weburl).read()
+ expected_content_version1 = urlopen('%s/img/iconpen.gif' % CFG_SITE_URL).read()
+ expected_content_version2 = urlopen('%s/img/head.gif' % CFG_SITE_URL).read()
expected_content_version3 = expected_content_version1
- content_version1 = urlopen('%s/record/%s/files/cds.gif?version=1' % (weburl, recid)).read()
- content_version2 = urlopen('%s/record/%s/files/cds.gif?version=2' % (weburl, recid)).read()
- content_version3 = urlopen('%s/record/%s/files/cds.gif?version=3' % (weburl, recid)).read()
+ content_version1 = urlopen('%s/record/%s/files/cds.gif?version=1' % (CFG_SITE_URL, recid)).read()
+ content_version2 = urlopen('%s/record/%s/files/cds.gif?version=2' % (CFG_SITE_URL, recid)).read()
+ content_version3 = urlopen('%s/record/%s/files/cds.gif?version=3' % (CFG_SITE_URL, recid)).read()
self.assertEqual(expected_content_version1, content_version1)
self.assertEqual(expected_content_version2, content_version2)
self.assertEqual(expected_content_version3, content_version3)
#print "\nRecid: " + str(recid) + "\n"
#print testrec_expected_hm + "\n"
#print print_record(recid, 'hm') + "\n"
bibupload.wipe_out_record_from_all_tables(recid)
def test_simple_fft_replace(self):
"""bibupload - simple FFT replace"""
# define the test case:
test_to_upload = """
<record>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s/img/iconpen.gif</subfield>
<subfield code="n">cds</subfield>
</datafield>
</record>
- """ % weburl
+ """ % CFG_SITE_URL
test_to_replace = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="FFT" ind1=" " ind2=" ">
<subfield code="a">%s/img/head.gif</subfield>
</datafield>
</record>
- """ % weburl
+ """ % CFG_SITE_URL
testrec_expected_xm = """
<record>
<controlfield tag="001">123456789</controlfield>
<controlfield tag="003">SzGeCERN</controlfield>
<datafield tag="100" ind1=" " ind2=" ">
<subfield code="a">Test, John</subfield>
<subfield code="u">Test University</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2=" ">
- <subfield code="u">%(weburl)s/record/123456789/files/head.gif</subfield>
+ <subfield code="u">%(siteurl)s/record/123456789/files/head.gif</subfield>
</datafield>
</record>
- """ % { 'weburl': weburl}
+ """ % { 'siteurl': CFG_SITE_URL}
testrec_expected_hm = """
001__ 123456789
003__ SzGeCERN
100__ $$aTest, John$$uTest University
- 8564_ $$u%(weburl)s/record/123456789/files/head.gif
- """ % { 'weburl': weburl}
- testrec_expected_url = "%(weburl)s/record/123456789/files/head.gif" % { 'weburl': weburl}
+ 8564_ $$u%(siteurl)s/record/123456789/files/head.gif
+ """ % { 'siteurl': CFG_SITE_URL}
+ testrec_expected_url = "%(siteurl)s/record/123456789/files/head.gif" % { 'siteurl': CFG_SITE_URL}
# insert test record:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_upload)
err, recid = bibupload.bibupload(recs[0], opt_mode='insert')
# replace test buffers with real recid of inserted test record:
testrec_expected_xm = testrec_expected_xm.replace('123456789',
str(recid))
testrec_expected_hm = testrec_expected_hm.replace('123456789',
str(recid))
testrec_expected_url = testrec_expected_url.replace('123456789',
str(recid))
test_to_replace = test_to_replace.replace('123456789',
str(recid))
# replace test record with new FFT:
task_set_option('verbose', 0)
recs = bibupload.xml_marc_to_records(test_to_replace)
bibupload.bibupload(recs[0], opt_mode='replace')
# compare expected results:
inserted_xm = print_record(recid, 'xm')
inserted_hm = print_record(recid, 'hm')
self.failUnless(try_url_download(testrec_expected_url))
self.failUnless(compare_xmbuffers(inserted_xm,
testrec_expected_xm))
self.failUnless(compare_hmbuffers(inserted_hm,
testrec_expected_hm))
- expected_content_version = urlopen('%s/img/head.gif' % weburl).read()
+ expected_content_version = urlopen('%s/img/head.gif' % CFG_SITE_URL).read()
- content_version = urlopen('%s/record/%s/files/head.gif' % (weburl, recid)).read()
+ content_version = urlopen('%s/record/%s/files/head.gif' % (CFG_SITE_URL, recid)).read()
self.assertEqual(expected_content_version, content_version)
#print "\nRecid: " + str(recid) + "\n"
#print testrec_expected_hm + "\n"
#print print_record(recid, 'hm') + "\n"
bibupload.wipe_out_record_from_all_tables(recid)
test_suite = make_test_suite(BibUploadInsertModeTest,
BibUploadAppendModeTest,
BibUploadCorrectModeTest,
BibUploadReplaceModeTest,
BibUploadReferencesModeTest,
BibUploadRecordsWithSYSNOTest,
BibUploadRecordsWithEXTOAIIDTest,
BibUploadRecordsWithOAIIDTest,
BibUploadFMTModeTest,
BibUploadIndicatorsTest,
BibUploadUpperLowerCaseTest,
BibUploadStrongTagsTest,
BibUploadFFTModeTest)
#test_suite = make_test_suite(BibUploadStrongTagsTest,)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/elmsubmit/doc/admin/elmsubmit-admin-guide.webdoc b/modules/elmsubmit/doc/admin/elmsubmit-admin-guide.webdoc
index fa7d5aba3..4fedb4fe7 100644
--- a/modules/elmsubmit/doc/admin/elmsubmit-admin-guide.webdoc
+++ b/modules/elmsubmit/doc/admin/elmsubmit-admin-guide.webdoc
@@ -1,25 +1,25 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: ElmSubmit Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>FIXME.</p>
diff --git a/modules/elmsubmit/lib/elmsubmit_config.py b/modules/elmsubmit/lib/elmsubmit_config.py
index a132c65af..8b13216bc 100644
--- a/modules/elmsubmit/lib/elmsubmit_config.py
+++ b/modules/elmsubmit/lib/elmsubmit_config.py
@@ -1,98 +1,98 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""ElmSubmit configuration parameters."""
__revision__ = "$Id$"
from invenio.config import CFG_SITE_ADMIN_EMAIL, \
- weburl, CFG_SITE_NAME
+ CFG_SITE_URL, CFG_SITE_NAME
# elmsubmit configuration file:
CFG_ELMSUBMIT_FILES = {
'mailprefix': 'mail',
'test_case_1': 'elmsubmit_tests_1.mbox',
'test_case_2': 'elmsubmit_tests_2.mbox',
}
# Messages we need to send to the user, before we've identified the
# correct language to talk to them in (so we assume English!):
# pylint: disable-msg=C0301
CFG_ELMSUBMIT_NOLANGMSGS = {'bad_email': 'Your email could not be parsed correctly to discover a submission. Please check your email client is functioning correctly.',
- 'bad_submission': 'The submission data that you have provided could not be parsed correctly. Please visit <%s> for a description of the correct format.' % weburl,
+ 'bad_submission': 'The submission data that you have provided could not be parsed correctly. Please visit <%s> for a description of the correct format.' % CFG_SITE_URL,
'missing_type': 'The submission data that you have provided does not contain a TYPE field. This is mandatory for all submissions.',
'unsupported_type': 'The TYPE field of your submission does not contain a recognized value.',
'missing_fields_1': 'Your submission of type',
'missing_fields_2': 'does not contain all the required fields:',
'bad_field': 'This field does not validate correctly:',
'correct_format': 'It must be formatted as follows:',
'missing_attachment': 'We could not find the following file attached to your submission email:',
'temp_problem': 'There is a temporary problem with %s\'s email submission interface. Please retry your submission again shortly.' % CFG_SITE_NAME}
CFG_ELMSUBMIT_SERVERS = {'smtp': 'localhost'}
CFG_ELMSUBMIT_PEOPLE = {'admin': CFG_SITE_ADMIN_EMAIL}
# fields required in the submission mail
CFG_ELMSUBMIT_REQUIRED_FIELDS = ['title',
'author',
'date',
'files']
# defines the mapping of metadata fields to their marc codes
# mapping code as a list means the first element is mapped to the first element
# of the list, and the rest to the second
CFG_ELMSUBMIT_MARC_MAPPING = {'author': ['100__a', '700__a'],
'title': '245__a',
'subtitle': '245__b',
'photocaption': '246__b',
'subject': '65017a',
'secondary_subject': '65027a',
'email': '8560_f',
'files': ['FFT__a', 'FFT__a'],
'affiliation': ['100__u', '700__u'],
'language': '041__a',
'abstract': '520__a',
'keywords': '6531_a',
'OAIid': '909COo',
'PrimaryReportNumber': '037__a',
'AdditionalReportNumber': '088__a',
'series': ['490__a','490__v'],
'year': '260__a',
'note': '500__a',
#test tags used in test cases
'test1': '111__a',
'test2': '111__b',
'test3': '111__c',
'test4': '111__d',
'test5': '111__e'
}
# the list of the fields determines which subfields should be joined into a
# single datafield
CFG_ELMSUBMIT_MARC_FIELDS_JOINED = {'700__': [['a', 'u']],
'100__': [['a', 'u']],
#test tags
'111__': [['a','c'],['b','d']]
}
diff --git a/modules/miscutil/doc/hacking/miscutil-dateutils.webdoc b/modules/miscutil/doc/hacking/miscutil-dateutils.webdoc
index 15078104d..f36215668 100644
--- a/modules/miscutil/doc/hacking/miscutil-dateutils.webdoc
+++ b/modules/miscutil/doc/hacking/miscutil-dateutils.webdoc
@@ -1,179 +1,179 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Date library -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="<WEBURL>/help/hacking/miscutil-internals">MiscUtil Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="<CFG_SITE_URL>/help/hacking/miscutil-internals">MiscUtil Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>These are the functions and methodologies for date handling in CDS Invenio.</p>
<h2>Contents</h2>
<ol>
<li><a href="#overview">Overview</a></li>
<li><a href="#converting">Converting dates</a></li>
<li><a href="#i18n">Internationalizing parts of dates</a></li>
<li><a href="#generating">Generating GUI elements</a></li>
</ol>
<h2>1. <a name="overview">Overview</a></h2>
Three ways of representing dates are handled by this library:
<dl>
<dt><b>datetext:</b></dt>
<dd>textual format =&gt; <code>'YEAR-MONTH-DAY HOUR:MINUTE:SECOND'</code><br />
e.g. <code>'2005-11-16 15:11:44'</code><br />
default value: <code>'0000-00-00 00:00:00'</code><br />
This format is the the one used by current Database for storing dates.
</dd>
<dt><b>datestruct:</b></dt>
<dd>
tuple format =&gt; see <a href="http://docs.python.org/lib/module-time.html">Python reference</a> <br />
<code>(YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEKDAY, YEARDAY, DAYLIGHT)</code><br />
e.g. <code>(2005, 11, 16, 15, 11, 44, 2, 320, 0)</code><br />
default value: <code>(0, 0, 0, 0, 0, 0, 0, 0, 0)</code><br />
This format is the one used by python. Time module provides methods for
calculations.
</dd>
<dt><b>dategui:</b></dt>
<dd>
textual format for output =&gt; <code>'DAY MONTH YEAR, HOUR:MINUTE'</code><br />
e.g. <code>'16 nov 2005, 15:11'</code>
default value: <code>&#95;("N/A")</code>
</dd>
</dl>
<p>The dateutils python module provides ways of converting between these formats.
The default value is used whenever a date is non-valid (for python, dates before
epoch are unvalid!).</p>
<h2>2. <a name="converting">Converting dates</a></h2>
The functions for conversion are listed below.
<dl>
<dt><b><code>convert&#95;datestruct&#95;to&#95;dategui(datestruct, ln=CFG_SITE_LANG)</code></b></dt>
<dd>
<code>(2005, 11, 16, 15, 11, 44, 2, 320, 0) =&gt; '16 nov 2005, 15:11'</code><br/>
Month is internationalized
</dd>
<dt><b><code>convert&#95;datestruct&#95;to&#95;datetext(datestruct)</code></b></dt>
<dd>
<code>(2005, 11, 16, 15, 11, 44, 2, 320, 0) =&gt; '2005-11-16 15:11:57'</code>
</dd>
<dt><b><code>convert&#95;datetext&#95;to&#95;dategui(datetext, ln=CFG_SITE_LANG)</code></b></dt>
<dd>
<code>'2005-11-16 15:11:57' =&gt; '16 nov 2005, 15:11'</code><br />
Month is internationalized
</dd>
<dt><b><code>convert&#95;datetext&#95;to&#95;datestruct(datetext)</code></b></dt>
<dd>
<code>'2005-11-16 15:11:57' =&gt; (2005, 11, 16, 15, 11, 44, 2, 320, 0)</code>
</dd>
<dt><b><code>get&#95;datestruct(year, month, day)</code></b></dt>
<dd>
<code>year=2005, month=11, day=16 =&gt; (2005, 11, 16, 0, 0, 0, 2, 320, -1)</code>
</dd>
<dt><b><code>get&#95;datetext(year, month, day)</code></b></dt>
<dd>
<code>year=2005, month=11, day=16 =&gt; '2005-11-16 00:00:00'</code>
</dd>
</dl>
<h2>3. <a name="i18n">Internationalizing parts of dates</a></h2>
The following functions provide means of internationalizing part of dates.
<dl>
<dt>
<b><code>get&#95;i18n&#95;day&#95;name(day&#95;nb, display='short', ln=CFG_SITE_LANG)</code></b>
</dt>
<dd>
get the string representation of a weekday, internationalized<br />
<code>
@param day&#95;nb: number of weekday UNIX like. =&gt; 0=Sunday<br />
@param ln: language for output<br />
@return the string representation of the day
</code>
</dd>
<dt>
<b><code>get&#95;i18n&#95;month&#95;name(month&#95;nb, display='short', ln=CFG_SITE_LANG)</code></b>
</dt>
<dd>
get a non-numeric representation of a month, internationalized.<br />
<code>
@param month&#95;nb: number of month, (1 based!) =&gt; 1=jan,..,12=dec<br />
@param ln: language for output<br />
@return the string representation of month
</code>
</dd>
</dl>
<h2>4. <a name="generating">Generating GUI elements</a></h2>
The following functions create HTML fields for date selection:
<dl>
<dt>
<b><code>create&#95;day&#95;selectbox(name, selected&#95;day=0, ln=CFG_SITE_LANG)</code></b>
</dt>
<dd>Creates an HTML menu for day selection. (<code>0..31</code> values).<br />
<code>@param name: name of the control (i.e. name of the var you'll get)<br />
@param selected&#95;day: preselect a day. Use 0 for the label 'Day'<br />
@param ln: language of the menu<br />
@return html as string
</code>
</dd>
<dt>
<b><code>create&#95;month&#95;selectbox(name, selected&#95;month=0, ln=CFG_SITE_LANG)</code></b>
</dt>
<dd>Creates an HTML menu for month selection. Value of selected field is numeric<br />
<code>@param name: name of the control (your form will be sent with name=value...)<br />
@param selected&#95;month: preselect a month. use 0 for the Label 'Month'<br />
@param ln: language of the menu<br />
@return html as string
</code>
</dd>
<dt>
<b><code>create&#95;year&#95;inputbox(name, value=0)</code></b>
</dt>
<dd>
Creates an HTML field (simple input) for year selection.<br />
<code>@param name: name of the control (i.e. name of the variable you'll get)<br />
@param value: prefilled value (int)<br />
@return html as string
</code>
<dt>
<b><code>create&#95;year&#95;selectbox(name, from&#95;year=-1, length=10, selected&#95;year=0, ln=CFG_SITE_LANG)</code></b>
</dt>
<dd>
Creates an HTML menu (dropdownbox) for year selection.<br />
<code>@param name: name of control( i.e. name of the variable you'll get)<br />
@param from&#95;year: year on which to begin. if <0 assume it is current year<br />
@param length: number of items in menu<br />
@param selected&#95;year: initial selected year (if in range), else: label is selected<br />
@param ln: language<br />
@return html as string
</code>
</dl>
diff --git a/modules/miscutil/doc/hacking/miscutil-dbquery.webdoc b/modules/miscutil/doc/hacking/miscutil-dbquery.webdoc
index 8894859ad..9cd38b36d 100644
--- a/modules/miscutil/doc/hacking/miscutil-dbquery.webdoc
+++ b/modules/miscutil/doc/hacking/miscutil-dbquery.webdoc
@@ -1,176 +1,176 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Database access API -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="miscutil-internals">MiscUtil Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="miscutil-internals">MiscUtil Internals</a> -->
<!-- WebDoc-Page-Navbar-Select: hacking-miscutil-dbquery -->
<p>dbquery module handles automatically connection (and reconnection)
to the database and provides the <code>run_sql()</code> function to
perform SQL queries and <code>run_sql_cached()</code> for rarely
changing SELECT queries. It also exports DB exceptions for the client
code to use (see below).
</p>
<h3><code>run_sql()</code> API</h3>
<p><code>run_sql()</code> signature:
<blockquote>
<pre>
def run_sql(sql, param=None, n=0, with_desc=0):
"""Run SQL on the server with PARAM and return result.
@param param: tuple of string params to insert in the query
(see notes below)
@param n: number of tuples in result (0 for unbounded)
@param with_desc: if true, will return a
DB API 7-tuple describing columns in query
@return: if SELECT, SHOW, DESCRIBE statements: tuples of data, followed
by description if parameter
provided
if INSERT: last row id.
else: SQL result as provided by database
When the site is closed for maintenance (as governed by the
config variable CFG_ACCESS_CONTROL_LEVEL_SITE), do not attempt
to run any SQL queries but return empty list immediately.
Useful to be able to have the website up while MySQL database
is down for maintenance, hot copies, table repairs, etc.
In case of problems, exceptions are returned according to the
Python DB API 2.0. The client code can import them from this
file and catch them.
"""
</pre>
</blockquote>
</p>
<p><code>run_sql()</code> normally escapes its parameters if you
pass them in a tuple. Usually the params must use the string format (<code>%s<code>):
<blockquote>
<pre>
from invenio.dbquery import run_sql
[...]
res = run_sql("SELECT id FROM collection WHERE name=%s", (c,))
if res:
colID = res[0][0]
</pre>
</blockquote>
If you want to escape the parameters yourself in the client code, you
could in principle import and make use of the
function <code>escape_string()</code>:
<blockquote>
<pre>
from invenio.dbquery import run_sql, escape_string
[...]
res = run_sql("SELECT id FROM collection WHERE name='%s'" % escape_string(c), None)
if res:
colID = res[0][0]
</pre>
</blockquote>
but beware, this function is not UTF-8 safe! So better do not use it.
</p>
<p>The <code>run_sql()</code> raises Python DB API 2.0 exceptions that
the client code should catch and handle. An example:
<blockquote>
<pre>
from invenio.dbquery import run_sql, OperationalError
[...]
query = "select citation_data from rnkCITATIONDATA"
try:
compressed_citation_dic = run_sql(query)
except OperationalError:
compressed_citation_dic = []
</pre>
</blockquote>
</p>
<p>For the list of all exceptions and the conditions when they are
raised, see <a href="http://www.python.org/dev/peps/pep-0249/">PEP 249</a>.
<h3>Note for handling date types</h3>
<p>There is an incompatibility in handling date types between MySQLdb 0.9 and MySQLdb 1.2 (while using Python 2.2 or 2.3). If a date field is in the received tuple, its format will be:</p>
<ul>
<li>string with MySQLdb 0.9</li>
<li>datetime with MySQLdb 1.2</li>
</ul>
<p>As Python 2.2 doesn't provide <code>datetime</code> class, handling of this
problem should be done for backwards compatibility reasons. The
solution is to force MySQL to convert date to a textual format:</p>
<pre>
SELECT DATE&#95;FORMAT(date&#95;field,'%%Y-%%m-%%d %%H:%%i:%%s') FROM table
</pre>
<p>This conversion will return a datetext format as described in <a href="miscutil-dateutils">dateutils library</a><code>(YEAR-MONTH-DAY HOUR:MINUTE:SECOND)</code>.</p>
<h3><code>run_sql_cached()</code> API</h3>
<p> If you execute a certain SELECT query often, you can
use <code>run_sql_cached()</code> that will cache its result in memory
and return it faster next time. The function signature and usage is
similar to the one known from <code>run_sql()</code>:
<blockquote>
<pre>
def run_sql_cached(sql, param=None, n=0, with_desc=0, affected_tables=[]):
"""
Run the SQL query and cache the SQL command for later reuse.
@param param: tuple of string params to insert in the query
(see notes below)
@param n: number of tuples in result (0 for unbounded)
@param with_desc: if true, will return a
DB API 7-tuple describing columns in query
@param affected_tables is a list of tablenames of affected tables,
used to decide whether we should update the cache or whether we
can return cached result, depending on the last modification time
for corresponding tables. If empty, and if the cached result is
present in the cache, always return the cached result without
recomputing it. (This is useful to speed up queries that operate
on objects that virtually never change, e.g. list of defined
logical fields, that remain usually constant in between Apache
restarts. Note that this may be a bit dangerous as a default for
any query.)
@return the result as provided by run_sql
Note that it is pointless and even wrong to use this function with
SQL commands different from SELECT.
"""
</pre>
</blockquote>
</p>
<h3>Logging SQL Queries</h3>
<p>If you want to investigate some DB related problems, note that you
can uncomment some lines in <code>dbquery.py</code> to obtain detailed
log of every SQL query and its parameters. Look for
string <code>log_sql_query</code> to know more.
</p>
diff --git a/modules/miscutil/doc/hacking/miscutil-errorlib.webdoc b/modules/miscutil/doc/hacking/miscutil-errorlib.webdoc
index 686f348b2..f54fe1ada 100644
--- a/modules/miscutil/doc/hacking/miscutil-errorlib.webdoc
+++ b/modules/miscutil/doc/hacking/miscutil-errorlib.webdoc
@@ -1,319 +1,319 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Error Library -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="miscutil-internals">MiscUtil Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="miscutil-internals">MiscUtil Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>
These are the functions and methodologies for error handling in CDS Invenio.
</p>
<h2>Contents</h2>
<ol>
<li><a href="#overview">Overview</a></li>
<li><a href="#creating">Creating errors</a></li>
<li><a href="#using">Using error library</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
</ol>
<h2>1. <a name="overview">Overview</a></h2>
<p>This API handles two concepts: Errors and Warnings.
<p>An error is an unexpected behavior that leads to the stopping of current process.
Discussing of web pages, errors should be displayed by instead of the
requested page. Errors are logged into <code>cds-invenio/var/log/cds-invenio.err</code>.
Errors can be logged with client information and a tracestack.</p>
<p>A warning is an unexpected behavior that can be ignored. Warnings are logged into
<code>cds-invenio/var/log/cds-invenio.log</code> with just the warning message.</p>
<p>Errors and warnings should be internationalized (see <a href="#i18n">below</a>).</p>
<h2>2. <a name="creating">Creating errors</a></h2>
<h3>2.1 Configuration file</h3>
<p>Every module should create a file containing error definitions, warning
definitions, variables avoiding &quot;magic&quot; number or strings, etc.</p>
<p>This file has to be named against a convention:</p>
<pre>
&lt;module-name&gt;&#95;config.py
</pre>
<p>e.g. <code>webmessage&#95;config.py</code> for the WebMessage module.</p>
<h3>2.2 Dictionaries of errors</h3>
<p>Errors and warnings are eventually stored into dictionaries. The dictionaries
are to be named against the following convention:</p>
<pre>
CFG&#95;&lt;MODULENAME&gt;&#95;ERROR&#95;MESSAGES and
CFG&#95;&lt;MODULENAME&gt;&#95;WARNING&#95;MESSAGES
</pre>
<p>These two dictionaries (one can choose to implement only one if he doesn&apos;t
need warnings, for example) contain an error-name -&gt; displayable message
association.</p>
<p>Errors are to be named against the following convention:</p>
<pre>
ERR&#95;&lt;MODULE-NAME&gt;&#95;ERROR&#95;NAME
</pre>
<p>Please note the use of uppercase.</p>
<p>Warnings can also be named as errors if convenient, and so have to
follow one of these rules:</p>
<pre>
WRN&#95;&lt;MODULE-NAME&gt;&#95;WARNING&#95;NAME or
ERR&#95;&lt;MODULE-NAME&gt;&#95;WARNING&#95;NAME
</pre>
<p>The associated message can obviously contain substituted variables like <code>%s</code>, <code>%d</code>...
<h3><a name="i18n">Internationalization</a></h3>
<p>Errors should also be internationalized. As the config file cannot receive
parameters, this is done by the error handling library. The convenient way that has
been chosen is to nest the classical <code>&#95;()</code> function inside the string.</p>
<p>An internationalized error message should look like this:</p>
<pre>
'&#95;("Internationalized error (%s) message")'
</pre>
<p>A complete example of correct dictionary is given below:</p>
<pre>
CFG&#95;WEBCOMMENT&#95;ERROR&#95;MESSAGES =
{ 'ERR&#95;WEBCOMMENT&#95;RECID&#95;INVALID' : '&#95;("%i is an invalid record ID")',
'ERR&#95;WEBCOMMENT&#95;RECID&#95;NAN' : '&#95;("Record ID %i is not a number")',
'ERR&#95;WEBCOMMENT&#95;UID&#95;INVALID' : '&#95;("%i is an invalid user ID")'
}
</pre>
<h2>3. <a name="using">Using error library</a></h2>
<h3>3.1 From a web interface</h3>
<p>When displaying a page, the <code>modules/webstyle/lib/webpage.py</code> python module should
be used. This module provides a <code>page()</code> function, convenient for webpage output,
which can handle errors (display and log).<br />
A call to this function should use the following arguments, assuming that language
information is stored in a variable called <code>ln</code>, and request information
are stored in req (will be used for IP logging, for example):</p>
<pre>
page(...,
req=req,
language=ln,
errors=error&#95;list,
warnings=warnings&#95;list,
...)
</pre>
<p>list of errors and warnings are behaving the same way. They are lists of tuples:</p>
<pre>
[(error&#95;name, param1, ..., paramN), ...]
</pre>
<p>The params are used to represent substitued values in messages. For example if
you want to throw one of the errors above, error&#95;list should look like this:</p>
<pre>
error&#95;list = [('ERR&#95;WEBCOMMENT&#95;RECID&#95;INVALID', 123456)]
</pre>
<h4>Example</h4>
<p>Business logic should be separated from web interface. We consider three files in the
following (real) example:
<ol>
<li><code>webmessage_webinterface.py</code>, which is the page as viewed by a browser,</li>
<li><code>webmessage.py</code>, which contains the business logic,</li>
<li><code>webmessage&#95;config</code>, which contains error definitions</li>
</ol>
<p>In this example, a user tries to read a message. We must ensure he doesn't
read another message, and that this message really exist in the system. For
a more convenient reading, some (non error-related) parts of code have been suppressed.</p>
<h5>webmessage&#95;config.py</h5>
<pre>
&#35; error messages. (should not happen, except in case of reload, or url altering)
CFG&#95;WEBMESSAGE&#95;ERROR&#95;MESSAGES = \
{ 'ERR&#95;WEBMESSAGE&#95;NOTOWNER': '&#95;("This message is not in your mailbox")',
'ERR&#95;WEBMESSAGE&#95;NONICKNAME':'&#95;("No nickname or user for uid #%s")',
'ERR&#95;WEBMESSAGE&#95;NOMESSAGE': '&#95;("This message doesn\'t exist")'
}
</pre>
<h5>webmessage.py: business logic</h5>
<pre>
from invenio.webmessage&#95;config import CFG&#95;WEBMESSAGE&#95;ERROR&#95;MESSAGES
def perform&#95;request&#95;display&#95;msg(uid, msgid, ln=CFG_SITE_LANG):
uid = wash&#95;url&#95;argument(uid, 'int')
msgid = wash&#95;url&#95;argument(msgid, 'int')
ln = wash&#95;language(ln)
errors = []
warnings = []
body = ""
if (check&#95;user&#95;owns&#95;message(uid, msgid) == 0):
&#35; The user doesn't own this message
errors.append(('ERR&#95;WEBMESSAGE&#95;NOTOWNER',))
else:
(msg&#95;id, ...) = get&#95;message(uid, msgid)
if (msg&#95;id == ""):
&#35; The message exists in table user&#95;msgMESSAGE
&#35; but not in table msgMESSAGE => table inconsistency
errors.append(('ERR&#95;WEBMESSAGE&#95;NOMESSAGE',))
else:
body = webmessage&#95;templates.tmpl&#95;display&#95;msg( ... )
return (body, errors, warnings)
</pre>
<h5>webmessage_webinterface.py: web interface</h5>
<pre>
from invenio.webpage import page
from invenio.webmessage import perform&#95;request&#95;display&#95;msg
def display&#95;msg(req, msgid=-1, ln=CFG_SITE_LANG):
&#95; = gettext&#95;set&#95;language(ln)
# Generate content
(body, errors, warnings) = perform&#95;request&#95;display&#95;msg(uid, msgid, ln)
title = &#95;("Read a message")
return page(title = title,
body = body,
navtrail = get&#95;navtrail(ln, title),
uid = uid,
lastupdated = &#95;&#95;lastupdated&#95;&#95;,
req = req,
language = ln,
errors = errors,
warnings = warnings)
</pre>
<h3>3.2 From a command line interface</h3>
<p>The following functions can be useful (see source code for other functions):</p>
<pre>
get&#95;msgs&#95;for&#95;code&#95;list(code&#95;list, stream='error', ln=CFG_SITE_LANG)
Returns formatted strings for the given errors
@param code&#95;list: list of tuples [(err&#95;name, arg1, ..., argN), ...]
@param stream: 'error' or 'warning'
@return list of tuples of length 2 [('ERR&#95;...', err&#95;msg), ...]
if code&#95;list empty, will return None.
if errors retrieving error messages, will append an error to
the list
register&#95;errors(errors&#95;or&#95;warnings&#95;list, stream, req=None)
log errors to invenio.err and warnings to invenio.log
errors will be logged with client information (if req is given)
and a tracestack
warnings will be logged with just the warning message
@param errors&#95;or&#95;warnings&#95;list: list of tuples (err&#95;name, err&#95;msg)
@param stream: 'error' or 'warning'
@param req = mod&#95;python request
@return integer 1 if successfully wrote to stream, integer 0 if not
will append another error to errors&#95;list if unsuccessful
send&#95;error&#95;report&#95;to&#95;admin(header, url, time, browser, client,
error, sys&#95;error, traceback)
Sends an email to the admin with client info and tracestack
</pre>
<h4>Example</h4>
<p>In the following example, two files are used:</p>
<ol>
<li><code>webmessage&#95;config</code>, containing error messages</li>
<li><code>webmessage&#95;example&#95;bin.py</code>, containing business logic</li>
</ol>
<p>Scenario: a function receives an error and wants to register it only if it is not a
messaging error</p>
<h5>webmessage&#95;config.py</h5>
<pre>
&#35; error messages. (should not happen, except in case of reload, or url altering)
CFG&#95;WEBMESSAGE&#95;ERROR&#95;MESSAGES = \
{ 'ERR&#95;WEBMESSAGE&#95;NOTOWNER': '&#95;("This message is not in your mailbox")',
'ERR&#95;WEBMESSAGE&#95;NONICKNAME':'&#95;("No nickname or user for uid #%s")',
'ERR&#95;WEBMESSAGE&#95;NOMESSAGE': '&#95;("This message doesn\'t exist")'
}
</pre>
<h5>webmessage&#95;example&#95;bin.py</h5>
<pre>
from invenio.webmessage&#95;config import CFG&#95;WEBMESSAGE&#95;ERROR&#95;MESSAGES
from invenio.errorlib import get&#95;msgs&#95;for&#95;code&#95;list, register&#95;errors
def handle&#95;error(error):
errorlist = get&#95;msgs&#95;for&#95;code&#95;list([error])
&#35; error is a tuple of error name, arguments => we only need the name
if CFG&#95;WEBMESSAGE&#95;ERROR&#95;MESSAGES[error[0]]:
print("Error in webmessage: %s" % errorlist[0][1])
else:
for error in errorlist:
print("Error: %s" % error[1])
register&#95;errors(errorlist, 'error')
</pre>
<h2>4. <a name="troubleshooting">Troubleshooting</a></h2>
<p>MiscUtil can generate errors. See miscutil&#95;config.py for a complete list.
One can see below some usual errors and their solutions:</p>
<dl>
<dt><b><code>ERR&#95;MISCUTIL&#95;IMPORT&#95;ERROR</code></b></dt>
<dd>The <code>&lt;module-name&gt;&#95;config.py</code> file has not been found. Check it
has the correct name and is deployed.<br />
Check that the error is named following this pattern:
<pre>
WRN&#95;&lt;MODULE-NAME&gt;&#95;WARNING&#95;NAME or
ERR&#95;&lt;MODULE-NAME&gt;&#95;WARNING&#95;NAME
</pre>
</dd>
<dt><b><code>ERR&#95;MISCUTIL&#95;NO&#95;DICT</code></b></dt>
<dd>No dictionary could be found in <code>&lt;module-name&gt;&#95;config.py</code>. Check
that your dictionary is correctly named:
<pre>
CFG&#95;&LT;MODULENAME&GT;&#95;ERROR&#95;MESSAGES
</pre>
You could also have inverted errors and warnings if only one dictionary was provided.<br/>
This can also happen when using direct API if the <code>stream</code> argument is misspelled.
</dd>
<dt><b><code>ERR&#95;MISCUTIL&#95;NO&#95;MESSAGE&#95;IN&#95;DICT</code></b></dt>
<dd>A dictionary was found but not the error in it. You probably misspelled
<code>error&#95;name</code>, or inverted errors and warnings dictionaries.
</dd>
<dt><b><code>ERR&#95;MISCUTIL&#95;UNDEFINED&#95;ERROR</code></b></dt>
<dd>The library couldn't guess the name of module. Check that the error name is beginning
with <code>ERR&#95;MODULE-NAME&#95;</code> or <code>WRN&#95;MODULE-NAME&#95;</code>. This library uses
underscores as separators to guess module name.
</dd>
<dt><b><code>ERR&#95;MISCUTIL&#95;TOO&#95;MANY&#95;ARGUMENT</code></b></dt>
<dd>As the library was rendering the display of error, a surnumerous text substitute was
found (surnumerous are ignored for final rendering, and this error is appened to list of errors):
<pre>
# Module knights:
'ERR&#95;KNIGHTS': '&#95;("We are the knights who say %s!")'
errors = ('ERR&#95;KNIGHTS', 'ni', 'ni')
</pre>
</dd>
<dt><b><code>ERR&#95;MISCUTIL&#95;TOO&#95;FEW&#95;ARGUMENT</code></b></dt>
<dd>Not enough arguments (text substitutes) were given for an error. Missing ones are
replaced by <code>'???'</code>:
<pre>
# Module knights
'ERR&#95;KNIGHTS': '&#95;("We are the knights who say %s! We demand a %s")'
errors = ('ERR&#95;KNIGHTS', 'ni') # so, where is the shrubbery??
</pre>
</dd>
<dt><b><code>ERR&#95;MISCUTIL&#95;BAD&#95;ARGUMENT&#95;TYPE</code></b></dt>
<dd>Your arguments (text substitutes) did not match with the error declaration<br />
e.g. inversion between integer (<code>%i</code>) and string (<code>%s</code>)
</dd>
</dl>
diff --git a/modules/miscutil/doc/hacking/miscutil-internals.webdoc b/modules/miscutil/doc/hacking/miscutil-internals.webdoc
index bdc82f30d..c789e0d81 100644
--- a/modules/miscutil/doc/hacking/miscutil-internals.webdoc
+++ b/modules/miscutil/doc/hacking/miscutil-internals.webdoc
@@ -1,39 +1,39 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: MiscUtil Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
This page summarizes all the information suitable to dig inside
the MiscUtil internals.
<blockquote>
<dl>
<dt><a href="miscutil-dbquery">Database access API</a> </dt>
<dd>Explains how to access CDS Invenio database from your Python programs.</dd>
<dt><a href="miscutil-errorlib">Error handling library</a> </dt>
<dd>Explains how to create errors and warnings and how to manage them.</dd>
<dt><a href="miscutil-dateutils">Date handling library</a> </dt>
<dd>Explains how to handle dates in CDS Invenio. All mentioned functions are represented with signature and additional explanations.</dd>
</dl>
</blockquote>
diff --git a/modules/miscutil/lib/errorlib.py b/modules/miscutil/lib/errorlib.py
index eb6ad1bc3..0f380155d 100644
--- a/modules/miscutil/lib/errorlib.py
+++ b/modules/miscutil/lib/errorlib.py
@@ -1,452 +1,452 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Error handling library """
__revision__ = "$Id$"
import traceback
import os
import sys
import time
from cStringIO import StringIO
-from invenio.config import CFG_SITE_LANG, CFG_LOGDIR, CFG_WEBALERT_ALERT_ENGINE_EMAIL, CFG_SITE_ADMIN_EMAIL, CFG_SITE_SUPPORT_EMAIL, CFG_SITE_NAME, weburl
+from invenio.config import CFG_SITE_LANG, CFG_LOGDIR, CFG_WEBALERT_ALERT_ENGINE_EMAIL, CFG_SITE_ADMIN_EMAIL, CFG_SITE_SUPPORT_EMAIL, CFG_SITE_NAME, CFG_SITE_URL
from invenio.miscutil_config import CFG_MISCUTIL_ERROR_MESSAGES
from invenio.urlutils import wash_url_argument
from invenio.messages import wash_language, gettext_set_language
from invenio.dateutils import convert_datestruct_to_datetext
def get_client_info(req):
"""
Returns a dictionary with client information
@param req: mod_python request
"""
try:
return \
{ 'host' : req.hostname,
'url' : req.unparsed_uri,
'time' : convert_datestruct_to_datetext(time.localtime()),
'browser' : req.headers_in.has_key('User-Agent') and req.headers_in['User-Agent'] or "N/A",
'client_ip' : req.connection.remote_ip
}
except:
return {}
def get_pretty_wide_client_info(req):
"""Return in a pretty way all the avilable information about the current
user/client"""
if req:
from invenio.webuser import collect_user_info
user_info = collect_user_info(req)
keys = user_info.keys()
keys.sort()
max_key = max([len(key) for key in keys])
ret = ""
fmt = "%% %is: %%s\n" % max_key
for key in keys:
if key in ('uri', 'referer'):
ret += fmt % (key, "<%s>" % user_info[key])
else:
ret += fmt % (key, user_info[key])
if ret.endswith('\n'):
return ret[:-1]
else:
return ret
else:
return "No client information available"
def get_tracestack():
"""
If an exception has been caught, return the system tracestack or else return tracestack of what is currently in the stack
"""
if traceback.format_tb(sys.exc_info()[2]):
delimiter = "\n"
tracestack_pretty = "Traceback: \n%s" % delimiter.join(traceback.format_tb(sys.exc_info()[2]))
else:
tracestack = traceback.extract_stack()[:-1] #force traceback except for this call
tracestack_pretty = "%sForced traceback (most recent call last)" % (' '*4,)
for trace_tuple in tracestack:
tracestack_pretty += """
File "%(file)s", line %(line)s, in %(function)s
%(text)s""" % \
{ 'file' : trace_tuple[0],
'line' : trace_tuple[1],
'function' : trace_tuple[2],
'text' : trace_tuple[3] is not None and str(trace_tuple[3]) or ""
}
return tracestack_pretty
def register_exception(force_stack=False, stream='error', req=None, prefix='', suffix='', alert_admin=False):
"""
log error exception to invenio.err and warning exception to invenio.log
errors will be logged with client information (if req is given)
@param force_stack: when True stack is always printed, while when False,
stack is printed only whenever the Exception type is not containing the
word Invenio
@param stream: 'error' or 'warning'
@param req = mod_python request
@param prefix a message to be printed before the exception in
the log
@param suffix a message to be printed before the exception in
the log
@param alert_admin wethever to send the exception to the administrator via email
@return 1 if successfully wrote to stream, 0 if not
"""
try:
## Let's extract exception information
exc_info = sys.exc_info()
if exc_info[0]:
## We found an exception.
## We want to extract the name of the Exception
exc_name = exc_info[0].__name__
exc_value = str(exc_info[1])
## Let's record when and where and what
www_data = "%(time)s -> %(name)s: %(value)s" % {
'time' : time.strftime("%Y-%m-%d %H:%M:%S"),
'name' : exc_name,
'value' : exc_value
}
## Let's retrieve contextual user related info, if any
try:
client_data = get_pretty_wide_client_info(req)
except Exception, e:
client_data = "Error in retrieving contextual information: %s" % e
## Let's extract the traceback
if not exc_name.startswith('Invenio') or force_stack:
## We put a large traceback only if requested
## or the Exception is not an Invenio one.
tracestack = traceback.extract_stack()[-5:-2]
tracestack_data = "%sForced traceback (most recent call last)" % (' '*4,)
for trace_tuple in tracestack:
tracestack_data += """
File "%(file)s", line %(line)s, in %(function)s
%(text)s""" % \
{ 'file' : trace_tuple[0],
'line' : trace_tuple[1],
'function' : trace_tuple[2],
'text' : trace_tuple[3] is not None and str(trace_tuple[3]) or ""
}
else:
tracestack_data = ""
exception_data = StringIO()
## Let's print the exception (and the traceback)
traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], None, exception_data)
exception_data = exception_data.getvalue()
if exception_data.endswith('\n'):
exception_data = exception_data[:-1]
log_stream = StringIO()
email_stream = StringIO()
## If a prefix was requested let's print it
if prefix:
print >> log_stream, prefix
print >> email_stream, prefix
- print >> email_stream, "The following problem occurred on <%s>" % weburl
+ print >> email_stream, "The following problem occurred on <%s>" % CFG_SITE_URL
print >> email_stream, ">>> Registered exception"
print >> log_stream, www_data
print >> email_stream, '%s\n' % www_data
print >> email_stream, ">>> User details"
print >> log_stream, client_data
print >> email_stream, '%s\n' % client_data
print >> email_stream, ">>> Traceback details"
if tracestack_data:
print >> log_stream, tracestack_data
print >> email_stream, tracestack_data
print >> log_stream, exception_data
print >> email_stream, '%s\n' % exception_data
## If a suffix was requested let's print it
if suffix:
print >> log_stream, suffix
print >> email_stream, suffix
log_text = log_stream.getvalue()
email_text = email_stream.getvalue()
## Preparing the exception dump
stream = stream=='error' and 'err' or 'log'
## We now have the whole trace
written_to_log = False
try:
## Let's try to write into the log.
open(os.path.join(CFG_LOGDIR, 'invenio.' + stream), 'a').write(log_text)
written_to_log = True
finally:
if alert_admin or not written_to_log:
## If requested or if it's impossible to write in the log
from invenio.mailutils import send_email
- send_email(CFG_SITE_ADMIN_EMAIL, CFG_SITE_ADMIN_EMAIL, subject='Registered exception at %s' % weburl, content=email_text, header='', footer='')
+ send_email(CFG_SITE_ADMIN_EMAIL, CFG_SITE_ADMIN_EMAIL, subject='Registered exception at %s' % CFG_SITE_URL, content=email_text, header='', footer='')
return 1
else:
return 0
except Exception, e:
print >> sys.stderr, "Error in registering exception to '%s': '%s'" % (CFG_LOGDIR + '/invenio.' + stream, e)
return 0
def register_errors(errors_or_warnings_list, stream, req=None):
"""
log errors to invenio.err and warnings to invenio.log
errors will be logged with client information (if req is given) and a tracestack
warnings will be logged with just the warning message
@param errors_or_warnings_list: list of tuples (err_name, err_msg)
err_name = ERR_ + %(module_directory_name)s + _ + %(error_name)s #ALL CAPS
err_name must be stored in file: module_directory_name + _config.py
as the key for dict with name: CFG_ + %(module_directory_name)s + _ERROR_MESSAGES
@param stream: 'error' or 'warning'
@param req = mod_python request
@return tuple integer 1 if successfully wrote to stream, integer 0 if not
will append another error to errors_list if unsuccessful
"""
client_info_dict = ""
if stream == "error":
# call the stack trace now
tracestack_pretty = get_tracestack()
# if req is given, get client info
if req:
client_info_dict = get_client_info(req)
if client_info_dict:
client_info = \
'''URL: http://%(host)s%(url)s
Browser: %(browser)s
Client: %(client_ip)s''' % client_info_dict
else:
client_info = "No client information available"
else:
client_info = "No client information available"
# check arguments
errors_or_warnings_list = wash_url_argument(errors_or_warnings_list, 'list')
stream = wash_url_argument(stream, 'str')
for etuple in errors_or_warnings_list:
etuple = wash_url_argument(etuple, 'tuple')
# check stream arg for presence of [error,warning]; when none, add error and default to warning
if stream == 'error':
stream = 'err'
elif stream == 'warning':
stream = 'log'
else:
stream = 'log'
error = 'ERR_MISCUTIL_BAD_FILE_ARGUMENT_PASSED'
errors_or_warnings_list.append((error, eval(CFG_MISCUTIL_ERROR_MESSAGES[error])% stream))
# update log_errors
stream_location = os.path.join(CFG_LOGDIR, '/invenio.' + stream)
errors = ''
for etuple in errors_or_warnings_list:
try:
errors += "%s%s : %s \n " % (' '*4*7+' ', etuple[0], etuple[1])
except:
errors += "%s%s \n " % (' '*4*7+' ', etuple)
if errors:
errors = errors[(4*7+1):-3] # get rid of begining spaces and last '\n'
msg = """
%(time)s --> %(errors)s%(error_file)s""" % \
{ 'time' : client_info_dict and client_info_dict['time'] or time.strftime("%Y-%m-%d %H:%M:%S"),
'errors' : errors,
'error_file' : stream=='err' and "\n%s%s\n%s\n" % (' '*4, client_info, tracestack_pretty) or ""
}
try:
stream_to_write = open(stream_location, 'a+')
stream_to_write.writelines(msg)
stream_to_write.close()
return_value = 1
except :
error = 'ERR_MISCUTIL_WRITE_FAILED'
errors_or_warnings_list.append((error, CFG_MISCUTIL_ERROR_MESSAGES[error] % stream_location))
return_value = 0
return return_value
def get_msg_associated_to_code(err_code, stream='error'):
"""
Returns string of code
@param code: error or warning code
@param stream: 'error' or 'warning'
@return tuple (err_code, formatted_message)
"""
err_code = wash_url_argument(err_code, 'str')
stream = wash_url_argument(stream, 'str')
try:
module_directory_name = err_code.split('_')[1].lower()
module_config = module_directory_name + '_config'
module_dict_name = "CFG_" + module_directory_name.upper() + "_%s_MESSAGES" % stream.upper()
module = __import__(module_config, globals(), locals(), [module_dict_name])
module_dict = getattr(module, module_dict_name)
err_msg = module_dict[err_code]
except ImportError:
error = 'ERR_MISCUTIL_IMPORT_ERROR'
err_msg = CFG_MISCUTIL_ERROR_MESSAGES[error] % (err_code,
module_config)
err_code = error
except AttributeError:
error = 'ERR_MISCUTIL_NO_DICT'
err_msg = CFG_MISCUTIL_ERROR_MESSAGES[error] % (err_code,
module_config,
module_dict_name)
err_code = error
except KeyError:
error = 'ERR_MISCUTIL_NO_MESSAGE_IN_DICT'
err_msg = CFG_MISCUTIL_ERROR_MESSAGES[error] % (err_code,
module_config + '.' + module_dict_name)
err_code = error
except:
error = 'ERR_MISCUTIL_UNDEFINED_ERROR'
err_msg = CFG_MISCUTIL_ERROR_MESSAGES[error] % err_code
err_code = error
return (err_code, err_msg)
def get_msgs_for_code_list(code_list, stream='error', ln=CFG_SITE_LANG):
"""
@param code_list: list of tuples [(err_name, arg1, ..., argN), ...]
err_name = ERR_ + %(module_directory_name)s + _ + %(error_name)s #ALL CAPS
err_name must be stored in file: module_directory_name + _config.py
as the key for dict with name: CFG_ + %(module_directory_name)s + _ERROR_MESSAGES
For warnings, same thing except:
err_name can begin with either 'ERR' or 'WRN'
dict name ends with _warning_messages
@param stream: 'error' or 'warning'
@return list of tuples of length 2 [('ERR_...', err_msg), ...]
if code_list empty, will return None.
if errors retrieving error messages, will append an error to the list
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
out = []
if type(code_list) is None:
return None
code_list = wash_url_argument(code_list, 'list')
stream = wash_url_argument(stream, 'str')
for code_tuple in code_list:
if not(type(code_tuple) is tuple):
code_tuple = (code_tuple,)
nb_tuple_args = len(code_tuple) - 1
err_code = code_tuple[0]
if stream == 'error' and not err_code.startswith('ERR'):
error = 'ERR_MISCUTIL_NO_ERROR_MESSAGE'
out.append((error, eval(CFG_MISCUTIL_ERROR_MESSAGES[error])))
continue
elif stream == 'warning' and not (err_code.startswith('ERR') or err_code.startswith('WRN')):
error = 'ERR_MISCUTIL_NO_WARNING_MESSAGE'
out.append((error, eval(CFG_MISCUTIL_ERROR_MESSAGES[error])))
continue
(new_err_code, err_msg) = get_msg_associated_to_code(err_code, stream)
if err_msg[:2] == '_(' and err_msg[-1] == ')':
# err_msg is internationalized
err_msg = eval(err_msg)
nb_msg_args = err_msg.count('%') - err_msg.count('%%')
parsing_error = ""
if new_err_code != err_code or nb_msg_args == 0:
# undefined_error or immediately displayable error
out.append((new_err_code, err_msg))
continue
try:
if nb_msg_args == nb_tuple_args:
err_msg = err_msg % code_tuple[1:]
elif nb_msg_args < nb_tuple_args:
err_msg = err_msg % code_tuple[1:nb_msg_args+1]
parsing_error = 'ERR_MISCUTIL_TOO_MANY_ARGUMENT'
parsing_error_message = eval(CFG_MISCUTIL_ERROR_MESSAGES[parsing_error])
parsing_error_message %= code_tuple[0]
elif nb_msg_args > nb_tuple_args:
code_tuple = list(code_tuple)
for dummy in range(nb_msg_args - nb_tuple_args):
code_tuple.append('???')
code_tuple = tuple(code_tuple)
err_msg = err_msg % code_tuple[1:]
parsing_error = 'ERR_MISCUTIL_TOO_FEW_ARGUMENT'
parsing_error_message = eval(CFG_MISCUTIL_ERROR_MESSAGES[parsing_error])
parsing_error_message %= code_tuple[0]
except:
parsing_error = 'ERR_MISCUTIL_BAD_ARGUMENT_TYPE'
parsing_error_message = eval(CFG_MISCUTIL_ERROR_MESSAGES[parsing_error])
parsing_error_message %= code_tuple[0]
out.append((err_code, err_msg))
if parsing_error:
out.append((parsing_error, parsing_error_message))
if not(out):
out = None
return out
def send_error_report_to_admin(header, url, time_msg,
browser, client, error,
sys_error, traceback_msg):
"""
Sends an email to the admin with client info and tracestack
"""
from_addr = '%s Alert Engine <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL)
to_addr = CFG_SITE_ADMIN_EMAIL
body = """
The following error was seen by a user and sent to you.
%(contact)s
%(header)s
%(url)s
%(time)s
%(browser)s
%(client)s
%(error)s
%(sys_error)s
%(traceback)s
Please see the %(logdir)s/invenio.err for traceback details.""" % \
{ 'header' : header,
'url' : url,
'time' : time_msg,
'browser' : browser,
'client' : client,
'error' : error,
'sys_error' : sys_error,
'traceback' : traceback_msg,
'logdir' : CFG_LOGDIR,
'contact' : "Please contact %s quoting the following information:" % (CFG_SITE_SUPPORT_EMAIL,)
}
from invenio.mailutils import send_email
send_email(from_addr, to_addr, subject="Error notification", content=body)
diff --git a/modules/miscutil/lib/errorlib_regression_tests.py b/modules/miscutil/lib/errorlib_regression_tests.py
index 2e24ac646..15d3e9e00 100644
--- a/modules/miscutil/lib/errorlib_regression_tests.py
+++ b/modules/miscutil/lib/errorlib_regression_tests.py
@@ -1,80 +1,80 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""errorlib Regression Test Suite."""
__revision__ = "$Id$"
import unittest
import os
from invenio.errorlib import register_exception
-from invenio.config import weburl, CFG_LOGDIR
+from invenio.config import CFG_SITE_URL, CFG_LOGDIR
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class ErrorlibWebPagesAvailabilityTest(unittest.TestCase):
"""Check errorlib web pages whether they are up or not."""
def test_your_baskets_pages_availability(self):
"""errorlib - availability of error sending pages"""
- baseurl = weburl + '/error/'
+ baseurl = CFG_SITE_URL + '/error/'
_exports = ['', 'send']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
class ErrorlibRegisterExceptionTest(unittest.TestCase):
"""Check errorlib register_exception functionality."""
def test_simple_register_exception(self):
"""errorlib - simple usage of register_exception"""
try:
raise Exception('test-exception')
except:
result = register_exception()
log_content = open(os.path.join(CFG_LOGDIR, 'invenio.err')).read()
self.failUnless('test_simple_register_exception' in log_content)
self.failUnless('test-exception' in log_content)
self.assertEqual(1, result, "register_exception have not returned 1")
def test_alert_admin_register_exception(self):
"""errorlib - alerting admin with register_exception"""
try:
raise Exception('test-exception')
except:
result = register_exception(alert_admin=True)
log_content = open(os.path.join(CFG_LOGDIR, 'invenio.err')).read()
self.failUnless('test_alert_admin_register_exception' in log_content)
self.failUnless('test-exception' in log_content)
self.assertEqual(1, result, "register_exception have not returned 1")
test_suite = make_test_suite(ErrorlibWebPagesAvailabilityTest,
ErrorlibRegisterExceptionTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/miscutil/lib/errorlib_webinterface.py b/modules/miscutil/lib/errorlib_webinterface.py
index 578b9c7ae..9480abf97 100644
--- a/modules/miscutil/lib/errorlib_webinterface.py
+++ b/modules/miscutil/lib/errorlib_webinterface.py
@@ -1,109 +1,109 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable-msg=C0301
"""ErrorLib web interface."""
__revision__ = "$Id$"
__lastupdated__ = "$Date$"
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.webpage import page
from invenio.errorlib import send_error_report_to_admin
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import redirect_to_url
from invenio.messages import gettext_set_language
class WebInterfaceErrorPages(WebInterfaceDirectory):
"""Defines the set of /error pages."""
_exports = ['', 'send']
def index(self, req, form):
"""Index page."""
- redirect_to_url(req, '%s/error/send' % weburl)
+ redirect_to_url(req, '%s/error/send' % CFG_SITE_URL)
def send(self, req, form):
"""
Confirmation page of error report sent the admin
parameters are the same as used for the error box. See webstyle_templates.tmpl_error_box
"""
argd = wash_urlargd(form, {'header': (str, "NA"),
'url': (str, "NA"),
'time': (str, "NA"),
'browser': (str, "NA"),
'client': (str, "NA"),
'error': (str, "NA"),
'sys_error': (str, "NA"),
'traceback': (str, "NA"),
'referer': (str, "NA"),
})
_ = gettext_set_language(argd['ln'])
if argd['client'] == "NA":
return page(title=_("Sorry"),
body=_("Cannot send error request, %s parameter missing.") % 'client',
lastupdated=__lastupdated__,
req=req)
elif argd['url'] == "NA":
return page(title=_("Sorry"),
body=_("Cannot send error request, %s parameter missing.") % 'url',
lastupdated=__lastupdated__,
req=req)
elif argd['time'] == "NA":
return page(title=_("Sorry"),
body=_("Cannot send error request, %s parameter missing.") % 'time',
lastupdated=__lastupdated__,
req=req)
elif argd['error'] == "NA":
return page(title=_("Sorry"),
body=_("Cannot send error request, %s parameter missing.") % 'error',
lastupdated=__lastupdated__,
req=req)
else:
send_error_report_to_admin(argd['header'],
argd['url'],
argd['time'],
argd['browser'],
argd['client'],
argd['error'],
argd['sys_error'],
argd['traceback'])
out = """
<p><span class="exampleleader">%(title)s</span>
<p>%(message)s
<p>%(back)s
""" % \
{'title' : _("The error report has been sent."),
'message' : _("Many thanks for helping us make CDS Invenio better."),
'back' : argd['referer']!="NA" and "<a href=\"%s\">back</a>" % (argd['referer'],) or \
_("Use the back button of your browser to return to the previous page.")
}
return page(title=_("Thank you!"),
body=out,
lastupdated=__lastupdated__,
req=req)
diff --git a/modules/miscutil/lib/inveniocfg.py b/modules/miscutil/lib/inveniocfg.py
index 126a82536..93ad6dccb 100644
--- a/modules/miscutil/lib/inveniocfg.py
+++ b/modules/miscutil/lib/inveniocfg.py
@@ -1,844 +1,836 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Invenio configuration and administration CLI tool.
Usage: inveniocfg [options]
General options:
-h, --help print this help
-V, --version print version number
Options to finish your installation:
--create-apache-conf create Apache configuration files
--create-tables create DB tables for Invenio
--drop-tables drop DB tables of Invenio
Options to set up and test a demo site:
--create-demo-site create demo site
--load-demo-records load demo records
--remove-demo-records remove demo records, keeping demo site
--drop-demo-site drop demo site configurations too
--run-unit-tests run unit test suite (need DB connectivity)
--run-regression-tests run regression test suite (need demo site)
Options to update config files in situ:
--update-all perform all the update options
--update-config-py update config.py file from invenio.conf file
--update-dbquery-py update dbquery.py with DB credentials from invenio.conf
--update-dbexec update dbexec with DB credentials from invenio.conf
--update-bibconvert-tpl update bibconvert templates with CFG_SITE_URL from invenio.conf
Options to update DB tables:
--reset-all perform all the reset options
--reset-sitename reset tables to take account of new CFG_SITE_NAME*
--reset-siteadminemail reset tables to take account of new CFG_SITE_ADMIN_EMAIL
--reset-fieldnames reset tables to take account of new I18N names from PO files
Options to help the work:
--list print names and values of all options from conf files
--get <some-opt> get value of a given option from conf files
--conf-dir </some/path> path to directory where invenio*.conf files are [optional]
"""
__revision__ = "$Id$"
from ConfigParser import ConfigParser
import os
import re
import shutil
import sys
def print_usage():
"""Print help."""
print __doc__
def print_version():
"""Print version information."""
print __revision__
def convert_conf_option(option_name, option_value):
"""
Convert conf option into Python config.py line, converting
values to ints or strings as appropriate.
"""
## 1) convert option name to uppercase:
option_name = option_name.upper()
- ## also, adjust some conf names due to backwards compatibility:
- option_name_replace_data = {'CFG_SITE_URL': 'weburl',
- }
- if option_name_replace_data.has_key(option_name):
- option_name = option_name_replace_data[option_name]
-
## 2) convert option value to int or string:
try:
option_value = int(option_value)
except ValueError:
option_value = '"' + option_value + '"'
## 3a) special cases: regexps
if option_name in ['CFG_BIBINDEX_CHARS_ALPHANUMERIC_SEPARATORS',
'CFG_BIBINDEX_CHARS_PUNCTUATION']:
option_value = 'r"[' + option_value[1:-1] + ']"'
## 3b) special cases: True, False, None
if option_value in ['"True"', '"False"', '"None"']:
option_value = option_value[1:-1]
## 3c) special cases: dicts or lists
if option_name in ['CFG_WEBSEARCH_FIELDS_CONVERT',
'CFG_WEBSEARCH_USE_JSMATH_FOR_FORMATS']:
option_value = option_value[1:-1]
## 3d) special cases: CFG_SITE_LANGS
if option_name == 'CFG_SITE_LANGS':
out = "["
for lang in option_value[1:-1].split(","):
out += "'%s', " % lang
out += "]"
option_value = out
## 3e) special cases: multiline
if option_name == 'CFG_OAI_IDENTIFY_DESCRIPTION':
# make triple quotes
option_value = '""' + option_value + '""'
## 3f) ignore some options:
if option_name == 'CFG_SITE_NAME_INTL':
# treated elsewhere
return
## 4) finally, return output line:
return '%s = %s' % (option_name, option_value)
def cli_cmd_update_config_py(conf):
"""
Update new config.py from conf options, keeping previous
config.py in a backup copy.
"""
print ">>> Going to update config.py..."
## location where config.py is:
configpyfile = conf.get("Invenio", "CFG_PYLIBDIR") + \
os.sep + 'invenio' + os.sep + 'config.py'
## backup current config.py file:
if os.path.exists(configpyfile):
shutil.copy(configpyfile, configpyfile + '.OLD')
## here we go:
fdesc = open(configpyfile, 'w')
## generate preamble:
fdesc.write("# -*- coding: utf-8 -*-\n")
fdesc.write("# DO NOT EDIT THIS FILE! IT WAS AUTOMATICALLY GENERATED\n")
fdesc.write("# FROM INVENIO.CONF BY EXECUTING:\n")
fdesc.write("# " + " ".join(sys.argv) + "\n")
## special treatment for CFG_SITE_NAME_INTL options:
fdesc.write("CFG_SITE_NAME_INTL = {}\n")
for lang in conf.get("Invenio", "CFG_SITE_LANGS").split(","):
fdesc.write("CFG_SITE_NAME_INTL['%s'] = \"%s\"\n" % (lang, conf.get("Invenio",
"CFG_SITE_NAME_INTL_" + lang)))
- ## special treatment for new CFG_SITE_URL options: (FIXME: remove them when weburl is phased out)
- fdesc.write("CFG_SITE_URL = '%s'\n" % conf.get("Invenio", "CFG_SITE_URL"))
## process all the options normally:
for section in conf.sections():
for option in conf.options(section):
if not option.startswith('CFG_DATABASE_'):
# put all options except for db credentials into config.py
line_out = convert_conf_option(option, conf.get(section, option))
if line_out:
fdesc.write(line_out + "\n")
## generate postamble:
fdesc.write("")
fdesc.write("# END OF GENERATED FILE")
## we are done:
fdesc.close()
print "You may want to restart Apache now."
print ">>> config.py updated successfully."
def cli_cmd_update_dbquery_py(conf):
"""
Update lib/dbquery.py file with DB parameters read from conf file.
Note: this edits dbquery.py in situ, taking a backup first.
Use only when you know what you are doing.
"""
print ">>> Going to update dbquery.py..."
## location where dbquery.py is:
dbquerypyfile = conf.get("Invenio", "CFG_PYLIBDIR") + \
os.sep + 'invenio' + os.sep + 'dbquery.py'
## backup current dbquery.py file:
if os.path.exists(dbquerypyfile):
shutil.copy(dbquerypyfile, dbquerypyfile + '.OLD')
## replace db parameters:
out = ''
for line in open(dbquerypyfile, 'r').readlines():
match = re.search(r'^CFG_DATABASE_(HOST|NAME|USER|PASS)(\s*=\s*)\'.*\'$', line)
if match:
dbparam = 'CFG_DATABASE_' + match.group(1)
out += "%s%s'%s'\n" % (dbparam, match.group(2),
conf.get('Invenio', dbparam))
else:
out += line
fdesc = open(dbquerypyfile, 'w')
fdesc.write(out)
fdesc.close()
print "You may want to restart Apache now."
print ">>> dbquery.py updated successfully."
def cli_cmd_update_dbexec(conf):
"""
Update bin/dbexec file with DB parameters read from conf file.
Note: this edits dbexec in situ, taking a backup first.
Use only when you know what you are doing.
"""
print ">>> Going to update dbexec..."
## location where dbexec is:
dbexecfile = conf.get("Invenio", "CFG_BINDIR") + \
os.sep + 'dbexec'
## backup current dbexec file:
if os.path.exists(dbexecfile):
shutil.copy(dbexecfile, dbexecfile + '.OLD')
## replace db parameters via sed:
out = ''
for line in open(dbexecfile, 'r').readlines():
match = re.search(r'^CFG_DATABASE_(HOST|NAME|USER|PASS)(\s*=\s*)\'.*\'$', line)
if match:
dbparam = 'CFG_DATABASE_' + match.group(1)
out += "%s%s'%s'\n" % (dbparam, match.group(2),
conf.get("Invenio", dbparam))
else:
out += line
fdesc = open(dbexecfile, 'w')
fdesc.write(out)
fdesc.close()
print ">>> dbexec updated successfully."
def cli_cmd_update_bibconvert_tpl(conf):
"""
Update bibconvert/config/*.tpl files looking for 856
http://.../record/ lines, replacing URL with CDSWEB taken from
conf file. Note: this edits tpl files in situ, taking a
backup first. Use only when you know what you are doing.
"""
print ">>> Going to update bibconvert templates..."
## location where bibconvert/config/*.tpl are:
tpldir = conf.get("Invenio", 'CFG_ETCDIR') + \
os.sep + 'bibconvert' + os.sep + 'config'
## find all *.tpl files:
for tplfilename in os.listdir(tpldir):
if tplfilename.endswith(".tpl"):
## change tpl file:
tplfile = tpldir + os.sep + tplfilename
shutil.copy(tplfile, tplfile + '.OLD')
out = ''
for line in open(tplfile, 'r').readlines():
match = re.search(r'^(.*)http://.*?/record/(.*)$', line)
if match:
out += "%s%s/record/%s\n" % (match.group(1),
conf.get("Invenio", 'CFG_SITE_URL'),
match.group(2))
else:
out += line
fdesc = open(tplfile, 'w')
fdesc.write(out)
fdesc.close()
print ">>> bibconvert templates updated successfully."
def cli_cmd_reset_sitename(conf):
"""
Reset collection-related tables with new CFG_SITE_NAME and
CFG_SITE_NAME_INTL* read from conf files.
"""
print ">>> Going to reset CFG_SITE_NAME and CFG_SITE_NAME_INTL..."
from invenio.dbquery import run_sql, IntegrityError
# reset CFG_SITE_NAME:
sitename = conf.get("Invenio", "CFG_SITE_NAME")
try:
run_sql("""INSERT INTO collection (id, name, dbquery, reclist, restricted) VALUES
(1,%s,NULL,NULL,NULL)""", (sitename,))
except IntegrityError:
run_sql("""UPDATE collection SET name=%s WHERE id=1""", (sitename,))
# reset CFG_SITE_NAME_INTL:
for lang in conf.get("Invenio", "CFG_SITE_LANGS").split(","):
sitename_lang = conf.get("Invenio", "CFG_SITE_NAME_INTL_" + lang)
try:
run_sql("""INSERT INTO collectionname (id_collection, ln, type, value) VALUES
(%s,%s,%s,%s)""", (1, lang, 'ln', sitename_lang))
except IntegrityError:
run_sql("""UPDATE collectionname SET value=%s
WHERE ln=%s AND id_collection=1 AND type='ln'""",
(sitename_lang, lang))
print "You may want to restart Apache now."
print ">>> CFG_SITE_NAME and CFG_SITE_NAME_INTL* reset successfully."
def cli_cmd_reset_siteadminemail(conf):
"""
Reset user-related tables with new CFG_SITE_ADMIN_EMAIL read from conf files.
"""
print ">>> Going to reset CFG_SITE_ADMIN_EMAIL..."
from invenio.dbquery import run_sql
siteadminemail = conf.get("Invenio", "CFG_SITE_ADMIN_EMAIL")
run_sql("DELETE FROM user WHERE id=1")
run_sql("""INSERT INTO user (id, email, password, note, nickname) VALUES
(1, %s, AES_ENCRYPT(email, ''), 1, 'admin')""",
(siteadminemail,))
print "You may want to restart Apache now."
print ">>> CFG_SITE_ADMIN_EMAIL reset successfully."
def cli_cmd_reset_fieldnames(conf):
"""
Reset I18N field names such as author, title, etc and other I18N
ranking method names such as word similarity. Their translations
are taken from the PO files.
"""
print ">>> Going to reset I18N field names..."
from invenio.messages import gettext_set_language, language_list_long
from invenio.dbquery import run_sql, IntegrityError
## get field id and name list:
field_id_name_list = run_sql("SELECT id, name FROM field")
## get rankmethod id and name list:
rankmethod_id_name_list = run_sql("SELECT id, name FROM rnkMETHOD")
## update names for every language:
for lang, dummy in language_list_long():
_ = gettext_set_language(lang)
## this list is put here in order for PO system to pick names
## suitable for translation
field_name_names = {"any field": _("any field"),
"title": _("title"),
"author": _("author"),
"abstract": _("abstract"),
"keyword": _("keyword"),
"report number": _("report number"),
"subject": _("subject"),
"reference": _("reference"),
"fulltext": _("fulltext"),
"collection": _("collection"),
"division": _("division"),
"year": _("year"),
"experiment": _("experiment"),
"record ID": _("record ID"),}
## update I18N names for every language:
for (field_id, field_name) in field_id_name_list:
try:
run_sql("""INSERT INTO fieldname (id_field,ln,type,value) VALUES
(%s,%s,%s,%s)""", (field_id, lang, 'ln',
field_name_names[field_name]))
except IntegrityError:
run_sql("""UPDATE fieldname SET value=%s
WHERE id_field=%s AND ln=%s AND type=%s""",
(field_name_names[field_name], field_id, lang, 'ln',))
## ditto for rank methods:
rankmethod_name_names = {"wrd": _("word similarity"),
"demo_jif": _("journal impact factor"),
"citation": _("times cited"),}
for (rankmethod_id, rankmethod_name) in rankmethod_id_name_list:
try:
run_sql("""INSERT INTO rnkMETHODNAME (id_rnkMETHOD,ln,type,value) VALUES
(%s,%s,%s,%s)""", (rankmethod_id, lang, 'ln',
rankmethod_name_names[rankmethod_name]))
except IntegrityError:
run_sql("""UPDATE rnkMETHODNAME SET value=%s
WHERE id_rnkMETHOD=%s AND ln=%s AND type=%s""",
(rankmethod_name_names[rankmethod_name], rankmethod_id, lang, 'ln',))
print ">>> I18N field names reset successfully."
def test_db_connection():
"""
Test DB connection, and if fails, advise user how to set it up.
Useful to be called during table creation.
"""
print "Testing DB connection...",
from invenio.textutils import wrap_text_in_a_box
from invenio.dbquery import run_sql, Error
## first, test connection to the DB server:
try:
run_sql("SHOW TABLES")
except Error, err:
from invenio.dbquery import CFG_DATABASE_HOST, CFG_DATABASE_NAME, \
CFG_DATABASE_USER, CFG_DATABASE_PASS
print wrap_text_in_a_box("""\
DATABASE CONNECTIVITY ERROR %(errno)d: %(errmsg)s.\n
Perhaps you need to set up database and connection rights?
If yes, then please login as MySQL admin user and run the
following commands now:
$ mysql -h %(dbhost)s -u root -p mysql
mysql> CREATE DATABASE %(dbname)s DEFAULT CHARACTER SET utf8;
mysql> GRANT ALL PRIVILEGES ON %(dbname)s.* TO %(dbuser)s@%(webhost)s IDENTIFIED BY '%(dbpass)s';
mysql> QUIT
The values printed above were detected from your configuration.
If they are not right, then please edit your invenio.conf file
and rerun 'inveniocfg --update-all' first.
If the problem is of different nature, then please inspect
the above error message and fix the problem before continuing.""" % \
{'errno': err.args[0],
'errmsg': err.args[1],
'dbname': CFG_DATABASE_NAME,
'dbhost': CFG_DATABASE_HOST,
'dbuser': CFG_DATABASE_USER,
'dbpass': CFG_DATABASE_PASS,
'webhost': CFG_DATABASE_HOST == 'localhost' and 'localhost' or os.popen('hostname -f', 'r').read().strip(),
})
sys.exit(1)
print "ok"
## second, test insert/select of a Unicode string to detect
## possible Python/MySQL/MySQLdb mis-setup:
print "Testing Python/MySQL/MySQLdb UTF-8 chain...",
try:
beta_in_utf8 = "β" # Greek beta in UTF-8 is 0xCEB2
run_sql("CREATE TEMPORARY TABLE test__invenio__utf8 (x char(1), y varbinary(2)) DEFAULT CHARACTER SET utf8")
run_sql("INSERT INTO test__invenio__utf8 (x, y) VALUES (%s, %s)", (beta_in_utf8, beta_in_utf8))
res = run_sql("SELECT x,y,HEX(x),HEX(y),LENGTH(x),LENGTH(y),CHAR_LENGTH(x),CHAR_LENGTH(y) FROM test__invenio__utf8")
assert res[0] == ('\xce\xb2', '\xce\xb2', 'CEB2', 'CEB2', 2L, 2L, 1L, 2L)
run_sql("DROP TEMPORARY TABLE test__invenio__utf8")
except Exception, err:
print wrap_text_in_a_box("""\
DATABASE RELATED ERROR %s\n
A problem was detected with the UTF-8 treatment in the chain
between the Python application, the MySQLdb connector, and
the MySQL database. You may perhaps have installed older
versions of some prerequisite packages?\n
Please check the INSTALL file and please fix this problem
before continuing.""" % err)
sys.exit(1)
print "ok"
def cli_cmd_create_tables(conf):
"""Create and fill Invenio DB tables. Useful for the installation process."""
print ">>> Going to create and fill tables..."
from invenio.config import CFG_PREFIX
test_db_connection()
for cmd in ["%s/bin/dbexec < %s/lib/sql/invenio/tabcreate.sql" % (CFG_PREFIX, CFG_PREFIX),
"%s/bin/dbexec < %s/lib/sql/invenio/tabfill.sql" % (CFG_PREFIX, CFG_PREFIX)]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
cli_cmd_reset_sitename(conf)
cli_cmd_reset_siteadminemail(conf)
cli_cmd_reset_fieldnames(conf)
for cmd in ["%s/bin/webaccessadmin -u admin -c -a" % CFG_PREFIX]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Tables created and filled successfully."
def cli_cmd_drop_tables(conf):
"""Drop Invenio DB tables. Useful for the uninstallation process."""
print ">>> Going to drop tables..."
from invenio.config import CFG_PREFIX
from invenio.textutils import wrap_text_in_a_box, wait_for_user
if '--yes-i-know' not in sys.argv:
wait_for_user(wrap_text_in_a_box("""\
WARNING: You are going to destroy your database tables!\n
Press Ctrl-C if you want to abort this action.\n
Press ENTER to proceed with this action."""))
cmd = "%s/bin/dbexec < %s/lib/sql/invenio/tabdrop.sql" % (CFG_PREFIX, CFG_PREFIX)
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Tables dropped successfully."
def cli_cmd_create_demo_site(conf):
"""Create demo site. Useful for testing purposes."""
print ">>> Going to create demo site..."
from invenio.config import CFG_PREFIX
from invenio.dbquery import run_sql
run_sql("TRUNCATE schTASK")
for cmd in ["%s/bin/dbexec < %s/lib/sql/invenio/democfgdata.sql" % (CFG_PREFIX, CFG_PREFIX),
"%s/bin/webaccessadmin -u admin -c -r -D" % CFG_PREFIX,
"%s/bin/webcoll -u admin" % CFG_PREFIX,
"%s/bin/webcoll 1" % CFG_PREFIX,]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Demo site created successfully."
def cli_cmd_load_demo_records(conf):
"""Load demo records. Useful for testing purposes."""
from invenio.config import CFG_PREFIX
from invenio.dbquery import run_sql
print ">>> Going to load demo records..."
run_sql("TRUNCATE schTASK")
for cmd in ["%s/bin/bibupload -i %s/var/tmp/demobibdata.xml" % (CFG_PREFIX, CFG_PREFIX),
"%s/bin/bibupload 1" % CFG_PREFIX,
"%s/bin/bibindex -u admin" % CFG_PREFIX,
"%s/bin/bibindex 2" % CFG_PREFIX,
"%s/bin/bibreformat -u admin -o HB" % CFG_PREFIX,
"%s/bin/bibreformat 3" % CFG_PREFIX,
"%s/bin/bibupload 4" % CFG_PREFIX,
"%s/bin/webcoll -u admin" % CFG_PREFIX,
"%s/bin/webcoll 5" % CFG_PREFIX,
"%s/bin/bibrank -u admin" % CFG_PREFIX,
"%s/bin/bibrank 6" % CFG_PREFIX,]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Demo records loaded successfully."
def cli_cmd_remove_demo_records(conf):
"""Remove demo records. Useful when you are finished testing."""
print ">>> Going to remove demo records..."
from invenio.config import CFG_PREFIX
from invenio.dbquery import run_sql
from invenio.textutils import wrap_text_in_a_box, wait_for_user
if '--yes-i-know' not in sys.argv:
wait_for_user(wrap_text_in_a_box("""\
WARNING: You are going to destroy your records and documents!\n
Press Ctrl-C if you want to abort this action.\n
Press ENTER to proceed with this action."""))
if os.path.exists(CFG_PREFIX + os.sep + 'var' + os.sep + 'data' + os.sep + 'files'):
shutil.rmtree(CFG_PREFIX + os.sep + 'var' + os.sep + 'data' + os.sep + 'files')
run_sql("TRUNCATE schTASK")
for cmd in ["%s/bin/dbexec < %s/lib/sql/invenio/tabbibclean.sql" % (CFG_PREFIX, CFG_PREFIX),
"%s/bin/webcoll -u admin" % CFG_PREFIX,
"%s/bin/webcoll 1" % CFG_PREFIX,]:
if os.system(cmd):
print "ERROR: failed execution of", cmd
sys.exit(1)
print ">>> Demo records removed successfully."
def cli_cmd_drop_demo_site(conf):
"""Drop demo site completely. Useful when you are finished testing."""
print ">>> Going to drop demo site..."
from invenio.textutils import wrap_text_in_a_box, wait_for_user
if '--yes-i-know' not in sys.argv:
wait_for_user(wrap_text_in_a_box("""\
WARNING: You are going to destroy your site and documents!\n
Press Ctrl-C if you want to abort this action.\n
Press ENTER to proceed with this action."""))
cli_cmd_drop_tables(conf)
cli_cmd_create_tables(conf)
cli_cmd_remove_demo_records(conf)
print ">>> Demo site dropped successfully."
def cli_cmd_run_unit_tests(conf):
"""Run unit tests, usually on the working demo site."""
from invenio.config import CFG_PREFIX
os.system("%s/bin/testsuite" % CFG_PREFIX)
def cli_cmd_run_regression_tests(conf):
"""Run regression tests, usually on the working demo site."""
from invenio.config import CFG_PREFIX
if '--yes-i-know' in sys.argv:
os.system("%s/bin/regressiontestsuite --yes-i-know" % CFG_PREFIX)
else:
os.system("%s/bin/regressiontestsuite" % CFG_PREFIX)
def cli_cmd_create_apache_conf(conf):
"""
Create Apache conf files for this site, keeping previous
files in a backup copy.
"""
print ">>> Going to create Apache conf files..."
from invenio.textutils import wrap_text_in_a_box
apache_conf_dir = conf.get("Invenio", 'CFG_ETCDIR') + \
os.sep + 'apache'
if not os.path.exists(apache_conf_dir):
os.mkdir(apache_conf_dir)
apache_vhost_file = apache_conf_dir + os.sep + \
'invenio-apache-vhost.conf'
apache_vhost_ssl_file = apache_conf_dir + os.sep + \
'invenio-apache-vhost-ssl.conf'
apache_vhost_body = """\
AddDefaultCharset UTF-8
ServerSignature Off
ServerTokens Prod
NameVirtualHost *:80
<Files *.pyc>
deny from all
</Files>
<Files *~>
deny from all
</Files>
<VirtualHost *:80>
ServerName %(servername)s
ServerAlias %(serveralias)s
ServerAdmin %(serveradmin)s
DocumentRoot %(webdir)s
<Directory %(webdir)s>
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog %(logdir)s/apache.err
LogLevel warn
CustomLog %(logdir)s/apache.log combined
DirectoryIndex index.en.html index.html
<LocationMatch "^(/+$|/index|/collection|/record|/author|/search|/browse|/youraccount|/youralerts|/yourbaskets|/yourmessages|/yourgroups|/submit|/getfile|/comments|/error|/oai2d|/rss|/help|/journal|/openurl)">
SetHandler python-program
PythonHandler invenio.webinterface_layout
PythonDebug On
</LocationMatch>
<Directory %(webdir)s>
AddHandler python-program .py
PythonHandler mod_python.publisher
PythonDebug On
</Directory>
</VirtualHost>
""" % {'servername': conf.get('Invenio', 'CFG_SITE_URL').replace("http://", ""),
'serveralias': conf.get('Invenio', 'CFG_SITE_URL').replace("http://", "").split('.')[0],
'serveradmin': conf.get('Invenio', 'CFG_SITE_ADMIN_EMAIL'),
'webdir': conf.get('Invenio', 'CFG_WEBDIR'),
'logdir': conf.get('Invenio', 'CFG_LOGDIR'),
}
apache_vhost_ssl_body = """\
ServerSignature Off
ServerTokens Prod
NameVirtualHost *:443
#SSLCertificateFile /etc/apache2/ssl/apache.pem
SSLCertificateFile /etc/apache2/ssl/server.crt
SSLCertificateKeyFile /etc/apache2/ssl/server.key
<Files *.pyc>
deny from all
</Files>
<Files *~>
deny from all
</Files>
<VirtualHost *:443>
ServerName %(servername)s
ServerAlias %(serveralias)s
ServerAdmin %(serveradmin)s
SSLEngine on
DocumentRoot %(webdir)s
<Directory %(webdir)s>
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog %(logdir)s/apache-ssl.err
LogLevel warn
CustomLog %(logdir)s/apache-ssl.log combined
DirectoryIndex index.en.html index.html
<LocationMatch "^(/+$|/index|/collection|/record|/search|/browse|/youraccount|/youralerts|/yourbaskets|/yourmessages|/yourgroups|/submit|/getfile|/comments|/error|/oai2d|/rss|/help|/journal|/openurl)">
SetHandler python-program
PythonHandler invenio.webinterface_layout
PythonDebug On
</LocationMatch>
<Directory %(webdir)s>
AddHandler python-program .py
PythonHandler mod_python.publisher
PythonDebug On
</Directory>
</VirtualHost>
""" % {'servername': conf.get('Invenio', 'CFG_SITE_URL_SECURE').replace("http://", ""),
'serveralias': conf.get('Invenio', 'CFG_SITE_URL_SECURE').replace("http://", "").split('.')[0],
'serveradmin': conf.get('Invenio', 'CFG_SITE_ADMIN_EMAIL'),
'webdir': conf.get('Invenio', 'CFG_WEBDIR'),
'logdir': conf.get('Invenio', 'CFG_LOGDIR'),
}
# write HTTP vhost snippet:
if os.path.exists(apache_vhost_file):
shutil.copy(apache_vhost_file,
apache_vhost_file + '.OLD')
fdesc = open(apache_vhost_file, 'w')
fdesc.write(apache_vhost_body)
fdesc.close()
print "Created file", apache_vhost_file
# write HTTPS vhost snippet:
if conf.get('Invenio', 'CFG_SITE_URL_SECURE') != \
conf.get('Invenio', 'CFG_SITE_URL'):
if os.path.exists(apache_vhost_ssl_file):
shutil.copy(apache_vhost_ssl_file,
apache_vhost_ssl_file + '.OLD')
fdesc = open(apache_vhost_ssl_file, 'w')
fdesc.write(apache_vhost_ssl_body)
fdesc.close()
print "Created file", apache_vhost_ssl_file
print ""
print wrap_text_in_a_box("""\
Apache virtual host configurations for your site have been
created. You can check created files and put the following
include statements in your httpd.conf:\n
Include %s
Include %s
""" % (apache_vhost_file, apache_vhost_ssl_file))
print ">>> Apache conf files created."
def cli_cmd_get(conf, varname):
"""
Return value of VARNAME read from CONF files. Useful for
third-party programs to access values of conf options such as
CFG_PREFIX. Return None if VARNAME is not found.
"""
# do not pay attention to upper/lower case:
varname = varname.lower()
# do not pay attention to section names yet:
all_options = {}
for section in conf.sections():
for option in conf.options(section):
all_options[option] = conf.get(section, option)
return all_options.get(varname, None)
def cli_cmd_list(conf):
"""
Print a list of all conf options and values from CONF.
"""
for section in conf.sections():
for option in conf.options(section):
print option, '=', conf.get(section, option)
def main():
"""Main entry point."""
conf = ConfigParser()
if '--help' in sys.argv or \
'-h' in sys.argv:
print_usage()
elif '--version' in sys.argv or \
'-V' in sys.argv:
print_version()
else:
confdir = None
if '--conf-dir' in sys.argv:
try:
confdir = sys.argv[sys.argv.index('--conf-dir') + 1]
except IndexError:
pass # missing --conf-dir argument value
if not os.path.exists(confdir):
print "ERROR: bad or missing --conf-dir option value."
sys.exit(1)
else:
## try to detect path to conf dir (relative to this bin dir):
confdir = re.sub(r'/bin$', '/etc', sys.path[0])
## read conf files:
for conffile in [confdir + os.sep + 'invenio.conf',
confdir + os.sep + 'invenio-autotools.conf',
confdir + os.sep + 'invenio-local.conf',]:
if os.path.exists(conffile):
conf.read(conffile)
else:
if not conffile.endswith("invenio-local.conf"):
# invenio-local.conf is optional, otherwise stop
print "ERROR: Badly guessed conf file location", conffile
print "(Please use --conf-dir option.)"
sys.exit(1)
## decide what to do:
done = False
for opt_idx in range(0, len(sys.argv)):
opt = sys.argv[opt_idx]
if opt == '--conf-dir':
# already treated before, so skip silently:
pass
elif opt == '--get':
try:
varname = sys.argv[opt_idx + 1]
except IndexError:
print "ERROR: bad or missing --get option value."
sys.exit(1)
if varname.startswith('-'):
print "ERROR: bad or missing --get option value."
sys.exit(1)
varvalue = cli_cmd_get(conf, varname)
if varvalue is not None:
print varvalue
else:
sys.exit(1)
done = True
elif opt == '--list':
cli_cmd_list(conf)
done = True
elif opt == '--create-tables':
cli_cmd_create_tables(conf)
done = True
elif opt == '--drop-tables':
cli_cmd_drop_tables(conf)
done = True
elif opt == '--create-demo-site':
cli_cmd_create_demo_site(conf)
done = True
elif opt == '--load-demo-records':
cli_cmd_load_demo_records(conf)
done = True
elif opt == '--remove-demo-records':
cli_cmd_remove_demo_records(conf)
done = True
elif opt == '--drop-demo-site':
cli_cmd_drop_demo_site(conf)
done = True
elif opt == '--run-unit-tests':
cli_cmd_run_unit_tests(conf)
done = True
elif opt == '--run-regression-tests':
cli_cmd_run_regression_tests(conf)
done = True
elif opt == '--update-all':
cli_cmd_update_config_py(conf)
cli_cmd_update_dbquery_py(conf)
cli_cmd_update_dbexec(conf)
cli_cmd_update_bibconvert_tpl(conf)
done = True
elif opt == '--update-config-py':
cli_cmd_update_config_py(conf)
done = True
elif opt == '--update-dbquery-py':
cli_cmd_update_dbquery_py(conf)
done = True
elif opt == '--update-dbexec':
cli_cmd_update_dbexec(conf)
done = True
elif opt == '--update-bibconvert-tpl':
cli_cmd_update_bibconvert_tpl(conf)
done = True
elif opt == '--reset-all':
cli_cmd_reset_sitename(conf)
cli_cmd_reset_siteadminemail(conf)
cli_cmd_reset_fieldnames(conf)
done = True
elif opt == '--reset-sitename':
cli_cmd_reset_sitename(conf)
done = True
elif opt == '--reset-siteadminemail':
cli_cmd_reset_siteadminemail(conf)
done = True
elif opt == '--reset-fieldnames':
cli_cmd_reset_fieldnames(conf)
done = True
elif opt == '--create-apache-conf':
cli_cmd_create_apache_conf(conf)
done = True
elif opt.startswith("-") and opt != '--yes-i-know':
print "ERROR: unknown option", opt
sys.exit(1)
if not done:
print """ERROR: Please specify a command. Please see '--help'."""
sys.exit(1)
if __name__ == '__main__':
main()
diff --git a/modules/miscutil/lib/mailutils.py b/modules/miscutil/lib/mailutils.py
index b24e00c3d..537c08fa2 100644
--- a/modules/miscutil/lib/mailutils.py
+++ b/modules/miscutil/lib/mailutils.py
@@ -1,297 +1,297 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Invenio mail sending utilities. send_email() is the main API function
people should be using; just check out its docstring.
"""
__revision__ = "$Id$"
import sys
from time import sleep
import smtplib
import socket
import re
import os
from email.Header import Header
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEImage import MIMEImage
from cStringIO import StringIO
from formatter import DumbWriter, AbstractFormatter
from invenio.config import \
CFG_SITE_SUPPORT_EMAIL, \
- weburl, \
+ CFG_SITE_URL, \
CFG_SITE_LANG, \
CFG_SITE_NAME_INTL, \
CFG_SITE_NAME, \
CFG_SITE_ADMIN_EMAIL, \
CFG_MISCUTIL_SMTP_HOST, \
CFG_MISCUTIL_SMTP_PORT, \
CFG_VERSION
from invenio.messages import wash_language, gettext_set_language
from invenio.errorlib import get_msgs_for_code_list, register_errors, register_exception
def send_email(fromaddr,
toaddr,
subject="",
content="",
html_content='',
html_images={},
header=None,
footer=None,
html_header=None,
html_footer=None,
copy_to_admin=0,
attempt_times=1,
attempt_sleeptime=10,
debug_level=0,
ln=CFG_SITE_LANG,
charset='utf-8'
):
"""Send an forged email to TOADDR from FROMADDR with message created from subjet, content and possibly
header and footer.
@param fromaddr: [string] sender
@param toaddr: [string] receivers separated by ,
@param subject: [string] subject of the email
@param content: [string] content of the email
@param html_content: [string] html version of the email
@param html_images: [dict] dictionary of image id, image path
@param header: [string] header to add, None for the Default
@param footer: [string] footer to add, None for the Default
@param html_header: [string] header to add to the html part, None for the Default
@param html_footer: [string] footer to add to the html part, None for the Default
@param copy_to_admin: [int] if 1 add emailamin in receivers
@attempt_times: [int] number of tries
@attempt_sleeptime: [int] seconds in between tries
@debug_level: [int] debug level
@ln: [string] invenio language
@param charset: which charset to use in message ('utf-8' by default)
If sending fails, try to send it ATTEMPT_TIMES, and wait for
ATTEMPT_SLEEPTIME seconds in between tries.
e.g.:
send_email('foo.bar@cern.ch', 'bar.foo@cern.ch', 'Let\'s try!'', 'check 1234', '<strong>check</strong> <em>1234</em><img src="cid:image1">', {'image1': '/tmp/quantum.jpg'})
@return [bool]: True if email was sent okay, False if it was not.
"""
toaddr = toaddr.strip()
usebcc = ',' in toaddr # More than one address, let's use Bcc in place of To
if copy_to_admin:
if len(toaddr) > 0:
toaddr += ",%s" % (CFG_SITE_ADMIN_EMAIL,)
else:
toaddr = CFG_SITE_ADMIN_EMAIL
body = forge_email(fromaddr, toaddr, subject, content, html_content, html_images, usebcc, header, footer, html_header, html_footer, ln, charset)
toaddr = toaddr.split(",")
if attempt_times < 1 or len(toaddr[0]) == 0:
log('ERR_MISCUTIL_NOT_ATTEMPTING_SEND_EMAIL', fromaddr, toaddr, body)
return False
try:
server = smtplib.SMTP(CFG_MISCUTIL_SMTP_HOST, CFG_MISCUTIL_SMTP_PORT)
if debug_level > 2:
server.set_debuglevel(1)
else:
server.set_debuglevel(0)
server.sendmail(fromaddr, toaddr, body)
server.quit()
except (smtplib.SMTPException, socket.error):
if attempt_times > 1:
if (debug_level > 1):
log('ERR_MISCUTIL_CONNECTION_SMTP', attempt_sleeptime, sys.exc_info()[0], fromaddr, toaddr, body)
sleep(attempt_sleeptime)
return send_email(fromaddr, toaddr, body, attempt_times-1, attempt_sleeptime)
else:
log('ERR_MISCUTIL_SENDING_EMAIL', fromaddr, toaddr, body)
return False
except Exception:
register_exception()
return False
return True
def email_header(ln=CFG_SITE_LANG):
"""The header of the email
@param ln: language
@return header as a string"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
#standard header
out = """%(hello)s
""" % {
'hello': _("Hello:")
}
return out
def email_html_header(ln=CFG_SITE_LANG):
"""The header of the email
@param ln: language
@return header as a string"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
#standard header
out = """%(hello)s<br />
""" % {
'hello': _("Hello:")
}
return out
def email_footer(ln=CFG_SITE_LANG):
"""The footer of the email
@param ln: language
@return footer as a string"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
#standard footer
out = """\n\n%(best_regards)s
--
-%(sitename)s <%(weburl)s>
+%(sitename)s <%(siteurl)s>
%(need_intervention_please_contact)s <%(sitesupportemail)s>
""" % {
'sitename': CFG_SITE_NAME_INTL[ln],
'best_regards': _("Best regards"),
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
'need_intervention_please_contact': _("Need human intervention? Contact"),
'sitesupportemail': CFG_SITE_SUPPORT_EMAIL
}
return out
def email_html_footer(ln=CFG_SITE_LANG):
"""The html footer of the email
@param ln: language
@return footer as a string"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
#standard footer
out = """<br /><br /><em>%(best_regards)s</em>
<hr />
-<a href="%(weburl)s"><strong>%(sitename)s</strong></a><br />
+<a href="%(siteurl)s"><strong>%(sitename)s</strong></a><br />
%(need_intervention_please_contact)s <a href="mailto:%(sitesupportemail)s">%(sitesupportemail)s</a>
""" % {
'sitename': CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
'best_regards': _("Best regards"),
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
'need_intervention_please_contact': _("Need human intervention? Contact"),
'sitesupportemail': CFG_SITE_SUPPORT_EMAIL
}
return out
def forge_email(fromaddr, toaddr, subject, content, html_content='',
html_images={}, usebcc=False, header=None, footer=None,
html_header=None, html_footer=None, ln=CFG_SITE_LANG,
charset='utf-8'):
"""Prepare email. Add header and footer if needed.
@param fromaddr: [string] sender
@param toaddr: [string] receivers separated by ,
@param usebcc: [bool] True for using Bcc in place of To
@param subject: [string] subject of the email
@param content: [string] content of the email
@param html_content: [string] html version of the email
@param html_images: [dict] dictionary of image id, image path
@param header: [string] None for the default header
@param footer: [string] None for the default footer
@param ln: language
@param charset: which charset to use in message ('utf-8' by default)
@return forged email as a string"""
if header is None:
content = email_header(ln) + content
else:
content = header + content
if footer is None:
content += email_footer(ln)
else:
content += footer
if html_content:
if html_header is None:
html_content = email_html_header(ln) + html_content
else:
html_content = html_header + content
if html_footer is None:
html_content += email_html_footer(ln)
else:
html_content += html_footer
msg_root = MIMEMultipart('related')
msg_root['Subject'] = Header(subject, charset)
msg_root['From'] = fromaddr
if usebcc:
msg_root['Bcc'] = toaddr
else:
msg_root['To'] = toaddr
msg_root.preamble = 'This is a multi-part message in MIME format.'
msg_alternative = MIMEMultipart('alternative')
msg_root.attach(msg_alternative)
msg_text = MIMEText(content, _charset=charset)
msg_alternative.attach(msg_text)
msg_text = MIMEText(html_content, 'html', _charset=charset)
msg_alternative.attach(msg_text)
for image_id, image_path in html_images.iteritems():
msg_image = MIMEImage(open(image_path, 'rb').read())
msg_image.add_header('Content-ID', '<%s>' % image_id)
msg_image.add_header('Content-Disposition', 'attachment', filename=os.path.split(image_path)[1])
msg_root.attach(msg_image)
else:
msg_root = MIMEText(content, _charset=charset)
msg_root['From'] = fromaddr
if usebcc:
msg_root['Bcc'] = toaddr
else:
msg_root['To'] = toaddr
msg_root['Subject'] = Header(subject, charset)
msg_root.add_header('User-Agent', 'CDS Invenio %s' % CFG_VERSION)
return msg_root.as_string()
RE_NEWLINES = re.compile(r'<br\s*/?>|</p>', re.I)
RE_SPACES = re.compile(r'\s+')
RE_HTML_TAGS = re.compile(r'<.+?>')
def email_strip_html(html_content):
"""Strip html tags from html_content, trying to respect formatting."""
html_content = RE_SPACES.sub(' ', html_content)
html_content = RE_NEWLINES.sub('\n', html_content)
html_content = RE_HTML_TAGS.sub('', html_content)
html_content = html_content.split('\n')
out = StringIO()
out_format = AbstractFormatter(DumbWriter(out))
for row in html_content:
out_format.add_flowing_data(row)
out_format.end_paragraph(1)
return out.getvalue()
def log(*error):
"""Register error
@param error: tuple of the form(ERR_, arg1, arg2...)
"""
_ = gettext_set_language(CFG_SITE_LANG)
errors = get_msgs_for_code_list([error], 'error', CFG_SITE_LANG)
register_errors(errors, 'error')
diff --git a/modules/miscutil/lib/testutils.py b/modules/miscutil/lib/testutils.py
index 78ea9c1dc..768fc9b4d 100644
--- a/modules/miscutil/lib/testutils.py
+++ b/modules/miscutil/lib/testutils.py
@@ -1,275 +1,275 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable-msg=E1102
"""
Helper functions for building and running test suites.
"""
__revision__ = "$Id$"
# if verbose level is set to 9, many debugging messages will be
# printed on stdout, so you may want to run:
# $ regressiontestsuite > /tmp/z.log
# or even:
# $ regressiontestsuite > /tmp/z.log 2> /tmp/z.err
CFG_TESTUTILS_VERBOSE = 1
import string
import sys
import time
import unittest
from urllib import urlencode
from itertools import chain, repeat
-from invenio.config import weburl, CFG_SITE_SECURE_URL, CFG_LOGDIR
+from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_LOGDIR
from invenio.w3c_validator import w3c_validate, w3c_errors_to_str, CFG_TESTS_REQUIRE_HTML_VALIDATION
def warn_user_about_tests():
""" Put a standard warning about running tests that might modify
user data"""
# Provide a command line option to avoid having to type the
# confirmation every time during development.
if '--yes-i-know' in sys.argv:
return
sys.stderr.write("""\
**********************************************************************
** **
** *** I M P O R T A N T W A R N I N G *** **
** **
** The regression test suite needs to be run on a clean demo site **
** that you can obtain by doing: **
** **
** $ inveniocfg --drop-demo-site \ **
** --create-demo-site \ **
** --load-demo-records **
** **
** Note that DOING THE ABOVE WILL ERASE YOUR ENTIRE DATABASE. **
** **
** In addition, due to the write nature of some of the tests, **
** the demo DATABASE will be ALTERED WITH JUNK DATA, so that **
** it is recommended to rebuild the demo site anew afterwards. **
** **
**********************************************************************
Please confirm by typing "Yes, I know!": """)
answer = raw_input('')
if answer != 'Yes, I know!':
sys.stderr.write("Aborted.\n")
raise SystemExit(0)
return
def warn_user_about_tests_and_run(testsuite):
""" Convenience function to embed in test suites """
warn_user_about_tests()
unittest.TextTestRunner(verbosity=2).run(testsuite)
def make_test_suite(*test_cases):
""" Build up a test suite given separate test cases"""
return unittest.TestSuite([unittest.makeSuite(case, 'test')
for case in test_cases])
def make_url(path, **kargs):
""" Helper to generate an absolute invenio URL with query
arguments"""
- url = weburl + path
+ url = CFG_SITE_URL + path
if kargs:
url += '?' + urlencode(kargs, doseq=True)
return url
def make_surl(path, **kargs):
""" Helper to generate an absolute invenio Secure URL with query
arguments"""
url = CFG_SITE_SECURE_URL + path
if kargs:
url += '?' + urlencode(kargs, doseq=True)
return url
class InvenioTestUtilsBrowserException(Exception):
"""Helper exception for the regression test suite browser."""
pass
def test_web_page_existence(url):
"""
Test whether URL exists and is well accessible.
Return True or raise exception in case of problems.
"""
import mechanize
browser = mechanize.Browser()
try:
browser.open(url)
except:
raise
return True
def test_web_page_content(url,
username="guest",
password="",
expected_text="</html>",
expected_link_target=None,
expected_link_label=None,
require_validate_p=CFG_TESTS_REQUIRE_HTML_VALIDATION):
"""Test whether web page URL as seen by user USERNAME contains
text EXPECTED_TEXT and, eventually, contains a link to
EXPECTED_LINK_TARGET (if set) labelled EXPECTED_LINK_LABEL (if
set). The EXPECTED_TEXT is checked via substring matching, the
EXPECTED_LINK_TARGET and EXPECTED_LINK_LABEL via exact string
matching.
EXPECTED_TEXT, EXPECTED_LINK_LABEL and EXPECTED_LINK_TARGET can
either be strings or list of strings (in order to check multiple
values inside same page).
Before doing the tests, login as USERNAME with password
PASSWORD. E.g. interesting values for USERNAME are "guest" or
"admin".
Return empty list in case of problems, otherwise list of error
messages that may have been encountered during processing of
page.
"""
if '--w3c-validate' in sys.argv:
require_validate_p = True
sys.stderr.write('Required validation\n')
error_messages = []
try:
import mechanize
except ImportError:
return ['WARNING: Cannot import mechanize, test skipped.']
browser = mechanize.Browser()
try:
# firstly login:
if username == "guest":
pass
else:
browser.open(CFG_SITE_SECURE_URL + "/youraccount/login")
browser.select_form(nr=0)
browser['p_un'] = username
browser['p_pw'] = password
browser.submit()
username_account_page_body = browser.response().read()
try:
string.index(username_account_page_body,
"You are logged in as %s." % username)
except ValueError:
raise InvenioTestUtilsBrowserException, \
'ERROR: Cannot login as %s, test skipped.' % username
# secondly read page body:
browser.open(url)
url_body = browser.response().read()
# now test for EXPECTED_TEXT:
# first normalize expected_text
if isinstance(expected_text, str):
expected_texts = [expected_text]
else:
expected_texts = expected_text
# then test
for cur_expected_text in expected_texts:
try:
string.index(url_body, cur_expected_text)
except ValueError:
raise InvenioTestUtilsBrowserException, \
'ERROR: Page %s (login %s) does not contain %s.' % \
(url, username, cur_expected_text)
# now test for EXPECTED_LINK_TARGET and EXPECTED_LINK_LABEL:
if expected_link_target or expected_link_label:
# first normalize expected_link_target and expected_link_label
if isinstance(expected_link_target, str) or \
expected_link_target is None:
expected_link_targets = [expected_link_target]
else:
expected_link_targets = expected_link_target
if isinstance(expected_link_label, str) or \
expected_link_label is None:
expected_link_labels = [expected_link_label]
else:
expected_link_labels = expected_link_label
max_links = max(len(expected_link_targets), len(expected_link_labels))
expected_link_labels = chain(expected_link_labels, repeat(None))
expected_link_targets = chain(expected_link_targets, repeat(None))
# then test
for dummy in range(0, max_links):
cur_expected_link_target = expected_link_targets.next()
cur_expected_link_label = expected_link_labels.next()
try:
browser.find_link(url=cur_expected_link_target,
text=cur_expected_link_label)
except mechanize.LinkNotFoundError:
raise InvenioTestUtilsBrowserException, \
'ERROR: Page %s (login %s) does not contain link to %s entitled %s.' % \
(url, username, cur_expected_link_target, cur_expected_link_label)
# now test for validation if required
if require_validate_p:
valid_p, errors, warnings = w3c_validate(url_body)
if not valid_p:
error_text = 'ERROR: Page %s (login %s) does not validate:\n %s' % \
(url, username, w3c_errors_to_str(errors, warnings))
open('%s/w3c-markup-validator.log' % CFG_LOGDIR, 'a').write(error_text)
raise InvenioTestUtilsBrowserException, error_text
except mechanize.HTTPError, msg:
error_messages.append('ERROR: Page %s (login %s) not accessible. %s' % \
(url, username, msg))
except InvenioTestUtilsBrowserException, msg:
error_messages.append('ERROR: Page %s (login %s) led to an error: %s.' % \
(url, username, msg))
# logout after tests:
browser.open(CFG_SITE_SECURE_URL + "/youraccount/logout")
if CFG_TESTUTILS_VERBOSE >= 9:
print "%s test_web_page_content(), tested page `%s', login `%s', expected text `%s', errors `%s'." % \
(time.strftime("%Y-%m-%d %H:%M:%S -->", time.localtime()),
url, username, expected_text,
string.join(error_messages, ","))
return error_messages
def merge_error_messages(error_messages):
"""If the ERROR_MESSAGES list is non-empty, merge them and return nicely
formatted string suitable for printing. Otherwise return empty
string.
"""
out = ""
if error_messages:
out = "\n*** " + string.join(error_messages, "\n*** ")
return out
diff --git a/modules/miscutil/lib/testutils_regression_tests.py b/modules/miscutil/lib/testutils_regression_tests.py
index 8c4c3114b..ee24fa42d 100644
--- a/modules/miscutil/lib/testutils_regression_tests.py
+++ b/modules/miscutil/lib/testutils_regression_tests.py
@@ -1,95 +1,95 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""TestUtils Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content
class TestFunctionTestWebPageContent(unittest.TestCase):
"""Check browser test_web_page_content() function."""
def test_twpc_username_arg(self):
"""testutils - test_web_page_content() and username arguments"""
# should login as admin without password:
self.assertEqual([],
- test_web_page_content(weburl,
+ test_web_page_content(CFG_SITE_URL,
username="admin",
expected_text="</html>"))
# should not login as admin with password:
- errmsgs = test_web_page_content(weburl,
+ errmsgs = test_web_page_content(CFG_SITE_URL,
username="admin",
password="foo",
expected_text="</html>")
if errmsgs[0].find("ERROR: Cannot login as admin, test skipped.") > -1:
pass
else:
self.fail("Should not be able to login as admin with foo password.")
return
def test_twpc_expected_text_arg(self):
"""testutils - test_web_page_content() and expected_text argument"""
# should find HTML in an HTML page:
self.assertEqual([],
- test_web_page_content(weburl + "/search?p=ellis",
+ test_web_page_content(CFG_SITE_URL + "/search?p=ellis",
expected_text="</html>"))
# should not find HTML tag in an XML page:
- errmsgs = test_web_page_content(weburl + "/search?p=ellis&of=xm")
+ errmsgs = test_web_page_content(CFG_SITE_URL + "/search?p=ellis&of=xm")
if errmsgs[0].find(" does not contain </html>") > -1:
pass
else:
self.fail("Should not find </html> in an XML page.")
return
def test_twpc_expected_link_arg(self):
"""testutils - test_web_page_content() and expected_link argument"""
# should find link to ALEPH:
self.assertEqual([],
- test_web_page_content(weburl,
- expected_link_target=weburl+"/collection/ALEPH"))
+ test_web_page_content(CFG_SITE_URL,
+ expected_link_target=CFG_SITE_URL+"/collection/ALEPH"))
# should find link entitled ISOLDE:
self.assertEqual([],
- test_web_page_content(weburl,
+ test_web_page_content(CFG_SITE_URL,
expected_link_label="ISOLDE"))
# should find link to ISOLDE entitled ISOLDE:
self.assertEqual([],
- test_web_page_content(weburl,
- expected_link_target=weburl+"/collection/ISOLDE",
+ test_web_page_content(CFG_SITE_URL,
+ expected_link_target=CFG_SITE_URL+"/collection/ISOLDE",
expected_link_label="ISOLDE"))
# should not find link to ALEPH entitled ISOLDE:
- errmsgs = test_web_page_content(weburl,
- expected_link_target=weburl+"/collection/ALEPH",
+ errmsgs = test_web_page_content(CFG_SITE_URL,
+ expected_link_target=CFG_SITE_URL+"/collection/ALEPH",
expected_link_label="ISOLDE")
if errmsgs[0].find(" does not contain link to ") > -1:
pass
else:
self.fail("Should not find link to ALEPH entitled ISOLDE.")
return
test_suite = make_test_suite(TestFunctionTestWebPageContent)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/miscutil/lib/urlutils.py b/modules/miscutil/lib/urlutils.py
index 94748c5d5..b3243ee17 100644
--- a/modules/miscutil/lib/urlutils.py
+++ b/modules/miscutil/lib/urlutils.py
@@ -1,255 +1,255 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
urlutils.py -- helper functions for URL related problems such as
argument washing, redirection, etc.
"""
__revision__ = "$Id$"
import re
from urllib import urlencode, quote_plus, quote
from urlparse import urlparse
from cgi import parse_qs, escape
try:
from mod_python import apache
except ImportError:
pass
from invenio.config import CFG_SITE_LANG
def wash_url_argument(var, new_type):
"""
Wash argument into 'new_type', that can be 'list', 'str',
'int', 'tuple' or 'dict'.
If needed, the check 'type(var) is not None' should be done before
calling this function.
@param var: variable value
@param new_type: variable type, 'list', 'str', 'int', 'tuple' or 'dict'
@return as much as possible, value var as type new_type
If var is a list, will change first element into new_type.
If int check unsuccessful, returns 0
"""
out = []
if new_type == 'list': # return lst
if type(var) is list:
out = var
else:
out = [var]
elif new_type == 'str': # return str
if type(var) is list:
try:
out = "%s" % var[0]
except:
out = ""
elif type(var) is str:
out = var
else:
out = "%s" % var
elif new_type == 'int': # return int
if type(var) is list:
try:
out = int(var[0])
except:
out = 0
elif type(var) is int:
out = var
elif type(var) is str:
try:
out = int(var)
except:
out = 0
else:
out = 0
elif new_type == 'tuple': # return tuple
if type(var) is tuple:
out = var
else:
out = (var,)
elif new_type == 'dict': # return dictionary
if type(var) is dict:
out = var
else:
out = {0:var}
return out
def redirect_to_url(req, url):
"""
Redirect current page to url.
@param req: request as received from apache
@param url: url to redirect to"""
req.err_headers_out.add("Location", url)
raise apache.SERVER_RETURN, apache.HTTP_MOVED_PERMANENTLY
def get_client_ip_address(req):
""" Returns IP address as string from an apache request. """
return str(req.get_remote_host(apache.REMOTE_NOLOOKUP))
def get_referer(req, replace_ampersands=False):
""" Return the referring page of a request.
Referer (wikipedia): Referer is a common misspelling of the word "referrer";
so common, in fact, that it made it into the official specification of HTTP.
When visiting a webpage, the referer or referring page is the URL of the
previous webpage from which a link was followed.
@param req: request
@param replace_ampersands: if 1, replace & by &amp; in url
(correct HTML cannot contain & characters alone).
"""
try:
referer = req.headers_in['Referer']
if replace_ampersands == 1:
return referer.replace('&', '&amp;')
return referer
except KeyError:
return ''
def drop_default_urlargd(urlargd, default_urlargd):
lndefault = {}
lndefault.update(default_urlargd)
lndefault['ln'] = (str, CFG_SITE_LANG)
canonical = {}
canonical.update(urlargd)
for k, v in urlargd.items():
try:
d = lndefault[k]
if d[1] == v:
del canonical[k]
except KeyError:
pass
return canonical
def make_canonical_urlargd(urlargd, default_urlargd):
""" Build up the query part of an URL from the arguments passed in
the 'urlargd' dictionary. 'default_urlargd' is a secondary dictionary which
contains tuples of the form (type, default value) for the query
arguments (this is the same dictionary as the one you can pass to
webinterface_handler.wash_urlargd).
When a query element has its default value, it is discarded, so
that the simplest (canonical) url query is returned.
The result contains the initial '?' if there are actual query
items remaining.
"""
canonical = drop_default_urlargd(urlargd, default_urlargd)
if canonical:
return '?' + urlencode(canonical, doseq=True).replace('&', '&amp;')
return ''
def create_html_link(urlbase, urlargd, link_label, linkattrd={},
escape_urlargd=True, escape_linkattrd=True):
"""Creates a W3C compliant link.
- @param urlbase: base url (e.g. invenio.config.weburl/search)
+ @param urlbase: base url (e.g. invenio.config.CFG_SITE_URL/search)
@param urlargd: dictionary of parameters. (e.g. p={'recid':3, 'of'='hb'})
@param link_label: text displayed in a browser (has to be already escaped)
@param linkattrd: dictionary of attributes (e.g. a={'class': 'img'})
@param escape_urlargd: boolean indicating if the function should escape
arguments (e.g. < becomes &lt; or " becomes &quot;)
@param escape_linkattrd: boolean indicating if the function should escape
attributes (e.g. < becomes &lt; or " becomes &quot;)
"""
attributes_separator = ' '
output = '<a href="' + create_url(urlbase, urlargd, escape_urlargd) + '"'
if linkattrd:
output += ' '
if escape_linkattrd:
attributes = [escape(str(key), quote=True) + '="' + \
escape(str(linkattrd[key]), quote=True) + '"'
for key in linkattrd.keys()]
else:
attributes = [str(key) + '="' + str(linkattrd[key]) + '"'
for key in linkattrd.keys()]
output += attributes_separator.join(attributes)
output += '>' + link_label + '</a>'
return output
def create_url(urlbase, urlargd, escape_urlargd=True):
"""Creates a W3C compliant URL. Output will look like this:
'urlbase?param1=value1&amp;param2=value2'
- @param urlbase: base url (e.g. invenio.config.weburl/search)
+ @param urlbase: base url (e.g. invenio.config.CFG_SITE_URL/search)
@param urlargd: dictionary of parameters. (e.g. p={'recid':3, 'of'='hb'}
@param escape_urlargd: boolean indicating if the function should escape
arguments (e.g. < becomes &lt; or " becomes &quot;)
"""
separator = '&amp;'
output = urlbase
if urlargd:
output += '?'
if escape_urlargd:
arguments = [escape(quote(str(key)), quote=True) + '=' + \
escape(quote(str(urlargd[key])), quote=True)
for key in urlargd.keys()]
else:
arguments = [str(key) + '=' + str(urlargd[key])
for key in urlargd.keys()]
output += separator.join(arguments)
return output
def same_urls_p(a, b):
""" Compare two URLs, ignoring reorganizing of query arguments """
ua = list(urlparse(a))
ub = list(urlparse(b))
ua[4] = parse_qs(ua[4], True)
ub[4] = parse_qs(ub[4], True)
return ua == ub
def urlargs_replace_text_in_arg(urlargs, regexp_argname, text_old, text_new):
"""Analyze `urlargs' (URL CGI GET query arguments in string form)
and for each occurrence of argument matching `regexp_argname'
replace every substring `text_old' by `text_new'. Return the
resulting new URL.
Used to be used for search engine's create_nearest_terms_box,
now it is not used there anymore. It is left here in case it
will become possibly useful later.
"""
out = ""
# parse URL arguments into a dictionary:
urlargsdict = parse_qs(urlargs)
## construct new URL arguments:
urlargsdictnew = {}
for key in urlargsdict.keys():
if re.match(regexp_argname, key): # replace `arg' by new values
urlargsdictnew[key] = []
for parg in urlargsdict[key]:
urlargsdictnew[key].append(parg.replace(text_old, text_new))
else: # keep old values
urlargsdictnew[key] = urlargsdict[key]
# build new URL for this word:
for key in urlargsdictnew.keys():
for val in urlargsdictnew[key]:
out += "&amp;" + key + "=" + quote_plus(val, '')
if out.startswith("&amp;"):
out = out[5:]
return out
diff --git a/modules/webaccess/doc/hacking/webaccess-admin-internals.webdoc b/modules/webaccess/doc/hacking/webaccess-admin-internals.webdoc
index 96bbc88ae..7bff52021 100644
--- a/modules/webaccess/doc/hacking/webaccess-admin-internals.webdoc
+++ b/modules/webaccess/doc/hacking/webaccess-admin-internals.webdoc
@@ -1,158 +1,158 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Access Control Admin Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="webaccess-internals">WebAccess Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="webaccess-internals">WebAccess Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
These are the functions called by the WebAccess Web Admin Interface to administrate
the wanted authorizations. This listing is of the most important functions.
For more information look in the source code or at the signatures.
CONTENTS
1. Adding Information
2. Finding Information
3. Deleting Information
1. Adding Information
acc_add_action(name_action='', description='', optional='no', *allowedkeywords)
function to create new entry in accACTION for an action
name_action - name of the new action, must be unique
keyvalstr - string with allowed keywords
allowedkeywords - a list of allowedkeywords
keyvalstr and allowedkeywordsdict can not be in use simultanously
success -> return id_action, name_action, description and allowedkeywords
failure -> return 0
acc_add_role(name_role, description, firerole_def_ser, firerole_def_src)
add a new role to accROLE in the database.
name_role - name of the role, must be unique
description - text to describe the role
firerole_def_ser - precompiled serialized firewall like role definition (firerole)
firerole_def_src - definition text source for repairing after Python upgrades
acc_add_user_role(id_user=0, id_role=0, email='', name_role='')
this function adds a new entry to table user_accROLE and returns it
id_user, id_role - self explanatory
email - email of the user
name_role - name of the role, to be used instead of id.
acc_add_role_action_arguments_names(name_role='', name_action='', arglistid=-1, optional=0, verbose=0, **keyval)
this function makes it possible to pass names when creating new entries instead of ids.
get ids for all the names,
create entries in accARGUMENT that does not exist,
pass on to id based function.
name_role, name_action - self explanatory
arglistid - add entries to or create group with arglistid, default -1 create new.
optional - create entry with optional keywords, **keyval is ignored, but should be empty
verbose - used to print extra information
**keyval - dictionary of keyword=value pairs, used to find ids.
2. Finding Information
acc_find_possible_actions(id_role, id_action)
Role based function to find all action combinations for a
give role and action.
id_role - id of role in the database
id_action - id of the action in the database
returns a list with all the combinations.
first row is used for header.
3. Deleting Information
acc_deletea_ction(id_action=0, name_action=0)
delete action in accACTION according to id, or secondly name.
entries in accROLE_accACTION_accARGUMENT will also be removed.
id_action - id of action to be deleted, prefered variable
name_action - this is used if id_action is not given
if the name or id is wrong, the function does nothing
acc_delete_role(id_role=0, name_role=0)
delete role entry in table accROLE and all references from other tables.
id_role - id of role to be deleted, prefered variable
name_role - this is used if id_role is not given
acc_delete_user_role(id_user, id_role=0, name_role=0)
function deletes entry from user_accROLE and reports the success.
id_user - user in database
id_role - role in the database, prefered parameter
name_role - can also delete role on background of role name.
acc_delete_role_action_arguments_names(name_role='', name_action='', arglistid=1, **keyval)
utilize the function on ids by first finding all ids and redirecting the function call.
break of and return 0 if any of the ids can't be found.
name_role = name of the role
name_action - name of the action
arglistid - the argumentlistid, all keyword=value pairs must be in this same group.
**keyval - dictionary of keyword=value pairs for the arguments.
</pre>
diff --git a/modules/webaccess/doc/hacking/webaccess-api.webdoc b/modules/webaccess/doc/hacking/webaccess-api.webdoc
index fa570ff06..64f84cf76 100644
--- a/modules/webaccess/doc/hacking/webaccess-api.webdoc
+++ b/modules/webaccess/doc/hacking/webaccess-api.webdoc
@@ -1,173 +1,173 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Access Control Engine API -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="webaccess-internals">WebAccess Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="webaccess-internals">WebAccess Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
CDS Invenio Access Control Engine can be called from within your Python programs
via both a regular Python API and CLI.
In addition the you get an explanation of the program flow.
Contents:
1. Regular API
2. Command Line Interface
3. Program Flow
4. External login methods
1. Regular API
Description:
There is not very much information in the database at the moment.
More can be added on demand.
Information on these will be added when time allows it.
Signature:
def acc_authorize_action(req, name_action, dict={}, **arguments):
"""Check if user is allowed to perform action
with given list of arguments.
Return (0, message) if authentication succeeds, (error code, error message) if it fails.
The arguments are as follows:
req - could be either one of these three things:
id_user of the current user
user_info dictionary built against the user details
req mod_python request object
name_action - the name of the action
arguments - dictionary with keyword=value pairs created automatically
by python on the extra arguments. these depend on the
given action.
"""
Examples:
>>> # import the functions from module
>>> # change this to your local settings...
>>> from invenio.access_control_engine import *
>>> # authorize user 109 for action WebSearch_search with collection="LHC"
>>> acc_authorize_action(109, 'cfgwebsearch', collection="LHC")
(0, "User authorized")
>>> # authorize user 109 for action WebSearch_search with collection="fail this"
>>> acc_authorize_action(109, 'cfgwebsearch', collection="fail this")
(8, "Error (8): Incorrect keyword given for specified action.")
>>> # authorize user 109 for action BibFormat_modify with format="htmlbrief"
>>> acc_authorize_action(109, 'cfgbibformat', format="htmlbrief")
(0, "User authorized")
>>> # authorize user foo.bar@cern.ch for action BibFormat_modify with format="htmlbrief"
>>> acc_authorize_action({'uid':109, 'email':'foo.bar@cern.ch'}, 'cfgbibformat', format="htmlbrief")
(0, "User authorized")
2. Command Line Interface
Description:
The Command Line Interface uses the regular API of acc_authorize_action.
Signature:
authaction id_user name_action keyword1 value1 keyword2 value2 ...
""" See description from function acc_authorize_action.
id_user - id of user to be authorized
name_action - name of the action
keyword1 - first keyword like in the keyword=value pairs,
same rules for the following ones.
always one word.
value1 - value that belongs in a pair with the corresponding keyword,
same rules for the following ones.
add quotes if it is more that one word.
the keyword=value pairs are collected in a dictionary
"""
Examples:
These are the same ones as for the regular API:
$ authaction 109 cfgwebsearch collection LHC
0 - User authorized
$ authaction 109 cfgwebsearch collection 'fail this'
8 - Error (8): Incorrect keyword given for specified action.
$ authaction 109 cfgbibformat format htmlbrief
0 - User authorized
3. Program Flow
this is a quick explanation of the different tasks
performed by the authorization engine.
I. find information for the action
use admin API to find info.
II. see if user is a superadmin
query the database for connection between user and role superadmin.
-> authorize if yes
III. check if the user exists and find all of the users roles and create string with the ids
query the database and build string of ids
-> don't authorize if no roles
IV. try to authorize without arguments
action without arguments: query database
-> authorize if yes
action with optional arguments
-> authorize if yes
V. create list of keyword=value pairs to query the database
run through dictionary and create string for adding to database query
VI. find all table entries from the database
query the database for table entries
create list of the tuples and sort it
-> don't authorize if no entries
-> authorize if only 1 argument and result
VII. combine entries and try to satisfy authorization
dictionary with the arguments as keys, all values 0
run throught the list created in VI
if moving on to new authorization
check dictionary values
-> authorize if combination found
reset values to 0 if not found
set dictionary[keyword] to 1.
(countinue loop)
VIII. all the above failed
-> authorization failed
4. External login methods
If you want to call defined external loginmethods, it can be done by:
import invenio.external_authentication
example_method = invenio.external_authentication.example()
example_method.auth_user("testuser", "testpassword")
Example is here a class used as an example, which must contain the method
auth_user.
</pre>
diff --git a/modules/webaccess/doc/hacking/webaccess-firerole-api.webdoc b/modules/webaccess/doc/hacking/webaccess-firerole-api.webdoc
index 2f5cde1d6..8044c9246 100644
--- a/modules/webaccess/doc/hacking/webaccess-firerole-api.webdoc
+++ b/modules/webaccess/doc/hacking/webaccess-firerole-api.webdoc
@@ -1,92 +1,92 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: FireRole Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="webaccess-internals">WebAccess Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="webaccess-internals">WebAccess Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
The "Firewall like role definition engine" (FireRole) is a tool for describing
formally sets of users and checking users against this description for their membership.
FireRole rules are strings defined as in the <a href="webaccess-admin-guide#6">FireRole language description</a> page.
In order to use FireRole rules with the engine you have to compile these strings with
the <strong>compile_role_definition</strong> function.
The compiled form could be serialized and stored into a database with the provided
function <strong>serialize</strong> and later loaded with <strong>deserialize</strong>.
To build a user description you have to use the <strong>collect_user_info</strong> from
the module <em>webuser</em>. This function, given a <em>mod_python request object</em>
or a <em>id_user</em> returns a dictionary with all the collectable user informations (when
using the request object these includes the <em>ip address</em> and the <em>host</em>
of the user, too).
Passing both the user_info dictionary and the compiled (deserialized) FireRole
definition to the <strong>acc_firerole_check_user</strong> function returns a
<em>boolean value</em> stating if the user belong to the set described by this
definition.
FireRole definitions are integrated with <em>accROLEs</em>, in fact, you can pass
a uid, a req_object or user_info a dictionary to <strong>acc_authorize_action</strong>.
If necessary collect_user_info will be implicitly called, and user authorizations
will be checked first against direct traditional uid membership and then against
FireRole definitions.
<H2>API</H2>
<em>from webuser</em>
def <strong>collect_user_info</strong>(req):
"""Given the mod_python request object rec or a uid it returns a dictionary
containing at least the keys uid, nickname, email, groups, plus any external keys in
the user preferences (collected at login time and built by the different
external authentication plugins) and if the mod_python request object is
provided, also the remote_ip and remote_host fields.
"""
<em>from access_control_firerole</em>
def <strong>compile_role_definition</strong>(FireRole_def_src):
""" Given a text in which every row contains a rule it returns the compiled
object definition.
Rules have the following syntax:
allow|deny [not] field {list of one or more (double)quoted string or regexp}
or allow|deny any
Every row may contain a # sign followed by a comment which are discarded.
Field could be any key contained in a user_info dictionary. If the key does
not exist in the dictionary, the rule is skipped.
The first rule which matches return.
"""
def <strong>serialize</strong>(FireRole_def_src):
""" Serialize and compress a definition."""
def <strong>deserialize</strong>(FireRole_def_ser):
""" Deserialize and decompress a definition."""
def <strong>acc_firerole_check_user</strong>(user_info, firerole_def_obj):
""" Given a user_info dictionary, it matches the rules inside the deserializez
compiled definition in order to discover if the current user match the roles
corresponding to this definition.
@param user_info a dict produced by collect_user_info which contains every
info about a user
@param firerole_def_obj a compiled deserialized definition produced by
compile_role_defintion
@return True if the user match the definition, False otherwise.
"""
</pre>
diff --git a/modules/webaccess/doc/hacking/webaccess-internals.webdoc b/modules/webaccess/doc/hacking/webaccess-internals.webdoc
index 9e5122847..e5a8f0959 100644
--- a/modules/webaccess/doc/hacking/webaccess-internals.webdoc
+++ b/modules/webaccess/doc/hacking/webaccess-internals.webdoc
@@ -1,42 +1,42 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebAccess Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
This page summarizes all the information suitable to dig inside
the WebAccess internals.
<blockquote>
<dl>
<dt><a href="webaccess-api">Access Control Engine API</a> </dt>
<dd>Explains how to call access control engine from your Python programs.<br />
In addition the program flow is explained briefly.</dd>
<dt><a href="webaccess-admin-internals">Access Control Admin Internals</a> </dt>
<dd>Explains how to administrate the authorizations stored in the database for <br />
WebAccess. Not all functions are mentioned. All mentioned functions are <br />
represented with signatures, some with additional explanations as well.</dd>
<dt><a href="webaccess-table-structure">Access Control Table Structure</a> </dt>
<dd>Explains the structure of the WebAccess tables and how to create authorizations.</dd>
<dt><a href="webaccess-firerole-api">FireRole Internals</a> </dt>
<dd>Explains how to use the functions concerning firewall like role definitions.</dd>
</dl>
</blockquote>
diff --git a/modules/webaccess/doc/hacking/webaccess-table-structure.webdoc b/modules/webaccess/doc/hacking/webaccess-table-structure.webdoc
index 53b781418..6cbe11b62 100644
--- a/modules/webaccess/doc/hacking/webaccess-table-structure.webdoc
+++ b/modules/webaccess/doc/hacking/webaccess-table-structure.webdoc
@@ -1,106 +1,106 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebAccess Table Structure -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="webaccess-internals">WebAccess Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="webaccess-internals">WebAccess Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
Explanation of the WebAccess table structure and how to create authorizations.
-- accROLE --
roles are collections of authorizations within specified areas. these should
have a clear connection, for example within a module or for a special type of
person, e.g a library administator.
id - specify the id of the role
name - unique name for the role
description - describe the authorization tasks the role will cover
firerole_def_ser - precompiled serialized firewall like role definition (firerole)
firerole_def_src - definition text source for repairing after Python upgrades
-- user_accROLE --
give users access to the authorizations/actions provided by the given role.
id_user - id of user in the database, usually just the CFG_SITE_SUPPORT_EMAIL and/or CFG_SITE_ADMIN_EMAIL
id_accROLE - id of role from accROLE
-- accACTION --
the actions specify what information is needed to create an authorization.
an action can take any number of arguments, and you also have the possibility to
make these arguments optional.
id - specify the id of the action
name - unique name for the action
description - describe authorization area
allowedkeywords - the keywords for the wanted arguments
optional - enum to define wether arguments can be optional
value: ['yes', 'no']
'no' - action without arguments or not optional arguments
'yes' - only actions with arguments
-- accARGUMENT --
arguments for the authorizations. one entry might be shared by several authorizations,
so it is important to not change these but simply add new ones when a change is needed.
id - id of the argument
keyword - the one listed in an action's allowedkeywords
value - the value connected to the keyword in an keyword=value argument pair
-- accROLE_accACTION_accARGUMENT --
ternary connection to create authorizations. when an authorization has more than
one argument they need to be connected somehow. the same id for role, action and
argumentlist does this. there must be at least as many entries as allowedkeywords
for the action. with more entries all authorizations are found using a cross product.
id_accARGUMENT and argumentlistid are both set to 0 when the action have no arguments.
with optional arguments they are both set to -1.
id_accROLE - reference to argument in table accROLE
id_accACTION - reference to argument in table accACTION
id_accARGUMENT - reference to argument in table accARGUMENT
argumentlistid - glue to bind entries into authorizations.
</pre>
diff --git a/modules/webaccess/lib/access_control_config.py b/modules/webaccess/lib/access_control_config.py
index 78f6cd1ed..79aedee45 100644
--- a/modules/webaccess/lib/access_control_config.py
+++ b/modules/webaccess/lib/access_control_config.py
@@ -1,214 +1,214 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio Access Control Config. """
__revision__ = \
"$Id$"
# pylint: disable-msg=C0301
-from invenio.config import CFG_SITE_NAME, weburl, CFG_SITE_SECURE_URL, CFG_SITE_SUPPORT_EMAIL, CFG_CERN_SITE
+from invenio.config import CFG_SITE_NAME, CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_SITE_SUPPORT_EMAIL, CFG_CERN_SITE
import cPickle
from zlib import compress
from invenio.messages import gettext_set_language
class InvenioWebAccessFireroleError(Exception):
"""Just an Exception to discover if it's a FireRole problem"""
pass
# VALUES TO BE EXPORTED
# CURRENTLY USED BY THE FILES access_control_engine.py access_control_admin.py webaccessadmin_lib.py
# name of the role giving superadmin rights
SUPERADMINROLE = 'superadmin'
# name of the webaccess webadmin role
WEBACCESSADMINROLE = 'webaccessadmin'
# name of the action allowing roles to access the web administrator interface
WEBACCESSACTION = 'cfgwebaccess'
# name of the action allowing roles to access the web administrator interface
VIEWRESTRCOLL = 'accrestrcoll'
# name of the action allowing roles to delegate the rights to other roles
# ex: libraryadmin to delegate libraryworker
DELEGATEADDUSERROLE = 'accdelegaterole'
# max number of users to display in the drop down selects
MAXSELECTUSERS = 25
# max number of users to display in a page (mainly for user area)
MAXPAGEUSERS = 25
""" Serialized compiled default role definition"""
CFG_ACC_EMPTY_ROLE_DEFINITION_SER=compress(cPickle.dumps((False, False, ()), -1))
""" Source of the default role definition"""
CFG_ACC_EMPTY_ROLE_DEFINITION_SRC='deny any'
# List of tags containing (multiple) emails of users who should authorize
# to access the corresponding record regardless of collection restrictions.
if CFG_CERN_SITE:
CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS=['859__f', '270__m']
else:
CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS=['8560_f']
# Use external source for access control?
# Atleast one must be added
# Adviced not to change the name, since it is used to identify the account
# Format is: System name: (System class, Default True/Flase), atleast one
# must be default
CFG_EXTERNAL_AUTHENTICATION = {"Local": (None, True)}
# Variables to set to the SSO Authentication name if using SSO
CFG_EXTERNAL_AUTH_USING_SSO = False
CFG_EXTERNAL_AUTH_LOGOUT_SSO = None
if CFG_CERN_SITE:
if False: #FIXME set this to True when we deploy SSO at CERN
import external_authentication_sso as ea_sso
CFG_EXTERNAL_AUTH_USING_SSO = "CERN"
# Link to reach in order to logout from SSO
CFG_EXTERNAL_AUTH_LOGOUT_SSO = 'https://login.cern.ch/adfs/ls/?wa=wsignout1.0'
CFG_EXTERNAL_AUTHENTICATION = {CFG_EXTERNAL_AUTH_USING_SSO : (ea_sso.ExternalAuthSSO(), True)}
else:
import external_authentication_cern as ea_cern
CFG_EXTERNAL_AUTHENTICATION = {"Local": (None, False), \
"CERN": (ea_cern.ExternalAuthCern(), True)}
# default data for the add_default_settings function
# Note: by default the definition is set to deny any. This won't be a problem
# because userid directly connected with roles will still be allowed.
# roles
# name description definition
DEF_ROLES = ((SUPERADMINROLE, 'superuser with all rights', 'deny any'),
(WEBACCESSADMINROLE, 'WebAccess administrator', 'deny any'))
# Demo site roles
DEF_DEMO_ROLES = (('photoadmin', 'Photo collection administrator', 'deny any'),
('thesesviewer', 'Theses viewer', 'allow group "Theses viewers"\nallow apache_group "theses"'))
DEF_DEMO_USER_ROLES = (('jekyll@cern.ch', 'thesesviewer'), )
# users
# list of e-mail addresses
DEF_USERS = []
# actions
# name desc allowedkeywords optional
DEF_ACTIONS = (
('cfgwebsearch', 'configure WebSearch', '', 'no'),
('cfgbibformat', 'configure BibFormat', '', 'no'),
('cfgwebsubmit', 'configure WebSubmit', '', 'no'),
('cfgbibrank', 'configure BibRank', '', 'no'),
('cfgwebcomment', 'configure WebComment', '', 'no'),
('cfgbibharvest', 'configure BibHarvest', '', 'no'),
('cfgoairepository', 'configure OAI Repository', '', 'no'),
('cfgbibindex', 'configure BibIndex', '', 'no'),
('runbibindex', 'run BibIndex', '', 'no'),
('runbibupload', 'run BibUpload', '', 'no'),
('runwebcoll', 'run webcoll', 'collection', 'yes'),
('runbibformat', 'run BibFormat', 'format', 'yes'),
('runbibclassify', 'run BibClassify', 'taxonomy', 'yes'),
('runbibtaskex', 'run BibTaskEx example', '', 'no'),
('runbibrank', 'run BibRank', '', 'no'),
('runoaiharvest', 'run oaiharvest task', '', 'no'),
('runoaiarchive', 'run oaiarchive task', '', 'no'),
('runbibedit', 'run BibEdit', '', 'no'),
('runwebstatadmin', 'run WebStadAdmin', '', 'no'),
('runinveniogc', 'run InvenioGC', '', 'no'),
('referee', 'referee document type doctype/category categ', 'doctype,categ', 'yes'),
('submit', 'use webSubmit', 'doctype,act', 'yes'),
('viewrestrdoc', 'view restricted document', 'status', 'no'),
(WEBACCESSACTION, 'configure WebAccess', '', 'no'),
(DELEGATEADDUSERROLE, 'delegate subroles inside WebAccess', 'role', 'no'),
(VIEWRESTRCOLL, 'view restricted collection', 'collection', 'no'),
)
# Default authorizations
# role action arglistid optional arguments
DEF_AUTHS = ()
# Demo site authorizations
# role action arglistid optional arguments
DEF_DEMO_AUTHS = (
('photoadmin', 'runwebcoll', -1, 0, {'collection': 'Pictures'}),
('thesesviewer', 'viewrestrdoc', -1, 0, {'status' : 'restrdoc'}),
('thesesviewer', VIEWRESTRCOLL, -1, 0, {'collection' : 'Theses'})
)
_ = gettext_set_language('en')
CFG_ACC_ACTIVITIES_URLS = {
- 'runbibedit' : (_("Run BibEdit"), "%s/admin/bibedit/bibeditadmin.py?ln=%%s" % weburl),
- 'cfgbibformat' : (_("Configure BibFormat"), "%s/admin/bibformat/bibformatadmin.py?ln=%%s" % weburl),
- 'cfgbibharvest' : (_("Configure BibHarvest"), "%s/admin/bibharvest/bibharvestadmin.py?ln=%%s" % weburl),
- 'cfgoairepository' : (_("Configure OAI Repository"), "%s/admin/bibharvest/oaiarchiveadmin.py?ln=%%s" % weburl),
- 'cfgbibindex' : (_("Configure BibIndex"), "%s/admin/bibindex/bibindexadmin.py?ln=%%s" % weburl),
- 'cfgbibrank' : (_("Configure BibRank"), "%s/admin/bibrank/bibrankadmin.py?ln=%%s" % weburl),
- 'cfgwebaccess' : (_("Configure WebAccess"), "%s/admin/webaccess/webaccessadmin.py?ln=%%s" % weburl),
- 'cfgwebcomment' : (_("Configure WebComment"), "%s/admin/webcomment/webcommentadmin.py?ln=%%s" % weburl),
- 'cfgwebsearch' : (_("Configure WebSearch"), "%s/admin/websearch/websearchadmin.py?ln=%%s" % weburl),
- 'cfgwebsubmit' : (_("Configure WebSubmit"), "%s/admin/websubmit/websubmitadmin.py?ln=%%s" % weburl),
+ 'runbibedit' : (_("Run BibEdit"), "%s/admin/bibedit/bibeditadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgbibformat' : (_("Configure BibFormat"), "%s/admin/bibformat/bibformatadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgbibharvest' : (_("Configure BibHarvest"), "%s/admin/bibharvest/bibharvestadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgoairepository' : (_("Configure OAI Repository"), "%s/admin/bibharvest/oaiarchiveadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgbibindex' : (_("Configure BibIndex"), "%s/admin/bibindex/bibindexadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgbibrank' : (_("Configure BibRank"), "%s/admin/bibrank/bibrankadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgwebaccess' : (_("Configure WebAccess"), "%s/admin/webaccess/webaccessadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgwebcomment' : (_("Configure WebComment"), "%s/admin/webcomment/webcommentadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgwebsearch' : (_("Configure WebSearch"), "%s/admin/websearch/websearchadmin.py?ln=%%s" % CFG_SITE_URL),
+ 'cfgwebsubmit' : (_("Configure WebSubmit"), "%s/admin/websubmit/websubmitadmin.py?ln=%%s" % CFG_SITE_URL),
}
CFG_WEBACCESS_MSGS = {
0: 'Try to <a href="%s/youraccount/login?referer=%%s">login</a> with another account.' % (CFG_SITE_SECURE_URL),
1: '<br />If you think this is not correct, please contact: <a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL),
2: '<br />If you have any questions, please write to <a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL),
3: 'Guest users are not allowed, please <a href="%s/youraccount/login">login</a>.' % CFG_SITE_SECURE_URL,
4: 'The site is temporarily closed for maintenance. Please come back soon.',
5: 'Authorization failure',
6: '%s temporarily closed' % CFG_SITE_NAME,
7: 'This functionality is temporarily closed due to server maintenance. Please use only the search engine in the meantime.',
8: 'Functionality temporarily closed'
}
CFG_WEBACCESS_WARNING_MSGS = {
0: 'Authorization granted',
1: 'Error(1): You are not authorized to perform this action.',
2: 'Error(2): You are not authorized to perform any action.',
3: 'Error(3): The action %s does not exist.',
4: 'Error(4): Unexpected error occurred.',
5: 'Error(5): Missing mandatory keyword argument(s) for this action.',
6: 'Error(6): Guest accounts are not authorized to perform this action.',
7: 'Error(7): Not enough arguments, user ID and action name required.',
8: 'Error(8): Incorrect keyword argument(s) for this action.',
9: """Error(9): Account '%s' is not yet activated.""",
10: """Error(10): You were not authorized by the authentication method '%s'.""",
11: """Error(11): The selected login method '%s' is not the default method for this account, please try another one.""",
12: """Error(12): Selected login method '%s' does not exist.""",
13: """Error(13): Could not register '%s' account.""",
14: """Error(14): Could not login using '%s', because this user is unknown.""",
15: """Error(15): Could not login using your '%s' account, because you have introduced a wrong password.""",
16: """Error(16): External authentication troubles using '%s' (maybe temporary network problems).""",
17: """Error(17): You have not yet confirmed the email address for the '%s' authentication method.""",
18: """Error(18): The administrator has not yet activated your account for the '%s' authentication method.""",
19: """Error(19): The site is having troubles in sending you an email for confirming your email address. The error has been logged and will be taken care of as soon as possible."""
}
diff --git a/modules/webaccess/lib/webaccess_regression_tests.py b/modules/webaccess/lib/webaccess_regression_tests.py
index 08c770b99..df8294662 100644
--- a/modules/webaccess/lib/webaccess_regression_tests.py
+++ b/modules/webaccess/lib/webaccess_regression_tests.py
@@ -1,69 +1,69 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebAccess Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class WebAccessWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebAccess web pages whether they are up or not."""
def test_webaccess_admin_interface_availability(self):
"""webaccess - availability of WebAccess Admin interface pages"""
- baseurl = weburl + '/admin/webaccess/webaccessadmin.py/'
+ baseurl = CFG_SITE_URL + '/admin/webaccess/webaccessadmin.py/'
_exports = ['', 'delegate_startarea', 'manageaccounts']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_webaccess_admin_guide_availability(self):
"""webaccess - availability of WebAccess Admin guide pages"""
- url = weburl + '/help/admin/webaccess-admin-guide'
+ url = CFG_SITE_URL + '/help/admin/webaccess-admin-guide'
error_messages = test_web_page_content(url,
expected_text="WebAccess Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(WebAccessWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/webaccess/lib/webaccessadmin_lib.py b/modules/webaccess/lib/webaccessadmin_lib.py
index a9c4049c6..397f834cd 100644
--- a/modules/webaccess/lib/webaccessadmin_lib.py
+++ b/modules/webaccess/lib/webaccessadmin_lib.py
@@ -1,3703 +1,3703 @@
## $Id$
## Administrator interface for WebAccess
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio WebAccess Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
## fill config variables:
import re
import random
import getopt
import sys
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_ACCESS_CONTROL_LEVEL_GUESTS, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN, \
CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
- weburl
+ CFG_SITE_URL
import invenio.access_control_engine as acce
import invenio.access_control_admin as acca
from invenio.mailutils import send_email
from invenio.bibrankadminlib import addadminbox, tupletotable, \
tupletotable_onlyselected, addcheckboxes, createhiddenform
from invenio.access_control_firerole import compile_role_definition, \
repair_role_definitions, serialize
from invenio.messages import gettext_set_language
from invenio.dbquery import run_sql
from invenio.webpage import page
from invenio.webuser import getUid, isGuestUser, page_not_authorized
from invenio.webuser import email_valid_p, get_user_preferences, \
set_user_preferences
from invenio.access_control_config import DEF_DEMO_USER_ROLES, \
DEF_DEMO_ROLES, DEF_DEMO_AUTHS, WEBACCESSACTION, MAXPAGEUSERS, \
SUPERADMINROLE, CFG_EXTERNAL_AUTHENTICATION, DELEGATEADDUSERROLE, \
CFG_ACC_EMPTY_ROLE_DEFINITION_SRC, InvenioWebAccessFireroleError, \
MAXSELECTUSERS
from invenio.bibtask import authenticate
from cgi import escape
def index(req, title='', body='', subtitle='', adminarea=2, authorized=0):
"""main function to show pages for webaccessadmin.
1. if user not logged in and administrator, show the mustlogin page
2. if used without body argument, show the startpage
3. show admin page with title, body, subtitle and navtrail.
adminarea - number codes that tell what extra info to put in the navtrail
0 - nothing extra
1 - add Delegate Rights
2 - add Manage WebAccess
maybe add:
3: role admin
4: action admin
5: user area
6: reset area
authorized - if 1, don't check if the user is allowed to be webadmin """
navtrail_previous_links = '<a class="navtrail" href="%s/help/admin">Admin Area' \
- '</a>' % (weburl,)
+ '</a>' % (CFG_SITE_URL,)
if body:
if adminarea == 1:
navtrail_previous_links += '&gt; <a class=navtrail ' \
' href=%s/admin/webaccess/webaccessadmin.py/delegate_startarea>' \
- 'Delegate Rights</a> ' % (weburl, )
+ 'Delegate Rights</a> ' % (CFG_SITE_URL, )
if adminarea >= 2 and adminarea < 7:
navtrail_previous_links += '&gt; ' \
'<a class="navtrail" href=%s/admin/webaccess/webaccessadmin.py>' \
- 'WebAccess Admin</a> ' % (weburl, )
+ 'WebAccess Admin</a> ' % (CFG_SITE_URL, )
if adminarea == 3:
navtrail_previous_links += '&gt; <a class=navtrail ' \
'href=%s/admin/webaccess/webaccessadmin.py/rolearea>' \
- 'Role Administration</a> ' % (weburl, )
+ 'Role Administration</a> ' % (CFG_SITE_URL, )
elif adminarea == 4:
navtrail_previous_links += '&gt; ' \
'<a class="navtrail" href=%s/admin/webaccess/webaccessadmin.py' \
- '/actionarea>Action Administration</a> ' % (weburl, )
+ '/actionarea>Action Administration</a> ' % (CFG_SITE_URL, )
elif adminarea == 5:
navtrail_previous_links += '&gt; ' \
'<a class="navtrail" href=%s/admin/webaccess/webaccessadmin.py' \
- '/userarea>User Administration</a> ' % (weburl, )
+ '/userarea>User Administration</a> ' % (CFG_SITE_URL, )
elif adminarea == 6:
navtrail_previous_links += '&gt; ' \
'<a class="navtrail" href=%s/admin/webaccess/webaccessadmin.py' \
- '/resetarea>Reset Authorizations</a> ' % (weburl, )
+ '/resetarea>Reset Authorizations</a> ' % (CFG_SITE_URL, )
elif adminarea == 7:
navtrail_previous_links += '&gt; ' \
'<a class="navtrail" href=%s/admin/webaccess/webaccessadmin.py' \
- '/manageaccounts>Manage Accounts</a> ' % (weburl, )
+ '/manageaccounts>Manage Accounts</a> ' % (CFG_SITE_URL, )
id_user = getUid(req)
(auth_code, auth_message) = is_adminuser(req)
if not authorized and auth_code != 0:
return mustloginpage(req, auth_message)
elif not body:
title = 'WebAccess Admin'
body = startpage()
elif type(body) != str: body = addadminbox(subtitle, datalist=body)
return page(title=title,
uid=id_user,
req=req,
body=body,
navtrail=navtrail_previous_links,
lastupdated=__lastupdated__)
def mustloginpage(req, message):
"""show a page asking the user to login."""
navtrail_previous_links = '<a class="navtrail" href="%s/admin/">' \
'Admin Area</a> &gt; <a class="navtrail" href="%s/admin/webaccess/">' \
- 'WebAccess Admin</a> ' % (weburl, weburl)
+ 'WebAccess Admin</a> ' % (CFG_SITE_URL, CFG_SITE_URL)
return page_not_authorized(req=req, text=message,
navtrail=navtrail_previous_links)
def is_adminuser(req):
"""check if user is a registered administrator. """
return acce.acc_authorize_action(req, WEBACCESSACTION)
def perform_rolearea(req):
"""create the role area menu page."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
header = ['id', 'name', 'description', 'firewall like role definition',
'users', 'authorizations / actions', 'role', '']
roles = acca.acc_get_all_roles()
roles2 = []
for (id, name, desc, dummy, firerole_def_src) in roles:
if len(desc) > 30:
desc = desc[:30] + '...'
if firerole_def_src and len(firerole_def_src) > 30:
firerole_def_src = firerole_def_src[:30] + '...'
roles2.append([id, name, desc, firerole_def_src])
for col in [(('add', 'adduserrole'),
('delete', 'deleteuserrole'),),
(('add', 'addauthorization'),
('modify', 'modifyauthorizations'),
('remove', 'deleteroleaction')),
(('modify', 'modifyrole'),
('delete', 'deleterole')),
(('show details', 'showroledetails'), )]:
roles2[-1].append('<a href="%s?id_role=%s">%s</a>' %
(col[0][1], id, col[0][0]))
for (str, function) in col[1:]:
roles2[-1][-1] += ' / <a href="%s?id_role=%s">%s</a>' % \
(function, id, str)
output = """
<dl>
<dt>Users:</dt>
<dd>add or remove users from the access to a role and its priviliges.</dd>
<dt>Authorizations/Actions:</dt>
<dd>these terms means almost the same, but an authorization is a <br />
connection between a role and an action (possibly) containing arguments.
</dd>
<dt>Roles:</dt>
<dd>see all the information attached to a role and decide if you want
to<br />delete it.</dd>
</dl>
"""
output += tupletotable(header=header, tuple=roles2)
extra = """
<dl>
<dt><a href="addrole">Create new role</a></dt>
<dd>go here to add a new role.</dd>
<dt><a href="addaction">Create new action</a></dt>
<dd>go here to add a new action.</dd>
</dl>
"""
return index(req=req,
title='Role Administration',
subtitle='administration with roles as access point',
body=[output, extra],
adminarea=2)
def perform_actionarea(req):
"""create the action area menu page."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
header = ['id', 'name', 'authorizations/roles', 'action', '']
actions = acca.acc_get_all_actions()
actions2 = []
roles2 = []
for (id, name, dummy) in actions:
actions2.append([id, name])
for col in [(('add', 'addauthorization'),
('modify', 'modifyauthorizations'),
('remove', 'deleteroleaction')),
(('delete', 'deleteaction'), ),
(('show details', 'showactiondetails'), )]:
actions2[-1].append('<a href="%s?id_action=%s&amp;reverse=1">%s'
'</a>' % (col[0][1], id, col[0][0]))
for (str, function) in col[1:]:
actions2[-1][-1] += ' / <a href="%s?id_action=%s&amp;' \
'reverse=1">%s</a>' % (function, id, str)
output = """
<dl>
<dt>Authorizations/Roles:</dt>
<dd>these terms means almost the same, but an authorization is a <br />
connection between a role and an action (possibly) containing
arguments.</dd>
<dt>Actions:</dt>
<dd>see all the information attached to an action and decide if you
want to<br />delete it.</dd>
</dl>
"""
output += tupletotable(header=header, tuple=actions2)
extra = """
<dl>
<dt><a href="addrole">Create new role</a>
<dd>go here to add a new role.
<dt><a href="addaction">Create new action</a>
<dd>go here to add a new action.
</dl>
"""
return index(req=req,
title='Action Administration',
subtitle='administration with actions as access point',
body=[output, extra],
adminarea=2)
def perform_userarea(req, email_user_pattern=''):
"""create area to show info about users. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
subtitle = 'step 1 - search for users'
output = """
<p>
search for users to display.
</p> """
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
text = ' <span class="adminlabel">1. search for user</span>\n'
text += ' <input class="admin_wvar" type="text" name="email_user_pattern"'\
' value="%s" />\n' % (email_user_pattern, )
output += createhiddenform(action="userarea",
text=text,
button="search for users")
if email_user_pattern:
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s
ORDER BY email LIMIT %s""", (email_user_pattern, MAXPAGEUSERS+1))
if not users1:
output += '<p>no matching users</p>'
else:
subtitle = 'step 2 - select what to do with user'
users = []
for (id, email) in users1[:MAXPAGEUSERS]:
users.append([id, email])
for col in [(('add', 'addroleuser'),
('remove', 'deleteuserrole')),
(('show details', 'showuserdetails'), )]:
users[-1].append('<a href="%s?email_user_pattern=%s&amp;'
'id_user=%s">%s</a>' % (col[0][1],
email_user_pattern, id, col[0][0]))
for (str, function) in col[1:]:
users[-1][-1] += ' / <a href="%s?email_user_pattern' \
'=%s&amp;id_user=%s&amp;reverse=1">%s</a>' % \
(function, email_user_pattern, id, str)
output += '<p>found <strong>%s</strong> matching users:</p>' % \
(len(users1), )
output += tupletotable(header=['id', 'email', 'roles', ''],
tuple=users)
if len(users1) > MAXPAGEUSERS:
output += '<p><strong>only showing the first %s users, ' \
'narrow your search...</strong></p>' % (MAXPAGEUSERS, )
return index(req=req,
title='User Administration',
subtitle=subtitle,
body=[output],
adminarea=2)
def perform_resetarea(req):
"""create the reset area menu page."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
output = """
<dl>
<dt><a href="resetdefaultsettings">Reset to Default Authorizations</a>
<dd>remove all changes that has been done to the roles and <br />
add only the default authorization settings.
<dt><a href="adddefaultsettings">Add Default Authorizations</a>
<dd>keep all changes and add the default authorization settings.
</dl>
"""
return index(req=req,
title='Reset Authorizations',
subtitle='reseting to or adding default authorizations',
body=[output],
adminarea=2)
def perform_resetdefaultsettings(req, superusers=[], confirm=0):
"""delete all roles, actions and authorizations presently in the database
and add only the default roles.
only selected users will be added to superadmin, rest is blank """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
# cleaning input
if type(superusers) == str: superusers = [superusers]
# remove not valid e-mails
for email in superusers:
if not check_email(email): superusers.remove(email)
# instructions
output = """
<p>
before you reset the settings, we need some users<br />
to connect to <strong>%s</strong>.<br />
enter as many e-mail addresses you want and press <strong>reset</strong>.<br />
<strong>confirm reset settings</strong> when you have added enough e-mails.<br />
<strong>%s</strong> is added as default.
</p>""" % (SUPERADMINROLE, CFG_SITE_SUPPORT_EMAIL)
# add more superusers
output += """
<p>enter user e-mail addresses: </p>
<form action="resetdefaultsettings" method="POST">"""
for email in superusers:
output += ' <input type="hidden" name="superusers" value="%s" />' % (email, )
output += """
<span class="adminlabel">e-mail</span>
<input class="admin_wvar" type="text" name="superusers" />
<input class="adminbutton" type="submit" value="add e-mail" />
</form>"""
if superusers:
# remove emails
output += """
<form action="resetdefaultsettings" method="POST">
have you entered wrong data?
<input class="adminbutton" type="submit" value="remove all e-mails" />
</form>
"""
# superusers confirm table
start = '<form action="resetdefaultsettings" method="POST">'
extra = ' <input type="hidden" name="confirm" value="1" />'
for email in superusers:
extra += '<input type="hidden" name="superusers" value="%s" />' % (email, )
extra += ' <input class="adminbutton" type="submit" value="confirm to reset settings" />'
end = '</form>'
output += '<p><strong>reset default settings</strong> with the users below? </p>'
output += tupletotable(header=['e-mail address'],
tuple=superusers,
start=start,
extracolumn=extra,
end=end)
if confirm in [1, "1"]:
res = acca.acc_reset_default_settings(superusers)
if res:
output += '<p>successfully reset default settings</p>'
else:
output += '<p>sorry, could not reset default settings</p>'
return index(req=req,
title='Reset Default Settings',
subtitle='reset settings',
body=[output],
adminarea=6)
def perform_adddefaultsettings(req, superusers=[], confirm=0):
"""add the default settings, and keep everything else.
probably nothing will be deleted, except if there has been made changes to the defaults."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
# cleaning input
if type(superusers) == str: superusers = [superusers]
# remove not valid e-mails
for email in superusers:
if not check_email(email): superusers.remove(email)
# instructions
output = """
<p>
before you add the settings, we need some users<br />
to connect to <strong>%s</strong>.<br />
enter as many e-mail addresses you want and press <strong>add</strong>.<br />
<strong>confirm add settings</strong> when you have added enough e-mails.<br />
<strong>%s</strong> is added as default.
</p>""" % (SUPERADMINROLE, CFG_SITE_SUPPORT_EMAIL)
# add more superusers
output += """
<p>enter user e-mail addresses: </p>
<form action="adddefaultsettings" method="POST">"""
for email in superusers:
output += ' <input type="hidden" name="superusers" value="%s" />' % (email, )
output += """
<span class="adminlabel">e-mail</span>
<input class="admin_wvar" type="text" name="superusers" />
<input class="adminbutton" type="submit" value="add e-mail" />
</form>
"""
if superusers:
# remove emails
output += """
<form action="adddefaultsettings" method="POST">
have you entered wrong data?
<input class="adminbutton" type="submit" value="remove all e-mails" />
</form>
"""
# superusers confirm table
start = '<form action="adddefaultsettings" method="POST">'
extra = ' <input type="hidden" name="confirm" value="1" />'
for email in superusers:
extra += '<input type="hidden" name="superusers" value="%s" />' % (email, )
extra += ' <input class="adminbutton" type="submit" value="confirm to add settings" />'
end = '</form>'
output += '<p><strong>add default settings</strong> with the users below? </p>'
output += tupletotable(header=['e-mail address'],
tuple=superusers,
start=start,
extracolumn=extra,
end=end)
if confirm in [1, "1"]:
res = acca.acc_add_default_settings(superusers)
if res:
output += '<p>successfully added default settings</p>'
else:
output += '<p>sorry, could not add default settings</p>'
return index(req=req,
title='Add Default Settings',
subtitle='add settings',
body=[output],
adminarea=6)
def perform_manageaccounts(req, mtype='', content='', confirm=0):
"""start area for managing accounts."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
subtitle = 'Overview'
fin_output = ''
fin_output += """
<table>
<tr>
<td><b>Menu</b></td>
</tr>
<tr>
<td>0.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/manageaccounts?mtype=perform_showall">Show all</a></small></td>
<td>1.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/manageaccounts?mtype=perform_accesspolicy#1">Access policy</a></small></td>
<td>2.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/manageaccounts?mtype=perform_accountoverview#2">Account overview</a></small></td>
<td>3.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/manageaccounts?mtype=perform_createaccount#3">Create account</a></small></td>
<td>4.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/manageaccounts?mtype=perform_modifyaccounts#4">Edit accounts</a></small></td>
</tr>
</table>
- """ % (weburl, weburl, weburl, weburl, weburl)
+ """ % (CFG_SITE_URL, CFG_SITE_URL, CFG_SITE_URL, CFG_SITE_URL, CFG_SITE_URL)
if mtype == "perform_accesspolicy" and content:
fin_output += content
elif mtype == "perform_accesspolicy" or mtype == "perform_showall":
fin_output += perform_accesspolicy(req, callback='')
fin_output += "<br />"
if mtype == "perform_accountoverview" and content:
fin_output += content
elif mtype == "perform_accountoverview" or mtype == "perform_showall":
fin_output += perform_accountoverview(req, callback='')
fin_output += "<br />"
if mtype == "perform_createaccount" and content:
fin_output += content
elif mtype == "perform_createaccount" or mtype == "perform_showall":
fin_output += perform_createaccount(req, callback='')
fin_output += "<br />"
if mtype == "perform_modifyaccounts" and content:
fin_output += content
elif mtype == "perform_modifyaccounts" or mtype == "perform_showall":
fin_output += perform_modifyaccounts(req, callback='')
fin_output += "<br />"
return index(req=req,
title='Manage Accounts',
subtitle=subtitle,
body=[fin_output],
adminarea=0,
authorized=1)
def perform_accesspolicy(req, callback='yes', confirm=0):
"""Modify default behaviour of a guest user or if new accounts should automatically/manually be modified."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """<a name="1"></a>1. Access policy.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ subtitle = """<a name="1"></a>1. Access policy.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
account_policy = {}
account_policy[0] = "Users can register new accounts. New accounts automatically activated."
account_policy[1] = "Users can register new accounts. Admin users must activate the accounts."
account_policy[2] = "Only admin can register new accounts. User cannot edit email address."
account_policy[3] = "Only admin can register new accounts. User cannot edit email address or password."
account_policy[4] = "Only admin can register new accounts. User cannot edit email address,password or login method."
site_policy = {}
site_policy[0] = "Normal operation of the site."
site_policy[1] = "Read-only site, all write operations temporarily closed."
site_policy[2] = "Site fully closed."
output = "(Modifications must be done in access_control_config.py)<br />"
output += "<br /><b>Current settings:</b><br />"
output += "Site status: %s<br />" % (site_policy[CFG_ACCESS_CONTROL_LEVEL_SITE])
output += "Guest accounts allowed: %s<br />" % (CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0 and "Yes" or "No")
output += "Account policy: %s<br />" % (account_policy[CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS])
output += "Allowed email addresses limited: %s<br />" % (CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN and CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN or "Not limited")
output += "Send email to admin when new account: %s<br />" % (CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS == 1 and "Yes" or "No")
output += "Send email to user after creating new account: %s<br />" % (CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT == 1 and "Yes" or "No")
output += "Send email to user when account is activated: %s<br />" % (CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION == 1 and "Yes" or "No")
output += "Send email to user when account is deleted/rejected: %s<br />" % (CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION == 1 and "Yes" or "No")
output += "<br />"
output += "<b>Available 'login via' methods:</b><br />"
methods = CFG_EXTERNAL_AUTHENTICATION.keys()
methods.sort()
for system in methods:
output += """%s %s<br />""" % (system, (CFG_EXTERNAL_AUTHENTICATION[system][1] and "(Default)" or ""))
output += "<br /><b>Changing the settings:</b><br />"
output += "Currently, all changes must be done using your favourite editor, and the webserver restarted for changes to take effect. For the settings to change, either look in the guide or in access_control_config.py ."
body = [output]
if callback:
return perform_manageaccounts(req, "perform_accesspolicy", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_accountoverview(req, callback='yes', confirm=0):
"""Modify default behaviour of a guest user or if new accounts should automatically/manually be modified."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """<a name="2"></a>2. Account overview.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ subtitle = """<a name="2"></a>2. Account overview.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
output = ""
res = run_sql("SELECT COUNT(*) FROM user WHERE email=''")
output += "Guest accounts: %s<br />" % res[0][0]
res = run_sql("SELECT COUNT(*) FROM user WHERE email!=''")
output += "Registered accounts: %s<br />" % res[0][0]
res = run_sql("SELECT COUNT(*) FROM user WHERE email!='' AND note='0' OR note IS NULL")
output += "Inactive accounts: %s " % res[0][0]
if res[0][0] > 0:
output += ' [<a href="modifyaccounts?email_user_pattern=&amp;limit_to=disabled&amp;maxpage=25&amp;page=1">Activate/Reject accounts</a>]'
res = run_sql("SELECT COUNT(*) FROM user")
output += "<br />Total nr of accounts: %s<br />" % res[0][0]
body = [output]
if callback:
return perform_manageaccounts(req, "perform_accountoverview", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_createaccount(req, email='', password='', callback='yes', confirm=0):
"""Modify default behaviour of a guest user or if new accounts should automatically/manually be modified."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """<a name="3"></a>3. Create account.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ subtitle = """<a name="3"></a>3. Create account.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
output = ""
text = ' <span class="adminlabel">Email:</span>\n'
text += ' <input class="admin_wvar" type="text" name="email" value="%s" /><br />' % (email, )
text += ' <span class="adminlabel">Password:</span>\n'
text += ' <input class="admin_wvar" type="text" name="password" value="%s" /><br />' % (password, )
output += createhiddenform(action="createaccount",
text=text,
confirm=1,
button="Create")
if confirm in [1, "1"] and email and email_valid_p(email):
res = run_sql("SELECT email FROM user WHERE email=%s", (email,))
if not res:
res = run_sql("INSERT INTO user (email,password, note) values(%s,AES_ENCRYPT(email,%s), '1')", (email, password))
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT == 1:
emailsent = send_new_user_account_warning(email, email, password) == 0
if password:
output += '<b><span class="info">Account created with password and activated.</span></b>'
else:
output += '<b><span class="info">Account created without password and activated.</span></b>'
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT == 1:
if emailsent:
output += '<br /><b><span class="info">An email has been sent to the owner of the account.</span></b>'
else:
output += '<br /><b><span class="important">Could not send an email to the owner of the account.</span></b>'
else:
output += '<b><span class="info">An account with the same email already exists.</span></b>'
elif confirm in [1, "1"]:
output += '<b><span class="info">Please specify an valid email-address.</span></b>'
body = [output]
if callback:
return perform_manageaccounts(req, "perform_createaccount", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyaccountstatus(req, userID, email_user_pattern, limit_to, maxpage, page, callback='yes', confirm=0):
"""set a disabled account to enabled and opposite"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
res = run_sql("SELECT id, email, note FROM user WHERE id=%s", (userID, ))
subtitle = ""
output = ""
if res:
if res[0][2] in [0, "0", None]:
res2 = run_sql("UPDATE user SET note=1 WHERE id=%s", (userID, ))
output += """<b><span class="info">The account '%s' has been activated.</span></b>""" % res[0][1]
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_ACTIVATION == 1:
password = int(random.random() * 1000000)
run_sql("UPDATE user SET password=AES_ENCRYPT(email, %s) "
"WHERE id=%s", (password, userID))
emailsent = send_account_activated_message(res[0][1], res[0][1], password)
if emailsent:
output += """<br /><b><span class="info">An email has been sent to the owner of the account.</span></b>"""
else:
output += """<br /><b><span class="info">Could not send an email to the owner of the account.</span></b>"""
elif res[0][2] in [1, "1"]:
res2 = run_sql("UPDATE user SET note=0 WHERE id=%s", (userID, ))
output += """<b><span class="info">The account '%s' has been set inactive.</span></b>""" % res[0][1]
else:
output += '<b><span class="info">The account id given does not exist.</span></b>'
body = [output]
if callback:
return perform_modifyaccounts(req, email_user_pattern, limit_to, maxpage, page, content=output, callback='yes')
else:
return addadminbox(subtitle, body)
def perform_editaccount(req, userID, mtype='', content='', callback='yes', confirm=-1):
"""form to modify an account. this method is calling other methods which again is calling this and sending back the output of the method.
if callback, the method will call perform_editcollection, if not, it will just return its output.
userID - id of the user
mtype - the method that called this method.
content - the output from that method."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
res = run_sql("SELECT id, email FROM user WHERE id=%s", (userID, ))
if not res:
if mtype == "perform_deleteaccount":
text = """<b><span class="info">The selected account has been deleted, to continue editing, go back to 'Manage Accounts'.</span></b>"""
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION == 1:
text += """<br /><b><span class="info">An email has been sent to the owner of the account.</span></b>"""
else:
text = """<b><span class="info">The selected accounts does not exist, please go back and select an account to edit.</span></b>"""
return index(req=req,
title='Edit Account',
subtitle="Edit account",
body=[text],
adminarea=7,
authorized=1)
fin_output = """
<table>
<tr>
<td><b>Menu</b></td>
</tr>
<tr>
<td>0.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/editaccount?userID=%s">Show all</a></small></td>
<td>1.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/editaccount?userID=%s&amp;mtype=perform_modifylogindata">Modify login-data</a></small></td>
<td>2.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/editaccount?userID=%s&amp;mtype=perform_modifypreferences">Modify preferences</a></small></td>
</tr><tr>
<td>3.&nbsp;<small><a href="%s/admin/webaccess/webaccessadmin.py/editaccount?userID=%s&amp;mtype=perform_deleteaccount">Delete account</a></small></td>
</tr>
</table>
- """ % (weburl, userID, weburl, userID, weburl, userID, weburl, userID)
+ """ % (CFG_SITE_URL, userID, CFG_SITE_URL, userID, CFG_SITE_URL, userID, CFG_SITE_URL, userID)
if mtype == "perform_modifylogindata" and content:
fin_output += content
elif mtype == "perform_modifylogindata" or not mtype:
fin_output += perform_modifylogindata(req, userID, callback='')
#if mtype == "perform_modifybasket" and content:
#fin_output += content
#elif mtype == "perform_modifybasket" or not mtype:
#fin_output += perform_modifybasket(req, userID, callback='')
if mtype == "perform_modifypreferences" and content:
fin_output += content
elif mtype == "perform_modifypreferences" or not mtype:
fin_output += perform_modifypreferences(req, userID, callback='')
#if mtype == "perform_modifyalerts" and content:
#fin_output += content
#elif mtype == "perform_modifyalerts" or not mtype:
#fin_output += perform_modifyalerts(req, userID, callback='')
if mtype == "perform_deleteaccount" and content:
fin_output += content
elif mtype == "perform_deleteaccount" or not mtype:
fin_output += perform_deleteaccount(req, userID, callback='')
return index(req=req,
title='Edit Account',
subtitle="Edit account '%s'" % res[0][1],
body=[fin_output],
adminarea=7,
authorized=1)
# Disabled because not secure and maybe not needed.
#def perform_modifybasket(req, userID, callback='yes', confirm=0):
#"""modify email and password of an account"""
#(auth_code, auth_message) = is_adminuser(req)
#if auth_code != 0: return mustloginpage(req, auth_message)
- #subtitle = """<a name="2"></a>2. Modify baskets.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ #subtitle = """<a name="2"></a>2. Modify baskets.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
#res = run_sql("SELECT id, email, FROM user WHERE id=%s", (userID, ))
#output = ""
#if res:
#text = """To modify the baskets for this account, you have to login as the user."""
#output += createhiddenform(action="%s/youraccount/login?" % CFG_SITE_SECURE_URL,
#text=text,
#p_email=res[0][1],
#p_pw=res[0][2],
- #referer="%s/yourbaskets/display" % weburl,
+ #referer="%s/yourbaskets/display" % CFG_SITE_URL,
#button="Login")
#output += "Remember that you will be logged out as the current user."
##baskets = run_sql("SELECT basket.id, basket.name, basket.public FROM basket, user_basket WHERE id_user=%s and user_basket.id_basket=basket.id" % userID)
##output += "<table><tr>"
##for (id, name, public) in baskets:
## output += "<tr><td>%s<br />Public: %s</td></tr>" % (name, (public=="y" and "Yes" or "No"))
## basket_records = run_sql("SELECT id_record, nb_order FROM basket_record WHERE id_basket=%s" % id)
## for (id_record, nb_order) in basket_records:
## output += "<tr><td></td><td>"
## output += print_record(id_record)
## output += "</td></tr>"
##
##output += "</tr></table>"
#else:
#output += '<b><span class="info">The account id given does not exist.</span></b>'
#body = [output]
#if callback:
#return perform_editaccount(req, userID, mtype='perform_modifybasket', content=addadminbox(subtitle, body), callback='yes')
#else:
#return addadminbox(subtitle, body)
def perform_modifylogindata(req, userID, nickname='', email='', password='', callback='yes', confirm=0):
"""modify email and password of an account"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """<a name="1"></a>1. Edit login-data.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ subtitle = """<a name="1"></a>1. Edit login-data.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
res = run_sql("SELECT id, email, nickname FROM user WHERE id=%s", (userID, ))
output = ""
if res:
if not email and not password:
email = res[0][1]
nickname = res[0][2]
text = ' <span class="adminlabel">Account id:</span>%s<br />\n' % userID
text = ' <span class="adminlabel">Nickname:</span>\n'
text += ' <input class="admin_wvar" type="text" name="nickname" value="%s" /><br />' % (nickname, )
text += ' <span class="adminlabel">Email:</span>\n'
text += ' <input class="admin_wvar" type="text" name="email" value="%s" /><br />' % (email, )
text += ' <span class="adminlabel">Password:</span>\n'
text += ' <input class="admin_wvar" type="text" name="password" value="%s" /><br />' % (password, )
output += createhiddenform(action="modifylogindata",
text=text,
userID=userID,
confirm=1,
button="Modify")
if confirm in [1, "1"] and email and email_valid_p(email):
res = run_sql("SELECT nickname FROM user WHERE nickname=%s AND id<>%s", (nickname, userID))
if res:
output += '<b><span class="info">Sorry, the specified nickname is already used.</span></b>'
else:
res = run_sql("UPDATE user SET email=%s WHERE id=%s", (email, userID))
if password:
res = run_sql("UPDATE user SET password=AES_ENCRYPT(email,%s) WHERE id=%s", (password, userID))
else:
output += '<b><span class="info">Password not modified.</span></b> '
res = run_sql("UPDATE user SET nickname=%s WHERE id=%s", (nickname, userID))
output += '<b><span class="info">Nickname/email and/or password modified.</span></b>'
elif confirm in [1, "1"]:
output += '<b><span class="info">Please specify an valid email-address.</span></b>'
else:
output += '<b><span class="info">The account id given does not exist.</span></b>'
body = [output]
if callback:
return perform_editaccount(req, userID, mtype='perform_modifylogindata', content=addadminbox(subtitle, body), callback='yes')
else:
return addadminbox(subtitle, body)
# Disabled because not secure and maybe not needed.
#def perform_modifyalerts(req, userID, callback='yes', confirm=0):
#"""modify email and password of an account"""
#(auth_code, auth_message) = is_adminuser(req)
#if auth_code != 0: return mustloginpage(req, auth_message)
- #subtitle = """<a name="3"></a>3. Modify alerts.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ #subtitle = """<a name="3"></a>3. Modify alerts.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
#res = run_sql("SELECT id, email, password FROM user WHERE id=%s" % userID)
#output = ""
#if res:
#text = """To modify the alerts for this account, you have to login as the user."""
#output += createhiddenform(action="%s/youraccount/login?" % CFG_SITE_SECURE_URL,
#text=text,
#p_email=res[0][1],
#p_pw=res[0][2],
- #referer="%s/youralerts/display" % weburl,
+ #referer="%s/youralerts/display" % CFG_SITE_URL,
#button="Login")
#output += "Remember that you will be logged out as the current user."
#res = """ SELECT q.id, q.urlargs, a.id_basket,
#a.alert_name, a.frequency, a.notification,
#DATE_FORMAT(a.date_creation,'%%d %%b %%Y'),
#DATE_FORMAT(a.date_lastrun,'%%d %%b %%Y')
#FROM query q, user_query_basket a
#WHERE a.id_user='%s' AND a.id_query=q.id
#ORDER BY a.alert_name ASC """ % userID
##res = run_sql(res)
##for (qID, qurlargs, id_basket, alertname, frequency, notification, date_creation, date_lastrun) in res:
## output += "%s - %s - %s - %s - %s - %s - %s<br />" % (qID, id_basket, alertname, frequency, notification, date_creation, date_lastrun)
#else:
#output += '<b><span class="info">The account id given does not exist.</span></b>'
#body = [output]
#if callback:
#return perform_editaccount(req, userID, mtype='perform_modifyalerts', content=addadminbox(subtitle, body), callback='yes')
#else:
#return addadminbox(subtitle, body)
def perform_modifypreferences(req, userID, login_method='', callback='yes', confirm=0):
"""modify email and password of an account"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """<a name="2"></a>2. Modify preferences.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ subtitle = """<a name="2"></a>2. Modify preferences.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
res = run_sql("SELECT id, email FROM user WHERE id=%s", (userID, ))
output = ""
if res:
user_pref = get_user_preferences(userID)
if confirm in [1, "1"]:
if login_method:
user_pref['login_method'] = login_method
set_user_preferences(userID, user_pref)
output += "Select default login method:<br />"
text = ""
methods = CFG_EXTERNAL_AUTHENTICATION.keys()
methods.sort()
for system in methods:
text += """<input type="radio" name="login_method" value="%s" %s>%s<br />""" % (system, (user_pref['login_method'] == system and "checked" or ""), system)
output += createhiddenform(action="modifypreferences",
text=text,
confirm=1,
userID=userID,
button="Select")
if confirm in [1, "1"]:
if login_method:
output += """<b><span class="info">The login method has been changed</span></b>"""
else:
output += """<b><span class="info">Nothing to update</span></b>"""
else:
output += '<b><span class="info">The account id given does not exist.</span></b>'
body = [output]
if callback:
return perform_editaccount(req, userID, mtype='perform_modifypreferences', content=addadminbox(subtitle, body), callback='yes')
else:
return addadminbox(subtitle, body)
def perform_deleteaccount(req, userID, callback='yes', confirm=0):
"""delete account"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """<a name="3"></a>3. Delete account.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ subtitle = """<a name="3"></a>3. Delete account.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
res = run_sql("SELECT id, email FROM user WHERE id=%s", (userID, ))
output = ""
if res:
if confirm in [0, "0"]:
text = '<b><span class="important">Are you sure you want to delete the account with email: "%s"?</span></b>' % res[0][1]
output += createhiddenform(action="deleteaccount",
text=text,
userID=userID,
confirm=1,
button="Delete")
elif confirm in [1, "1"]:
res2 = run_sql("DELETE FROM user WHERE id=%s", (userID, ))
output += '<b><span class="info">Account deleted.</span></b>'
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION == 1:
emailsent = send_account_deleted_message(res[0][1], res[0][1])
else:
output += '<b><span class="info">The account id given does not exist.</span></b>'
body = [output]
if callback:
return perform_editaccount(req, userID, mtype='perform_deleteaccount', content=addadminbox(subtitle, body), callback='yes')
else:
return addadminbox(subtitle, body)
def perform_rejectaccount(req, userID, email_user_pattern, limit_to, maxpage, page, callback='yes', confirm=0):
"""Delete account and send an email to the owner."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
res = run_sql("SELECT id, email, note FROM user WHERE id=%s", (userID, ))
output = ""
subtitle = ""
if res:
res2 = run_sql("DELETE FROM user WHERE id=%s", (userID, ))
output += '<b><span class="info">Account rejected and deleted.</span></b>'
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_DELETION == 1:
if not res[0][2] or res[0][2] == "0":
emailsent = send_account_rejected_message(res[0][1], res[0][1])
elif res[0][2] == "1":
emailsent = send_account_deleted_message(res[0][1], res[0][1])
if emailsent:
output += """<br /><b><span class="info">An email has been sent to the owner of the account.</span></b>"""
else:
output += """<br /><b><span class="info">Could not send an email to the owner of the account.</span></b>"""
else:
output += '<b><span class="info">The account id given does not exist.</span></b>'
body = [output]
if callback:
return perform_modifyaccounts(req, email_user_pattern, limit_to, maxpage, page, content=output, callback='yes')
else:
return addadminbox(subtitle, body)
def perform_modifyaccounts(req, email_user_pattern='', limit_to=-1, maxpage=MAXPAGEUSERS, page=1, content='', callback='yes', confirm=0):
"""Modify default behaviour of a guest user or if new accounts should automatically/manually be modified."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
- subtitle = """<a name="4"></a>4. Edit accounts.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % weburl
+ subtitle = """<a name="4"></a>4. Edit accounts.&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/webaccess-admin-guide#4">?</a>]</small>""" % CFG_SITE_URL
output = ""
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
try:
maxpage = int(maxpage)
except:
maxpage = MAXPAGEUSERS
try:
page = int(page)
if page < 1:
page = 1
except:
page = 1
text = ' <span class="adminlabel">Email (part of):</span>\n'
text += ' <input class="admin_wvar" type="text" name="email_user_pattern" value="%s" /><br />' % (email_user_pattern, )
text += """<span class="adminlabel">Limit to:</span>
<select name="limit_to" class="admin_w200">
<option value="all" %s>All accounts</option>
<option value="enabled" %s>Active accounts</option>
<option value="disabled" %s>Inactive accounts</option>
</select><br />""" % ((limit_to=="all" and "selected" or ""), (limit_to=="enabled" and "selected" or ""), (limit_to=="disabled" and "selected" or ""))
text += """<span class="adminlabel">Accounts per page:</span>
<select name="maxpage" class="admin_wvar">
<option value="25" %s>25</option>
<option value="50" %s>50</option>
<option value="100" %s>100</option>
<option value="250" %s>250</option>
<option value="500" %s>500</option>
<option value="1000" %s>1000</option>
</select><br />""" % ((maxpage==25 and "selected" or ""), (maxpage==50 and "selected" or ""), (maxpage==100 and "selected" or ""), (maxpage==250 and "selected" or ""), (maxpage==500 and "selected" or ""), (maxpage==1000 and "selected" or ""))
output += createhiddenform(action="modifyaccounts",
text=text,
button="search for accounts")
if limit_to not in [-1, "-1"] and maxpage:
users1 = "SELECT id,email,note FROM user WHERE "
if limit_to == "enabled":
users1 += " email!='' AND note=1"
elif limit_to == "disabled":
users1 += " email!='' AND note=0 OR note IS NULL"
elif limit_to == "guest":
users1 += " email=''"
else:
users1 += " email!=''"
if email_user_pattern:
users1 += " AND email RLIKE '%s'" % (email_user_pattern)
users1 += " ORDER BY email LIMIT %s" % (maxpage * page + 1)
users1 = run_sql(users1)
if not users1:
output += '<b><span class="info">There are no accounts matching the email given.</span></b>'
else:
users = []
if maxpage * (page - 1) > len(users1):
page = len(users1) / maxpage + 1
for (id, email, note) in users1[maxpage * (page - 1):(maxpage * page)]:
users.append(['', id, email, (note=="1" and '<strong class="info">Active</strong>' or '<strong class="important">Inactive</strong>')])
for col in [(((note=="1" and 'Inactivate' or 'Activate'), 'modifyaccountstatus'), ((note == "0" and 'Reject' or 'Delete'), 'rejectaccount'), ),
(('Edit account', 'editaccount'), ),]:
users[-1].append('<a href="%s?userID=%s&amp;email_user_pattern=%s&amp;limit_to=%s&amp;maxpage=%s&amp;page=%s&amp;rand=%s">%s</a>' % (col[0][1], id, email_user_pattern, limit_to, maxpage, page, random.randint(0, 1000), col[0][0]))
for (str, function) in col[1:]:
users[-1][-1] += ' / <a href="%s?userID=%s&amp;email_user_pattern=%s&amp;limit_to=%s&amp;maxpage=%s&amp;page=%s&amp;rand=%s">%s</a>' % (function, id, email_user_pattern, limit_to, maxpage, page, random.randint(0, 1000), str)
last = ""
next = ""
if len(users1) > maxpage:
if page > 1:
last += '<b><span class="info"><a href="modifyaccounts?email_user_pattern=%s&amp;limit_to=%s&amp;maxpage=%s&amp;page=%s">Last Page</a></span></b>' % (email_user_pattern, limit_to, maxpage, (page - 1))
if len(users1[maxpage * (page - 1):(maxpage * page)]) == maxpage:
next += '<b><span class="info"><a href="modifyaccounts?email_user_pattern=%s&amp;limit_to=%s&amp;maxpage=%s&amp;page=%s">Next page</a></span></b>' % (email_user_pattern, limit_to, maxpage, (page + 1))
output += '<b><span class="info">Showing accounts %s-%s:</span></b>' % (1 + maxpage * (page - 1), maxpage * page)
else:
output += '<b><span class="info">%s matching account(s):</span></b>' % len(users1)
output += tupletotable(header=[last, 'id', 'email', 'Status', '', '', next], tuple=users)
else:
output += '<b><span class="info">Please select which accounts to find and how many to show per page.</span></b>'
if content:
output += "<br />%s" % content
body = [output]
if callback:
return perform_manageaccounts(req, "perform_modifyaccounts", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_delegate_startarea(req):
"""start area for lower level delegation of rights."""
# refuse access to guest users:
uid = getUid(req)
if isGuestUser(uid):
return index(req=req,
title='Delegate Rights',
adminarea=0,
authorized=0)
subtitle = 'select what to do'
output = ''
if is_adminuser(req)[0] == 0:
output += """
<p>
You are also allowed to be in the <a href="../webaccessadmin.py">Main Admin Area</a> which gives you<br />
the access to the full functionality of WebAccess.
</p>
"""
output += """
<dl>
<dt><a href="delegate_adduserrole">Connect users to roles</a></dt>
<dd>add users to the roles you have delegation rights to.</dd>
<dt><a href="delegate_deleteuserrole">Remove users from roles</a></dt>
<dd>remove users from the roles you have delegation rights to.</dd>
</dl>
<dl>
<dt><a href="delegate_adminsetup">Set up delegation rights</a></dt>
<dd>specialized area to set up the delegation rights used in the areas above. <br />
you need to be a web administrator to access the area.</dd>
</dl>
"""
return index(req=req,
title='Delegate Rights',
subtitle=subtitle,
body=[output],
adminarea=0,
authorized=1)
def perform_delegate_adminsetup(req, id_role_admin=0, id_role_delegate=0, confirm=0):
"""lets the webadmins set up the delegation rights for the other roles
id_role_admin - the role to be given delegation rights
id_role_delegate - the role over which the delegation rights are given
confirm - make the connection happen """
subtitle = 'step 1 - select admin role'
admin_roles = acca.acc_get_all_roles()
output = """
<p>
This is a specialized area to handle a task that also can be handled<br />
from the &quot;add authorization&quot; interface.
</p>
<p>
By handling the delegation rights here you get the advantage of<br />
not having to select the correct action <i>(%s)</i> or<br />
remembering the names of available roles.
</p>
""" % (DELEGATEADDUSERROLE, )
output += createroleselect(id_role=id_role_admin,
step=1,
button='select admin role',
name='id_role_admin',
action='delegate_adminsetup',
roles=admin_roles)
if str(id_role_admin) != '0':
subtitle = 'step 2 - select delegate role'
name_role_admin = acca.acc_get_role_name(id_role=id_role_admin)
delegate_roles_old = acca.acc_find_delegated_roles(id_role_admin=id_role_admin)
delegate_roles = []
delegate_roles_old_names = []
for role in admin_roles:
if (role,) not in delegate_roles_old:
delegate_roles.append(role)
else:
delegate_roles_old_names.append(role[1])
if delegate_roles_old_names:
delegate_roles_old_names.sort()
names_str = ''
for name in delegate_roles_old_names:
if names_str: names_str += ', '
names_str += name
output += '<p>previously selected roles: <strong>%s</strong>.</p>' % (names_str, )
extra = """
<dl>
<dt><a href="modifyauthorizations?id_role=%s&amp;id_action=%s">Remove delegated roles</a></dt>
<dd>use the standard administration area to remove delegation rights
you no longer want to be available.</dd>
</dl>
""" % (id_role_admin, acca.acc_get_action_id(name_action=DELEGATEADDUSERROLE))
else:
output += '<p>no previously selected roles.</p>'
output += createroleselect(id_role=id_role_delegate,
step=2,
button='select delegate role',
name='id_role_delegate',
action='delegate_adminsetup',
roles=delegate_roles,
id_role_admin=id_role_admin)
if str(id_role_delegate) != '0':
subtitle = 'step 3 - confirm to add delegation right'
name_role_delegate = acca.acc_get_role_name(id_role=id_role_delegate)
output += """
<p>
<span class="warning"><strong>Warning:</strong> don't hand out delegation rights that can harm the system (e.g. delegating superrole).</span>
</p> """
output += createhiddenform(action="delegate_adminsetup",
text='let role <strong>%s</strong> delegate rights over role <strong>%s</strong>?' % (name_role_admin, name_role_delegate),
id_role_admin=id_role_admin,
id_role_delegate=id_role_delegate,
confirm=1)
if int(confirm):
subtitle = 'step 4 - confirm delegation right added'
# res1 = acca.acc_add_role_action_arguments_names(name_role=name_role_admin,
# name_action=DELEGATEADDUSERROLE,
# arglistid=-1,
# optional=0,
# role=name_role_delegate)
res1 = acca.acc_add_authorization(name_role=name_role_admin,
name_action=DELEGATEADDUSERROLE,
optional=0,
role=name_role_delegate)
if res1:
output += '<p>confirm: role <strong>%s</strong> delegates role <strong>%s</strong>.' % (name_role_admin, name_role_delegate)
else: output += '<p>sorry, delegation right could not be added,<br />it probably already exists.</p>'
# see if right hand menu is available
try: body = [output, extra]
except NameError: body = [output]
return index(req=req,
title='Delegate Rights',
subtitle=subtitle,
body=body,
adminarea=1)
def perform_delegate_adduserrole(req, id_role=0, email_user_pattern='', id_user=0, confirm=0):
"""let a lower level web admin add users to a limited set of roles.
id_role - the role to connect to a user
id_user - the user to connect to a role
confirm - make the connection happen """
# finding the allowed roles for this user
id_admin = getUid(req)
id_action = acca.acc_get_action_id(name_action=DELEGATEADDUSERROLE)
actions = acca.acc_find_possible_actions_user(id_user=id_admin, id_action=id_action)
allowed_roles = []
allowed_id_roles = []
for (id, arglistid, name_role_help) in actions[1:]:
id_role_help = acca.acc_get_role_id(name_role=name_role_help)
if id_role_help and [id_role_help, name_role_help, ''] not in allowed_roles:
allowed_roles.append([id_role_help, name_role_help, ''])
allowed_id_roles.append(str(id_role_help))
output = ''
if not allowed_roles:
subtitle = 'no delegation rights'
output += """
<p>
You do not have the delegation rights over any roles.<br />
If you think you should have such rights, contact a WebAccess Administrator.
</p>"""
extra = ''
else:
subtitle = 'step 1 - select role'
output += """
<p>
Lower level delegation of access rights to roles.<br />
An administrator with all rights have to give you these rights.
</p>"""
email_out = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
output += createroleselect(id_role=id_role, step=1, name='id_role',
action='delegate_adduserrole', roles=allowed_roles)
if str(id_role) != '0' and str(id_role) in allowed_id_roles:
subtitle = 'step 2 - search for users'
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
text = ' <span class="adminlabel">2. search for user </span>\n'
text += ' <input class="admin_wvar" type="text" name="email_user_pattern" value="%s" />\n' % (email_user_pattern, )
output += createhiddenform(action="delegate_adduserrole",
text=text,
button="search for users",
id_role=id_role)
# pattern is entered
if email_user_pattern:
# users with matching email-address
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s ORDER BY email """, (email_user_pattern, ))
# users that are connected
users2 = run_sql("""SELECT DISTINCT u.id, u.email
FROM user u LEFT JOIN user_accROLE ur ON u.id = ur.id_user
WHERE ur.id_accROLE = %s AND u.email RLIKE %s
ORDER BY u.email """, (id_role, email_user_pattern))
# no users that match the pattern
if not (users1 or users2):
output += '<p>no qualified users, try new search.</p>'
# too many matching users
elif len(users1) > MAXSELECTUSERS:
output += '<p><strong>%s hits</strong>, too many qualified users, specify more narrow search. (limit %s)</p>' % (len(users1), MAXSELECTUSERS)
# show matching users
else:
subtitle = 'step 3 - select a user'
users = []
extrausers = []
for (id, email) in users1:
if (id, email) not in users2: users.append([id,email,''])
for (id, email) in users2:
extrausers.append([-id, email,''])
output += createuserselect(id_user=id_user,
action="delegate_adduserrole",
step=3,
users=users,
extrausers=extrausers,
button="add this user",
id_role=id_role,
email_user_pattern=email_user_pattern)
try: id_user = int(id_user)
except ValueError: pass
# user selected already connected to role
if id_user < 0:
output += '<p>users in brackets are already attached to the role, try another one...</p>'
# a user is selected
elif email_out:
subtitle = "step 4 - confirm to add user"
output += createhiddenform(action="delegate_adduserrole",
text='add user <strong>%s</strong> to role <strong>%s</strong>?' % (email_out, name_role),
id_role=id_role,
email_user_pattern=email_user_pattern,
id_user=id_user,
confirm=1)
# it is confirmed that this user should be added
if confirm:
# add user
result = acca.acc_add_user_role(id_user=id_user, id_role=id_role)
if result and result[2]:
subtitle = 'step 5 - confirm user added'
output += '<p>confirm: user <strong>%s</strong> added to role <strong>%s</strong>.</p>' % (email_out, name_role)
else:
subtitle = 'step 5 - user could not be added'
output += '<p>sorry, but user could not be added.</p>'
extra = """
<dl>
<dt><a href="delegate_deleteuserrole?id_role=%s">Remove users from role</a></dt>
<dd>remove users from the roles you have delegating rights to.</dd>
</dl>
""" % (id_role, )
return index(req=req,
title='Connect users to roles',
subtitle=subtitle,
body=[output, extra],
adminarea=1,
authorized=1)
def perform_delegate_deleteuserrole(req, id_role=0, id_user=0, confirm=0):
"""let a lower level web admin remove users from a limited set of roles.
id_role - the role to connect to a user
id_user - the user to connect to a role
confirm - make the connection happen """
subtitle = 'in progress...'
output = '<p>in progress...</p>'
# finding the allowed roles for this user
id_admin = getUid(req)
id_action = acca.acc_get_action_id(name_action=DELEGATEADDUSERROLE)
actions = acca.acc_find_possible_actions_user(id_user=id_admin, id_action=id_action)
output = ''
if not actions:
subtitle = 'no delegation rights'
output += """
<p>
You do not have the delegation rights over any roles.<br />
If you think you should have such rights, contact a WebAccess Administrator.
</p>"""
extra = ''
else:
subtitle = 'step 1 - select role'
output += """
<p>
Lower level delegation of access rights to roles.<br />
An administrator with all rights have to give you these rights.
</p>"""
email_out = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
# create list of allowed roles
allowed_roles = []
allowed_id_roles = []
for (id, arglistid, name_role_help) in actions[1:]:
id_role_help = acca.acc_get_role_id(name_role=name_role_help)
if id_role_help and [id_role_help, name_role_help, ''] not in allowed_roles:
allowed_roles.append([id_role_help, name_role_help, ''])
allowed_id_roles.append(str(id_role_help))
output += createroleselect(id_role=id_role, step=1,
action='delegate_deleteuserrole', roles=allowed_roles)
if str(id_role) != '0' and str(id_role) in allowed_id_roles:
subtitle = 'step 2 - select user'
users = acca.acc_get_role_users(id_role)
output += createuserselect(id_user=id_user,
step=2,
action='delegate_deleteuserrole',
users=users,
id_role=id_role)
if str(id_user) != '0':
subtitle = 'step 3 - confirm delete of user'
email_user = acca.acc_get_user_email(id_user=id_user)
output += createhiddenform(action="delegate_deleteuserrole",
text='delete user %s from %s?'
% (headerstrong(user=id_user), headerstrong(role=id_role)),
id_role=id_role,
id_user=id_user,
confirm=1)
if confirm:
res = acca.acc_delete_user_role(id_user=id_user, id_role=id_role)
if res:
subtitle = 'step 4 - confirm user deleted from role'
output += '<p>confirm: deleted user <strong>%s</strong> from role <strong>%s</strong>.</p>' % (email_user, name_role)
else:
subtitle = 'step 4 - user could not be deleted'
output += 'sorry, but user could not be deleted<br />user is probably already deleted.'
extra = """
<dl>
<dt><a href="delegate_adduserrole?id_role=%s">Connect users to role</a></dt>
<dd>add users to the roles you have delegating rights to.</dd>
</dl>
""" % (id_role, )
return index(req=req,
title='Remove users from roles',
subtitle=subtitle,
body=[output, extra],
adminarea=1,
authorized=1)
def perform_addaction(req, name_action='', arguments='', optional='no', description='put description here.', confirm=0):
"""form to add a new action with these values:
name_action - name of the new action
arguments - allowedkeywords, separated by whitespace
description - optional description of the action"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
name_action = cleanstring(name_action)
arguments = cleanstring(arguments, comma=1)
title = 'Add Action'
subtitle = 'step 1 - give values to the requested fields'
output = """
<form action="addaction" method="POST">
<span class="adminlabel">action name </span>
<input class="admin_wvar" type="text" name="name_action" value="%s" /> <br />
<span class="adminlabel">arguments </span>
<input class="admin_wvar" type="text" name="arguments" value="%s" />
<small>keywords for arguments, separate with comma, no whitespace.</small> <br />
<span class="adminlabel">optional arguments</span>
<select name="optional" class="admin_w200">
<option value="no" selected="selected">no, not allowed</option>
<option value="yes" %s>yes, allowed</option>
</select><br />
<span class="adminlabel">description </span>
<textarea class="admin_wvar" rows="6" cols="30" name="description">%s</textarea>
<input class="adminbutton" type="submit" value="add action" />
</form>
""" % (name_action, arguments, optional == 'yes' and 'selected="selected"' or '', description)
if name_action:
# description must be changed before it is submitted
if description == 'put description here.': internaldesc = ''
else: internaldesc = description
if arguments:
subtitle = 'step 2 - confirm to add action with %s arguments' % (optional == 'yes' and 'optional' or '', )
arguments = arguments.replace(' ', '')
text = 'add action with: <br />\n'
text += 'name: <strong>%s</strong><br />\n' % (name_action, )
if internaldesc:
text += 'description: <strong>%s</strong><br />\n' % (description, )
text += '%sarguments: <strong>%s</strong><br />' % (optional == 'yes' and 'optional ' or '', arguments)
text += 'optional: <strong>%s</strong>?' % (optional, )
else:
optional = 'no'
subtitle = 'step 2 - confirm to add action without arguments'
text = 'add action <strong>%s</strong> without arguments' % (name_action, )
if internaldesc: text += '<br />\nand description: <strong>%s</strong>?\n' % (description, )
else: text += '?\n'
output += createhiddenform(action="addaction",
text=text,
name_action=name_action,
arguments=arguments,
optional=optional,
description=description,
confirm=1)
if confirm not in ["0", 0]:
arguments = arguments.split(',')
result = acca.acc_add_action(name_action,
internaldesc,
optional,
*arguments)
if result:
subtitle = 'step 3 - action added'
output += '<p>action added:</p>'
output += tupletotable(header=['id', 'action name', 'description', 'allowedkeywords', 'optional'],
tuple=[result])
else:
subtitle = 'step 3 - action could not be added'
output += '<p>sorry, could not add action, <br />action with the same name probably exists.</p>'
extra = """
<dl>
<dt><a href="addauthorization?id_action=%s&amp;reverse=1">Add authorization</a></dt>
<dd>start adding new authorizations to action %s.</dd>
</dl> """ % (acca.acc_get_action_id(name_action=name_action), name_action)
try: body = [output, extra]
except NameError: body = [output]
return index(req=req,
title=title,
body=body,
subtitle=subtitle,
adminarea=4)
def perform_deleteaction(req, id_action="0", confirm=0):
"""show all roles connected, and ask for confirmation.
id_action - id of action to delete """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
title='Delete action'
subtitle='step 1 - select action to delete'
name_action = acca.acc_get_action_name(id_action=id_action)
output = createactionselect(id_action=id_action,
action="deleteaction",
step=1,
actions=acca.acc_get_all_actions(),
button="delete action")
if id_action != "0" and name_action:
subtitle = 'step 2 - confirm the delete'
output += actiondetails(id_action=id_action)
if acca.acc_get_action_roles(id_action=id_action):
output += createhiddenform(action="deleteroleaction",
text="""rather delete only connection between action %s
and a selected role?""" % (name_action, ),
id_action=id_action,
reverse=1,
button='go there')
output += createhiddenform(action="deleteaction",
text=' delete action <strong>%s</strong> and all connections?' % (name_action, ),
confirm=1,
id_action=id_action)
if confirm:
subtitle = 'step 3 - confirm delete of action'
res = acca.acc_delete_action(id_action=id_action)
if res:
output += '<p>confirm: action <strong>%s</strong> deleted.<br />\n' % (name_action, )
output += '%s entries deleted all in all.</p>\n' % (res, )
else:
output += '<p>sorry, action could not be deleted.</p>\n'
elif id_action != "0":
output += '<p>the action has been deleted...</p>'
return index(req=req,
title=title,
subtitle=subtitle,
body=[output],
adminarea=4)
def perform_showactiondetails(req, id_action):
"""show the details of an action. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
output = createactionselect(id_action=id_action,
action="showactiondetails",
step=1,
actions=acca.acc_get_all_actions(),
button="select action")
if id_action not in [0, '0']:
output += actiondetails(id_action=id_action)
extra = """
<dl>
<dt><a href="addauthorization?id_action=%s&amp;reverse=1">Add new authorization</a></dt>
<dd>add an authorization.</dd>
<dt><a href="modifyauthorizations?id_action=%s&amp;reverse=1">Modify authorizations</a></dt>
<dd>modify existing authorizations.</dd>
<dt><a href="deleteroleaction?id_action=%s&amp;reverse=1">Remove role</a></dt>
<dd>remove all authorizations from action and a role.</dd>
</dl>
""" % (id_action, id_action, id_action)
body = [output, extra]
else:
output += '<p>no details to show</p>'
body = [output]
return index(req=req,
title='Show Action Details',
subtitle='show action details',
body=body,
adminarea=4)
def actiondetails(id_action=0):
"""show details of given action. """
output = ''
if id_action not in [0, '0']:
name_action = acca.acc_get_action_name(id_action=id_action)
output += '<p>action details:</p>'
output += tupletotable(header=['id', 'name', 'description', 'allowedkeywords', 'optional'],
tuple=[acca.acc_get_action_details(id_action=id_action)])
roleshlp = acca.acc_get_action_roles(id_action=id_action)
if roleshlp:
roles = []
for (id, name, dummy) in roleshlp:
roles.append([id, name,
'<a href="simpleauthorization?id_role=%s&amp;id_action=%s">show authorization details</a>'
% (id, id_action),
'<a href="showroleusers?id_role=%s">show connected users</a>' % (id, )])
roletable = tupletotable(header=['id', 'name', '', ''], tuple=roles)
output += '<p>roles connected to %s:</p>\n' % (headerstrong(action=name_action, query=0), )
output += roletable
else:
output += '<p>no roles connected to %s.</p>\n' % (headerstrong(action=name_action, query=0), )
else:
output += '<p>no details to show</p>'
return output
def perform_addrole(req, id_role=0, name_role='', description='put description here.', firerole_def_src=CFG_ACC_EMPTY_ROLE_DEFINITION_SRC, confirm=0):
"""form to add a new role with these values:
name_role - name of the new role
description - optional description of the role """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
name_role = cleanstring(name_role)
title='Add Role'
subtitle = 'step 1 - give values to the requested fields'
output = """
<form action="addrole" method="POST">
<table><tbody><tr><td align='right' valign='top'>
<span class="adminlabel">role name </span>
</td><td>
<input class="admin_wvar" type="text" name="name_role" value="%s" />
</td></tr><tr><td align='right' valign='top'>
<span class="adminlabel">description </span>
</td><td>
<textarea class="admin_wvar" rows="6" cols="30" name="description">%s</textarea>
</td></tr><tr><td align='right' valign='top'>
<span class="adminlabel">firewall like role definition [<a href="/admin/webaccess/firerole.html">?</a>]</span>
</td><td>
<textarea class="admin_wvar" rows="6" cols="30" name="firerole_def_src">%s</textarea>
</td></tr><tr><td></td><td>
<input class="adminbutton" type="submit" value="add role" />
</td></tr></tbody></table>
</form>
""" % (escape(name_role, '"'), escape(description), escape(firerole_def_src))
if name_role:
# description must be changed before submitting
subtitle = 'step 2 - confirm to add role'
internaldesc = ''
if description != 'put description here.':
internaldesc = description
try:
firerole_def_ser = serialize(compile_role_definition(firerole_def_src))
except InvenioWebAccessFireroleError, msg:
output += "<strong>%s</strong>" % msg
else:
text = """
add role with: <br />\n
name: <strong>%s</strong> <br />""" % (name_role, )
if internaldesc:
text += 'description: <strong>%s</strong>?\n' % (description, )
output += createhiddenform(action="addrole",
text=text,
name_role=escape(name_role, '"'),
description=escape(description, '"'),
firerole_def_src=escape(firerole_def_src, '"'),
confirm=1)
if confirm not in ["0", 0]:
result = acca.acc_add_role(name_role=name_role,
description=internaldesc,
firerole_def_ser=firerole_def_ser,
firerole_def_src=firerole_def_src)
if result:
subtitle = 'step 3 - role added'
output += '<p>role added: </p>'
result = list(result)
result[3] = result[3].replace('\n', '<br/>')
result = tuple(result)
output += tupletotable(header=['id', 'role name', 'description', 'firewall like role definition'],
tuple=[result])
else:
subtitle = 'step 3 - role could not be added'
output += '<p>sorry, could not add role, <br />role with the same name probably exists.</p>'
id_role = acca.acc_get_role_id(name_role=name_role)
extra = """
<dl>
<dt><a href="addauthorization?id_role=%s">Add authorization</a></dt>
<dd>start adding new authorizations to role %s.</dd>
</dl>
<dt><a href="adduserrole?id_role=%s">Connect user</a></dt>
<dd>connect a user to role %s.</dd>
<dl>
</dl>""" % (id_role, name_role, id_role, name_role)
try: body = [output, extra]
except NameError: body = [output]
return index(req=req,
title=title,
body=body,
subtitle=subtitle,
adminarea=3)
def perform_modifyrole(req, id_role='0', name_role='', description='put description here.', firerole_def_src='', modified='0', confirm=0):
"""form to add a new role with these values:
name_role - name of the role to be changed
description - optional description of the role
firerole_def_src - optional firerole like definition of the role
"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
ret = acca.acc_get_role_details(id_role)
if ret and modified =='0':
name_role = ret[1]
description = ret[2]
firerole_def_src = ret[3]
if not firerole_def_src or firerole_def_src == '' or firerole_def_src is None:
firerole_def_src = 'deny any'
name_role = cleanstring(name_role)
title='Modify Role'
subtitle = 'step 1 - give values to the requested fields and confirm to modify role'
output = """
<form action="modifyrole" method="POST">
<table><tbody><tr><td align='right' valign='top'>
<input type="hidden" name="id_role" value="%s" />
<span class="adminlabel">role name </span>
</td><td>
<input class="admin_wvar" type="text" name="name_role" value="%s" /> <br />
</td></tr><tr><td align='right' valign='top'>
<span class="adminlabel">description </span>
</td><td>
<textarea class="admin_wvar" rows="6" cols="30" name="description">%s</textarea> <br />
</td></tr><tr><td align='right' valign='top'>
<span class="adminlabel">firewall like role definition</span> [<a href="/admin/webaccess/firerole.html">?</a>]
</td><td>
<textarea class="admin_wvar" rows="6" cols="30" name="firerole_def_src">%s</textarea><br />
</td></tr><tr><td></td><td>
<input class="adminbutton" type="submit" value="modify role" />
<input type="hidden" name="modified" value="1" />
</td></tr></tbody></table>
</form>
""" % (id_role, escape(name_role), escape(description), escape(firerole_def_src))
if modified in [1, '1']:
# description must be changed before submitting
internaldesc = ''
if description != 'put description here.':
internaldesc = description
text = """
modify role with: <br />\n
name: <strong>%s</strong> <br />""" % (name_role, )
if internaldesc:
text += 'description: <strong>%s</strong>?<br />' % (description, )
text += 'firewall like role definition: <strong>%s</strong>' % firerole_def_src.replace('\n', '<br />')
try:
firerole_def_ser = serialize(compile_role_definition(firerole_def_src))
except InvenioWebAccessFireroleError, msg:
subtitle = 'step 2 - role could not be modified'
output += '<p>sorry, could not modify role because of troubles with its definition:<br />%s</p>' % msg
else:
output += createhiddenform(action="modifyrole",
text=text,
id_role = id_role,
name_role=escape(name_role, True),
description=escape(description, True),
firerole_def_src=escape(firerole_def_src, True),
modified=1,
confirm=1)
if confirm not in ["0", 0]:
result = acca.acc_update_role(id_role, name_role=name_role,
description=internaldesc, firerole_def_ser=firerole_def_ser, firerole_def_src=firerole_def_src)
if result:
subtitle = 'step 2 - role modified'
output += '<p>role modified: </p>'
output += tupletotable(header=['id', 'role name',
'description', 'firewall like role definition'],
tuple=[(id_role, name_role, description, firerole_def_src.replace('\n', '<br />'))])
else:
subtitle = 'step 2 - role could not be modified'
output += '<p>sorry, could not modify role, <br />please contact the administrator.</p>'
body = [output]
return index(req=req,
title=title,
body=body,
subtitle=subtitle,
adminarea=3)
def perform_deleterole(req, id_role="0", confirm=0):
"""select a role and show all connected information,
users - users that can access the role.
actions - actions with possible authorizations."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
title = 'Delete role'
subtitle = 'step 1 - select role to delete'
name_role = acca.acc_get_role_name(id_role=id_role)
output = createroleselect(id_role=id_role,
action="deleterole",
step=1,
roles=acca.acc_get_all_roles(),
button="delete role")
if id_role != "0" and name_role:
subtitle = 'step 2 - confirm delete of role'
output += roledetails(id_role=id_role)
output += createhiddenform(action="deleterole",
text='delete role <strong>%s</strong> and all connections?' % (name_role, ),
id_role=id_role,
confirm=1)
if confirm:
res = acca.acc_delete_role(id_role=id_role)
subtitle = 'step 3 - confirm role deleted'
if res:
output += "<p>confirm: role <strong>%s</strong> deleted.<br />" % (name_role, )
output += "<strong>%s</strong> entries were removed.</p>" % (res, )
else:
output += "<p>sorry, the role could not be deleted.</p>"
elif id_role != "0":
output += '<p>the role has been deleted...</p>'
return index(req=req,
title=title,
subtitle=subtitle,
body=[output],
adminarea=3)
def perform_showroledetails(req, id_role):
"""show the details of a role."""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
output = createroleselect(id_role=id_role,
action="showroledetails",
step=1,
roles=acca.acc_get_all_roles(),
button="select role")
if id_role not in [0, '0']:
name_role = acca.acc_get_role_name(id_role=id_role)
output += roledetails(id_role=id_role)
extra = """
<dl>
<dt><a href="modifyrole?id_role=%(id_role)s">Modify role</a><dt>
<dd>modify the role you are seeing</dd>
<dt><a href="addauthorization?id_role=%(id_role)s">Add new authorization</a></dt>
<dd>add an authorization.</dd>
<dt><a href="modifyauthorizations?id_role=%(id_role)s">Modify authorizations</a></dt>
<dd>modify existing authorizations.</dd>
</dl>
<dl>
<dt><a href="adduserrole?id_role=%(id_role)s">Connect user</a></dt>
<dd>connect a user to role %(name_role)s.</dd>
<dt><a href="deleteuserrole?id_role=%(id_role)s">Remove user</a></dt>
<dd>remove a user from role %(name_role)s.</dd>
</dl>
""" % {'id_role' : id_role, 'name_role' : name_role}
body = [output, extra]
else:
output += '<p>no details to show</p>'
body = [output]
return index(req=req,
title='Show Role Details',
subtitle='show role details',
body=body,
adminarea=3)
def roledetails(id_role=0):
"""create the string to show details about a role. """
name_role = acca.acc_get_role_name(id_role=id_role)
usershlp = acca.acc_get_role_users(id_role)
users = []
for (id, email, dummy) in usershlp:
users.append([id, email, '<a href="showuserdetails?id_user=%s">show user details</a>' % (id, )])
usertable = tupletotable(header=['id', 'email'], tuple=users)
actionshlp = acca.acc_get_role_actions(id_role)
actions = []
for (action_id, name, dummy) in actionshlp:
actions.append([action_id, name,
'<a href="showactiondetails?id_role=%s&amp;id_action=%s">show action details</a>' % (id_role, action_id),
'<a href="simpleauthorization?id_role=%s&amp;id_action=%s">show authorization details</a>' % (id_role, action_id)])
actiontable = tupletotable(header=['id', 'name', '', ''], tuple=actions)
# show role details
details = '<p>role details:</p>'
role_details = acca.acc_get_role_details(id_role=id_role)
role_details[3] = role_details[3].replace('\n', '<br />') # Hack for preformatting firerole rules
details += tupletotable(header=['id', 'name', 'description', 'firewall like role definition'],
tuple=[role_details])
# show connected users
details += '<p>users connected to %s:</p>' % (headerstrong(role=name_role, query=0), )
if users:
details += usertable
else:
details += '<p>no users connected.</p>'
# show connected authorizations
details += '<p>authorizations for %s:</p>' % (headerstrong(role=name_role, query=0), )
if actions:
details += actiontable
else:
details += '<p>no authorizations connected</p>'
return details
def perform_adduserrole(req, id_role='0', email_user_pattern='', id_user='0', confirm=0):
"""create connection between user and role.
id_role - id of the role to add user to
email_user_pattern - search for users using this pattern
id_user - id of user to add to the role. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
email_out = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
title = 'Connect user to role '
subtitle = 'step 1 - select a role'
output = createroleselect(id_role=id_role,
action="adduserrole",
step=1,
roles=acca.acc_get_all_roles())
# role is selected
if id_role != "0":
title += name_role
subtitle = 'step 2 - search for users'
# remove letters not allowed in an email
email_user_pattern = cleanstring_email(email_user_pattern)
text = ' <span class="adminlabel">2. search for user </span>\n'
text += ' <input class="admin_wvar" type="text" name="email_user_pattern" value="%s" />\n' % (email_user_pattern, )
output += createhiddenform(action="adduserrole",
text=text,
button="search for users",
id_role=id_role)
# pattern is entered
if email_user_pattern:
# users with matching email-address
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s ORDER BY email """, (email_user_pattern, ))
# users that are connected
users2 = run_sql("""SELECT DISTINCT u.id, u.email
FROM user u LEFT JOIN user_accROLE ur ON u.id = ur.id_user
WHERE ur.id_accROLE = %s AND u.email RLIKE %s
ORDER BY u.email """, (id_role, email_user_pattern))
# no users that match the pattern
if not (users1 or users2):
output += '<p>no qualified users, try new search.</p>'
elif len(users1) > MAXSELECTUSERS:
output += '<p><strong>%s hits</strong>, too many qualified users, specify more narrow search. (limit %s)</p>' % (len(users1), MAXSELECTUSERS)
# show matching users
else:
subtitle = 'step 3 - select a user'
users = []
extrausers = []
for (user_id, email) in users1:
if (user_id, email) not in users2: users.append([user_id,email,''])
for (user_id, email) in users2:
extrausers.append([-user_id, email,''])
output += createuserselect(id_user=id_user,
action="adduserrole",
step=3,
users=users,
extrausers=extrausers,
button="add this user",
id_role=id_role,
email_user_pattern=email_user_pattern)
try: id_user = int(id_user)
except ValueError: pass
# user selected already connected to role
if id_user < 0:
output += '<p>users in brackets are already attached to the role, try another one...</p>'
# a user is selected
elif email_out:
subtitle = "step 4 - confirm to add user"
output += createhiddenform(action="adduserrole",
text='add user <strong>%s</strong> to role <strong>%s</strong>?' % (email_out, name_role),
id_role=id_role,
email_user_pattern=email_user_pattern,
id_user=id_user,
confirm=1)
# it is confirmed that this user should be added
if confirm:
# add user
result = acca.acc_add_user_role(id_user=id_user, id_role=id_role)
if result and result[2]:
subtitle = 'step 5 - confirm user added'
output += '<p>confirm: user <strong>%s</strong> added to role <strong>%s</strong>.</p>' % (email_out, name_role)
else:
subtitle = 'step 5 - user could not be added'
output += '<p>sorry, but user could not be added.</p>'
extra = """
<dl>
<dt><a href="addrole">Create new role</a></dt>
<dd>go here to add a new role.</dd>
</dl>
"""
if str(id_role) != "0":
extra += """
<dl>
<dt><a href="deleteuserrole?id_role=%s">Remove users</a></dt>
<dd>remove users from role %s.</dd>
<dt><a href="showroleusers?id_role=%s">Connected users</a></dt>
<dd>show all users connected to role %s.</dd>
</dl>
<dl>
<dt><a href="addauthorization?id_role=%s">Add authorization</a></dt>
<dd>start adding new authorizations to role %s.</dd>
</dl>
""" % (id_role, name_role, id_role, name_role, id_role, name_role)
return index(req=req,
title=title,
subtitle=subtitle,
body=[output, extra],
adminarea=3)
def perform_addroleuser(req, email_user_pattern='', id_user='0', id_role='0', confirm=0):
"""delete connection between role and user.
id_role - id of role to disconnect
id_user - id of user to disconnect. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
email_out = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
# used to sort roles, and also to determine right side links
con_roles = []
not_roles = []
title = 'Connect user to roles'
subtitle = 'step 1 - search for users'
# clean email search string
email_user_pattern = cleanstring_email(email_user_pattern)
text = ' <span class="adminlabel">1. search for user </span>\n'
text += ' <input class="admin_wvar" type="text" name="email_user_pattern" value="%s" />\n' % (email_user_pattern, )
output = createhiddenform(action='addroleuser',
text=text,
button='search for users',
id_role=id_role)
if email_user_pattern:
subtitle = 'step 2 - select user'
users1 = run_sql("""SELECT id, email FROM user WHERE email<>'' AND email RLIKE %s ORDER BY email """, (email_user_pattern, ))
users = []
for (id, email) in users1: users.append([id, email, ''])
# no users
if not users:
output += '<p>no qualified users, try new search.</p>'
# too many users
elif len(users) > MAXSELECTUSERS:
output += '<p><strong>%s hits</strong>, too many qualified users, specify more narrow search. (limit %s)</p>' % (len(users), MAXSELECTUSERS)
# ok number of users
else:
output += createuserselect(id_user=id_user,
action='addroleuser',
step=2,
users=users,
button='select user',
email_user_pattern=email_user_pattern)
if int(id_user):
subtitle = 'step 3 - select role'
# roles the user is connected to
role_ids = acca.acc_get_user_roles(id_user=id_user)
# all the roles, lists are sorted on the background of these...
all_roles = acca.acc_get_all_roles()
# sort the roles in connected and not connected roles
for (id, name, description, dummy, dummy) in all_roles:
if (id, ) in role_ids: con_roles.append([-id, name, description])
else: not_roles.append([id, name, description])
# create roleselect
output += createroleselect(id_role=id_role,
action='addroleuser',
step=3,
roles=not_roles,
extraroles=con_roles,
extrastamp='(connected)',
button='add this role',
email_user_pattern=email_user_pattern,
id_user=id_user)
if int(id_role) < 0:
name_role = acca.acc_get_role_name(id_role=-int(id_role))
output += '<p>role %s already connected to the user, try another one...<p>' % (name_role, )
elif int(id_role):
subtitle = 'step 4 - confirm to add role to user'
output += createhiddenform(action='addroleuser',
text='add role <strong>%s</strong> to user <strong>%s</strong>?' % (name_role, email_out),
email_user_pattern=email_user_pattern,
id_user=id_user,
id_role=id_role,
confirm=1)
if confirm:
# add role
result = acca.acc_add_user_role(id_user=id_user, id_role=id_role)
if result and result[2]:
subtitle = 'step 5 - confirm role added'
output += '<p>confirm: role <strong>%s</strong> added to user <strong>%s</strong>.</p>' % (name_role, email_out)
else:
subtitle = 'step 5 - role could not be added'
output += '<p>sorry, but role could not be added</p>'
extra = """
<dl>
<dt><a href="addrole">Create new role</a></dt>
<dd>go here to add a new role.</dd>
"""
if int(id_user) and con_roles:
extra += """
</dl>
<dl>
<dt><a href="deleteuserrole?id_user=%s&amp;reverse=1">Remove roles</a></dt>
<dd>disconnect roles from user %s.</dd>
</dl>
""" % (id_user, email_out)
if int(id_role):
if int(id_role) < 0: id_role = -int(id_role)
extra += """
<dl>
<dt><a href="deleteuserrole?id_role=%s">Remove users</a></dt>
<dd>disconnect users from role %s.<dd>
</dl>
""" % (id_role, name_role)
return index(req=req,
title=title,
subtitle=subtitle,
body=[output, extra],
adminarea=5)
def perform_deleteuserrole(req, id_role='0', id_user='0', reverse=0, confirm=0):
"""delete connection between role and user.
id_role - id of role to disconnect
id_user - id of user to disconnect. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
title = 'Remove user from role'
email_user = acca.acc_get_user_email(id_user=id_user)
name_role = acca.acc_get_role_name(id_role=id_role)
output = ''
if reverse in [0, '0']:
adminarea = 3
subtitle = 'step 1 - select the role'
output += createroleselect(id_role=id_role,
action="deleteuserrole",
step=1,
roles=acca.acc_get_all_roles())
if id_role != "0":
subtitle = 'step 2 - select the user'
output += createuserselect(id_user=id_user,
action="deleteuserrole",
step=2,
users=acca.acc_get_role_users(id_role=id_role),
id_role=id_role)
else:
adminarea = 5
# show only if user is connected to a role, get users connected to roles
users = run_sql("""SELECT DISTINCT(u.id), u.email, u.note
FROM user u LEFT JOIN user_accROLE ur
ON u.id = ur.id_user
WHERE ur.id_accROLE != 'NULL' AND u.email != ''
ORDER BY u.email """)
has_roles = 1
# check if the user is connected to any roles
for (id, email, note) in users:
if str(id) == str(id_user): break
# user not connected to a role
else:
subtitle = 'step 1 - user not connected'
output += '<p>no need to remove roles from user <strong>%s</strong>,<br />user is not connected to any roles.</p>' % (email_user, )
has_roles, id_user = 0, '0' # stop the rest of the output below...
# user connected to roles
if has_roles:
output += createuserselect(id_user=id_user,
action="deleteuserrole",
step=1,
users=users,
reverse=reverse)
if id_user != "0":
subtitle = 'step 2 - select the role'
role_ids = acca.acc_get_user_roles(id_user=id_user)
all_roles = acca.acc_get_all_roles()
roles = []
for (id, name, desc, dummy, dummy) in all_roles:
if (id, ) in role_ids: roles.append([id, name, desc])
output += createroleselect(id_role=id_role,
action="deleteuserrole",
step=2,
roles=roles,
id_user=id_user,
reverse=reverse)
if id_role != '0' and id_user != '0':
subtitle = 'step 3 - confirm delete of user'
output += createhiddenform(action="deleteuserrole",
text='delete user %s from %s?' % (headerstrong(user=id_user), headerstrong(role=id_role)),
id_role=id_role,
id_user=id_user,
reverse=reverse,
confirm=1)
if confirm:
res = acca.acc_delete_user_role(id_user=id_user, id_role=id_role)
if res:
subtitle = 'step 4 - confirm delete of user'
output += '<p>confirm: deleted user <strong>%s</strong> from role <strong>%s</strong>.</p>' % (email_user, name_role)
else:
subtitle = 'step 4 - user could not be deleted'
output += 'sorry, but user could not be deleted<br />user is probably already deleted.'
extra = ''
if str(id_role) != "0":
extra += """
<dl>
<dt><a href="adduserrole?id_role=%s">Connect user</a></dt>
<dd>add users to role %s.</dd>
""" % (id_role, name_role)
if int(reverse):
extra += """
<dt><a href="deleteuserrole?id_role=%s">Remove user</a></dt>
<dd>remove users from role %s.</dd> """ % (id_role, name_role)
extra += '</dl>'
if str(id_user) != "0":
extra += """
<dl>
<dt><a href="addroleuser?email_user_pattern=%s&amp;id_user=%s">Connect role</a></dt>
<dd>add roles to user %s.</dd>
""" % (email_user, id_user, email_user)
if not int(reverse):
extra += """
<dt><a href="deleteuserrole?id_user=%s&amp;email_user_pattern=%s&amp;reverse=1">Remove role</a></dt>
<dd>remove roles from user %s.</dd> """ % (id_user, email_user, email_user)
extra += '</dl>'
if extra: body = [output, extra]
else: body = [output]
return index(req=req,
title=title,
subtitle=subtitle,
body=body,
adminarea=adminarea)
def perform_showuserdetails(req, id_user=0):
"""show the details of a user. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
if id_user not in [0, '0']:
output = userdetails(id_user=id_user)
email_user = acca.acc_get_user_email(id_user=id_user)
extra = """
<dl>
<dt><a href="addroleuser?id_user=%s&amp;email_user_pattern=%s">Connect role</a></dt>
<dd>connect a role to user %s.</dd>
<dt><a href="deleteuserrole?id_user=%s&amp;reverse=1">Remove role</a></dt>
<dd>remove a role from user %s.</dd>
</dl>
""" % (id_user, email_user, email_user, id_user, email_user)
body = [output, extra]
else:
body = ['<p>no details to show</p>']
return index(req=req,
title='Show User Details',
subtitle='show user details',
body=body,
adminarea=5)
def userdetails(id_user=0):
"""create the string to show details about a user. """
# find necessary details
email_user = acca.acc_get_user_email(id_user=id_user)
userroles = acca.acc_get_user_roles(id_user=id_user)
conn_roles = []
# find connected roles
for (id, name, desc, dummy, dummy) in acca.acc_get_all_roles():
if (id, ) in userroles:
conn_roles.append([id, name, desc])
conn_roles[-1].append('<a href="showroledetails?id_role=%s">show details</a>' % (id, ))
if conn_roles:
# print details
details = '<p>roles connected to user <strong>%s</strong></p>' % (email_user, )
details += tupletotable(header=['id', 'name', 'description', ''], tuple=conn_roles)
else:
details = '<p>no roles connected to user <strong>%s</strong>.</p>' % (email_user, )
return details
def perform_addauthorization(req, id_role="0", id_action="0", optional=0, reverse="0", confirm=0, **keywords):
""" form to add new connection between user and role:
id_role - role to connect
id_action - action to connect
reverse - role or action first? """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
# values that might get used
name_role = acca.acc_get_role_name(id_role=id_role) or id_role
name_action = acca.acc_get_action_name(id_action=id_action) or id_action
optional = optional == 'on' and 1 or int(optional)
extra = """
<dl>
<dt><a href="addrole">Create new role</a></dt>
<dd>go here to add a new role.</dd>
<dt><a href="addaction">Create new action</a></dt>
<dd>go here to add a new action.</dd>
</dl>
"""
# create the page according to which step the user is on
# role -> action -> arguments
if reverse in ["0", 0]:
adminarea = 3
subtitle = 'step 1 - select role'
output = createroleselect(id_role=id_role,
action="addauthorization",
step=1,
roles=acca.acc_get_all_roles(),
reverse=reverse)
if str(id_role) != "0":
subtitle = 'step 2 - select action'
rolacts = acca.acc_get_role_actions(id_role)
allhelp = acca.acc_get_all_actions()
allacts = []
for r in allhelp:
if r not in rolacts: allacts.append(r)
output += createactionselect(id_action=id_action,
action="addauthorization",
step=2,
actions=rolacts,
extraactions=allacts,
id_role=id_role,
reverse=reverse)
# action -> role -> arguments
else:
adminarea = 4
subtitle = 'step 1 - select action'
output = createactionselect(id_action=id_action,
action="addauthorization",
step=1,
actions=acca.acc_get_all_actions(),
reverse=reverse)
if str(id_action) != "0":
subtitle = 'step 2 - select role'
actroles = acca.acc_get_action_roles(id_action)
allhelp = acca.acc_get_all_roles()
allroles = []
for r in allhelp:
if r not in actroles: allroles.append(r)
output += createroleselect(id_role=id_role,
action="addauthorization",
step=2,
roles=actroles,
extraroles=allroles,
id_action=id_action,
reverse=reverse)
# ready for step 3 no matter which direction we took to get here
if id_action != "0" and id_role != "0":
# links to adding authorizations in the other direction
if str(reverse) == "0":
extra += """
<dl>
<dt><a href="addauthorization?id_action=%s&amp;reverse=1">Add authorization</a></dt>
<dd>add authorizations to action %s.</dd>
</dl> """ % (id_action, name_action)
else:
extra += """
<dl>
<dt><a href="addauthorization?id_role=%s">Add authorization</a></dt>
<dd>add authorizations to role %s.</dd>
</dl> """ % (id_role, name_role)
subtitle = 'step 3 - enter values for the keywords\n'
output += """
<form action="addauthorization" method="POST">
<input type="hidden" name="id_role" value="%s">
<input type="hidden" name="id_action" value="%s">
<input type="hidden" name="reverse" value="%s">
""" % (id_role, id_action, reverse)
# the actions argument keywords
res_keys = acca.acc_get_action_keywords(id_action=id_action)
# res used to display existing authorizations
# res used to determine if showing "create connection without arguments"
res_auths = acca.acc_find_possible_actions(id_role, id_action)
if not res_keys:
# action without arguments
if not res_auths:
output += """
<input type="hidden" name="confirm" value="1">
create connection between %s?
<input class="adminbutton" type="submit" value="confirm">
</form>
""" % (headerstrong(role=name_role, action=name_action, query=0), )
else:
output += '<p><strong>connection without arguments is already created.</strong></p>'
else:
# action with arguments
optionalargs = acca.acc_get_action_is_optional(id_action=id_action)
output += '<span class="adminlabel">3. authorized arguments</span><br />'
if optionalargs:
# optional arguments
output += """
<p>
<input type="radio" name="optional" value="1" %s />
connect %s to %s for any arguments <br />
<input type="radio" name="optional" value="0" %s />
connect %s to %s for only these argument cases:
</p>
""" % (optional and 'checked="checked"' or '', name_role, name_action, not optional and 'checked="checked"' or '', name_role, name_action)
# list the arguments
allkeys = 1
for key in res_keys:
output += '<span class="adminlabel" style="margin-left: 30px;">%s </span>\n <input class="admin_wvar" type="text" name="%s"' % (key, key)
try:
val = keywords[key] = cleanstring_argumentvalue(keywords[key])
if val: output += 'value="%s" ' % (val, )
else: allkeys = 0
except KeyError: allkeys = 0
output += ' /> <br />\n'
output = output[:-5] + ' <input class="adminbutton" type="submit" value="create authorization -->" />\n'
output += '</form>\n'
# ask for confirmation
if str(allkeys) != "0" or optional:
keys = keywords.keys()
keys.reverse()
subtitle = 'step 4 - confirm add of authorization\n'
text = """
create connection between <br />
%s <br />
""" % (headerstrong(role=name_role, action=name_action, query=0), )
if optional:
text += 'withouth arguments'
keywords = {}
else:
for key in keys:
text += '<strong>%s</strong>: %s \n' % (key, keywords[key])
output += createhiddenform(action="addauthorization",
text=text,
id_role=id_role,
id_action=id_action,
reverse=reverse,
confirm=1,
optional=optional,
**keywords)
# show existing authorizations, found authorizations further up in the code...
# res_auths = acca.acc_find_possible_actions(id_role, id_action)
output += '<p>existing authorizations:</p>'
if res_auths:
output += tupletotable(header=res_auths[0], tuple=res_auths[1:])
# shortcut to modifying authorizations
extra += """
<dl>
<dt><a href="modifyauthorizations?id_role=%s&amp;id_action=%s&amp;reverse=%s">Modify authorizations</a></dt>
<dd>modify the existing authorizations.</dd>
</dl> """ % (id_role, id_action, reverse)
else: output += '<p>no details to show</p>'
# user confirmed to add entries
if confirm:
subtitle = 'step 5 - confirm authorization added'
res1 = acca.acc_add_authorization(name_role=name_role,
name_action=name_action,
optional=optional,
**keywords)
if res1:
res2 = acca.acc_find_possible_actions(id_role, id_action)
arg = res1[0][3] # the arglistid
new = [res2[0]]
for row in res2[1:]:
if int(row[0]) == int(arg): new.append(row)
newauths = tupletotable(header=new[0], tuple=new[1:])
newentries = tupletotable(header=['role id', 'action id', 'argument id', '#'], tuple=res1)
st = 'style="vertical-align: top"'
output += """
<p>new authorization and entries:</p>
<table><tr>
<td class="admintd" %s>%s</td>
<td class="admintd" %s>%s</td>
</tr></table> """ % (st, newauths, st, newentries)
else: output += '<p>sorry, authorization could not be added,<br />it probably already exists</p>'
# trying to put extra link on the right side
try: body = [output, extra]
except NameError: body = [output]
return index(req=req,
title = 'Create entry for new authorization',
subtitle=subtitle,
body=body,
adminarea=adminarea)
def perform_deleteroleaction(req, id_role="0", id_action="0", reverse=0, confirm=0):
"""delete all connections between a role and an action.
id_role - id of the role
id_action - id of the action
reverse - 0: ask for role first
1: ask for action first"""
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
title = 'Remove action from role '
if reverse in ["0", 0]:
# select role -> action
adminarea = 3
subtitle = 'step 1 - select a role'
output = createroleselect(id_role=id_role,
action="deleteroleaction",
step=1,
roles=acca.acc_get_all_roles(),
reverse=reverse)
if id_role != "0":
rolacts = acca.acc_get_role_actions(id_role=id_role)
subtitle = 'step 2 - select the action'
output += createactionselect(id_action=id_action,
action="deleteroleaction",
step=2,
actions=rolacts,
reverse=reverse,
id_role=id_role,
button="remove connection and all authorizations")
else:
# select action -> role
adminarea = 4
subtitle = 'step 1 - select an action'
output = createactionselect(id_action=id_action,
action="deleteroleaction",
step=1,
actions=acca.acc_get_all_actions(),
reverse=reverse)
if id_action != "0":
actroles = acca.acc_get_action_roles(id_action=id_action)
subtitle = 'step 2 - select the role'
output += createroleselect(id_role=id_role,
action="deleteroleaction",
step=2,
roles=actroles,
button="remove connection and all authorizations",
id_action=id_action,
reverse=reverse)
if id_action != "0" and id_role != "0":
subtitle = 'step 3 - confirm to remove authorizations'
# ask for confirmation
res = acca.acc_find_possible_actions(id_role, id_action)
if res:
output += '<p>authorizations that will be deleted:</p>'
output += tupletotable(header=res[0], tuple=res[1:])
output += createhiddenform(action="deleteroleaction",
text='remove %s from %s' % (headerstrong(action=id_action), headerstrong(role=id_role)),
confirm=1,
id_role=id_role,
id_action=id_action,
reverse=reverse)
else:
output += 'no authorizations'
# confirmation is given
if confirm:
subtitle = 'step 4 - confirm authorizations removed '
res = acca.acc_delete_role_action(id_role=id_role, id_action=id_action)
if res:
output += '<p>confirm: removed %s from %s<br />' % (headerstrong(action=id_action), headerstrong(role=id_role))
output += '<strong>%s</strong> entries were removed.</p>' % (res, )
else:
output += '<p>sorry, no entries could be removed.</p>'
return index(req=req,
title=title,
subtitle=subtitle,
body=[output],
adminarea=adminarea)
def perform_modifyauthorizations(req, id_role="0", id_action="0", reverse=0, confirm=0, errortext='', sel='', authids=[]):
"""given ids of a role and an action, show all possible action combinations
with checkboxes and allow user to access other functions.
id_role - id of the role
id_action - id of the action
reverse - 0: ask for role first
1: ask for action first
sel - which button and modification that is selected
errortext - text to print when no connection exist between role and action
authids - ids of checked checkboxes """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0: return mustloginpage(req, auth_message)
name_role = acca.acc_get_role_name(id_role)
name_action = acca.acc_get_action_name(id_action)
output = ''
try: id_role, id_action, reverse = int(id_role), int(id_action), int(reverse)
except ValueError: pass
extra = """
<dl>
<dt><a href="addrole">Create new role</a></dt>
<dd>go here to add a new role.</dd>
<dt><a href="addaction">Create new action</a></dt>
<dd>go here to add a new action.</dd>
</dl>
"""
if id_role or id_action:
extra += '\n<dl>\n'
if id_role and id_action:
extra += """
<dt><a href="addauthorization?id_role=%s&amp;id_action=%s&amp;reverse=%s">Add authorizations</a></dt>
<dd>add an authorization to the existing ones.</dd> """ % (id_role, id_action, reverse)
if id_role:
extra += """
<dt><a href="addauthorization?id_role=%s">Add authorizations</a></dt>
<dd>add to role %s.</dd> """ % (id_role, name_role)
if id_action:
extra += """
<dt><a href="addauthorization?id_action=%s&amp;reverse=1">Add authorizations</a></dt>
<dd>add to action %s.</dd> """ % (id_action, name_action)
extra += '\n</dl>\n'
if not reverse:
# role -> action
adminarea = 3
subtitle = 'step 1 - select the role'
output += createroleselect(id_role=str(id_role),
action="modifyauthorizations",
step=1,
roles=acca.acc_get_all_roles(),
reverse=reverse)
if id_role:
rolacts = acca.acc_get_role_actions(id_role=id_role)
subtitle = 'step 2 - select the action'
output += createactionselect(id_action=str(id_action),
action="modifyauthorizations",
step=2,
actions=rolacts,
id_role=id_role,
reverse=reverse)
else:
adminarea = 4
# action -> role
subtitle = 'step 1 - select the action'
output += createactionselect(id_action=str(id_action),
action="modifyauthorizations",
step=1,
actions=acca.acc_get_all_actions(),
reverse=reverse)
if id_action:
actroles = acca.acc_get_action_roles(id_action=id_action)
subtitle = 'step 2 - select the role'
output += createroleselect(id_role=str(id_role),
action="modifyauthorizations",
step=2,
roles=actroles,
id_action=id_action,
reverse=reverse)
if errortext: output += '<p>%s</p>' % (errortext, )
if id_role and id_action:
# adding to main area
if type(authids) is not list: authids = [authids]
subtitle = 'step 3 - select groups and modification'
# get info
res = acca.acc_find_possible_actions(id_role, id_action)
# clean the authids
hiddenids = []
if sel in ['delete selected']:
hiddenids = authids[:]
elif sel in ['split groups', 'merge groups']:
for authid in authids:
arghlp = res[int(authid)][0]
if authid not in hiddenids and arghlp not in [-1, '-1', 0, '0']: hiddenids.append(authid)
authids = hiddenids[:]
if confirm:
# do selected modification and output with new authorizations
if sel == 'split groups':
res = splitgroups(id_role, id_action, authids)
elif sel == 'merge groups':
res = mergegroups(id_role, id_action, authids)
elif sel == 'delete selected':
res = deleteselected(id_role, id_action, authids)
authids = []
res = acca.acc_find_possible_actions(id_role, id_action)
output += 'authorizations after <strong>%s</strong>.<br />\n' % (sel, )
elif sel and authids:
output += 'confirm choice of authorizations and modification.<br />\n'
else:
output += 'select authorizations and perform modification.<br />\n'
if not res:
errortext = 'all connections deleted, try different '
if reverse in ["0", 0]:
return perform_modifyauthorizations(req=req, id_role=id_role, errortext=errortext + 'action.')
else:
return perform_modifyauthorizations(req=req, id_action=id_action, reverse=reverse, errortext=errortext + 'role.')
# display
output += modifyauthorizationsmenu(id_role, id_action, header=res[0], tuple=res[1:], checked=authids, reverse=reverse)
if sel and authids:
subtitle = 'step 4 - confirm to perform modification'
# form with hidden authids
output += '<form action="%s" method="POST">\n' % ('modifyauthorizations', )
for hiddenid in hiddenids:
output += '<input type="hidden" name="authids" value="%s" />\n' % (hiddenid, )
# choose what to do
if sel == 'split groups':
output += '<p>split groups containing:</p>'
elif sel == 'merge groups':
output += '<p>merge groups containing:</p>'
elif sel == 'delete selected':
output += '<p>delete selected entries:</p>'
extracolumn = '<input type="checkbox" name="confirm" value="1" />\n'
extracolumn += '<input class="adminbutton" type="submit" value="confirm" />\n'
# show the entries here...
output += tupletotable_onlyselected(header=res[0],
tuple=res[1:],
selected=hiddenids,
extracolumn=extracolumn)
output += '<input type="hidden" name="id_role" value="%s" />\n' \
% (id_role, )
output += '<input type="hidden" name="id_action" value="%s" />\n' \
% (id_action, )
output += '<input type="hidden" name="sel" value="%s" />\n' \
% (sel, )
output += '<input type="hidden" name="reverse" value="%s" />\n' \
% (reverse, )
output += '</form>'
# tried to perform modification without something selected
elif sel and not authids and not confirm:
output += '<p>no valid groups selected</p>'
# trying to put extra link on the right side
try:
body = [output, extra]
except NameError:
body = [output]
# Display the page
return index(req=req,
title='Modify Authorizations',
subtitle=subtitle,
body=body,
adminarea=adminarea)
def modifyauthorizationsmenu(id_role, id_action, tuple=[], header=[],
checked=[], reverse=0):
"""create table with header and checkboxes, used for multiple choice.
makes use of tupletotable to add the actual table
id_role - selected role, hidden value in the form
id_action - selected action, hidden value in the form
tuple - all rows to be put in the table (with checkboxes)
header - column headers, empty strings added at start and end
checked - ids of rows to be checked """
if not tuple:
return 'no authorisations...'
argnum = len(acca.acc_get_action_keywords(id_action=id_action))
tuple2 = []
for t in tuple:
tuple2.append(t[:])
tuple2 = addcheckboxes(datalist=tuple2, name='authids', startindex=1,
checked=checked)
hidden = '<input type="hidden" name="id_role" value="%s" /> \n' \
% (id_role, )
hidden += '<input type="hidden" name="id_action" value="%s" /> \n' \
% (id_action, )
hidden += '<input type="hidden" name="reverse" value="%s" /> \n' \
% (reverse, )
button = '<input type="submit" class="adminbutton" ' \
'value="delete selected" name="sel" />\n'
if argnum > 1:
button += '<input type="submit" class="adminbutton" ' \
'value="split groups" name="sel" />\n'
button += '<input type="submit" class="adminbutton" ' \
'value="merge groups" name="sel" />\n'
hdrstr = ''
for h in [''] + header + ['']:
hdrstr += ' <th class="adminheader">%s</th>\n' % (h, )
if hdrstr:
hdrstr = ' <tr>\n%s\n </tr>\n' % (hdrstr, )
output = '<form action="modifyauthorizations" method="POST">\n'
output += '<table class="admin_wvar_nomargin"> \n'
output += hdrstr
output += '<tr><td>%s</td></tr>\n' % (hidden, )
align = ['admintdleft'] * len(tuple2[0])
try:
align[1] = 'admintdright'
except IndexError:
pass
output += '<tr>'
for i in range(len(tuple2[0])):
output += '<td class="%s">%s</td>\n' % (align[i], tuple2[0][i])
output += '<td rowspan="%s" style="vertical-align: bottom">\n%s\n</td>\n' \
% (len(tuple2), button)
output += '</tr>\n'
for row in tuple2[1:]:
output += ' <tr>\n'
for i in range(len(row)):
output += '<td class="%s">%s</td>\n' % (align[i], row[i])
output += ' </tr>\n'
output += '</table>\n</form>\n'
return output
def splitgroups(id_role=0, id_action=0, authids=[]):
"""get all the old ones, gather up the arglistids find a list of
arglistidgroups to be split, unique get all actions in groups outside
of the old ones, (old arglistid is allowed).
show them like in showselect. """
if not id_role or not id_action or not authids:
return 0
# find all the actions
datalist = acca.acc_find_possible_actions(id_role, id_action)
if type(authids) is str:
authids = [authids]
for i in range(len(authids)):
authids[i] = int(authids[i])
# argumentlistids of groups to be split
splitgrps = []
for authid in authids:
hlp = datalist[authid][0]
if hlp not in splitgrps and authid in range(1, len(datalist)):
splitgrps.append(hlp)
# split groups and return success or failure
result = 1
for splitgroup in splitgrps:
result = 1 and acca.acc_split_argument_group(id_role, id_action,
splitgroup)
return result
def mergegroups(id_role=0, id_action=0, authids=[]):
"""get all the old ones, gather up the argauthids find a list
of arglistidgroups to be split, unique get all actions in groups
outside of the old ones, (old arglistid is allowed).
show them like in showselect."""
if not id_role or not id_action or not authids:
return 0
datalist = acca.acc_find_possible_actions(id_role, id_action)
if type(authids) is str:
authids = [authids]
for i in range(len(authids)):
authids[i] = int(authids[i])
# argumentlistids of groups to be merged
mergegroups = []
for authid in authids:
hlp = datalist[authid][0]
if hlp not in mergegroups and authid in range(1, len(datalist)):
mergegroups.append(hlp)
# merge groups and return success or failure
if acca.acc_merge_argument_groups(id_role, id_action, mergegroups):
return 1
else:
return 0
def deleteselected(id_role=0, id_action=0, authids=[]):
"""delete checked authorizations/possible actions, ids in authids.
id_role - role to delete from
id_action - action to delete from
authids - listids for which possible actions to delete."""
if not id_role or not id_action or not authids:
return 0
if type(authids) in [str, int]:
authids = [authids]
for i in range(len(authids)):
authids[i] = int(authids[i])
result = acca.acc_delete_possible_actions(id_role=id_role,
id_action=id_action,
authids=authids)
return result
def headeritalic(**ids):
"""transform keyword=value pairs to string with value in italics.
**ids - a dictionary of pairs to create string from """
output = ''
value = ''
table = ''
for key in ids.keys():
if key in ['User', 'user']:
value, table = 'email', 'user'
elif key in ['Role', 'role']:
value, table = 'name', 'accROLE'
elif key in ['Action', 'action']:
value, table = 'name', 'accACTION'
else:
if output:
output += ' and '
output += ' %s <i>%s</i>' % (key, ids[key])
continue
res = run_sql("""SELECT %%s FROM %s WHERE id = %%s""" % table, (value, ids[key]))
if res:
if output:
output += ' and '
output += ' %s <i>%s</i>' % (key, res[0][0])
return output
def headerstrong(query=1, **ids):
"""transform keyword=value pairs to string with value in strong text.
**ids - a dictionary of pairs to create string from
query - 1 -> try to find names to ids of role, user and action.
0 -> do not try to find names, use the value passed on """
output = ''
value = ''
table = ''
for key in ids.keys():
if key in ['User', 'user']:
value, table = 'email', 'user'
elif key in ['Role', 'role']:
value, table = 'name', 'accROLE'
elif key in ['Action', 'action']:
value, table = 'name', 'accACTION'
else:
if output:
output += ' and '
output += ' %s <strong>%s</strong>' % (key, ids[key])
continue
if query:
res = run_sql("""SELECT %%s FROM %s WHERE id = %%s""" % table, (value, ids[key]))
if res:
if output:
output += ' and '
output += ' %s <strong>%s</strong>' % (key, res[0][0])
else:
if output:
output += ' and '
output += ' %s <strong>%s</strong>' % (key, ids[key])
return output
def startpage():
"""create the menu for the startpage"""
body = """
<table class="admin_wvar" width="100%" summary="">
<thead>
<tr>
<th class="adminheaderleft">selection for WebAccess Admin</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<dl>
<dt><a href="webaccessadmin.py/rolearea">Role Area</a></dt>
<dd>main area to configure administration rights and authorization rules.</dd>
<dt><a href="webaccessadmin.py/actionarea">Action Area</a></dt>
<dd>configure administration rights with the actions as starting point.</dd>
<dt><a href="webaccessadmin.py/userarea">User Area</a></dt>
<dd>configure administration rights with the users as starting point.</dd>
<dt><a href="webaccessadmin.py/resetarea">Reset Area</a></dt>
<dd>reset roles, actions and authorizations.</dd>
</dl>
</td>
</tr>
</tbody>
</table>"""
return body
def rankarea():
return "Rankmethod area"
def perform_simpleauthorization(req, id_role=0, id_action=0):
"""show a page with simple overview of authorizations between a
connected role and action. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0:
return mustloginpage(req, auth_message)
res = acca.acc_find_possible_actions(id_role, id_action)
if res:
extra = createhiddenform(action='modifyauthorizations',
button='modify authorizations',
id_role=id_role,
id_action=id_action)
output = '<p>authorizations for %s:</p>' \
% (headerstrong(action=id_action, role=id_role), )
output += tupletotable(header=res[0], tuple=res[1:], extracolumn=extra)
else:
output = 'no details to show'
return index(req=req,
title='Simple authorization details',
subtitle='simple authorization details',
body=[output],
adminarea=3)
def perform_showroleusers(req, id_role=0):
"""show a page with simple overview of a role and connected users. """
(auth_code, auth_message) = is_adminuser(req)
if auth_code != 0:
return mustloginpage(req, auth_message)
res = acca.acc_get_role_users(id_role=id_role)
name_role = acca.acc_get_role_name(id_role=id_role)
if res:
users = []
for (role_id, name, dummy) in res:
users.append([role_id, name, '<a href="showuserdetails?'
'id_user=%s">show user details</a>' % (role_id, )])
output = '<p>users connected to %s:</p>' \
% (headerstrong(role=id_role), )
output += tupletotable(header=['id', 'name', ''], tuple=users)
else:
output = 'no users connected to role <strong>%s</strong>' \
% (name_role, )
extra = """
<dl>
<dt><a href="adduserrole?id_role=%s">Connect user</a></dt>
<dd>connect users to the role.</dd>
</dl>
""" % (id_role, )
return index(req=req,
title='Users connected to role %s' % (name_role, ),
subtitle='simple details',
body=[output, extra],
adminarea=3)
def createselect(id_input="0", label="", step=0, name="",
action="", list=[], extralist=[], extrastamp='',
button="", **hidden):
"""create form with select and hidden values
id - the one to choose as selected if exists
label - label shown to the left of the select
name - the name of the select on which to reference it
list - primary list to select from
extralist - list of options to be put in paranthesis
extrastamp - stamp extralist entries with this if not ''
usually paranthesis around the entry
button - the value/text to be put on the button
**hidden - name=value pairs to be put as hidden in the form. """
step = step and '%s. ' % step or ''
output = '<form action="%s" method="POST">\n' % (action, )
output += ' <span class="adminlabel">%s</span>\n' % (step + label, )
output += ' <select name="%s" class="admin_w200">\n' % (name, )
if not list and not extralist:
output += ' <option value="0">*** no %ss to select from ***' \
'</option>\n' % (label.split()[-1], )
else:
output += ' <option value="0">*** %s ***</option>\n' % (label, )
for elem in list:
elem_id = elem[0]
email = elem[1]
if str(elem_id) == id_input:
output += ' <option value="%s" selected="selected">' \
'%s</option>\n' % (elem_id, email)
else:
output += ' <option value="%s">%s</option>\n' \
% (elem_id, email)
for elem in extralist:
elem_id = elem[0]
email = elem[1]
if str(elem_id) == id_input:
if not extrastamp:
output += ' <option value="%s" selected="selected">' \
'(%s)</option>\n' % (elem_id, email)
else:
output += ' <option value="%s">%s %s</option>\n' \
% (elem_id, email, extrastamp)
elif not extrastamp:
output += ' <option value="%s">(%s)</option>\n' \
% (elem_id, email)
else:
output += ' <option value="%s">%s %s</option>\n' \
% (elem_id, email, extrastamp)
output += ' </select>\n'
for key in hidden.keys():
output += ' <input type="hidden" name="%s" value="%s" />\n' \
% (key, hidden[key])
output += ' <input class="adminbutton" type="submit" value="%s" />\n' \
% (button, )
output += '</form>\n'
return output
def createactionselect(id_action="0", label="select action", step=0,
name="id_action", action="", actions=[], extraactions=[],
extrastamp='', button="select action", **hidden):
"""create a select for roles in a form. see createselect."""
return createselect(id_input=id_action, label=label, step=step, name=name,
action=action, list=actions, extralist=extraactions,
extrastamp=extrastamp, button=button, **hidden)
def createroleselect(id_role="0", label="select role", step=0, name="id_role",
action="", roles=[], extraroles=[], extrastamp='',
button="select role", **hidden):
"""create a select for roles in a form. see createselect."""
return createselect(id_input=id_role, label=label, step=step, name=name,
action=action, list=roles, extralist=extraroles, extrastamp=extrastamp,
button=button, **hidden)
def createuserselect(id_user="0", label="select user", step=0, name="id_user",
action="", users=[], extrausers=[], extrastamp='(connected)',
button="select user", **hidden):
"""create a select for users in a form.see createselect."""
return createselect(id_input=id_user, label=label, step=step, name=name,
action=action, list=users, extralist=extrausers, extrastamp=extrastamp,
button=button, **hidden)
def cleanstring(txt='', comma=0):
"""clean all the strings before submitting to access control admin.
remove characters not letter, number or underscore, also remove leading
underscores and numbers. return cleaned string.
str - string to be cleaned
comma - 1 -> allow the comma to divide multiple arguments
0 -> wash commas as well """
# remove not allowed characters
txt = re.sub(r'[^a-zA-Z0-9_,]', '', txt)
# split string on commas
items = txt.split(',')
txt = ''
for item in items:
if not item:
continue
if comma and txt:
txt += ','
# create valid variable names
txt += re.sub(r'^([0-9_])*', '', item)
return txt
def cleanstring_argumentvalue(txt=''):
"""clean the value of an argument before submitting it.
allowed characters: a-z A-Z 0-9 _ and space
txt - string to be cleaned """
# remove not allowed characters
txt = re.sub(r'[^a-zA-Z0-9_ .]', '', txt)
# trim leading and ending spaces
txt = re.sub(r'^ *| *$', '', txt)
return txt
def cleanstring_email(txt=''):
"""clean the string and return a valid email address.
txt - string to be cleaned """
# remove not allowed characters
txt = re.sub(r'[^a-zA-Z0-9_.@-]', '', txt)
return txt
def check_email(txt=''):
"""control that submitted emails are correct.
this little check is not very good, but better than nothing. """
r = re.compile(r'(.)+\@(.)+\.(.)+')
return r.match(txt) and 1 or 0
def send_account_activated_message(account_email, send_to, password, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new activated
account."""
_ = gettext_set_language(ln)
sub = _("Your account on '%s' has been activated") % CFG_SITE_NAME
body = _("Your account earlier created on '%s' has been activated:\n\n") \
% CFG_SITE_NAME
body += _(" Username/Email: %s\n") % account_email
body += _(" Password: %s\n") % ("*" * len(password))
body += "\n---------------------------------"
body += "\n%s" % CFG_SITE_NAME
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, sub, body, header='')
def send_new_user_account_warning(new_account_email, send_to, password, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account
new_account_email."""
_ = gettext_set_language(ln)
sub = _("Account created on '%s'") % CFG_SITE_NAME
body = _("An account has been created for you on '%s':\n\n") % CFG_SITE_NAME
body += _(" Username/Email: %s\n") % new_account_email
body += _(" Password: %s\n") % ("*" * len(password))
body += "\n---------------------------------"
body += "\n%s" % CFG_SITE_NAME
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, sub, body, header='')
def send_account_rejected_message(new_account_email, send_to, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account
new_account_email."""
_ = gettext_set_language(ln)
sub = _("Account rejected on '%s'") % CFG_SITE_NAME
body = _("Your request for an account has been rejected on '%s':\n\n") \
% CFG_SITE_NAME
body += _(" Username/Email: %s\n") % new_account_email
body += "\n---------------------------------"
body += "\n%s" % CFG_SITE_NAME
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, sub, body, header='')
def send_account_deleted_message(new_account_email, send_to, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account
new_account_email."""
_ = gettext_set_language(ln)
sub = _("Account deleted on '%s'") % CFG_SITE_NAME
body = _("Your account on '%s' has been deleted:\n\n") % CFG_SITE_NAME
body += _(" Username/Email: %s\n") % new_account_email
body += "\n---------------------------------"
body += "\n%s" % CFG_SITE_NAME
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, sub, body, header='')
def usage(exitcode=1, msg=""):
"""Prints usage info."""
if msg:
print >> sys.stderr, "Error: %s." % msg
print >> sys.stderr
print >> sys.stderr, """Usage: %s [options]
General options:
-h, --help\t\tprint this help
-V, --version\t\tprint version number
Authentication options:
-u, --user=USER\tUser name needed to perform the administrative task
Option to administrate authorizations:
-a, --add\t\tadd default authorization settings
-c, --compile\t\tcompile firewall like role definitions (FireRole)
-r, --reset\t\treset to default settings
-D, --demo\t\tto be used with -a or -r in order to consider demo site authorizationss
""" % sys.argv[0]
sys.exit(exitcode)
def main():
"""Main function that analyzes command line input and calls whatever
is appropriate. """
## parse command line:
# set user-defined options:
options = {'user' : '', 'reset' : 0, 'compile' : 0, 'add' : 0, 'demo' : 0}
try:
opts, args = getopt.getopt(sys.argv[1:], "hVu:racD",
["help", "version", "user=",
"reset", "add", "compile", "demo"])
except getopt.GetoptError, err:
usage(1, err)
try:
for opt in opts:
if opt[0] in ("-h", "--help"):
usage(0)
elif opt[0] in ("-V", "--version"):
print __revision__
sys.exit(0)
elif opt[0] in ("-u", "--user"):
options["user"] = opt[1]
elif opt[0] in ("-r", "--reset"):
options["reset"] = 1
elif opt[0] in ("-a", "--add"):
options["add"] = 1
elif opt[0] in ("-c", "--compile"):
options["compile"] = 1
elif opt[0] in ("-D", "--demo"):
options["demo"] = 1
else:
usage(1)
if options['add'] or options['reset'] or options['compile']:
if acca.acc_get_action_id('cfgwebaccess'):
# Action exists hence authentication works :-)
options['user'] = authenticate(options['user'],
authorization_msg="WebAccess Administration",
authorization_action="cfgwebaccess")
if options['reset'] and options['demo']:
acca.acc_reset_default_settings([CFG_SITE_SUPPORT_EMAIL], DEF_DEMO_USER_ROLES, DEF_DEMO_ROLES, DEF_DEMO_AUTHS)
print "Reset default demo site settings."
elif options['reset']:
acca.acc_reset_default_settings([CFG_SITE_SUPPORT_EMAIL])
print "Reset default settings."
elif options['add'] and options['demo']:
acca.acc_add_default_settings([CFG_SITE_SUPPORT_EMAIL], DEF_DEMO_USER_ROLES, DEF_DEMO_ROLES, DEF_DEMO_AUTHS)
print "Added default demo site settings."
elif options['add']:
acca.acc_add_default_settings([CFG_SITE_SUPPORT_EMAIL])
print "Added default settings."
if options['compile']:
repair_role_definitions()
print "Compiled firewall like role definitions."
else:
usage(1, "You must specify at least one command")
except StandardError, e:
usage(e)
return
### okay, here we go:
if __name__ == '__main__':
main()
diff --git a/modules/webalert/doc/admin/webalert-admin-guide.webdoc b/modules/webalert/doc/admin/webalert-admin-guide.webdoc
index ed8a57ac6..021d60fce 100644
--- a/modules/webalert/doc/admin/webalert-admin-guide.webdoc
+++ b/modules/webalert/doc/admin/webalert-admin-guide.webdoc
@@ -1,76 +1,76 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebAlert Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p><table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
WARNING: THIS ADMIN GUIDE IS NOT FULLY COMPLETED
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
This Admin Guide is not yet completed. Moreover, some
admin-level functionality for this module exists only in the form of
manual recipes. We are in the process of developing both the
guide as well as the web admin interface. If you are interested
in seeing some specific things implemented with high priority,
please contact us at <CFG_SITE_SUPPORT_EMAIL>. Thanks for your interest!
</td>
</tr>
</tbody>
</table>
<h2>Overview</h2>
<p><CFG_SITE_NAME> users may set up an automatic notification email alerts
that would send them documents corresponding to the user profile by
email either daily, weekly, or monthly. It is the job of the WebAlert
module to permit this functionality.
<h2>Configuring Alert Queries</h2>
<p>Users may set up alert queries for example from their <a
-href="<WEBURL>/youralerts/display">search history</a> pages.
+href="<CFG_SITE_URL>/youralerts/display">search history</a> pages.
<p>Administrators may edit existing users' alerts by modifying the
<code>user_query_basket</code> table. (There is no web interface yet
for this task.)
<h2>Running Alert Engine</h2>
<p>The alert engine has to be run each day in order to send users
email notifications for the alerts they have set up:
<blockquote>
<pre>
$ alertengine
</pre>
</blockquote>
<strong>HINT:</strong> You may want to set up an external cron job
to call <code>alertengine</code> each day.
diff --git a/modules/webalert/lib/alert_engine.py b/modules/webalert/lib/alert_engine.py
index 878b64495..5c6cdda51 100644
--- a/modules/webalert/lib/alert_engine.py
+++ b/modules/webalert/lib/alert_engine.py
@@ -1,365 +1,365 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Alert engine implementation."""
## rest of the Python code goes below
__revision__ = "$Id$"
from cgi import parse_qs
from re import search, sub
from time import strftime
import datetime
from invenio.config import \
CFG_LOGDIR, \
CFG_SITE_SUPPORT_EMAIL, \
- weburl
+ CFG_SITE_URL
from invenio.search_engine import perform_request_search
from invenio.webinterface_handler import wash_urlargd
from invenio.dbquery import run_sql
from invenio.webuser import get_email
from invenio.mailutils import send_email
from invenio.errorlib import register_exception
from invenio.alert_engine_config import CFG_WEBALERT_DEBUG_LEVEL, \
CFG_WEBALERT_SEND_EMAIL_NUMBER_OF_TRIES, \
CFG_WEBALERT_SEND_EMAIL_SLEEPTIME_BETWEEN_TRIES
import invenio.template
websearch_templates = invenio.template.load('websearch')
webalert_templates = invenio.template.load('webalert')
def update_date_lastrun(alert):
return run_sql('update user_query_basket set date_lastrun=%s where id_user=%s and id_query=%s and id_basket=%s;', (strftime("%Y-%m-%d"), alert[0], alert[1], alert[2],))
def get_alert_queries(frequency):
return run_sql('select distinct id, urlargs from query q, user_query_basket uqb where q.id=uqb.id_query and uqb.frequency=%s and uqb.date_lastrun <= now();', (frequency,))
def get_alert_queries_for_user(uid):
return run_sql('select distinct id, urlargs, uqb.frequency from query q, user_query_basket uqb where q.id=uqb.id_query and uqb.id_user=%s and uqb.date_lastrun <= now();', (uid,))
def get_alerts(query, frequency):
r = run_sql('select id_user, id_query, id_basket, frequency, date_lastrun, alert_name, notification from user_query_basket where id_query=%s and frequency=%s;', (query['id_query'], frequency,))
return {'alerts': r, 'records': query['records'], 'argstr': query['argstr'], 'date_from': query['date_from'], 'date_until': query['date_until']}
# def add_record_to_basket(record_id, basket_id):
# if CFG_WEBALERT_DEBUG_LEVEL > 0:
# print "-> adding record %s into basket %s" % (record_id, basket_id)
# try:
# return run_sql('insert into basket_record (id_basket, id_record) values(%s, %s);', (basket_id, record_id,))
# except:
# return 0
# def add_records_to_basket(record_ids, basket_id):
# # TBD: generate the list and all all records in one step (see below)
# for i in record_ids:
# add_record_to_basket(i, basket_id)
# Optimized version:
def add_records_to_basket(record_ids, basket_id):
nrec = len(record_ids)
if nrec > 0:
vals = '(%s,%s)' % (basket_id, record_ids[0])
if nrec > 1:
for i in record_ids[1:]:
vals += ',(%s, %s)' % (basket_id, i)
if CFG_WEBALERT_DEBUG_LEVEL > 0:
print "-> adding %s records into basket %s: %s" % (nrec, basket_id, vals)
try:
if CFG_WEBALERT_DEBUG_LEVEL < 4:
return run_sql('insert into basket_record (id_basket, id_record) values %s;' % vals) # Cannot use the run_sql(<query>, (<arg>,)) form for some reason
else:
print ' NOT ADDED, DEBUG LEVEL == 4'
return 0
except Exception:
register_exception()
return 0
else:
return 0
def get_query(alert_id):
r = run_sql('select urlargs from query where id=%s', (alert_id,))
return r[0][0]
def email_notify(alert, records, argstr):
if len(records) == 0:
return
msg = ""
if CFG_WEBALERT_DEBUG_LEVEL > 0:
msg = "*** THIS MESSAGE WAS SENT IN DEBUG MODE ***\n\n"
- url = weburl + "/search?" + argstr
+ url = CFG_SITE_URL + "/search?" + argstr
# Extract the pattern and catalogue list from the formatted query
query = parse_qs(argstr)
pattern = query.get('p', [''])[0]
catalogues = query.get('c', [])
frequency = alert[3]
msg += webalert_templates.tmpl_alert_email_body(
alert[5], url, records, pattern, catalogues, frequency)
email = get_email(alert[0])
if email == 'guest':
print "********************************************************************************"
print "The following alert was not send, because cannot detect user email address:"
print " " + repr(argstr)
print "********************************************************************************"
return
if CFG_WEBALERT_DEBUG_LEVEL > 0:
print "********************************************************************************"
print msg
print "********************************************************************************"
if CFG_WEBALERT_DEBUG_LEVEL < 2:
send_email(fromaddr=webalert_templates.tmpl_alert_email_from(),
toaddr=email,
subject=webalert_templates.tmpl_alert_email_title(alert[5]),
content=msg,
header='',
footer='',
attempt_times=CFG_WEBALERT_SEND_EMAIL_NUMBER_OF_TRIES,
attempt_sleeptime=CFG_WEBALERT_SEND_EMAIL_SLEEPTIME_BETWEEN_TRIES)
if CFG_WEBALERT_DEBUG_LEVEL == 4:
send_email(fromaddr=webalert_templates.tmpl_alert_email_from(),
toaddr=CFG_SITE_SUPPORT_EMAIL,
subject=webalert_templates.tmpl_alert_email_title(alert[5]),
content=msg,
header='',
footer='',
attempt_times=CFG_WEBALERT_SEND_EMAIL_NUMBER_OF_TRIES,
attempt_sleeptime=CFG_WEBALERT_SEND_EMAIL_SLEEPTIME_BETWEEN_TRIES)
def get_argument(args, argname):
if args.has_key(argname):
return args[argname]
else:
return []
def _date_to_tuple(date):
return [int(part) for part in (date.year, date.month, date.day)]
def get_record_ids(argstr, date_from, date_until):
argd = wash_urlargd(parse_qs(argstr), websearch_templates.search_results_default_urlargd)
p = get_argument(argd, 'p')
c = get_argument(argd, 'c')
cc = get_argument(argd, 'cc')
as = get_argument(argd, 'as')
f = get_argument(argd, 'f')
so = get_argument(argd, 'so')
sp = get_argument(argd, 'sp')
ot = get_argument(argd, 'ot')
as = get_argument(argd, 'as')
p1 = get_argument(argd, 'p1')
f1 = get_argument(argd, 'f1')
m1 = get_argument(argd, 'm1')
op1 = get_argument(argd, 'op1')
p2 = get_argument(argd, 'p2')
f2 = get_argument(argd, 'f2')
m2 = get_argument(argd, 'm2')
op2 = get_argument(argd, 'op2')
p3 = get_argument(argd, 'p3')
f3 = get_argument(argd, 'f3')
m3 = get_argument(argd, 'm3')
sc = get_argument(argd, 'sc')
d1y, d1m, d1d = _date_to_tuple(date_from)
d2y, d2m, d2d = _date_to_tuple(date_until)
return perform_request_search(of='id', p=p, c=c, cc=cc, f=f, so=so, sp=sp, ot=ot,
as=as, p1=p1, f1=f1, m1=m1, op1=op1, p2=p2, f2=f2,
m2=m2, op2=op2, p3=p3, f3=f3, m3=m3, sc=sc, d1y=d1y,
d1m=d1m, d1d=d1d, d2y=d2y, d2m=d2m, d2d=d2d)
def get_argument_as_string(argstr, argname):
args = parse_qs(argstr)
a = get_argument(args, argname)
r = ''
if len(a):
r = a[0]
for i in a[1:len(a)]:
r += ", %s" % i
return r
def get_pattern(argstr):
return get_argument_as_string(argstr, 'p')
def get_catalogue(argstr):
return get_argument_as_string(argstr, 'c')
def get_catalogue_num(argstr):
args = parse_qs(argstr)
a = get_argument(args, 'c')
return len(a)
def run_query(query, frequency, date_until):
"""Return a dictionary containing the information of the performed query.
The information contains the id of the query, the arguments as a
string, and the list of found records."""
if frequency == 'day':
date_from = date_until - datetime.timedelta(days=1)
elif frequency == 'week':
date_from = date_until - datetime.timedelta(weeks=1)
else:
# Months are not an explicit notion of timedelta (it's the
# most ambiguous too). So we explicitely take the same day of
# the previous month.
d, m, y = (date_until.day, date_until.month, date_until.year)
m = m - 1
if m == 0:
m = 12
y = y - 1
date_from = datetime.date(year=y, month=m, day=d)
recs = get_record_ids(query[1], date_from, date_until)
n = len(recs)
if n:
log('query %08s produced %08s records' % (query[0], len(recs)))
if CFG_WEBALERT_DEBUG_LEVEL > 2:
print "[%s] run query: %s with dates: from=%s, until=%s\n found rec ids: %s" % (
strftime("%c"), query, date_from, date_until, recs)
return {'id_query': query[0], 'argstr': query[1],
'records': recs, 'date_from': date_from, 'date_until': date_until}
def process_alert_queries(frequency, date):
"""Run the alerts according to the frequency.
Retrieves the queries for which an alert exists, performs it, and
processes the corresponding alerts."""
alert_queries = get_alert_queries(frequency)
for aq in alert_queries:
q = run_query(aq, frequency, date)
alerts = get_alerts(q, frequency)
process_alerts(alerts)
def replace_argument(argstr, argname, argval):
"""Replace the given date argument value with the new one.
If the argument is missing, it is added."""
if search('%s=\d+' % argname, argstr):
r = sub('%s=\d+' % argname, '%s=%s' % (argname, argval), argstr)
else:
r = argstr + '&%s=%s' % (argname, argval)
return r
def update_arguments(argstr, date_from, date_until):
"""Replace date arguments in argstr with the ones specified by date_from and date_until.
Absent arguments are added."""
d1y, d1m, d1d = _date_to_tuple(date_from)
d2y, d2m, d2d = _date_to_tuple(date_until)
r = replace_argument(argstr, 'd1y', d1y)
r = replace_argument(r, 'd1m', d1m)
r = replace_argument(r, 'd1d', d1d)
r = replace_argument(r, 'd2y', d2y)
r = replace_argument(r, 'd2m', d2m)
r = replace_argument(r, 'd2d', d2d)
return r
def log(msg):
try:
log = open(CFG_LOGDIR + '/alertengine.log', 'a')
log.write(strftime('%Y%m%d%H%M%S#'))
log.write(msg + '\n')
log.close()
except Exception:
register_exception()
def process_alerts(alerts):
# TBD: do not generate the email each time, forge it once and then
# send it to all appropriate people
for a in alerts['alerts']:
if alert_use_basket_p(a):
add_records_to_basket(alerts['records'], a[2])
if alert_use_notification_p(a):
argstr = update_arguments(alerts['argstr'], alerts['date_from'], alerts['date_until'])
email_notify(a, alerts['records'], argstr)
update_date_lastrun(a)
def alert_use_basket_p(alert):
return alert[2] != 0
def alert_use_notification_p(alert):
return alert[6] == 'y'
def run_alerts(date):
"""Run the alerts.
First decide which alerts to run according to the current local
time, and runs them."""
if date.day == 1:
process_alert_queries('month', date)
if date.isoweekday() == 1: # first day of the week
process_alert_queries('week', date)
process_alert_queries('day', date)
def process_alert_queries_for_user(uid, date):
"""Process the alerts for the given user id.
All alerts are with reference date set as the current local time."""
alert_queries = get_alert_queries_for_user(uid)
print alert_queries
for aq in alert_queries:
frequency = aq[2]
q = run_query(aq, frequency, date)
alerts = get_alerts(q, frequency)
process_alerts(alerts)
diff --git a/modules/webalert/lib/webalert_regression_tests.py b/modules/webalert/lib/webalert_regression_tests.py
index 7ab2aefcf..d7c470110 100644
--- a/modules/webalert/lib/webalert_regression_tests.py
+++ b/modules/webalert/lib/webalert_regression_tests.py
@@ -1,53 +1,53 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebAlert Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class WebAlertWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebAlert web pages whether they are up or not."""
def test_your_alerts_pages_availability(self):
"""webalert - availability of Your Alerts pages"""
- baseurl = weburl + '/youralerts/'
+ baseurl = CFG_SITE_URL + '/youralerts/'
_exports = ['', 'display', 'input', 'modify', 'list', 'add',
'update', 'remove']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(WebAlertWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/webalert/lib/webalert_templates.py b/modules/webalert/lib/webalert_templates.py
index f90f68b53..39ab20a2d 100644
--- a/modules/webalert/lib/webalert_templates.py
+++ b/modules/webalert/lib/webalert_templates.py
@@ -1,561 +1,561 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import cgi
import time
import string
from invenio.config import \
CFG_WEBALERT_ALERT_ENGINE_EMAIL, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
- weburl
+ CFG_SITE_URL
from invenio.messages import gettext_set_language
from invenio.htmlparser import get_as_text, wrap
from invenio.alert_engine_config import CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL
class Template:
def tmpl_errorMsg(self, ln, error_msg, rest = ""):
"""
Adds an error message to the output
Parameters:
- 'ln' *string* - The language to display the interface in
- 'error_msg' *string* - The error message
- 'rest' *string* - The rest of the page
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<div class="quicknote">%(error)s</div><br />%(rest)s""" % {
'error' : error_msg,
'rest' : rest
}
return out
def tmpl_textual_query_info_from_urlargs(self, ln, args):
"""
Displays a human inteligible textual representation of a query
Parameters:
- 'ln' *string* - The language to display the interface in
- 'args' *array* - The URL arguments array (parsed)
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if args.has_key('p'):
out += "<strong>" + _("Pattern") + ":</strong> " + string.join(args['p'], "; ") + "<br />"
if args.has_key('f'):
out += "<strong>" + _("Field") + ":</strong> " + string.join(args['f'], "; ") + "<br />"
if args.has_key('p1'):
out += "<strong>" + _("Pattern 1") + ":</strong> " + string.join(args['p1'], "; ") + "<br />"
if args.has_key('f1'):
out += "<strong>" + _("Field 1") + ":</strong> " + string.join(args['f1'], "; ") + "<br />"
if args.has_key('p2'):
out += "<strong>" + _("Pattern 2") + ":</strong> " + string.join(args['p2'], "; ") + "<br />"
if args.has_key('f2'):
out += "<strong>" + _("Field 2") + ":</strong> " + string.join(args['f2'], "; ") + "<br />"
if args.has_key('p3'):
out += "<strong>" + _("Pattern 3") + ":</strong> " + string.join(args['p3'], "; ") + "<br />"
if args.has_key('f3'):
out += "<strong>" + _("Field 3") + ":</strong> " + string.join(args['f3'], "; ") + "<br />"
if args.has_key('c'):
out += "<strong>" + _("Collections") + ":</strong> " + string.join(args['c'], "; ") + "<br />"
elif args.has_key('cc'):
out += "<strong>" + _("Collection") + ":</strong> " + string.join(args['cc'], "; ") + "<br />"
return out
def tmpl_account_list_alerts(self, ln, alerts):
"""
Displays all the alerts in the main "Your account" page
Parameters:
- 'ln' *string* - The language to display the interface in
- 'alerts' *array* - The existing alerts IDs ('id' + 'name' pairs)
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<form name="displayalert" action="../youralerts/list" method="post">
%(you_own)s:
<select name="id_alert">
<option value="0">- %(alert_name)s -</option>""" % {
'you_own' : _("You own the following alerts:"),
'alert_name' : _("alert name"),
}
for alert in alerts :
out += """<option value="%(id)s">%(name)s</option>""" % \
{'id': alert['id'], 'name': cgi.escape(alert['name'])}
out += """</select>
&nbsp;<code class="blocknote">
<input class="formbutton" type="submit" name="action" value="%(show)s" /></code>
</form>""" % {
'show' : _("SHOW"),
}
return out
def tmpl_input_alert(self, ln, query, alert_name, action, frequency, notification,
baskets, old_id_basket, id_basket, id_query,
guest, guesttxt):
"""
Displays an alert adding form.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'query' *string* - The HTML code of the textual representation of the query (as returned ultimately by tmpl_textual_query_info_from_urlargs...)
- 'alert_name' *string* - The alert name
- 'action' *string* - The action to complete ('update' or 'add')
- 'frequency' *string* - The frequency of alert running ('day', 'week', 'month')
- 'notification' *string* - If notification should be sent by email ('y', 'n')
- 'baskets' *array* - The existing baskets ('id' + 'name' pairs)
- 'old_id_basket' *string* - The id of the previous basket of this alert
- 'id_basket' *string* - The id of the basket of this alert
- 'id_query' *string* - The id of the query associated to this alert
- 'guest' *bool* - If the user is a guest user
- 'guesttxt' *string* - The HTML content of the warning box for guest users (produced by webaccount.tmpl_warning_guest_user)
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
out += """<table style="border: 0px; padding: 2px; width: 650px;">
<tr><td colspan="3">%(notify_cond)s</td></tr>
<tr>
<td></td>
<td style="text-align: left; vertical-align: top; width: 10px; font-weight: bold;">%(query_text)s:</td>
<td style="text-align: left; vertical-align: top; width: 500px">%(query)s</td>
</tr>
</table>""" % {
'notify_cond' : _("This alert will notify you each time/only if a new item satisfies the following query:"),
'query_text' : _("QUERY"),
'query' : query,
}
out += """<form name="setalert" action="../youralerts/%(action)s" method="get">
<table style="background-color:F1F1F1; border:thin groove grey; padding: 0px;">
<tr>
<td>
<table style="border: 0px; padding:10px;">
<tr>
<td style="text-align: right; vertical-align:top; font-weight: bold;">%(alert_name)s</td>
<td><input type="text" name="name" size="20" maxlength="50" value="%(alert)s" /></td>
</tr>
<tr>
<td style="text-align: right; font-weight: bold;">%(freq)s</td>
<td>
<select name="freq">
<option value="month" %(freq_month)s>%(monthly)s</option>
<option value="week" %(freq_week)s>%(weekly)s</option>
<option value="day" %(freq_day)s>%(daily)s</option>
</select>
</td>
</tr>
<tr>
<td style="text-align:right; font-weight: bold">%(send_email)s</td>
<td>
<select name="notif">
<option value="y" %(notif_yes)s>%(yes)s</option>
<option value="n" %(notif_no)s>%(no)s</option>
</select>
<small class="quicknote"> (%(specify)s)</small>
</td>
</tr>
<tr>
<td style="text-align: right; vertical-align:top; font-weight: bold;">%(store_basket)s</td>
<td>%(baskets)s
""" % {
'action': action,
'alert_name' : _("Alert identification name:"),
'alert' : cgi.escape(alert_name, 1),
'freq' : _("Search-checking frequency:"),
'freq_month' : (frequency == 'month' and 'selected="selected"' or ""),
'freq_week' : (frequency == 'week' and 'selected="selected"' or ""),
'freq_day' : (frequency == 'day' and 'selected="selected"' or ""),
'monthly' : _("monthly"),
'weekly' : _("weekly"),
'daily' : _("daily"),
'send_email' : _("Send notification email?"),
'notif_yes' : (notification == 'y' and 'selected="selected"' or ""),
'notif_no' : (notification == 'n' and 'selected="selected"' or ""),
'yes' : _("yes"),
'no' : _("no"),
'specify' : _("if %(x_fmt_open)sno%(x_fmt_close)s you must specify a basket") % {'x_fmt_open': '<b>',
'x_fmt_close': '</b>'},
'store_basket' : _("Store results in basket?"),
'baskets': baskets
}
out += """ </td>
</tr>
<tr>
<td colspan="2" style="text-align:center">
<input type="hidden" name="idq" value="%(idq)s" />
<input type="hidden" name="ln" value="%(ln)s" />
<code class="blocknote"><input class="formbutton" type="submit" name="action" value="&nbsp;%(set_alert)s&nbsp;" /></code>&nbsp;
<code class="blocknote"><input class="formbutton" type="reset" value="%(clear_data)s" /></code>
</td>
</tr>
</table>
</td>
</tr>
</table>
""" % {
'idq' : id_query,
'ln' : ln,
'set_alert' : _("SET ALERT"),
'clear_data' : _("CLEAR DATA"),
}
if action == "update":
out += '<input type="hidden" name="old_idb" value="%s" />' % old_id_basket
out += "</form>"
if guest:
out += guesttxt
return out
def tmpl_list_alerts(self, ln, alerts, guest, guesttxt):
"""
Displays the list of alerts
Parameters:
- 'ln' *string* - The language to display the interface in
- 'alerts' *array* - The existing alerts:
- 'queryid' *string* - The id of the associated query
- 'queryargs' *string* - The query string
- 'textargs' *string* - The textual description of the query string
- 'userid' *string* - The user id
- 'basketid' *string* - The basket id
- 'basketname' *string* - The basket name
- 'alertname' *string* - The alert name
- 'frequency' *string* - The frequency of alert running ('day', 'week', 'month')
- 'notification' *string* - If notification should be sent by email ('y', 'n')
- 'created' *string* - The date of alert creation
- 'lastrun' *string* - The last running date
- 'guest' *bool* - If the user is a guest user
- 'guesttxt' *string* - The HTML content of the warning box for guest users (produced by webaccount.tmpl_warning_guest_user)
"""
# load the right message language
_ = gettext_set_language(ln)
out = '<p>' + _("Set a new alert from %(x_url1_open)syour searches%(x_url1_close)s, the %(x_url2_open)spopular searches%(x_url2_close)s, or the input form.") + '</p>'
out %= {'x_url1_open': '<a href="display?ln=' + ln + '">',
'x_url1_close': '</a>',
'x_url2_open': '<a href="display?ln=' + ln + '&amp;p=y">',
'x_url2_close': '</a>',
}
if len(alerts):
out += """<table class="alrtTable">
<tr class="pageboxlefttop" style="text-align: center;">
<td style="font-weight: bold">%(no)s</td>
<td style="font-weight: bold">%(name)s</td>
<td style="font-weight: bold">%(search_freq)s</td>
<td style="font-weight: bold">%(notification)s</td>
<td style="font-weight: bold">%(result_basket)s</td>
<td style="font-weight: bold">%(date_run)s</td>
<td style="font-weight: bold">%(date_created)s</td>
<td style="font-weight: bold">%(query)s</td>
<td style="font-weight: bold">%(action)s</td></tr>""" % {
'no' : _("No"),
'name' : _("Name"),
'search_freq' : _("Search checking frequency"),
'notification' : _("Notification by email"),
'result_basket' : _("Result in basket"),
'date_run' : _("Date last run"),
'date_created' : _("Creation date"),
'query' : _("Query"),
'action' : _("Action"),
}
i = 0
for alert in alerts:
i += 1
if alert['frequency'] == "day":
frequency = _("daily")
else:
if alert['frequency'] == "week":
frequency = _("weekly")
else:
frequency = _("monthly")
if alert['notification'] == "y":
notification = _("yes")
else:
notification = _("no")
out += """<tr>
<td style="font-style: italic">#%(index)d</td>
<td style="font-weight: bold; text-wrap:none;">%(alertname)s</td>
<td>%(frequency)s</td>
<td style="text-align:center">%(notification)s</td>
<td style="text-wrap:none;">%(basketname)s</td>
<td style="text-wrap:none;">%(lastrun)s</td>
<td style="text-wrap:none;">%(created)s</td>
<td>%(textargs)s</td>
<td>
<a href="./remove?ln=%(ln)s&amp;name=%(alertname)s&amp;idq=%(queryid)d&amp;idb=%(basketid)d">%(remove)s</a><br />
<a href="./modify?ln=%(ln)s&amp;idq=%(queryid)d&amp;name=%(alertname)s&amp;freq=%(freq)s&amp;notif=%(notif)s&amp;idb=%(basketid)d&amp;old_idb=%(basketid)d">%(modify)s</a><br />
- <a href="%(weburl)s/search?%(queryargs)s&amp;ln=%(ln)s" style="white-space:nowrap">%(search)s</a>
+ <a href="%(siteurl)s/search?%(queryargs)s&amp;ln=%(ln)s" style="white-space:nowrap">%(search)s</a>
</td>
</tr>""" % {
'index' : i,
'alertname' : cgi.escape(alert['alertname']),
'frequency' : frequency,
'notification' : notification,
'basketname' : alert['basketname'] and cgi.escape(alert['basketname']) \
or "- " + _("no basket") + " -",
'lastrun' : alert['lastrun'],
'created' : alert['created'],
'textargs' : alert['textargs'],
'queryid' : alert['queryid'],
'basketid' : alert['basketid'],
'freq' : alert['frequency'],
'notif' : alert['notification'],
'ln' : ln,
'remove' : _("Remove"),
'modify' : _("Modify"),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'search' : _("Execute search"),
'queryargs' : cgi.escape(alert['queryargs'])
}
out += '</table>'
out += '<p>' + (_("You have defined %s alerts.") % ('<b>' + str(len(alerts)) + '</b>' )) + '</p>'
if guest:
out += guesttxt
return out
def tmpl_display_alerts(self, ln, permanent, nb_queries_total, nb_queries_distinct, queries, guest, guesttxt):
"""
Displays the list of alerts
Parameters:
- 'ln' *string* - The language to display the interface in
- 'permanent' *string* - If displaying most popular searches ('y') or only personal searches ('n')
- 'nb_queries_total' *string* - The number of personal queries in the last period
- 'nb_queries_distinct' *string* - The number of distinct queries in the last period
- 'queries' *array* - The existing queries:
- 'id' *string* - The id of the associated query
- 'args' *string* - The query string
- 'textargs' *string* - The textual description of the query string
- 'lastrun' *string* - The last running date (only for personal queries)
- 'guest' *bool* - If the user is a guest user
- 'guesttxt' *string* - The HTML content of the warning box for guest users (produced by webaccount.tmpl_warning_guest_user)
"""
# load the right message language
_ = gettext_set_language(ln)
if len(queries) == 0:
out = _("You have not executed any search yet. Please go to the %(x_url_open)ssearch interface%(x_url_close)s first.") % \
- {'x_url_open': '<a href="' + weburl + '/?ln=' + ln +'">',
+ {'x_url_open': '<a href="' + CFG_SITE_URL + '/?ln=' + ln +'">',
'x_url_close': '</a>'}
return out
out = ''
# display message: number of items in the list
if permanent == "n":
msg = _("You have performed %(x_nb1)s searches (%(x_nb2)s different questions) during the last 30 days or so.") % {'x_nb1': nb_queries_total,
'x_nb2': nb_queries_distinct}
out += '<p>' + msg + '</p>'
else:
# permanent="y"
msg = _("Here are the %s most popular searches.")
msg %= ('<b>' + str(len(queries)) + '</b>')
out += '<p>' + msg + '</p>'
# display the list of searches
out += """<table class="alrtTable">
<tr class="pageboxlefttop">
<td style="font-weight: bold">%(no)s</td>
<td style="font-weight: bold">%(question)s</td>
<td style="font-weight: bold">%(action)s</td>""" % {
'no' : "#",
'question' : _("Question"),
'action' : _("Action")
}
if permanent == "n":
out += '<td style="font-weight: bold">%s</td>' % _("Last Run")
out += "</tr>\n"
i = 0
for query in queries :
i += 1
# id, pattern, base, search url and search set alert, date
out += """<tr>
<td style="font-style: italic;">#%(index)d</td>
<td>%(textargs)s</td>
- <td><a href="%(weburl)s/search?%(args)s">%(execute_query)s</a><br />
- <a href="%(weburl)s/youralerts/input?ln=%(ln)s&amp;idq=%(id)d">%(set_alert)s</a></td>""" % {
+ <td><a href="%(siteurl)s/search?%(args)s">%(execute_query)s</a><br />
+ <a href="%(siteurl)s/youralerts/input?ln=%(ln)s&amp;idq=%(id)d">%(set_alert)s</a></td>""" % {
'index' : i,
'textargs' : query['textargs'],
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'args' : cgi.escape(query['args']),
'id' : query['id'],
'ln': ln,
'execute_query' : _("Execute search"),
'set_alert' : _("Set new alert")
}
if permanent == "n":
out += '<td>%s</td>' % query['lastrun']
out += """</tr>\n"""
out += "</table><br />\n"
if guest :
out += guesttxt
return out
def tmpl_alert_email_title(self, name):
return 'Alert %s run on %s' % (
name, time.strftime("%Y-%m-%d"))
def tmpl_alert_email_from(self):
return '%s Alert Engine <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL)
def tmpl_alert_email_body(self, name, url, records, pattern,
catalogues, frequency):
l = len(catalogues)
if l == 0:
collections = ''
elif l == 1:
collections = "collection: %s\n" % catalogues[0]
else:
collections = "collections: %s\n" % wrap(', '.join(catalogues))
if pattern:
pattern = 'pattern: %s\n' % pattern
frequency = {'day': 'daily',
'week': 'weekly',
'month': 'monthly'}[frequency]
l = len(records)
if l == 1:
total = '1 record'
else:
total = '%d records' % l
body = """\
Hello:
Below are the results of the email notification alert that
you set up with the %(sitename)s.
This is an automatic message, please don't reply to it.
For any question, please use <%(sitesupportemail)s> instead.
alert name: %(name)s
%(pattern)s%(collections)sfrequency: %(frequency)s
run time: %(runtime)s
found: %(total)s
url: <%(url)s>
""" % {'sitesupportemail': CFG_SITE_SUPPORT_EMAIL,
'name': name,
'sitename': CFG_SITE_NAME,
'pattern': pattern,
'collections': collections,
'frequency': frequency,
'runtime': time.strftime("%a %Y-%m-%d %H:%M:%S"),
'total': total,
'url': url}
for index, recid in enumerate(records[:CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL]):
body += "\n%i) " % (index + 1)
body += self.tmpl_alert_email_record(recid)
body += "\n"
if len(records) > CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL:
body += '''
Only the first %s records were displayed. Please consult the search
URL given at the top of this email to see all the results.
''' % CFG_WEBALERT_MAX_NUM_OF_RECORDS_IN_ALERT_EMAIL
body += '''
--
%s Alert Service <%s>
Unsubscribe? See <%s>
Need human intervention? Contact <%s>
-''' % (CFG_SITE_NAME, weburl, weburl + '/youralerts/list', CFG_SITE_SUPPORT_EMAIL)
+''' % (CFG_SITE_NAME, CFG_SITE_URL, CFG_SITE_URL + '/youralerts/list', CFG_SITE_SUPPORT_EMAIL)
return body
def tmpl_alert_email_record(self, recid):
""" Format a single record."""
out = wrap(get_as_text(recid))
- out += "Detailed record: <%s/record/%s>" % (weburl, recid)
+ out += "Detailed record: <%s/record/%s>" % (CFG_SITE_URL, recid)
return out
diff --git a/modules/webalert/lib/webalert_webinterface.py b/modules/webalert/lib/webalert_webinterface.py
index dc5f491bf..38a2c1c6b 100644
--- a/modules/webalert/lib/webalert_webinterface.py
+++ b/modules/webalert/lib/webalert_webinterface.py
@@ -1,368 +1,368 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""PERSONAL FEATURES - YOUR ALERTS"""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import sys
import time
import zlib
import urllib
from mod_python import apache
-from invenio.config import weburl, CFG_SITE_SECURE_URL, CFG_SITE_LANG, CFG_SITE_NAME, \
+from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_SITE_LANG, CFG_SITE_NAME, \
CFG_ACCESS_CONTROL_LEVEL_SITE, CFG_SITE_NAME_INTL
from invenio.webpage import page
from invenio import webalert
from invenio.webuser import getUid, page_not_authorized, isGuestUser
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import redirect_to_url, make_canonical_urlargd
from invenio.messages import gettext_set_language
import invenio.template
webalert_templates = invenio.template.load('webalert')
class WebInterfaceYourAlertsPages(WebInterfaceDirectory):
"""Defines the set of /youralerts pages."""
_exports = ['', 'display', 'input', 'modify', 'list', 'add',
'update', 'remove']
def index(self, req, form):
"""Index page."""
- redirect_to_url(req, '%s/youralerts/list' % weburl)
+ redirect_to_url(req, '%s/youralerts/list' % CFG_SITE_URL)
def display(self, req, form):
"""Display search history page. A misnomer."""
argd = wash_urlargd(form, {'p': (str, "n")
})
uid = getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/youralerts/display" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="youralerts")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/youralerts/display%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
return page(title=_("Display searches"),
body=webalert.perform_display(argd['p'], uid, ln=argd['ln']),
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln': argd['ln'],
'account' : _("Your Account"),
},
description=_("%s Personalize, Display searches") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
uid=uid,
language=argd['ln'],
req=req,
lastupdated=__lastupdated__,
navmenuid='youralerts')
def input(self, req, form):
argd = wash_urlargd(form, {'idq': (int, None),
'name': (str, ""),
'freq': (str, "week"),
'notif': (str, "y"),
'idb': (int, 0),
'error_msg': (str, ""),
})
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/youralerts/input" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="youralerts")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/youralerts/input%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
# load the right message language
_ = gettext_set_language(argd['ln'])
html = webalert.perform_input_alert("add", argd['idq'], argd['name'], argd['freq'],
argd['notif'], argd['idb'], uid, ln=argd['ln'])
if argd['error_msg'] != "":
html = webalert_templates.tmpl_errorMsg(
ln = argd['ln'],
error_msg = argd['error_msg'],
rest = html,
)
return page(title=_("Set a new alert"),
body=html,
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln': argd['ln'],
'account' : _("Your Account"),
},
description=_("%s Personalize, Set a new alert") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
uid=uid,
language=argd['ln'],
req=req,
lastupdated=__lastupdated__,
navmenuid='youralerts')
def modify(self, req, form):
argd = wash_urlargd(form, {'idq': (int, None),
'old_idb': (int, None),
'name': (str, ""),
'freq': (str, "week"),
'notif': (str, "y"),
'idb': (int, 0),
'error_msg': (str, ""),
})
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/youralerts/modify" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="youralerts")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/youralerts/modify%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
# load the right message language
_ = gettext_set_language(argd['ln'])
html = webalert.perform_input_alert("update", argd['idq'], argd['name'], argd['freq'],
argd['notif'], argd['idb'], uid, argd['old_idb'], ln=argd['ln'])
if argd['error_msg'] != "":
html = webalert_templates.tmpl_errorMsg(
ln = argd['ln'],
error_msg = argd['error_msg'],
rest = html,
)
return page(title=_("Modify alert settings"),
body=html,
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln': argd['ln'],
'account' : _("Your Account"),
},
description=_("%s Personalize, Modify alert settings") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
uid=uid,
language=argd['ln'],
req=req,
lastupdated=__lastupdated__,
navmenuid='youralerts')
def list(self, req, form):
argd = wash_urlargd(form, {})
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/youralerts/list" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="youralerts")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/youralerts/list%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
# load the right message language
_ = gettext_set_language(argd['ln'])
return page(title=_("Display alerts"),
body=webalert.perform_list_alerts(uid, ln = argd['ln']),
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln': argd['ln'],
'account' : _("Your Account"),
},
description=_("%s Personalize, Display alerts") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
uid=uid,
language=argd['ln'],
req=req,
lastupdated=__lastupdated__,
navmenuid='youralerts')
def add(self, req, form):
argd = wash_urlargd(form, {'idq': (int, None),
'name': (str, None),
'freq': (str, None),
'notif': (str, None),
'idb': (int, None),
})
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/youralerts/add" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="youralerts")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/youralerts/add%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
# load the right message language
_ = gettext_set_language(argd['ln'])
try:
html = webalert.perform_add_alert(argd['name'], argd['freq'], argd['notif'],
argd['idb'], argd['idq'], uid, ln=argd['ln'])
except webalert.AlertError, e:
html = e
#return self.input(req, form)
return page(title=_("Display alerts"),
body=html,
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln': argd['ln'],
'account' : _("Your Account"),
},
description=_("%s Personalize, Display alerts") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
uid=uid,
language=argd['ln'],
req=req,
lastupdated=__lastupdated__,
navmenuid='youralerts')
def update(self, req, form):
argd = wash_urlargd(form, {'name': (str, None),
'freq': (str, None),
'notif': (str, None),
'idb': (int, None),
'idq': (int, None),
'old_idb': (int, None),
})
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/youralerts/update" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="youralerts")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/youralerts/update%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
# load the right message language
_ = gettext_set_language(argd['ln'])
try:
html = webalert.perform_update_alert(argd['name'], argd['freq'], argd['notif'],
argd['idb'], argd['idq'], argd['old_idb'], uid, ln=argd['ln'])
except webalert.AlertError, e:
return self.modify(req, form)
return page(title=_("Display alerts"),
body=html,
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln': argd['ln'],
'account' : _("Your Account"),
},
description=_("%s Personalize, Display alerts") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
uid=uid,
language=argd['ln'],
req=req,
lastupdated=__lastupdated__,
navmenuid='youralerts')
def remove(self, req, form):
argd = wash_urlargd(form, {'name': (str, None),
'idq': (int, None),
'idb': (int, None),
})
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/youralerts/remove" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="youralerts")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/youralerts/remove%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
# load the right message language
_ = gettext_set_language(argd['ln'])
return page(title=_("Display alerts"),
body=webalert.perform_remove_alert(argd['name'], argd['idq'],
argd['idb'], uid, ln=argd['ln']),
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln': argd['ln'],
'account' : _("Your Account"),
},
description=_("%s Personalize, Display alerts") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(argd['ln'], CFG_SITE_NAME),
uid=uid,
language=argd['ln'],
req=req,
lastupdated=__lastupdated__,
navmenuid='youralerts')
diff --git a/modules/webbasket/doc/admin/webbasket-admin-guide.webdoc b/modules/webbasket/doc/admin/webbasket-admin-guide.webdoc
index 3c6f8e6be..35071db77 100644
--- a/modules/webbasket/doc/admin/webbasket-admin-guide.webdoc
+++ b/modules/webbasket/doc/admin/webbasket-admin-guide.webdoc
@@ -1,25 +1,25 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebBasket Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>FIXME.</p>
diff --git a/modules/webbasket/lib/webbasket.py b/modules/webbasket/lib/webbasket.py
index 3984305ad..2600b2884 100644
--- a/modules/webbasket/lib/webbasket.py
+++ b/modules/webbasket/lib/webbasket.py
@@ -1,856 +1,856 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Web Baskets features."""
__revision__ = "$Id$"
import cgi
from zlib import decompress
-from invenio.config import CFG_SITE_LANG, weburl
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL
from invenio.messages import gettext_set_language
from invenio.dateutils import convert_datetext_to_dategui, \
convert_datetext_to_datestruct,\
convert_datestruct_to_dategui
from invenio.bibformat import format_record
from invenio.webbasket_config import CFG_WEBBASKET_SHARE_LEVELS, \
CFG_WEBBASKET_SHARE_LEVELS_ORDERED, \
CFG_WEBBASKET_CATEGORIES, \
CFG_WEBBASKET_WARNING_MESSAGES, \
CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS
from invenio.webuser import isGuestUser
from invenio.search_engine import record_exists
import invenio.webbasket_dblayer as db
try:
import invenio.template
webbasket_templates = invenio.template.load('webbasket')
except ImportError:
pass
def perform_request_display(uid,
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0,
selected_group_id=0,
ln=CFG_SITE_LANG):
"""Display all the baskets of given category, topic or group.
@param uid: user id
@param category: selected category (see webbasket_config.py)
@param selected_topic: # of selected topic to display baskets
@param selected_group_id: id of group to display baskets
@param ln: language"""
warnings = []
errors = []
baskets_html = []
_ = gettext_set_language(ln)
nb_groups = db.count_groups_user_member_of(uid)
nb_external_baskets = db.count_external_baskets(uid)
selectionbox = ''
infobox = ''
if category == CFG_WEBBASKET_CATEGORIES['EXTERNAL']:
baskets = db.get_external_baskets_infos(uid)
if len(baskets):
map(list, baskets)
else:
category = CFG_WEBBASKET_CATEGORIES['PRIVATE']
if category == CFG_WEBBASKET_CATEGORIES['GROUP']:
groups = db.get_group_infos(uid)
if len(groups):
if selected_group_id == 0 and len(groups):
selected_group_id = groups[0][0]
selectionbox = webbasket_templates.tmpl_group_selection(groups,
selected_group_id,
ln)
baskets = db.get_group_baskets_infos(selected_group_id)
def adapt_group_rights(item):
"""Suppress unused element in tuple."""
out = list(item)
if out[-1] == uid:
out[-2] = CFG_WEBBASKET_SHARE_LEVELS['MANAGE']
return out[:-1]
baskets = map(adapt_group_rights, baskets)
else:
category = CFG_WEBBASKET_CATEGORIES['PRIVATE']
if category == CFG_WEBBASKET_CATEGORIES['PRIVATE']:
topics_list = db.get_personal_topics_infos(uid)
if not selected_topic and len(topics_list):
selected_topic = 0
selectionbox = webbasket_templates.tmpl_topic_selection(topics_list,
selected_topic,
ln)
if len(topics_list) > 0:
baskets = db.get_personal_baskets_infos(uid, topics_list[selected_topic][0])
else:
baskets = []
def add_manage_rights(item):
""" Convert a tuple to a list and add rights"""
out = list(item)
out.append(CFG_WEBBASKET_SHARE_LEVELS['MANAGE'])
return out
baskets = map(add_manage_rights, baskets)
bskids = []
for basket in baskets:
bskids.append(basket[0])
levels = dict(db.is_shared_to(bskids))
create_link = ''
if category == CFG_WEBBASKET_CATEGORIES['PRIVATE']:
create_link = webbasket_templates.tmpl_create_basket_link(selected_topic, ln)
infobox = webbasket_templates.tmpl_baskets_infobox(map(lambda x: (x[0], x[1], x[2]),
baskets),
create_link,
ln)
for (bskid, name, date_modification,
nb_views, nb_items, last_added, share_level) in baskets:
(bsk_html, bsk_e, bsk_w) = __display_basket(bskid,
name,
date_modification,
nb_views,
nb_items,
last_added,
share_level,
levels[bskid],
category,
selected_topic,
selected_group_id,
ln)
baskets_html.append(bsk_html)
errors.extend(bsk_e)
warnings.extend(bsk_w)
body = webbasket_templates.tmpl_display(selectionbox,
infobox,
baskets_html,
category,
nb_groups,
nb_external_baskets,
ln)
return (body, errors, warnings)
def __display_basket(bskid, name, date_modification, nb_views,
nb_items, last_added,
share_level, group_sharing_level,
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0, selected_group_id=0,
ln=CFG_SITE_LANG):
"""Private function. Display a basket giving its category and topic or group.
@param share_level: rights user has on basket
@param group_sharing_level: None if basket is not shared,
0 if public basket,
> 0 if shared to usergroups but not public.
@param category: selected category (see webbasket_config.py)
@param selected_topic: # of selected topic to display baskets
@param selected_group_id: id of group to display baskets
@param ln: language"""
_ = gettext_set_language(ln)
errors = []
warnings = []
nb_bsk_cmts = 0
last_cmt = _("N/A")
records = []
cmt_dates = []
date_modification = convert_datetext_to_dategui(date_modification, ln)
items = db.get_basket_content(bskid, 'hb')
for (recid, nb_cmt, last_cmt, ext_val, int_val, score) in items:
cmt_dates.append(convert_datetext_to_datestruct(last_cmt))
last_cmt = convert_datetext_to_dategui(last_cmt, ln)
val = ''
nb_bsk_cmts += nb_cmt
if recid < 0:
if ext_val:
val = decompress(ext_val)
else:
if int_val:
val = decompress(int_val)
else:
val = format_record(recid, 'hb', on_the_fly=True)
records.append((recid, nb_cmt, last_cmt, val, score))
if len(cmt_dates) > 0:
last_cmt = convert_datestruct_to_dategui(max(cmt_dates), ln)
body = webbasket_templates.tmpl_basket(bskid,
name,
date_modification,
nb_views,
nb_items, last_added,
(__check_sufficient_rights(share_level, CFG_WEBBASKET_SHARE_LEVELS['READITM']),
__check_sufficient_rights(share_level, CFG_WEBBASKET_SHARE_LEVELS['MANAGE']),
__check_sufficient_rights(share_level, CFG_WEBBASKET_SHARE_LEVELS['READCMT']),
__check_sufficient_rights(share_level, CFG_WEBBASKET_SHARE_LEVELS['ADDITM']),
__check_sufficient_rights(share_level, CFG_WEBBASKET_SHARE_LEVELS['DELITM'])),
nb_bsk_cmts, last_cmt,
group_sharing_level,
category, selected_topic, selected_group_id,
records,
ln)
return (body, errors, warnings)
def perform_request_display_item(uid, bskid, recid, format='hb',
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
topic=0, group_id=0, ln=CFG_SITE_LANG):
"""Display an item of a basket of given category, topic or group.
@param uid: user id
@param bskid: basket_id
@param recid: record id
@param format: format of the record (hb, hd, etc.)
@param category: selected category (see webbasket_config.py)
@param topic: # of selected topic to display baskets
@param group_id: id of group to display baskets
@param ln: language"""
body = ''
errors = []
warnings = []
rights = db.get_max_user_rights_on_basket(uid, bskid)
if not(__check_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['READITM'])):
errors.append('ERR_WEBBASKET_NO_RIGHTS')
return (body, errors, warnings)
if category == CFG_WEBBASKET_CATEGORIES['PRIVATE']:
topics_list = db.get_personal_topics_infos(uid)
if not topic and len(topics_list):
topic = 0
topicsbox = webbasket_templates.tmpl_topic_selection(topics_list, topic, ln)
elif category == CFG_WEBBASKET_CATEGORIES['GROUP']:
groups = db.get_group_infos(uid)
if group_id == 0 and len(groups):
group_id = groups[0][0]
topicsbox = webbasket_templates.tmpl_group_selection(groups, group_id, ln)
else:
topicsbox = ''
record = db.get_basket_record(bskid, recid, format)
comments = db.get_comments(bskid, recid)
group_sharing_level = None
levels = db.is_shared_to(bskid)
if len(levels):
group_sharing_level = levels[0][1]
basket = db.get_basket_general_infos(bskid)
if not(len(basket)):
errors.append('ERR_WEBBASKET_DB_ERROR')
return (body, errors, warnings)
item_html = webbasket_templates.tmpl_item(basket,
recid, record, comments,
group_sharing_level,
(__check_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['READCMT']),
__check_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['ADDCMT']),
__check_sufficient_rights(rights, CFG_WEBBASKET_SHARE_LEVELS['DELCMT'])),
selected_category=category, selected_topic=topic, selected_group_id=group_id,
ln=ln)
body = webbasket_templates.tmpl_display(topicsbox=topicsbox, baskets=[item_html],
selected_category=category,
nb_groups=db.count_groups_user_member_of(uid),
nb_external_baskets=db.count_external_baskets(uid),
ln=ln)
return (body, errors, warnings)
def perform_request_write_comment(uid, bskid, recid, cmtid=0,
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
topic=0, group_id=0,
ln=CFG_SITE_LANG):
"""Display a comment writing form
@param uid: user id
@param bskid: basket id
@param recid: record id (comments are on a specific record in a specific basket)
@param cmtid: if provided this comment is a reply to comment cmtid.
@param category: selected category
@param topic: selected topic
@param group id: selected group id
@param ln: language
"""
body = ''
warnings = []
errors = []
cmt_body = ''
if not __check_user_can_comment(uid, bskid):
errors.append(('ERR_WEBBASKET_CANNOT_COMMENT'))
return (body, errors, warnings)
if cmtid:
# this is a reply to another comment
comment = db.get_comment(cmtid)
if comment:
cmt_body = webbasket_templates.tmpl_quote_comment(comment[2], # title
uid,
comment[0], # nickname
comment[4], # date
comment[3],
ln)
else:
warning = (CFG_WEBBASKET_WARNING_MESSAGES['ERR_WEBBASKET_cmtid_INVALID'], cmtid)
warnings.append(warning)
record = db.get_basket_record(bskid, recid, 'hb')
body = webbasket_templates.tmpl_write_comment(bskid=bskid,
recid=recid,
cmt_body=cmt_body,
record = record,
selected_category=category,
selected_topic=topic,
selected_group_id=group_id,
warnings=warnings)
if category == CFG_WEBBASKET_CATEGORIES['PRIVATE']:
topics_list = db.get_personal_topics_infos(uid)
if not topic and len(topics_list):
topic = 0
topicsbox = webbasket_templates.tmpl_topic_selection(topics_list, topic, ln)
elif category == CFG_WEBBASKET_CATEGORIES['GROUP']:
groups = db.get_group_infos(uid)
if group_id == 0 and len(groups):
group_id = groups[0][0]
topicsbox = webbasket_templates.tmpl_group_selection(groups, group_id, ln)
else:
topicsbox = ''
body = webbasket_templates.tmpl_display(topicsbox, '', [ body ], category, ln)
return (body, errors, warnings)
def perform_request_save_comment(uid, bskid, recid, title='', text='', ln=CFG_SITE_LANG):
""" Save a given comment if able to.
@param uid: user id (int)
@param bskid: basket id (int)
@param recid: record id (int)
@param title: title of comment (string)
@param text: comment's body (string)
@param ln: language (string)
@return (errors, infos) where errors: list of errors while saving
infos: list of informations to display"""
_ = gettext_set_language(ln)
errors = []
infos = []
if not __check_user_can_comment(uid, bskid):
errors.append(('ERR_WEBBASKET_CANNOT_COMMENT'))
return (errors, infos)
if not(db.save_comment(uid, bskid, recid, title, text)):
errors.append(('ERR_WEBBASKET_DB_ERROR'))
else:
infos.append(_('Your comment has been successfully posted'))
return (errors, infos)
def perform_request_delete_comment(uid, bskid, recid, cmtid):
"""Delete comment cmtid on record recid for basket bskid."""
errors = []
if __check_user_can_perform_action(uid, bskid, CFG_WEBBASKET_SHARE_LEVELS['DELCMT']):
db.delete_comment(bskid, recid, cmtid)
else:
errors.append('ERR_WEBBASKET_NO_RIGHTS')
return errors
def perform_request_add(uid, recids=[], bskids=[], referer='',
new_basket_name='', new_topic_name='', create_in_topic='',
ln=CFG_SITE_LANG):
"""Add records to baskets
@param uid: user id
@param recids: list of records to add
@param bskids: list of baskets to add records to. if not provided, will return a
page where user can select baskets
@param referer: URL of the referring page
@param new_basket_name: add record to new basket
@param new_topic_name: new basket goes into new topic
@param create_in_topic: # of topic to put basket into
@param ln: language
@return (body, errors, warnings) tuple
"""
body = ''
errors = []
warnings = []
if not(type(recids) == list):
recids = [recids]
validated_recids = []
for recid in recids:
recid = int(recid)
if record_exists(recid) == 1:
validated_recids.append(recid)
if not(len(validated_recids)):
warnings.append('WRN_WEBBASKET_NO_RECORD')
body += webbasket_templates.tmpl_warnings(warnings, ln)
- if referer and not(referer.find(weburl) == -1):
+ if referer and not(referer.find(CFG_SITE_URL) == -1):
body += webbasket_templates.tmpl_back_link(referer, ln)
return (body, errors, warnings)
if new_basket_name != '':
new_topic_name = new_topic_name.strip()
if new_topic_name:
topic = new_topic_name
elif create_in_topic != -1:
topics = map(lambda x: x[0], db.get_personal_topics_infos(uid))
try:
topic = topics[create_in_topic]
except IndexError:
topic = ''
else:
topic = ''
warnings.append('WRN_WEBBASKET_NO_GIVEN_TOPIC')
body += webbasket_templates.tmpl_warnings(warnings, ln)
bskids = []
if topic:
id_bsk = db.create_basket(uid, new_basket_name, topic)
bskids.append(id_bsk)
#it may be useful to add a warning message when no basket is selected : bskids != ['-1']
if len(bskids) and bskids != ['-1']:
# save
bskids = [int(bskid) for bskid in bskids]
for bskid in bskids:
if bskid != -1:
if not(__check_user_can_perform_action(uid,
bskid,
CFG_WEBBASKET_SHARE_LEVELS['ADDITM'])):
errors.append('ERR_WEBBASKET_NO_RIGHTS')
break
if len(errors):
return (body, errors, warnings)
nb_modified_baskets = db.add_to_basket(uid, validated_recids, bskids)
body = webbasket_templates.tmpl_added_to_basket(nb_modified_baskets, ln)
body += webbasket_templates.tmpl_back_link(referer, ln)
else:
# Display basket_selection
personal_baskets = db.get_all_personal_baskets_names(uid)
group_baskets = db.get_all_group_baskets_names(uid)
external_baskets = db.get_all_external_baskets_names(uid)
topics = map(lambda x: x[0], db.get_personal_topics_infos(uid))
body += webbasket_templates.tmpl_add(recids=validated_recids,
personal_baskets=personal_baskets,
group_baskets=group_baskets,
external_baskets=external_baskets,
topics=topics,
referer=referer,
ln=ln)
body += webbasket_templates.tmpl_back_link(referer, ln)
return (body, errors, warnings)
def perform_request_delete(uid, bskid, confirmed=0,
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0, selected_group_id=0,
ln=CFG_SITE_LANG):
"""Delete a given basket.
@param uid: user id (user has to be owner of this basket)
@param bskid: basket id
@param confirmed: if 0 will return a confirmation page; if 1 will delete basket.
@param category: category currently displayed
@param selected_topic: topic currently displayed
@param selected_group id: if category is group, id of the group currently displayed
@param ln: language"""
body = ''
errors = []
warnings = []
if not(db.check_user_owns_baskets(uid, [bskid])):
errors.append(('ERR_WEBBASKET_NO_RIGHTS',))
return (body, errors, warnings)
if confirmed:
success = db.delete_basket(bskid)
if not success:
errors.append(('ERR_WEBBASKET_DB_ERROR',))
else:
body = webbasket_templates.tmpl_confirm_delete(bskid,
db.count_subscribers(uid, bskid),
category,
selected_topic, selected_group_id,
ln)
return (body, errors, warnings)
def delete_record(uid, bskid, recid):
"""Delete a given record in a given basket.
@param uid: user id (user has to have sufficient rights on basket
@param bskid: basket id
@param recid: record id
"""
if __check_user_can_perform_action(uid,
bskid,
CFG_WEBBASKET_SHARE_LEVELS['DELITM']):
db.delete_item(bskid, recid)
def move_record(uid, bskid, recid, direction):
"""Move a record up or down in a basket (change score).
@param uid: user id (user has to have manage rights over this basket)
@param bskid: basket id
@param recid: record we want to move
@param direction: CFG_WEBBASKET_ACTIONS['UP'] or CFG_WEBBASKET_ACTIONS['DOWN']
"""
if __check_user_can_perform_action(uid,
bskid,
CFG_WEBBASKET_SHARE_LEVELS['MANAGE']):
db.move_item(bskid, recid, direction)
def perform_request_edit(uid, bskid, topic=0, new_name='',
new_topic = '', new_topic_name='',
groups=[], external='',
ln=CFG_SITE_LANG):
"""Interface for management of basket. If names, groups or external is
provided, will save new rights into database, else will provide interface.
@param uid: user id (user has to have sufficient rights on this basket
@param bskid: basket id to change rights on
@param topic: topic currently used (int)
@param new_name: new name of basket
@param new_topic: topic in which to move basket (int),
new_topic_name must be left blank
@param new_topic_name: new topic in which to move basket
(will overwrite param new_topic)
@param groups: list of strings formed in this way: group_id + '_' + rights
@param external: rights for everybody (can be 'NO')
@param ln: language
"""
body = ''
errors = []
warnings = []
rights = db.get_max_user_rights_on_basket(uid, bskid)
if rights != CFG_WEBBASKET_SHARE_LEVELS['MANAGE']:
errors.append(('ERR_WEBBASKET_NO_RIGHTS',))
return (body, errors, warnings)
bsk_name = db.get_basket_name(bskid)
if not(groups) and not(external) and not(new_name) and not(new_topic) and not(new_topic_name):
# display interface
topics = map(lambda x: x[0], db.get_personal_topics_infos(uid))
groups_rights = db.get_groups_subscribing_to_basket(bskid)
external_rights = ''
if groups_rights and groups_rights[0][0] == 0:
external_rights = groups_rights[0][2]
groups_rights = groups_rights[1:]
display_delete = db.check_user_owns_baskets(uid, bskid)
display_general = display_delete
if isGuestUser(uid):
display_sharing = 0
else:
display_sharing = 1
body = webbasket_templates.tmpl_edit(bskid=bskid, bsk_name=bsk_name,
display_general=display_general,
topics=topics, topic=topic,
display_delete=display_delete,
display_sharing=display_sharing,
groups_rights=groups_rights,
external_rights=external_rights,
ln=ln)
else:
out_groups = {}
if len(groups):
for group in groups:
(group_id, group_rights) = group.split('_')
out_groups[group_id] = group_rights
out_groups['0'] = external
if not(isGuestUser(uid)):
db.update_rights(bskid, out_groups)
if new_name != bsk_name:
db.rename_basket(bskid, new_name)
if new_topic_name:
db.move_baskets_to_topic(uid, bskid, new_topic_name)
elif new_topic != -1:
if db.check_user_owns_baskets(uid, bskid):
topics = map(lambda x: x[0], db.get_personal_topics_infos(uid))
try:
new_topic_name = topics[new_topic]
db.move_baskets_to_topic(uid, bskid, new_topic_name)
except:
errors.append(('ERR_WEBBASKET_DB_ERROR'))
else:
topic = 0
errors.append(('ERR_WEBBASKET_NOT_OWNER'))
return (body, errors, warnings)
def perform_request_add_group(uid, bskid, topic=0, group_id=0, ln=CFG_SITE_LANG):
"""If group id is specified, share basket bskid to this group with
READITM rights;
else return a page for selection of a group.
@param uid: user id (selection only of groups user is member of)
@param bskid: basket id
@param topic: topic currently displayed
@param group_id: id of group to share basket to
@param ln: language
"""
if group_id:
db.share_basket_with_group(bskid,
group_id,
CFG_WEBBASKET_SHARE_LEVELS['READITM'])
else:
groups = db.get_groups_user_member_of(uid)
body = webbasket_templates.tmpl_add_group(bskid, topic, groups, ln)
return body
def perform_request_create_basket(uid,
new_basket_name='',
new_topic_name='', create_in_topic=-1,
topic_number=-1,
ln=CFG_SITE_LANG):
"""if new_basket_name and topic infos are given create a basket and return topic number,
else return (body, errors, warnings) tuple of basket creation form.
@param uid: user id (int)
@param new_basket_name: name of the basket to create (str)
@param new_topic_name: name of new topic to create new basket in (str)
@param create_in_topic: identification number of topic to create new basket in (int)
@param topic_number: number of topic to preselect on the creation form.
@pram ln: language
"""
if new_basket_name and (new_topic_name or create_in_topic != -1):
topics_infos = map(lambda x: x[0], db.get_personal_topics_infos(uid))
new_topic_name = new_topic_name.strip()
if new_topic_name:
topic = new_topic_name
else:
try:
topic = topics_infos[create_in_topic]
except IndexError:
return 0
db.create_basket(uid, new_basket_name, topic)
topics = map(lambda x: x[0], topics_infos)
try:
return topics.index(topic)
except ValueError:
return 0
else:
topics = map(lambda x: x[0], db.get_personal_topics_infos(uid))
if topic_number in range (0, len(topics)):
create_in_topic = topics[topic_number]
body = webbasket_templates.tmpl_create_basket(new_basket_name,
new_topic_name,
create_in_topic,
topics,
ln)
return (body, [], [])
def perform_request_display_public(bskid=0, of='hb', ln=CFG_SITE_LANG):
"""return html representation of a public basket
@param bskid: basket id
@param of: format
@param ln: language"""
_ = gettext_set_language(ln)
body = ''
errors = []
warnings = []
basket = db.get_public_basket_infos(bskid)
if of[0] == 'x':
items = []
if len(basket) == 7:
content = db.get_basket_content(bskid)
for item in content:
items.append(format_record(item[0], of))
return webbasket_templates.tmpl_xml_basket(items)
if len(basket) == 7:
items = db.get_basket_content(bskid)
last_cmt = _("N/A")
records = []
cmt_dates = []
for (recid, nb_cmt, last_cmt, ext_val, int_val, score) in items:
cmt_dates.append(convert_datetext_to_datestruct(last_cmt))
last_cmt = convert_datetext_to_dategui(last_cmt, ln)
val = ''
if recid < 0:
if ext_val:
val = decompress(ext_val)
else:
if int_val:
val = format_record(recid, 'hb')
records.append((recid, nb_cmt, last_cmt, val, score))
body = webbasket_templates.tmpl_display_public(basket, records, ln)
else:
errors.append('ERR_WEBBASKET_RESTRICTED_ACCESS')
return (body, errors, warnings)
def perform_request_list_public_baskets(inf_limit=0, order=1, asc=1, ln=CFG_SITE_LANG):
"""Display list of public baskets.
@param inf_limit: display baskets from inf_limit
@param order: 1: order by name of basket, 2: number of views, 3: owner
@param asc: ascending order if 1, descending if 0
@param ln: language
"""
errors = []
warnings = []
total_baskets = db.count_public_baskets()
baskets = db.get_public_baskets_list(inf_limit, CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS, order, asc)
body = webbasket_templates.tmpl_display_list_public_baskets(baskets, inf_limit, total_baskets, order, asc, ln)
return (body, errors, warnings)
def perform_request_subscribe(uid, bskid):
"""subscribe to external basket bskid"""
errors = []
if db.is_public(bskid):
db.subscribe(uid, bskid)
else:
errors.append('ERR_WEBBASKET_RESTRICTED_ACCESS')
return errors
def perform_request_unsubscribe(uid, bskid):
"""unsubscribe from external basket bskid"""
db.unsubscribe(uid, bskid)
def __check_user_can_comment(uid, bskid):
""" Private function. check if a user can comment """
min_right = CFG_WEBBASKET_SHARE_LEVELS['ADDCMT']
rights = db.get_max_user_rights_on_basket(uid, bskid)
if rights:
if CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(rights) >= CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(min_right):
return 1
return 0
def __check_user_can_perform_action(uid, bskid, rights):
""" Private function, check if a user has sufficient rights"""
min_right = rights
rights = db.get_max_user_rights_on_basket(uid, bskid)
if rights:
if CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(rights) >= CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(min_right):
return 1
return 0
def __check_sufficient_rights(rights_user_has, rights_needed):
"""Private function, check if the rights are sufficient."""
try:
out = CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(rights_user_has) >= \
CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(rights_needed)
except ValueError:
out = 0
return out
def create_guest_warning_box(ln=CFG_SITE_LANG):
"""return a warning message about logging into system"""
return webbasket_templates.tmpl_create_guest_warning_box(ln)
def create_personal_baskets_selection_box(uid,
html_select_box_name='baskets',
selected_bskid=None,
ln=CFG_SITE_LANG):
"""Return HTML box for basket selection. Only for personal baskets.
@param uid: user id
@param html_select_box_name: name used in html form
@param selected_bskid: basket currently selected
@param ln: language
"""
baskets = db.get_all_personal_baskets_names(uid)
return webbasket_templates.tmpl_personal_baskets_selection_box(
baskets,
html_select_box_name,
selected_bskid,
ln)
def create_basket_navtrail(uid,
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
topic=0, group=0,
bskid=0, ln=CFG_SITE_LANG):
"""display navtrail for basket navigation.
@param uid: user id (int)
@param category: selected category (see CFG_WEBBASKET_CATEGORIES)
@param topic: selected topic # if personal baskets
@param group: selected group id for displaying (int)
@param bskid: basket id (int)
@param ln: language"""
_ = gettext_set_language(ln)
out = ''
if category == CFG_WEBBASKET_CATEGORIES['PRIVATE']:
out += ' &gt; <a class="navtrail" href="%s/yourbaskets/display?%s">'\
'%s</a>'
- out %= (weburl,
+ out %= (CFG_SITE_URL,
'category=' + category + '&amp;ln=' + ln,
_("Personal baskets"))
topics = map(lambda x: x[0], db.get_personal_topics_infos(uid))
if topic in range(0, len(topics)):
out += ' &gt; '
out += '<a class="navtrail" href="%s/yourbaskets/display?%s">'\
'%s</a>'
- out %= (weburl,
+ out %= (CFG_SITE_URL,
'category=' + category + '&amp;topic=' + \
str(topic) + '&amp;ln=' + ln,
cgi.escape(topics[topic]))
if bskid:
basket = db.get_public_basket_infos(bskid)
if basket:
out += ' &gt; '
out += '<a class="navtrail" href="%s/yourbaskets/display'\
'?%s">%s</a>'
- out %= (weburl,
+ out %= (CFG_SITE_URL,
'category=' + category + '&amp;topic=' + \
str(topic) + '&amp;ln=' + ln + '#bsk' + str(bskid),
cgi.escape(basket[1]))
elif category == CFG_WEBBASKET_CATEGORIES['GROUP']:
out += ' &gt; <a class="navtrail" href="%s/yourbaskets/display?%s">'\
'%s</a>'
- out %= (weburl, 'category=' + category + '&amp;ln=' + ln, _("Group baskets"))
+ out %= (CFG_SITE_URL, 'category=' + category + '&amp;ln=' + ln, _("Group baskets"))
groups = db.get_group_infos(uid)
if group:
groups = filter(lambda x: x[0] == group, groups)
if len(groups):
out += ' &gt; '
out += '<a class="navtrail" href="%s/yourbaskets/display?%s">%s</a>'
- out %= (weburl,
+ out %= (CFG_SITE_URL,
'category=' + category + '&amp;group=' + \
str(group) + '&amp;ln=' + ln,
cgi.escape(groups[0][1]))
if bskid:
basket = db.get_public_basket_infos(bskid)
if basket:
out += ' &gt; '
out += '<a class="navtrail" href="%s/yourbaskets/display?'\
'%s">%s</a>'
- out %= (weburl,
+ out %= (CFG_SITE_URL,
'category=' + category + '&amp;group=' + \
str(group) + '&amp;ln=' + ln + '#bsk' + str(bskid),
cgi.escape(basket[1]))
elif category == CFG_WEBBASKET_CATEGORIES['EXTERNAL']:
out += ' &gt; <a class="navtrail" href="%s/yourbaskets/display?%s">'\
'%s</a>'
- out %= (weburl,
+ out %= (CFG_SITE_URL,
'category=' + category + '&amp;ln=' + ln,
_("Others' baskets"))
if bskid:
basket = db.get_public_basket_infos(bskid)
if basket:
out += ' &gt; '
out += '<a class="navtrail" href="%s/yourbaskets/display?%s">'\
'%s</a>'
- out %= (weburl,
+ out %= (CFG_SITE_URL,
'category=' + category + '&amp;ln=' + ln + \
'#bsk' + str(bskid),
cgi.escape(basket[1]))
return out
def create_infobox(infos=[]):
"""Create an infos box. infos param should be a list of strings.
Return formatted infos"""
return webbasket_templates.tmpl_create_infobox(infos)
def account_list_baskets(uid, ln=CFG_SITE_LANG):
"""Display baskets informations on account page"""
_ = gettext_set_language(ln)
(personal, group, external) = db.count_baskets(uid)
link = '<a href="%s">%s</a>'
- base_url = weburl + '/yourbaskets/display?category=%s&amp;ln=' + ln
+ base_url = CFG_SITE_URL + '/yourbaskets/display?category=%s&amp;ln=' + ln
personal_text = personal
if personal:
url = base_url % CFG_WEBBASKET_CATEGORIES['PRIVATE']
personal_text = link % (url, personal_text)
group_text = group
if group:
url = base_url % CFG_WEBBASKET_CATEGORIES['GROUP']
group_text = link % (url, group_text)
external_text = external
if external:
url = base_url % CFG_WEBBASKET_CATEGORIES['EXTERNAL']
else:
- url = weburl + '/yourbaskets/list_public_baskets?ln=' + ln
+ url = CFG_SITE_URL + '/yourbaskets/list_public_baskets?ln=' + ln
external_text = link % (url, external_text)
out = _("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.") %\
{'x_nb_perso': personal_text,
'x_nb_group': group_text,
'x_nb_public': external_text}
return out
diff --git a/modules/webbasket/lib/webbasket_regression_tests.py b/modules/webbasket/lib/webbasket_regression_tests.py
index 253de039e..f5ec111bc 100644
--- a/modules/webbasket/lib/webbasket_regression_tests.py
+++ b/modules/webbasket/lib/webbasket_regression_tests.py
@@ -1,201 +1,201 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebBasket Regression Test Suite."""
__revision__ = "$Id$"
import unittest
import mechanize
import re
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, make_url, make_surl, merge_error_messages
class WebBasketWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebBasket web pages whether they are up or not."""
def test_your_baskets_pages_availability(self):
"""webbasket - availability of Your Baskets pages"""
- baseurl = weburl + '/yourbaskets/'
+ baseurl = CFG_SITE_URL + '/yourbaskets/'
_exports = ['', 'display', 'display_item', 'write_comment',
'save_comment', 'delete_comment', 'add', 'delete',
'modify', 'edit', 'create_basket', 'display_public',
'list_public_baskets', 'unsubscribe', 'subscribe']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
class WebBasketRecordsAdditionTest(unittest.TestCase):
"""Test addition of records to webbasket"""
def _login(self, browser, user, password):
"""Log the user in an existing browser using his password"""
browser.open(make_surl('/youraccount/login'))
browser.select_form(nr=0)
browser['p_un'] = user
browser['p_pw'] = password
browser.submit()
def _perform_search(self, browser, search_criteria):
"""Perform search in an existing browser using the specified criteria.
Calling the method is equal of typing the criteria in the search box
and pressing the 'search' button."""
# open the search page that in our case is the default page
browser.open(make_url('/'))
# perform search
browser.select_form(name = 'search')
browser['p'] = search_criteria
browser.submit(name = 'action_search')
def _select_records_for_adding_to_basket(self, browser, records):
"""Calling this method is is equal of selecting records from
the search results and pressing 'ADD TO BASKET' button.
browser - the browser object where the selection takes place.
It is supposed that the browser contains form with search results.
records - list of numbers (first record is 0) indicating
whisch records to be selected from the search results. """
# select the proper form containing the check boxes for marking the records
browser.select_form(nr = 2)
# select the records
control = browser.find_control('recid')
for current_record in records:
control.items[current_record].selected = True
# press 'ADD TO BASKET' button
browser.submit();
def _create_new_basket_and_add_records(self, browser, basket_name, topic_name):
"""creates a new basket. After submiting the form for basket creation
the records will be automaticaly added to the basket. """
browser.select_form(name = 'add_to_basket')
browser['new_basket_name'] = basket_name
browser['new_topic_name'] = topic_name
browser.submit()
def _delete_basket(self, browser):
"""deletes the first basket in the list of baskets on Display baskets page"""
# go to Display baskets page
browser.open(make_surl('/yourbaskets/display?ln=en'))
# click Edit basket link
browser.follow_link(text_regex=re.compile('.*Edit basket', re.I))
# click Delete basket button on the page
browser.select_form(name = 'edit')
browser.submit(name = 'delete')
# answer yes to the question "Are you sure..."
browser.select_form(name = 'validate')
browser.submit()
def _check_basket_content(self, browser, expected_texts):
"""goes to the baskets page and checks the content for a specified text.
expected_texts is a list of strings containing text that is we expect
to be shown on the page."""
browser.open(make_surl('/yourbaskets/display?ln=en'))
url_body = browser.response().read()
for current_expected_text in expected_texts:
if current_expected_text not in url_body:
self.fail('Expects to find ' + current_expected_text + ' in the basket')
def _add_records_into_new_basket(self, browser, basket_name, topic_name):
"""perform a search and add records into new basket"""
self._perform_search(browser, 'ellis')
self._select_records_for_adding_to_basket(browser, [0, 6])
self._create_new_basket_and_add_records(browser, basket_name, topic_name)
def _add_records_to_basket_and_check_content(self, browser):
"""add records to basket and check content of baskets page for
expexted strings """
self._add_records_into_new_basket(browser, basket_name = 'Test Basket', topic_name = 'Test Topic')
expected_texts = ['Test Topic', 'Test Basket', '2 records',
'Thermal conductivity of dense quark matter and cooling of stars',
'The total cross section for the production of heavy quarks in hadronic collisions']
self._check_basket_content(browser, expected_texts)
def test_records_addition_as_guest_user(self):
"""webbasket - addition of records as guest"""
browser = mechanize.Browser()
self._add_records_to_basket_and_check_content(browser)
def test_records_addition_as_registered_user(self):
"""webbasket - addition of records as registered user"""
browser = mechanize.Browser()
self._login(browser, 'jekyll', 'j123ekyll')
self._add_records_to_basket_and_check_content(browser)
self._delete_basket(browser)
def test_adding_records_into_new_basket_twice(self):
"""webbasket - test adding records in new basket after second addition """
browser = mechanize.Browser()
self._login(browser, 'jekyll', 'j123ekyll')
# add twice records into the same basket, creating the basket every time
self._add_records_into_new_basket(browser, basket_name = 'New Basket', topic_name = 'New Topic')
self._add_records_into_new_basket(browser, basket_name = 'New Basket2', topic_name = 'New Topic2')
url_body = browser.response().read()
error_message = "Error: Sorry, you don't have sufficient rights on this basket"
if(error_message in url_body):
self._delete_basket(browser)
self._delete_basket(browser)
self.fail('Does not expect to find message "' + error_message + ' after creating twice the same basket.')
self._delete_basket(browser)
self._delete_basket(browser)
test_suite = make_test_suite(WebBasketWebPagesAvailabilityTest, WebBasketRecordsAdditionTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/webbasket/lib/webbasket_templates.py b/modules/webbasket/lib/webbasket_templates.py
index af1f69b5e..508cb88d1 100644
--- a/modules/webbasket/lib/webbasket_templates.py
+++ b/modules/webbasket/lib/webbasket_templates.py
@@ -1,1562 +1,1562 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Templating for webbasket module """
__revision__ = "$Id$"
import cgi
from invenio.messages import gettext_set_language
from invenio.webbasket_config import \
CFG_WEBBASKET_CATEGORIES, \
CFG_WEBBASKET_SHARE_LEVELS, \
CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS
from invenio.webmessage_mailutils import email_quoted_txt2html, email_quote_txt
-from invenio.config import weburl, CFG_SITE_SECURE_URL, CFG_SITE_LANG
+from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_SITE_LANG
from invenio.webuser import get_user_info
from invenio.dateutils import convert_datetext_to_dategui
class Template:
"""Templating class for webbasket module"""
######################## General interface ################################
def tmpl_display(self,
topicsbox='',
baskets_infobox='',
baskets=[],
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
nb_groups=0,
nb_external_baskets=0,
ln=CFG_SITE_LANG):
"""Generic display. takes already formatted baskets (list of formatted
baskets), infobox and topicsbox, add tabs and returns complete
interface"""
_ = gettext_set_language(ln)
if type(baskets) not in (list, tuple):
baskets = [baskets]
tabs = self.__create_tabs(selected_category,
nb_groups, nb_external_baskets, ln)
out = """
<div id="bskcontainer">
<div id="bsktabs">%s
</div>
<div id="bskcontent">""" % tabs
if topicsbox:
out += topicsbox
out += """
<div id="bskbaskets">"""
if baskets_infobox:
out += """
<div id="bskinfos">%s
</div>""" % baskets_infobox
for basket in baskets:
out += basket
out += """
</div>
</div>
</div>"""
return out
def __create_tabs(self,
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
nb_groups=0,
nb_external_baskets=0,
ln=CFG_SITE_LANG):
"""Private function, display tabs
(private baskets, group baskets, others' basket)."""
_ = gettext_set_language(ln)
selected = ' id="bsktab_selected"'
private = CFG_WEBBASKET_CATEGORIES['PRIVATE']
group = CFG_WEBBASKET_CATEGORIES['GROUP']
external = CFG_WEBBASKET_CATEGORIES['EXTERNAL']
tab = """
<div class="bsktab"%(selected)s>
<img src="%(url)s/img/%(img)s" alt="%(label)s" />
<a href="%(url)s/yourbaskets/display?category=%(cat)s&amp;ln=%(ln)s">%(label)s</a>
</div>"""
out = tab % {'selected': selected_category == private and selected or '',
- 'url': weburl,
+ 'url': CFG_SITE_URL,
'img': 'webbasket_us.png',
'label': _("Personal baskets"),
'cat': private,
'ln': ln}
if nb_groups:
out += tab % {'selected': selected_category == group and selected or '',
- 'url': weburl,
+ 'url': CFG_SITE_URL,
'img': 'webbasket_ugs.png',
'label': _("Group baskets"),
'cat': group,
'ln': ln}
if nb_external_baskets:
out += tab % {'selected': selected_category == external and selected or '',
- 'url': weburl,
+ 'url': CFG_SITE_URL,
'img': 'webbasket_ws.png',
'label': _("Others' baskets"),
'cat': external,
'ln': ln}
return out
def tmpl_topic_selection(self,
topics_list=[],
selected_topic=0,
ln=CFG_SITE_LANG):
"""Display the topics selection area.
@param topics_list: list of (topic name, number of baskets) tuples
@param selected_topic: # of selected topic in topics_list"""
category = CFG_WEBBASKET_CATEGORIES['PRIVATE']
if len(topics_list):
if selected_topic not in range(0, len(topics_list) + 1):
selected_topic = 0
i = 0
out = ''
for (topic, number_of_baskets) in topics_list:
out += '<span class="bsktopic">'
topic_label = topic + ' (' + str(number_of_baskets) + ')'
if i != selected_topic:
topic_link = '%s/yourbaskets/display?category=%s&amp;'\
'topic=%i&amp;ln=%s'
- topic_link %= (weburl, category, i, ln)
+ topic_link %= (CFG_SITE_URL, category, i, ln)
out += '<a href="%s">' % topic_link
out += cgi.escape(topic_label) + '</a>'
else:
out += cgi.escape(topic_label)
out += '</span> '
i += 1
out = """
<table id="bsktopics">
<tr>
<td>%s
</td>
</tr>
</table>""" % out
else:
out = self.tmpl_create_basket(ln=ln)
return out
def tmpl_group_selection(self,
groups_list=[],
selected_group_id=0,
ln=CFG_SITE_LANG):
"""Display the group selection area which appears on the top of group
baskets category.
@param groups_list: list of (group id, group name, number of baskets)
@param selected_group id: id of group selected
"""
out = ''
category = CFG_WEBBASKET_CATEGORIES['GROUP']
if len(groups_list):
for (group_id, group_name, number_of_baskets) in groups_list:
out += '<span class="bsktopic">'
group_label = group_name + ' (' + str(number_of_baskets) + ')'
if group_id != selected_group_id:
group_link = '%s/yourbaskets/display?category=%s&amp;'\
'group=%i&amp;ln=%s'
- group_link %= (weburl, category, group_id, ln)
+ group_link %= (CFG_SITE_URL, category, group_id, ln)
out += '<a href="%s">' % group_link
out += group_label + '</a>'
else:
out += group_label
out += '</span>'
out = """
<table id="bsktopics">
<tr>
<td>%s
</td>
</tr>
</table>""" % out
return out
def tmpl_baskets_infobox(self, basket_infos=[], create_link='',
ln=CFG_SITE_LANG):
"""
displays infos about baskets.
@param basket_infos: list of (bskid, bsk_name, bsk_last_update)
@param create_link: link for the creation of basket
(will appear next to descriptions)
@param ln: language
@return html as string
"""
_ = gettext_set_language(ln)
label = _("There are %i baskets") % len(basket_infos)
basket_list = ''
if len(basket_infos):
basket_list += '<ul>\n'
for (bskid, name, last_update) in basket_infos:
last_update = convert_datetext_to_dategui(last_update)
basket_list += '<li><a href="#bsk%i">%s</a> - ' % \
(bskid, cgi.escape(name))
basket_list += _("updated on") + ' ' + last_update + '</li>\n'
basket_list += '</ul>'
if len(basket_infos) < 2:
label = ''
basket_list = ''
out = """
<table style="vertical-align: top;">
<tr>
<td style="vertical-align: top">%s</td>
<td>%s</td>
<td style="vertical-align: top; padding-left: 80px; text-align: right">%s</td>
</tr>
</table>""" % (label, basket_list, create_link)
return out
def tmpl_display_public(self,
basket_infos,
bsk_items=[],
ln=CFG_SITE_LANG):
"""
Display public basketwith link to subscribe to it.
@param basket_infos:
(bskid, bsk_name, bsk_date_modification, bsk_nb_views,
bsk_id_owner, bsk_owner_nickname, public rights on basket)
"""
_ = gettext_set_language(ln)
(bskid, bsk_name, bsk_date_modification, dummy,
bsk_id_owner, bsk_owner_nickname, dummy) = basket_infos
items_html = ''
if not(len(bsk_items)):
items_html = """
<tr>
<td colspan="3" style="text-align:center; height:100px">
%s
</td>
</tr>""" % _("Basket is empty")
for item in bsk_items:
items_html += self.__tmpl_basket_item(bskid=bskid,
item=item, ln=ln)
if bsk_owner_nickname:
display = bsk_owner_nickname
else:
(bsk_id_owner, bsk_owner_nickname, display) = get_user_info(\
bsk_id_owner)
messaging_link = self.__create_messaging_link(bsk_owner_nickname,
display, ln)
link_subscribe = '<a href="%s/yourbaskets/subscribe?'\
- 'bskid=%i&amp;ln=%s">' % (weburl, bskid, ln)
+ 'bskid=%i&amp;ln=%s">' % (CFG_SITE_URL, bskid, ln)
general_label = _("This basket belongs to %(x_name)s. You can freely "\
"%(x_url_open)ssubscribe%(x_url_close)s to it") % \
{'x_name': messaging_link,
'x_url_open': link_subscribe,
'x_url_close': '</a>'}
out = """
%(general_label)s
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td>
- <img src="%(weburl)s/img/webbasket_world.png" alt="%(image_label)s"/>
+ <img src="%(siteurl)s/img/webbasket_world.png" alt="%(image_label)s"/>
</td>
<td class="bsktitle">
<b>%(name)s</b></a>
%(nb_items)i %(records_label)s - %(last_update_label)s: %(last_update)s
</td>
<td class="bskcmtcol">
%(link_subscribe)s%(subscribe_label)s</a>
</td>
</tr>
</thead>
<tbody>
%(items)s
</tbody>
</table>""" % {'general_label': general_label,
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
'image_label': _("Public basket"),
'name': cgi.escape(bsk_name),
'nb_items': len(bsk_items),
'records_label': _("records"),
'last_update_label': _("last update"),
'last_update': convert_datetext_to_dategui(\
bsk_date_modification),
'bskid': bskid,
'ln': ln,
'link_subscribe': link_subscribe,
'subscribe_label': _("Subscribe to this basket"),
'items': items_html}
return out
def tmpl_display_list_public_baskets(self, baskets, inf_limit,
total_baskets, order, asc,
ln=CFG_SITE_LANG):
"""Display list of public baskets.
@param baskets: list of (bskid, name, nb_views,
owner_id, owner_nickname)
@param inf limit: inferior limit
@param total baskets: nb of baskets in total (>len(baskets) generally)
@param order: 1: order by name,
2: order by nb of views,
3: order by owner
@param asc: 1 for ascending, 0 for descending
"""
_ = gettext_set_language(ln)
name_label = _("Basket's name")
nb_views_label = _("Number of views")
owner_label = _("Owner")
- base_url = weburl + \
+ base_url = CFG_SITE_URL + \
'/yourbaskets/list_public_baskets?order=%i&amp;asc=%i&amp;ln=' +\
ln
- asc_image = '<img src="' + weburl + '/img/webbasket_' + \
+ asc_image = '<img src="' + CFG_SITE_URL + '/img/webbasket_' + \
(asc and 'down.png' or 'up.png') + \
'" style="vertical-align: middle; border: 0px;" alt="Reorder basket" />'
if order == 1:
name_label = '<a href="' + base_url % (1, int(not(asc))) + '">' + \
cgi.escape(name_label) + ' ' + asc_image + '</a>'
nb_views_label = '<a href="' + base_url % (2, 1) + '">' + \
nb_views_label + '</a>'
owner_label = '<a href="' + base_url % (3, 1) + '">' + \
owner_label + '</a>'
elif order == 2:
name_label = '<a href="' + base_url % (1, 1) + '">' + \
cgi.escape(name_label) + '</a>'
nb_views_label = '<a href="' + base_url % (2, int(not(asc))) + \
'">' + nb_views_label + ' ' + asc_image + '</a>'
owner_label = '<a href="' + base_url % (3, 1) + '">' + \
owner_label + '</a>'
else:
name_label = '<a href="' + base_url % (1, 1) + '">' + \
cgi.escape(name_label) + '</a>'
nb_views_label = '<a href="' + base_url % (2, 1) + '">' + \
nb_views_label + '</a>'
owner_label = '<a href="' + base_url % (3, int(not(asc))) + '">' + \
owner_label + ' ' + asc_image + '</a>'
baskets_html = ''
for (bskid, name, nb_views, owner_id, owner_nickname) in baskets:
if owner_nickname:
display = owner_nickname
else:
(owner_id, owner_nickname, display) = get_user_info(owner_id)
messaging_link = self.__create_messaging_link(owner_nickname,
display, ln)
form_view = """
-<form action="%(weburl)s/yourbaskets/display_public" method="GET">
+<form action="%(siteurl)s/yourbaskets/display_public" method="GET">
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="bskid" value="%(bskid)i" />
<input type="submit" value="%(display_public_label)s" class="formbutton" />
-</form>""" % {'weburl': weburl,
+</form>""" % {'siteurl': CFG_SITE_URL,
'ln': ln,
'bskid': bskid,
'display_public_label': _("View")}
form_subscribe = """
-<form action="%(weburl)s/yourbaskets/subscribe" method="GET">
+<form action="%(siteurl)s/yourbaskets/subscribe" method="GET">
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="bskid" value="%(bskid)i" />
<input type="submit" value="%(subscribe_label)s" class="formbutton"/>
-</form>""" % {'weburl': weburl,
+</form>""" % {'siteurl': CFG_SITE_URL,
'ln': ln,
'bskid': bskid,
'subscribe_label': _("Subscribe")}
baskets_html += """
<tr>
<td>%s</td>
<td style="text-align:center">%i</td>
<td>%s</td>
<td style="vertical-align: middle; text-align:center;">%s</td>
<td style="vertical-align: middle">%s</td>
</tr>""" % (cgi.escape(name), nb_views,
messaging_link, form_view, form_subscribe)
if not(len(baskets_html)):
baskets_html = '<tr><td colspan="5">' + \
_("There is currently no publicly accessible basket") + \
'</td></tr>'
- change_page = '<a href="' + weburl + '/yourbaskets/list_public_baskets?'\
+ change_page = '<a href="' + CFG_SITE_URL + '/yourbaskets/list_public_baskets?'\
'inf_limit=%i&amp;order=' + str(order)
change_page += '&amp;asc=' + str(asc) + '&amp;ln=' + str(ln) + '">'\
'<img src="%(public_basket)s" style="border: 0px;" alt="%(public_basket)s"/></a> ' % {'public_basket' : _("Public basket")}
footer = ''
if inf_limit > (CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS * 2) - 1:
- footer += change_page % (0, weburl + '/img/sb.gif')
+ footer += change_page % (0, CFG_SITE_URL + '/img/sb.gif')
if inf_limit > 0:
footer += change_page % (inf_limit - CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS,
- weburl + '/img/sp.gif')
+ CFG_SITE_URL + '/img/sp.gif')
footer += ' ' + _("Displaying baskets %(x_nb_begin)i-%(x_nb_end)i out of %(x_nb_total)i baskets in total.") %\
{'x_nb_begin': total_baskets!=0 and inf_limit+1 or 0,
'x_nb_end': inf_limit + len(baskets),
'x_nb_total': total_baskets}
footer += ' '
if inf_limit + len(baskets) < total_baskets:
footer += change_page % (inf_limit + CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS,
- weburl + '/img/sn.gif')
+ CFG_SITE_URL + '/img/sn.gif')
if inf_limit + len(baskets) < total_baskets - CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS:
footer += change_page % (total_baskets - CFG_WEBBASKET_MAX_NUMBER_OF_DISPLAYED_BASKETS,
- weburl + '/img/se.gif')
+ CFG_SITE_URL + '/img/se.gif')
out = """
<table>
<thead class="bskbasketheader">
<tr>
<td style="vertical-align:middle; padding: 0 20 0 20"><span class="bsktopic">%(name)s</span></td>
<td style="vertical-align:middle; padding: 0 20 0 20"><span class="bsktopic">%(nb_views)s</span></td>
<td style="vertical-align:middle; padding: 0 20 0 20"><span class="bsktopic">%(user)s</span></td>
<td colspan="2" style="vertical-align:middle; padding: 0 20 0 20"><span class="bsktopic" style="text-weight:normal;">%(actions)s</span></td>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="5" class="bskbasketfooter" style="text-align:center">%(footer)s</td>
</tr>
</tfoot>
<tbody>
%(baskets)s
</tbody>
</table>""" % {'name': name_label,
'nb_views': nb_views_label,
'user': owner_label,
'actions': _("Actions"),
'baskets': baskets_html,
'footer': footer}
return out
############################ Baskets ###################################
def tmpl_basket(self, bskid,
name,
date_modification,
nb_views,
nb_items, last_added,
(user_can_view_content, user_can_edit_basket,
user_can_view_comments, user_can_add_item, user_can_delete_item),
nb_comments, last_comment,
group_sharing_level,
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0, selected_group=0,
items=[],
ln=CFG_SITE_LANG):
"""
display a basket.
@param group_sharing_level: Indicate to which level a basket is shared
(None for nobody, 0 for everybody, any other positive int for group)
@param items: list of (record id, nb of comments, last comment (date), body to display, score (int)) tuples
"""
_ = gettext_set_language(ln)
items_html = ''
actions = '<table class="bskbasketheaderactions"><tr>'
if group_sharing_level is None:
group_img_name = 'webbasket_user.png'
group_alt = _("Non-shared basket")
elif group_sharing_level == 0:
group_img_name = 'webbasket_world.png'
group_alt = _("Shared basket")
else:
group_img_name = 'webbasket_usergroup.png'
group_alt = _("Group-shared basket")
- logo = "<img src=\"%s/img/%s\" alt=\"%s\" />" % (weburl, group_img_name, group_alt)
+ logo = "<img src=\"%s/img/%s\" alt=\"%s\" />" % (CFG_SITE_URL, group_img_name, group_alt)
if user_can_edit_basket:
- url = weburl + '/yourbaskets/edit?bskid=%i&amp;topic=%i&amp;ln=%s'
+ url = CFG_SITE_URL + '/yourbaskets/edit?bskid=%i&amp;topic=%i&amp;ln=%s'
url %= (bskid, selected_topic, ln)
logo = '<a href="%s">%s</a>' % (url, logo + '<br />' + _("Edit basket"))
actions += "<td>" + logo + "</td>"
actions += "</tr></table>"
if user_can_view_content:
if not(len(items)):
items_html = """
<tr>
<td colspan="3" style="text-align:center; height:100px">
%s
</td>
</tr>""" % _("Basket is empty")
for item in items:
copy = 1
go_up = go_down = delete = 0
if user_can_add_item:
go_up = go_down = 1
if item == items[0]:
go_up = 0
if item == items[-1]:
go_down = 0
if user_can_delete_item:
delete = 1
items_html += self.__tmpl_basket_item(
bskid=bskid, item=item,
uparrow=go_up,
downarrow=go_down,
copy_item=copy,
delete_item=delete,
view_comments=user_can_view_comments,
selected_category=selected_category,
selected_topic=selected_topic,
selected_group=selected_group,
ln=ln)
else:
items_html = """
<tr>
<td colspan="3" style="text-align:center; height:100px">
%s
</td>
</tr>""" % _("You do not have sufficient rights to view this basket's content.")
content = ''
if selected_category == CFG_WEBBASKET_CATEGORIES['EXTERNAL']:
- url = "%s/yourbaskets/unsubscribe?bskid=%i&amp;ln=%s" % (weburl, bskid, ln)
+ url = "%s/yourbaskets/unsubscribe?bskid=%i&amp;ln=%s" % (CFG_SITE_URL, bskid, ln)
action = "<a href=\"%s\">%s</a>"
action %= (url, _("Unsubscribe from this basket"))
content += action
footer = self.tmpl_basket_footer(bskid,
selected_category,
selected_topic,
selected_group,
group_sharing_level,
content,
ln)
comments_field = ''
if nb_comments:
comments_field = """
%i %s<br/>
%s %s""" % (nb_comments, _("comments"), _("last comment:"), last_comment)
out = """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td>%(actions)s</td>
<td class="bsktitle">
<a name="bsk%(bskid)i"><b>%(name)s</b></a><br />
%(nb_items)i %(records_label)s - %(last_update_label)s: %(last_update)s
</td>
<td class="bskcmtcol">
%(comments_field)s
</td>
</tr>
</thead>
<tbody>
%(items)s
%(footer)s
</tbody>
</table>"""
out %= {'actions': actions,
'name': cgi.escape(name),
'bskid': bskid,
'nb_items': nb_items,
'records_label': _('records'),
'last_update_label': _("last update"),
'last_update': date_modification,
'comments_field': user_can_view_comments and comments_field or '',
'items': items_html,
'footer': footer}
return out
def __tmpl_basket_item(self,
bskid,
item,
uparrow=0, downarrow=0, copy_item=0, delete_item=0,
view_comments=0,
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0, selected_group=0,
ln=CFG_SITE_LANG):
"""
display a row in a basket (row is item description and actions).
@param bskid: basket id (int)
@param item: (record id, nb of comments, last comment date,
body to display, score) tuple
@param uparrow,
downarrow,
copy_item,
delete_item,
view_comments: actions. set to 1 to display these actions.
"""
_ = gettext_set_language(ln)
(recid, nb_cmt, last_cmt, val, score) = item
actions = ''
if uparrow:
url = "%s/yourbaskets/modify?action=moveup&amp;bskid=%i&amp;recid=%i" % \
- (weburl, bskid, recid)
+ (CFG_SITE_URL, bskid, recid)
url += "&amp;category=%s&amp;topic=%i&amp;group=%i&amp;ln=%s" % \
(selected_category, selected_topic, selected_group, ln)
- img = "%s/img/webbasket_up.png" % weburl
+ img = "%s/img/webbasket_up.png" % CFG_SITE_URL
actions += '<a href="%s"><img src="%s" alt="%s" /></a>' % \
(url, img, _("Move item up"))
if downarrow:
url = "%s/yourbaskets/modify?action=movedown&amp;bskid=%i&amp;recid=%i" % \
- (weburl, bskid, recid)
+ (CFG_SITE_URL, bskid, recid)
url += "&amp;category=%s&amp;topic=%i&amp;group=%i&amp;ln=%s" % \
(selected_category, selected_topic, selected_group, ln)
- img = "%s/img/webbasket_down.png" % weburl
+ img = "%s/img/webbasket_down.png" % CFG_SITE_URL
actions += '<a href="%s"><img src="%s" alt="%s" /></a>' % \
(url, img, _("Move item down"))
if copy_item:
url = "%s/yourbaskets/modify?action=copy&amp;bskid=%i&amp;recid=%i" % \
- (weburl, bskid, recid)
+ (CFG_SITE_URL, bskid, recid)
url += "&amp;category=%s&amp;topic=%i&amp;group_id=%i&amp;ln=%s" % \
(selected_category, selected_topic, selected_group, ln)
- img = "%s/img/webbasket_move.png" % weburl
+ img = "%s/img/webbasket_move.png" % CFG_SITE_URL
actions += '<a href="%s"><img src="%s" alt="%s" /></a>' % \
(url, img, _("Copy item"))
if delete_item:
url = "%s/yourbaskets/modify?action=delete&amp;bskid=%i&amp;recid=%i" % \
- (weburl, bskid, recid)
+ (CFG_SITE_URL, bskid, recid)
url += "&amp;category=%s&amp;topic=%i&amp;group=%i&amp;ln=%s" % \
(selected_category, selected_topic, selected_group, ln)
- img = "%s/img/webbasket_delete.png" % weburl
+ img = "%s/img/webbasket_delete.png" % CFG_SITE_URL
actions += "<a href=\"%s\"><img src=\"%s\" alt=\"%s\" /></a>" % \
(url, img, _("Remove item"))
if recid < 0:
actions += '<img src="%s/img/webbasket_extern.png" alt="%s" />' % \
- (weburl, _("External record"))
+ (CFG_SITE_URL, _("External record"))
# Uncomment when external records are available
#else:
# pass
# #actions += "<img src=\"%s/img/webbasket_intern.png\" alt=\"%s\" />"
- # #actions = actions % (weburl, _("Internal record"))
+ # #actions = actions % (CFG_SITE_URL, _("Internal record"))
out = """
<tr>
<td class="bskactions">%(actions)s</td>
<td class="bskcontentcol" colspan="2">
%(content)s
<hr />"""
if view_comments:
if nb_cmt > 0:
out += """
%(nb_cmts)i %(cmts_label)s; %(last_cmt_label)s: %(last_cmt)s<br />
"""
- out += '\n<a href="%(weburl)s/yourbaskets/display_item?'\
+ out += '\n<a href="%(siteurl)s/yourbaskets/display_item?'\
'bskid=%(bskid)s&amp;recid=%(recid)i&amp;'\
'category=%(category)s&amp;group=%(group)i&amp;'\
'topic=%(topic)i&amp;ln=%(ln)s">%(detailed_label)s</a>'
out += """
</td>
</tr>"""
out = out % {'actions': actions,
'content': val,
'nb_cmts': nb_cmt,
'last_cmt': last_cmt,
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
'bskid': bskid,
'recid': recid,
'cmts_label': _("comments"),
'last_cmt_label': _("last"),
'detailed_label': _("Details and comments"),
'category': selected_category,
'topic': selected_topic,
'group': selected_group,
'ln': ln}
return out
def tmpl_basket_footer(self,
bskid,
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0,
selected_group=0,
group_sharing_level=None,
content='',
ln=CFG_SITE_LANG):
"""display footer of a basket.
@param group sharing level: None: basket is not shared,
0: basket is publcly accessible,
any positive int: basket is shared to groups"""
_ = gettext_set_language(ln)
public_infos = ''
if group_sharing_level == 0:
- public_url = weburl + '/yourbaskets/display_public?bskid=' + str(bskid)
+ public_url = CFG_SITE_URL + '/yourbaskets/display_public?bskid=' + str(bskid)
public_link = '<a href="%s">%s</a>' % (public_url, public_url)
public_infos = _("This basket is publicly accessible at the following address:") + ' ' + public_link
if content:
content += '<br />'
if not(content) and not(public_infos):
content += '<br />'
out = """
<tr>
<td colspan="3" class="bskbasketfooter">
<!-- Commented. Ready for next release
- <form name="bsk_sort_%(bskid)i" action="%(weburl)s/yourbaskets/display" method="GET">
+ <form name="bsk_sort_%(bskid)i" action="%(siteurl)s/yourbaskets/display" method="GET">
<input type="hidden" name="bsk_to_sort" value="%(bskid)i" />
<input type="hidden" name="category" value="%(category)s" />
<input type="hidden" name="topic" value="%(topic)i" />
<input type="hidden" name="group" value="%(group_id)i" />
<input type="hidden" name="ln" value="%(ln)s" />
%(sort_label)s
<input type="submit" name="sort_by_title" value="%(title_label)s" class="nonsubmitbutton" />
<input type="submit" name="sort_by_date" value="%(date_label)s" class="nonsubmitbutton" />
</form>-->
%(content)s
%(public_infos)s
</td>
</tr>
"""
- out %= {'weburl': weburl,
+ out %= {'siteurl': CFG_SITE_URL,
'bskid': int(bskid),
'category': selected_category,
'topic': int(selected_topic),
'group_id': int(selected_group),
'ln': ln,
'sort_label': _("Sort by:"),
'title_label': _("Title"),
'date_label': _("Date"),
'content': content,
'public_infos': public_infos
}
return out
######################## Display of items and commenting ###################
def tmpl_item(self,
basket_infos,
recid, record, comments,
group_sharing_level, rights_on_item,
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0, selected_group_id=0, ln=CFG_SITE_LANG):
"""display a specific item inside a basket.
@param basket_infos: (bskid, bsk_name, bsk_date_modification,
nb_views, bsk_nb_records, id_owner)
as returned from db_layer: get_basket_general_infos
@param group sharing level: None: basket is not shared,
0: basket is publcly accessible,
any positive int: basket is shared to groups
@param rights_on_item: tuple of booleans expressing capabilities :
(view comments, add comment, delete comments)
@param comments: list of comments as string
"""
_ = gettext_set_language(ln)
(bskid, bsk_name, bsk_date_modification,
dummy, bsk_nb_records, dummy) = basket_infos
(user_can_view_comments,
user_can_add_comment,
user_can_delete_comment) = rights_on_item
total_comments = len(comments)
- action = weburl + '/yourbaskets/write_comment?bskid=%i&amp;recid=%i'
+ action = CFG_SITE_URL + '/yourbaskets/write_comment?bskid=%i&amp;recid=%i'
action += '&amp;category=%s&amp;topic=%i&amp;group=%i&amp;ln=%s'
action %= (bskid, recid, selected_category, selected_topic, selected_group_id, ln)
- back_url = weburl + '/yourbaskets/display?category=%s&amp;topic=%i&amp;group=%i'
+ back_url = CFG_SITE_URL + '/yourbaskets/display?category=%s&amp;topic=%i&amp;group=%i'
back_url %= (selected_category, selected_topic, selected_group_id)
def list_to_str(elt1, elt2):
"""return elt1 <br /> elt2"""
return elt1 + "<br />\n" + elt2
if comments and user_can_view_comments:
comments = [self.__tmpl_display_comment(bskid, recid, comment,
(user_can_add_comment, user_can_delete_comment),
selected_category, selected_topic, selected_group_id,
ln)
for comment in comments]
comments = reduce(list_to_str, comments)
else:
comments = ''
record_text = ''
if record:
record_text = record[-1]
body = """
%(record)s
<hr /><br />"""
if user_can_view_comments:
body += """
<h2>%(comments_label)s</h2>
%(total_label)s<br />"""
if user_can_add_comment:
body += """
<form name="write_comment" method="post" action="%(action)s">
<input type="submit" value="%(button_label)s" style="margin: 10px;" class="formbutton" />
</form>"""
if user_can_view_comments:
body += """
<br />
%(comments)s"""
body %= {'record': record_text,
'comments_label': _("Comments"),
'total_label': _("There is a total of %i comments") % total_comments,
'action': action,
'button_label': _("Write a comment"),
'comments': comments}
if group_sharing_level is None:
- img = '<img src="%s/img/webbasket_user.png" alt="%s" />' % (weburl, _("Non-shared basket"))
+ img = '<img src="%s/img/webbasket_user.png" alt="%s" />' % (CFG_SITE_URL, _("Non-shared basket"))
elif group_sharing_level == 0:
- img = '<img src="%s/img/webbasket_world.png" alt="%s" />' % (weburl, _("Shared basket"))
+ img = '<img src="%s/img/webbasket_world.png" alt="%s" />' % (CFG_SITE_URL, _("Shared basket"))
else:
- img = '<img src="%s/img/webbasket_usergroup.png" alt="%s" />' % (weburl, _("Group-shared basket"))
+ img = '<img src="%s/img/webbasket_usergroup.png" alt="%s" />' % (CFG_SITE_URL, _("Group-shared basket"))
content = ''
if selected_category == CFG_WEBBASKET_CATEGORIES['EXTERNAL']:
- url = "%s/yourbaskets/unsubscribe?bskid=%i&amp;ln=%s" % (weburl, bskid, ln)
+ url = "%s/yourbaskets/unsubscribe?bskid=%i&amp;ln=%s" % (CFG_SITE_URL, bskid, ln)
action = "<a href=\"%s\">%s</a>"
action %= (url, _("Unsubscribe from this basket"))
content += action
footer = self.tmpl_basket_footer(bskid,
selected_category,
selected_topic,
selected_group_id,
group_sharing_level,
content,
ln)
out = """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td>%(img)s</td>
<td class="bsktitle" style="height: 65px">
- <b><a href="%(weburl)s/yourbaskets/display?bskid=%(bskid)s&amp;category=%(category)s&amp;topic=%(topic)i&amp;group=%(group)i&amp;ln=%(ln)s">%(name)s</a></b><br />
+ <b><a href="%(siteurl)s/yourbaskets/display?bskid=%(bskid)s&amp;category=%(category)s&amp;topic=%(topic)i&amp;group=%(group)i&amp;ln=%(ln)s">%(name)s</a></b><br />
%(nb_items)i %(records_label)s - %(last_update_label)s: %(last_update)s
</td>
<td class="bskcmtcol"></td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="3" style="padding: 5px;">
%(body)s
</td>
</tr>
%(footer)s
</tbody>
</table>""" % {'img': img,
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
'bskid': bskid,
'category': selected_category,
'topic': selected_topic,
'group': selected_group_id,
'ln': ln,
'name': cgi.escape(bsk_name),
'nb_items': bsk_nb_records,
'records_label': _("records"),
'last_update_label': _("last update"),
'last_update': convert_datetext_to_dategui(bsk_date_modification),
'body': body,
'footer': footer}
out += """
<a href="%s">%s</a>""" % (back_url, _("Back to baskets"))
return out
def __tmpl_display_comment(self, bskid, recid,
(cmt_uid, cmt_nickname, cmt_title, cmt_body, cmt_date, cmt_priority, cmtid),
(user_can_add_comment, user_can_delete_comment),
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0, selected_group_id=0, ln=CFG_SITE_LANG):
"""Display a given comment. """
_ = gettext_set_language(ln)
out = """
<div class="bskcomment">
<b>%(title)s</b>, %(label_author)s <a href="%(url)s/yourmessages/write?msg_to=%(user)s">%(user_display)s</a> %(label_date)s <i>%(date)s</i><br/><br/>
%(body)s
<br />"""
if user_can_add_comment:
out += '\n<a href="%(url)s/yourbaskets/write_comment?bskid=%(bskid)i'\
'&amp;recid=%(recid)i&amp;cmtid=%(cmtid)i&amp;'\
'category=%(category)s&amp;topic=%(topic)i&amp;'\
'group=%(group_id)i&amp;ln=%(ln)s">%(reply_label)s</a>'
if user_can_delete_comment:
out += '\n| <a href="%(url)s/yourbaskets/delete_comment?'\
'bskid=%(bskid)i&amp;recid=%(recid)i&amp;cmtid=%(cmtid)i'\
'&amp;category=%(category)s&amp;topic=%(topic)i&amp;'\
'group=%(group_id)i&amp;ln=%(ln)s">%(delete_label)s</a>'
out += """
</div>"""
out %= {'title': cmt_title,
- 'url': weburl,
+ 'url': CFG_SITE_URL,
'label_author': _("by"),
'label_date': _("on"),
'user': cmt_nickname or cmt_uid,
'user_display': cmt_nickname or get_user_info(cmt_uid)[2],
'date': convert_datetext_to_dategui(cmt_date),
'body': email_quoted_txt2html(cmt_body),
'bskid': bskid,
'recid': recid,
'cmtid': cmtid,
'category': selected_category,
'topic': selected_topic,
'group_id': selected_group_id,
'ln': ln,
'reply_label': _("Reply"),
'delete_label': _("Delete comment")}
return out
def tmpl_quote_comment(self, title, uid, nickname, date, body, ln=CFG_SITE_LANG):
"""Return a comment in a quoted form (i.e. with '>' signs before each line)
@param title: title of comment to quote
@param uid: user id of user who posted comment to quote
@param nickname: nickname of user who posted comment to quote
@param date: date of post of comment to quote
@param body: body of comment to quote
@param ln: language"""
_ = gettext_set_language(ln)
if not(nickname):
nickname = get_user_info(uid)[2]
out = title + ', ' + _("by") + ' ' + nickname + ' ' + _("on") + ' ' + date + '\n' + body
return email_quote_txt(out)
def tmpl_write_comment(self, bskid, recid,
record,
cmt_body='',
selected_category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0, selected_group_id=0,
ln=CFG_SITE_LANG,
warnings=[]):
"""Display interface to write a comment.
@param bskid: basket id (int)
@param recid: record id (int)
@param record: text of the record (str)
@param selected_category: CFG_WEBBASKET_CATEGORIES
@param selected_topic: # of topic
@param selected_group_id: in case of category: group, id of selected group
@param ln: language
@param warnings: list of warnings"""
_ = gettext_set_language(ln)
- action = '%s/yourbaskets/save_comment?bskid=%i&amp;recid=%i' % (weburl, bskid, recid)
+ action = '%s/yourbaskets/save_comment?bskid=%i&amp;recid=%i' % (CFG_SITE_URL, bskid, recid)
action += '&amp;category=%s&amp;topic=%i&amp;group=%i&amp;ln=%s' % ( selected_category, selected_topic, selected_group_id, ln)
if warnings:
warnings_box = self.tmpl_warnings(warnings, ln)
else:
warnings_box = ''
out = """
<div style="width:100%%">%(warnings)s
%(record)s
<hr />
<h2>%(write_label)s</h2>
<form name="write_comment" method="post" action="%(action)s">
<p class="bsklabel">%(title_label)s:</p>
<input type="text" name="title" size="80" />
<p class="bsklabel">%(comment_label)s:</p>
<textarea name="text" rows="20" cols="80">%(cmt_body)s</textarea><br />
<input type="submit" class="formbutton" value="%(button_label)s" />
</form>
</div>""" % {'warnings': warnings_box,
'record': record and record[-1] or '',
'write_label': _("Add Comment"),
'title_label': _("Title"),
'comment_label': _("Comment"),
'action': action,
'cmt_body': cmt_body,
'button_label': _("Add Comment")
}
return out
############################ Basket creation ###################################
def tmpl_create_basket_link(self, selected_topic=0, ln=CFG_SITE_LANG):
""" Create link to basket creation """
_ = gettext_set_language(ln)
- url = weburl + '/yourbaskets/create_basket?topic_number=%i&amp;ln=%s'
+ url = CFG_SITE_URL + '/yourbaskets/create_basket?topic_number=%i&amp;ln=%s'
url %= (selected_topic, ln)
- image = '<img src="%s/img/webbasket_create_small.png" style="vertical-align: middle; margin-right: 5px" alt="Create basket"/>' % weburl
+ image = '<img src="%s/img/webbasket_create_small.png" style="vertical-align: middle; margin-right: 5px" alt="Create basket"/>' % CFG_SITE_URL
out = """
<div class="bsk_create_link">
<a href="%s">%s%s</a>
</div>""" % (url, image, _("Create new basket"))
return out
def __tmpl_basket_box(self, img='', title='&nbsp;', subtitle='&nbsp;', body=''):
""" private function, display a basket/topic selection box """
out = """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>%(label)s</b><br />
%(count)s
</td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2">
<table>%(basket_list)s
</table>
</td>
</tr>
</tbody>
</table>"""
out %= {'logo': img,
'label': title, 'count': subtitle,
'basket_list': body}
return out
def tmpl_create_box(self, new_basket_name='', new_topic_name='',
topics=[], selected_topic=None,
ln=CFG_SITE_LANG):
"""Display a HTML box for creation of a new basket
@param new_basket_name: prefilled value (string)
@param new_topic_name: prefilled value (string)
@param topics: list of topics (list of strings)
@param selected_topic: preselected value for topic selection
@param ln: language"""
_ = gettext_set_language(ln)
topics_html = ''
if selected_topic:
try:
selected_topic = topics.index(selected_topic)
except:
selected_topic = None
if len(topics):
topics = zip(range(len(topics)), topics)
topics.insert(0, (-1, _("Select topic")))
topics_html = self.__create_select_menu('create_in_topic', topics, selected_topic)
create_html = """
<tr>
<td style="padding: 10 5 0 5;">%s</td>
<td style="padding: 10 5 0 0;">
<input type="text" name="new_basket_name" value="%s"/>
</td>
</tr>
<tr>
<td style="padding: 10 5 0 5;">%s</td>
<td style="padding: 10 5 0 0;">%s</td>
</tr>
<tr>
<td style="padding: 10 5 0 5;">%s</td>
<td style="padding: 10 5 0 0;"><input type="text" name="new_topic_name" value="%s"/></td>
</tr>""" % (_("Basket's name"), new_basket_name,
topics_html != '' and _("Choose topic") or '', topics_html,
topics_html != '' and _("or create a new one") or _("Create new topic"), new_topic_name)
- return self.__tmpl_basket_box(img=weburl + '/img/webbasket_create.png',
+ return self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_create.png',
title=_("Create a new basket"),
body=create_html)
def tmpl_create_basket(self, new_basket_name='',
new_topic_name='', create_in_topic=None, topics=[],
ln=CFG_SITE_LANG):
"""Template for basket creation
@param new_basket_name: prefilled value (string)
@param new_topic_name: prefilled value (string)
@param topics: list of topics (list of strings)
@param create_in_topic: preselected value for topic selection
@param ln: language"""
_ = gettext_set_language(ln)
out = """
<form name="create_basket" action="%(action)s" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<div style="padding:10px;">
%(create_box)s
<input type="submit" value="%(label)s" class="formbutton"/>
</div>
-</form>""" % {'action': weburl + '/yourbaskets/create_basket',
+</form>""" % {'action': CFG_SITE_URL + '/yourbaskets/create_basket',
'ln': ln,
'create_box': self.tmpl_create_box(new_basket_name=new_basket_name,
new_topic_name=new_topic_name,
topics=topics,
selected_topic=create_in_topic,
ln=ln),
'label': _("Create new basket")}
return out
########################## functions on baskets #########################
def tmpl_add(self, recids,
personal_baskets,
group_baskets,
external_baskets,
topics,
referer, ln=CFG_SITE_LANG):
""" returns HTML for the basket selection form when adding new records
@param recids: list of record ids
@param personal_baskets: list of (basket id, basket name, topic) tuples
@param group_baskets: list of (bskid, bsk_name, group_name) tuples
@param external_baskets: list of (bskid, bsk_name) tuples
@param topics: list of all the topics the user owns
@param referer: url from where this page has been reached
@param ln: language"""
_ = gettext_set_language(ln)
personal = ''
group = ''
external = ''
if personal_baskets:
topic_names = {}
map(topic_names.setdefault, [row[2] for row in personal_baskets])
topic_names = topic_names.keys()
topic_names.sort()
personal_html = ''
for topic_name in topic_names:
baskets = map(lambda x: (x[0], x[1]),
filter(lambda x: x[2]==topic_name,
personal_baskets))
baskets.insert(0, (-1, _("Select basket")))
personal_html += """<tr>
<td>%s</td>
<td>%s</td>
</tr>"""
personal_html %= (topic_name,
self.__create_select_menu('bskids', baskets))
- personal = self.__tmpl_basket_box(weburl + '/img/webbasket_user.png',
+ personal = self.__tmpl_basket_box(CFG_SITE_URL + '/img/webbasket_user.png',
_("Add to a personal basket"),
_("%i baskets") % len(personal_baskets),
personal_html)
if group_baskets:
group_names = {}
map(group_names.setdefault, [row[2] for row in group_baskets])
group_names = group_names.keys()
group_names.sort()
groups_html = ''
for group_name in group_names:
baskets = map(lambda x: (x[0], x[1]),
filter(lambda x: x[2]==group_name,
group_baskets))
baskets.insert(0, (-1, _("Select basket")))
groups_html += """<tr>
<td>%s</td>
<td>%s</td>
</tr>"""
groups_html %= (group_name,
self.__create_select_menu('bskids', baskets))
- group = self.__tmpl_basket_box(weburl + '/img/webbasket_usergroup.png',
+ group = self.__tmpl_basket_box(CFG_SITE_URL + '/img/webbasket_usergroup.png',
_("Add to a group-shared basket"),
_("%i baskets") % len(group_baskets),
groups_html)
if external_baskets:
external_html = """
<tr>
<td>
<select name="bskids">
<option value="-1">%s</option>""" % _("Select basket")
for basket in external_baskets:
value = int(basket[0])
label = basket[1]
external_html += '<option value="%i">%s</option>'% (value, label)
external_html += """
</select>
</td>
</tr>"""
- external = self.__tmpl_basket_box(weburl + '/img/webbasket_world.png',
+ external = self.__tmpl_basket_box(CFG_SITE_URL + '/img/webbasket_world.png',
_("Add to a public basket"),
_("%i baskets") % len(external_baskets),
external_html)
create = self.tmpl_create_box(topics=topics, ln=ln)
out_hidden_recids = ""
for recid in recids:
out_hidden_recids += """<input type="hidden" name="recid" value="%s" />""" % recid
fields = filter(lambda x: x != '', [personal, group, external, create])
while (len(fields) != 4):
fields.append('')
out = """
<form name="add_to_basket" action="%(action)s" method="post">
<p>%(label)s:</p>
<input type="hidden" name="referer" value="%(referer)s" />
%(out_hidden_recids)s
<table style="width:100%%;">
<tr>
<td style="width:50%%;vertical-align:top;">%(field1)s</td>
<td style="width: 50%%;vertical-align:top;">%(field2)s</td>
</tr>
<tr>
<td style="vertical-align:top;">%(field3)s</td>
<td style="vertical-align:top;">%(field4)s</td>
</tr>
<tr>
<td colspan="2">
<input name="submit" type="submit" class="formbutton" value="%(submit_label)s" />
</td>
</tr>
</table>
-</form>""" % {'action': weburl + '/yourbaskets/add?ln=' + ln,
+</form>""" % {'action': CFG_SITE_URL + '/yourbaskets/add?ln=' + ln,
'referer': referer,
'out_hidden_recids': out_hidden_recids,
'label': _("Adding %i records to these baskets") % len(recids),
'field1': fields[0],
'field2': fields[1],
'field3': fields[2],
'field4': fields[3],
'submit_label': _("Add to baskets")}
return out
def tmpl_added_to_basket(self, nb_baskets_modified=0, ln=CFG_SITE_LANG):
"""Display message for addition of records to baskets"""
_ = gettext_set_language(ln)
if nb_baskets_modified:
out = _("The selected records have been successfully added to %i baskets.")
out %= nb_baskets_modified
else:
out = _("No records were added to the selected baskets.")
return '<p>' + out + '</p>'
def tmpl_confirm_delete(self, bskid,
(nb_users, nb_groups, nb_alerts),
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
selected_topic=0, selected_group_id=0,
ln=CFG_SITE_LANG):
"""
display a confirm message
@param bskid: basket id
@param nb*: nb of users/groups/alerts linked to this basket
@param category: private, group or external baskets are selected
@param selected_topic: if private baskets, topic nb
@param selected_group_id: if group: group to display baskets of
@param ln: language
@return html output
"""
_ = gettext_set_language(ln)
message = _("Are you sure you want to delete this basket?")
if nb_users:
message += '<p>' + _("%i users are subscribed to this basket.")% nb_users + '</p>'
if nb_groups:
message += '<p>' + _("%i user groups are subscribed to this basket.")% nb_groups + '</p>'
if nb_alerts:
message += '<p>' + _("You have set %i alerts on this basket.")% nb_alerts + '</p>'
out = """
<table class="confirmoperation">
<tr>
<td colspan="2" class="confirmmessage">
%(message)s
</td>
</tr>
<tr>
<td>
<form name="validate" action="%(url_ok)s" method="post">
<input type="hidden" name="confirmed" value="1" />
<input type="hidden" name="category" value="%(category)s" />
<input type="hidden" name="group" value="%(group)i" />
<input type="hidden" name="topic" value="%(topic)i" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="bskid" value="%(bskid)i" />
<input type="submit" value="%(yes_label)s" class="formbutton" />
</form>
</td>
<td>
<form name="cancel" action="%(url_cancel)s" method="get">
<input type="hidden" name="category" value="%(category)s" />
<input type="hidden" name="group" value="%(group)i" />
<input type="hidden" name="topic" value="%(topic)i" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="submit" value="%(no_label)s" class="formbutton" />
</form>
</td>
</tr>
</table>"""% {'message': message,
'bskid': bskid,
'url_ok': 'delete',
'url_cancel': 'display',
'category': category,
'topic': selected_topic,
'group': selected_group_id,
'ln':ln,
'yes_label': _("Yes"),
'no_label': _("Cancel")}
return out
def tmpl_edit(self, bskid, bsk_name, topic, topics, groups_rights, external_rights,
display_general=0, display_sharing=0, display_delete=0, ln=CFG_SITE_LANG):
"""Display interface for rights management over the given basket
@param group_rights: list of (group id, name, rights) tuples
@param external_rights: rights as defined in CFG_WEBBASKET_SHARE_LEVELS for public access.
@param display_general: display fields name and topic, used with personal baskets
@param display_sharing: display sharing possibilities
@param display_delete: display delete basket button
"""
_ = gettext_set_language(ln)
general_body = ''
if display_general:
general_body = """
<tr>
<td class="bskcontentcol">%s</td>
<td class="bskcontentcol"><input type="text" name="new_name" value="%s"/></td>
</tr>""" % (_("Basket's name"), cgi.escape(bsk_name,1))
topics_selection = zip(range(len(topics)), topics)
topics_selection.insert(0, (-1, _("Choose topic")))
topics_body = """
<tr>
<td style="padding: 10 5 0 5;">%s</td>
<td style="padding: 10 5 0 0;">%s</td>
</tr>
<tr>
<td style="padding: 0 5 10 5;">%s</td>
<td style="padding: 0 5 10 0;"><input type="text" name="new_topic_name" />
</tr>""" % (_("Choose topic"),
self.__create_select_menu('new_topic', topics_selection, topic),
_("or create a new one"))
general_body += topics_body
- general_box = self.__tmpl_basket_box(img=weburl + '/img/webbasket_user.png',
+ general_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_user.png',
title=_("General settings"),
body = general_body)
groups_body = ''
if display_sharing:
for (group_id, name, rights) in groups_rights:
groups_body += """
<tr>
<td>%s</td>
<td>%s</td>
</tr>""" % (name, self.__create_group_rights_selection_menu(group_id, rights, ln))
groups_body += """
<tr>
<td colspan="2">
<input type="submit" name="add_group" class="nonsubmitbutton" value="%s"/>
</td>
</tr>""" % _("Add group")
else:
groups_body = '<tr><td colspan="2">%s</td></tr>'
groups_body %= self.tmpl_create_guest_forbidden_box(ln)
- groups_box = self.__tmpl_basket_box(img=weburl + '/img/webbasket_usergroup.png',
+ groups_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_usergroup.png',
title=_("Manage group rights"),
body=groups_body)
if display_sharing:
external_body = """
<tr>
<td>%s</td>
</tr>""" % self.__create_rights_selection_menu('external', external_rights, ln)
else:
external_body = '<tr><td colspan="2">%s</td></tr>'
external_body %= self.tmpl_create_guest_forbidden_box(ln)
- external_box = self.__tmpl_basket_box(img=weburl + '/img/webbasket_world.png',
+ external_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_world.png',
title=_("Manage global sharing rights"),
body=external_body)
delete_button = ''
if display_delete:
delete_button = '<input type="submit" class="nonsubmitbutton" name="delete" value="%s" />'
delete_button %= _("Delete basket")
out = """
<form name="edit" action="%(action)s" method="post">
<p>%(label)s</p>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="bskid" value="%(bskid)i" />
<input type="hidden" name="topic" value ="%(topic)i" />
<table>
<tr>
<td colspan="3">%(general)s</td>
</tr>
<tr>
<td colspan="3">%(groups)s</td>
</tr>
<tr>
<td colspan="3">%(external)s</td>
</tr>
<tr>
<td><input type="submit" class="formbutton" name="submit" value="%(submit_label)s" /></td>
<td><input type="submit" class="nonsubmitbutton" name="cancel" value="%(cancel_label)s" /></td>
<td>%(delete_button)s</td>
</tr>
</table>
</form>""" % {'label': _('Editing basket') + ' ' + cgi.escape(bsk_name),
- 'action': weburl + '/yourbaskets/edit',
+ 'action': CFG_SITE_URL + '/yourbaskets/edit',
'ln': ln,
'topic': topic,
'bskid': bskid,
'general': general_box,
'groups': groups_box,
'external': external_box,
'submit_label': _("Save changes"),
'cancel_label': _("Cancel"),
'delete_button': delete_button}
return out
def __create_rights_selection_menu(self, name, current_rights, ln=CFG_SITE_LANG):
"""Private function. create a drop down menu for selection of rights
@param name: name of menu (for HTML name attribute)
@param current_rights: rights as defined in CFG_WEBBASKET_SHARE_LEVELS
@param ln: language
"""
_ = gettext_set_language(ln)
elements = [('NO', _("No rights")),
(CFG_WEBBASKET_SHARE_LEVELS['READITM'],
_("View records")),
(CFG_WEBBASKET_SHARE_LEVELS['READCMT'],
'... ' + _("and") + ' ' + _("view comments")),
(CFG_WEBBASKET_SHARE_LEVELS['ADDCMT'],
'... ' + _("and") + ' ' + _("add comments")),
(CFG_WEBBASKET_SHARE_LEVELS['ADDITM'],
'... ' + _("and") + ' ' + _("add records")),
(CFG_WEBBASKET_SHARE_LEVELS['DELCMT'],
'... ' + _("and") + ' ' + _("delete comments")),
(CFG_WEBBASKET_SHARE_LEVELS['DELITM'],
'... ' + _("and") + ' ' + _("remove records")),
(CFG_WEBBASKET_SHARE_LEVELS['MANAGE'],
'... ' + _("and") + ' ' + _("manage sharing rights"))
]
return self.__create_select_menu(name, elements, current_rights)
def __create_group_rights_selection_menu(self, group_id, current_rights, ln=CFG_SITE_LANG):
"""Private function. create a drop down menu for selection of rights
@param current_rights: rights as defined in CFG_WEBBASKET_SHARE_LEVELS
@param ln: language
"""
_ = gettext_set_language(ln)
elements = [(str(group_id) + '_' + 'NO', _("No rights")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['READITM'],
_("View records")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['READCMT'],
'... ' + _("and") + ' ' + _("view comments")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['ADDCMT'],
'... ' + _("and") + ' ' + _("add comments")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['ADDITM'],
'... ' + _("and") + ' ' + _("add records")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['DELCMT'],
'... ' + _("and") + ' ' + _("delete comments")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['DELITM'],
'... ' + _("and") + ' ' + _("remove records")),
(str(group_id) + '_' + CFG_WEBBASKET_SHARE_LEVELS['MANAGE'],
'... ' + _("and") + ' ' + _("manage sharing rights"))
]
return self.__create_select_menu('groups', elements, str(group_id) + '_' + current_rights)
def tmpl_add_group(self, bskid, selected_topic, groups=[], ln=CFG_SITE_LANG):
"""
return form for selection of groups.
@param bskid: basket id (int)
@param selected_topic: topic currently displayed (int)
@param groups: list of tuples (group id, group name)
@param ln: language
"""
_ = gettext_set_language(ln)
if len(groups):
groups_body = """
<tr>
<td>%s</td>
</tr>""" % self.__create_select_menu('new_group', groups, selected_key=None)
else:
groups_body = """
<tr>
<td>%s</td>
</tr>""" % _("You are not a member of a group.")
- groups_box = self.__tmpl_basket_box(img=weburl + '/img/webbasket_usergroup.png',
+ groups_box = self.__tmpl_basket_box(img=CFG_SITE_URL + '/img/webbasket_usergroup.png',
title=_("Add group"),
body=groups_body)
out = """
<form name="add_group" action="%(action)s" method="post">
<p>%(label)s</p>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="bskid" value="%(bskid)i" />
<input type="hidden" name="topic" value ="%(topic)i" />
<table style="width:100%%;">
<tr>
<td style="width:50%%;vertical-align:top;">%(groups)s</td>
<td style="width:50%%;vertical-align:top;"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" class="formbutton" name="group_cancel" value="%(cancel_label)s" />
<input type="submit" class="formbutton" name="add_group" value="%(submit_label)s" />
</td>
</tr>
</table>
</form>""" % {'label': _('Sharing basket to a new group'),
- 'action': weburl + '/yourbaskets/edit',
+ 'action': CFG_SITE_URL + '/yourbaskets/edit',
'ln': ln,
'topic': selected_topic,
'bskid': bskid,
'groups': groups_box,
'cancel_label': _("Cancel"),
'submit_label': _("Add group")}
return out
def tmpl_personal_baskets_selection_box(self,
baskets=[],
select_box_name='baskets',
selected_bskid=None,
ln=CFG_SITE_LANG):
"""return an HTML popupmenu
@param baskets: list of (bskid, bsk_name, bsk_topic) tuples
@param select_box_name: name that will be used for the control
@param selected_bskid: id of the selcte basket, use None for no selection
@param ln: language"""
_ = gettext_set_language(ln)
elements = [(0, '- ' + _("no basket") + ' -')]
for (bskid, bsk_name, bsk_topic) in baskets:
elements.append((bskid, bsk_topic + ' > ' + bsk_name))
return self.__create_select_menu(select_box_name, elements, selected_bskid)
def tmpl_create_guest_warning_box(self, ln=CFG_SITE_LANG):
"""return html warning box for non registered users"""
_ = gettext_set_language(ln)
message = _("You are logged in as a guest user, so your baskets will disappear at the end of the current session.") + ' '
message += _("If you wish you can %(x_url_open)slogin or register here%(x_url_close)s.") %\
{'x_url_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/login?ln=' + ln + '">',
'x_url_close': '</a>'}
out = """
<table class="errorbox">
<tr>
<th class="errorboxheader">%s</th>
</tr>
</table>"""
return out % message
def tmpl_create_guest_forbidden_box(self, ln=CFG_SITE_LANG):
"""return html warning box for non registered users"""
_ = gettext_set_language(ln)
message = _("This functionality is forbidden to guest users.") + ' '
message += _("If you wish you can %(x_url_open)slogin or register here%(x_url_close)s.") %\
{'x_url_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/login?ln=' + ln + '">',
'x_url_close': '</a>'}
out = """
<table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">%s</th>
</tr>
</thead>
</table>"""
return out % message
############################ Utilities ###################################
def __create_select_menu(self, name, elements, selected_key=None):
""" private function, returns a popup menu
@param name: name of HTML control
@param elements: list of (key, value)
@param selected_key: item that should be selected (key of elements tuple)
"""
out = '<select name="%s">' % name
for (key, label) in elements:
selected = ''
if key == selected_key:
selected = ' selected="selected"'
out += '<option value="%s"%s>%s</option>'% (key, selected, cgi.escape(label))
out += '</select>'
return out
def tmpl_warnings(self, warnings=[], ln=CFG_SITE_LANG):
""" returns HTML for warnings """
from invenio.errorlib import get_msgs_for_code_list
out = ''
if type(warnings) is not list:
warnings = [warnings]
if len(warnings):
warnings_parsed = get_msgs_for_code_list(warnings, 'warning', ln)
for (warning_code, warning_text) in warnings_parsed:
out += '<div class="important" style="padding: 10px;">%s</div>' % warning_text
return out
def tmpl_create_infobox(self, infos = []):
""" returns html for general informations
@param infos: list of strings to display"""
out = ''
if len(infos):
out += '<div>'
for info in infos:
out += info + '<br />'
out += '</div>'
return out
def tmpl_back_link(self, link, ln=CFG_SITE_LANG):
""" returns HTML for a link whose label should be
'Back to search results'
"""
_ = gettext_set_language(ln)
label = _("Back to search results")
out = '<a href="%s">%s</a>' % (link, label)
return out
def __create_messaging_link(self, to, display_name, ln=CFG_SITE_LANG):
"""prints a link to the messaging system"""
- link = "%s/yourmessages/write?msg_to=%s&amp;ln=%s" % (weburl, to, ln)
+ link = "%s/yourmessages/write?msg_to=%s&amp;ln=%s" % (CFG_SITE_URL, to, ln)
if to:
return '<a href="%s" class="maillink">%s</a>' % (link, display_name)
else:
return display_name
def tmpl_xml_basket(self, items=[]):
"""Template for XML output of basket
@param items: XML version of each item (list)"""
items_xml = ''
for item in items:
items_xml += ' ' + item + '\n'
return """<?xml version="1.0" encoding="UTF-8"?>
<collection>
%s
</collection>
""" % items_xml
diff --git a/modules/webbasket/lib/webbasket_webinterface.py b/modules/webbasket/lib/webbasket_webinterface.py
index 5a2269c25..01b5fb4c6 100644
--- a/modules/webbasket/lib/webbasket_webinterface.py
+++ b/modules/webbasket/lib/webbasket_webinterface.py
@@ -1,742 +1,742 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebBasket Web Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
from mod_python import apache
-from invenio.config import weburl, CFG_WEBDIR, CFG_SITE_LANG, \
+from invenio.config import CFG_SITE_URL, CFG_WEBDIR, CFG_SITE_LANG, \
CFG_ACCESS_CONTROL_LEVEL_SITE
from invenio.messages import gettext_set_language
from invenio.webpage import page
from invenio.webuser import getUid, page_not_authorized, isGuestUser
from invenio.webbasket import *
from invenio.webbasket_config import CFG_WEBBASKET_CATEGORIES, \
CFG_WEBBASKET_ACTIONS
from invenio.urlutils import get_referer, redirect_to_url
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
class WebInterfaceYourBasketsPages(WebInterfaceDirectory):
"""Defines the set of /yourbaskets pages."""
_exports = ['', 'display', 'display_item', 'write_comment',
'save_comment', 'delete_comment', 'add', 'delete',
'modify', 'edit', 'create_basket', 'display_public',
'list_public_baskets', 'unsubscribe', 'subscribe']
def index(self, req, form):
"""Index page."""
- redirect_to_url(req, '%s/yourbaskets/display?%s' % (weburl, req.args))
+ redirect_to_url(req, '%s/yourbaskets/display?%s' % (CFG_SITE_URL, req.args))
def display(self, req, form):
"""Display basket"""
argd = wash_urlargd(form, {'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (int, 0),
'group': (int, 0),
'bsk_to_sort': (int, 0),
'sort_by_title': (str, ""),
'sort_by_date': (str, ""),
'of': (str, '')
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/display",
navmenuid = 'yourbaskets')
(body, errors, warnings) = perform_request_display(uid,
argd['category'],
argd['topic'],
argd['group'],
argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
navtrail = '<a class="navtrail" href="%s/youraccount/display?ln=%s">'\
'%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(uid=uid,
category=argd['category'],
topic=argd['topic'],
group=argd['group'],
ln=argd['ln'])
return page(title = _("Display baskets"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def display_item(self, req, form):
""" Display basket item """
argd = wash_urlargd(form, {'bskid': (int, 0),
'recid': (int, 0),
'format': (str, "hb"),
'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (int, 0),
'group': (int, 0),
'of': (str, '')
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/display_item",
navmenuid = 'yourbaskets')
(body, errors, warnings) = perform_request_display_item(
uid=uid,
bskid=argd['bskid'],
recid=argd['recid'],
format=argd['format'],
category=argd['category'],
topic=argd['topic'],
group_id=argd['group'],
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
navtrail = '<a class="navtrail" href="%s/youraccount/display?ln=%s">'\
'%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(uid=uid,
category=argd['category'],
topic=argd['topic'],
group=argd['group'],
bskid=argd['bskid'],
ln=argd['ln'])
return page(title = _("Details and comments"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def write_comment(self, req, form):
"""Write a comment (just interface for writing)"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'recid': (int, 0),
'cmtid': (int, 0),
'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (int, 0),
'group': (int, 0),
'of' : (str, '')
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/write_comment",
navmenuid = 'yourbaskets')
(body, errors, warnings) = perform_request_write_comment(
uid=uid,
bskid=argd['bskid'],
recid=argd['recid'],
cmtid=argd['cmtid'],
category=argd['category'],
topic=argd['topic'],
group_id=argd['group'],
ln=argd['ln'])
navtrail = '<a class="navtrail" href="%s/youraccount/display?ln=%s">'\
'%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(uid=uid,
category=argd['category'],
topic=argd['topic'],
group=argd['group'],
bskid=argd['bskid'],
ln=argd['ln'])
return page(title = _("Write a comment"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def save_comment(self, req, form):
"""Save comment on record in basket"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'recid': (int, 0),
'title': (str, ""),
'text': (str, ""),
'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (int, 0),
'group': (int, 0),
'of' : (str, '')
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/save_comment",
navmenuid = 'yourbaskets')
(errors_saving, infos) = perform_request_save_comment(
uid=uid,
bskid=argd['bskid'],
recid=argd['recid'],
title=argd['title'],
text=argd['text'],
ln=argd['ln'])
(body, errors_displaying, warnings) = perform_request_display_item(
uid=uid,
bskid=argd['bskid'],
recid=argd['recid'],
format='hb',
category=argd['category'],
topic=argd['topic'],
group_id=argd['group'],
ln=argd['ln'])
body = create_infobox(infos) + body
errors = errors_saving.extend(errors_displaying)
navtrail = '<a class="navtrail" href="%s/youraccount/display?ln=%s">'\
'%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(uid=uid,
category=argd['category'],
topic=argd['topic'],
group=argd['group'],
bskid=argd['bskid'],
ln=argd['ln'])
return page(title = _("Details and comments"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def delete_comment(self, req, form):
"""Delete a comment
@param bskid: id of basket (int)
@param recid: id of record (int)
@param cmtid: id of comment (int)
@param category: category (see webbasket_config) (str)
@param topic: nb of topic currently displayed (int)
@param group: id of group baskets currently displayed (int)
@param ln: language"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'recid': (int, 0),
'cmtid': (int, 0),
'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (int, 0),
'group': (int, 0),
'of' : (str, '')
})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/delete_comment",
navmenuid = 'yourbaskets')
_ = gettext_set_language(argd['ln'])
- url = weburl + '/yourbaskets/display_item?recid=%i&bskid=%i' % \
+ url = CFG_SITE_URL + '/yourbaskets/display_item?recid=%i&bskid=%i' % \
(argd['recid'], argd['bskid'])
url += '&category=%s&topic=%i&group=%i&ln=%s' % \
(argd['category'], argd['topic'],
argd['group'], argd['ln'])
errors = perform_request_delete_comment(uid,
argd['bskid'],
argd['recid'],
argd['cmtid'])
if not(len(errors)):
redirect_to_url(req, url)
else:
return page(uid = uid,
title = '',
body = '',
language = argd['ln'],
errors = errors,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def add(self, req, form):
"""Add records to baskets.
@param recid: list of records to add
@param bskids: list of baskets to add records to. if not provided,
will return a page where user can select baskets
@param referer: URL of the referring page
@param new_basket_name: add record to new basket
@param new_topic_name: new basket goes into new topic
@param create_in_topic: # of topic to put basket into
@param ln: language"""
argd = wash_urlargd(form, {'recid': (list, []),
'bskids': (list, []),
'referer': (str, ""),
'new_basket_name': (str, ""),
'new_topic_name': (str, ""),
'create_in_topic': (int, -1),
"of" : (str, '')
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/add",
navmenuid = 'yourbaskets')
if not argd['referer']:
argd['referer'] = get_referer(req)
(body, errors, warnings) = perform_request_add(
uid=uid,
recids=argd['recid'],
bskids=argd['bskids'],
referer=argd['referer'],
new_basket_name=argd['new_basket_name'],
new_topic_name=argd['new_topic_name'],
create_in_topic=argd['create_in_topic'],
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
if not(len(warnings)) :
title = _("Your Baskets")
else:
title = _("Add records to baskets")
navtrail = '<a class="navtrail" href="%s/youraccount/display?ln=%s">'\
'%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
return page(title = title,
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def delete(self, req, form):
"""Delete basket interface"""
argd = wash_urlargd(form, {'bskid': (int, -1),
'confirmed': (int, 0),
'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (int, 0),
'group': (int, 0),
'of' : (str, '')
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/delete",
navmenuid = 'yourbaskets')
(body, errors, warnings)=perform_request_delete(
uid=uid,
bskid=argd['bskid'],
confirmed=argd['confirmed'],
category=argd['category'],
selected_topic=argd['topic'],
selected_group_id=argd['group'],
ln=argd['ln'])
if argd['confirmed']:
- url = weburl
+ url = CFG_SITE_URL
url += '/yourbaskets/display?category=%s&topic=%i&group=%i&ln=%s' %\
(argd['category'], argd['topic'], argd['group'], argd['ln'])
redirect_to_url(req, url)
else:
navtrail = '<a class="navtrail" href="%s/youraccount/display?ln=%s">'\
'%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(uid=uid,
category=argd['category'],
topic=argd['topic'],
group=argd['group'],
bskid=argd['bskid'],
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
return page(title = _("Delete a basket"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def modify(self, req, form):
"""Modify basket content interface (reorder, suppress record, etc.)"""
argd = wash_urlargd(form, {'action': (str, ""),
'bskid': (int, -1),
'recid': (int, 0),
'category':
(str, CFG_WEBBASKET_CATEGORIES['PRIVATE']),
'topic': (int, 0),
'group': (int, 0),
'of' : (str, '')
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/modify",
navmenuid = 'yourbaskets')
- url = weburl
+ url = CFG_SITE_URL
url += '/yourbaskets/display?category=%s&topic=%i&group=%i&ln=%s' %\
(argd['category'], argd['topic'], argd['group'], argd['ln'])
if argd['action'] == CFG_WEBBASKET_ACTIONS['DELETE']:
delete_record(uid, argd['bskid'], argd['recid'])
redirect_to_url(req, url)
elif argd['action'] == CFG_WEBBASKET_ACTIONS['UP']:
move_record(uid, argd['bskid'], argd['recid'], argd['action'])
redirect_to_url(req, url)
elif argd['action'] == CFG_WEBBASKET_ACTIONS['DOWN']:
move_record(uid, argd['bskid'], argd['recid'], argd['action'])
redirect_to_url(req, url)
elif argd['action'] == CFG_WEBBASKET_ACTIONS['COPY']:
title = _("Copy record to basket")
referer = get_referer(req)
(body, errors, warnings) = perform_request_add(uid=uid,
recids=argd['recid'],
referer=referer,
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
else:
title = ''
body = ''
warnings = ''
errors = [('ERR_WEBBASKET_UNDEFINED_ACTION',)]
navtrail = '<a class="navtrail" href="%s/youraccount/display?ln=%s">'\
'%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(uid=uid,
category=argd['category'],
topic=argd['topic'],
group=argd['group'],
bskid=argd['bskid'],
ln=argd['ln'])
return page(title = title,
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def edit(self, req, form):
"""Edit basket interface"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'groups': (list, []),
'topic': (int, 0),
'add_group': (str, ""),
'group_cancel': (str, ""),
'submit': (str, ""),
'cancel': (str, ""),
'delete': (str, ""),
'new_name': (str, ""),
'new_topic': (int, -1),
'new_topic_name': (str, ""),
'new_group': (str, ""),
'external': (str, ""),
'of' : (str, '')
})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/edit",
navmenuid = 'yourbaskets')
_ = gettext_set_language(argd['ln'])
if argd['cancel']:
- url = weburl + '/yourbaskets/display?category=%s&topic=%i&ln=%s'
+ url = CFG_SITE_URL + '/yourbaskets/display?category=%s&topic=%i&ln=%s'
url %= (CFG_WEBBASKET_CATEGORIES['PRIVATE'], argd['topic'],
argd['ln'])
redirect_to_url(req, url)
elif argd['delete']:
- url = weburl
+ url = CFG_SITE_URL
url += '/yourbaskets/delete?bskid=%i&category=%s&topic=%i&ln=%s' %\
(argd['bskid'], CFG_WEBBASKET_CATEGORIES['PRIVATE'],
argd['topic'], argd['ln'])
redirect_to_url(req, url)
elif argd['add_group'] and not(argd['new_group']):
body = perform_request_add_group(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
ln=argd['ln'])
errors = []
warnings = []
elif (argd['add_group'] and argd['new_group']) or argd['group_cancel']:
if argd['add_group']:
perform_request_add_group(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
group_id=argd['new_group'],
ln=argd['ln'])
(body, errors, warnings) = perform_request_edit(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
ln=argd['ln'])
elif argd['submit']:
(body, errors, warnings) = perform_request_edit(
uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
new_name=argd['new_name'],
new_topic=argd['new_topic'],
new_topic_name=argd['new_topic_name'],
groups=argd['groups'],
external=argd['external'],
ln=argd['ln'])
if argd['new_topic'] != -1:
argd['topic'] = argd['new_topic']
- url = weburl + '/yourbaskets/display?category=%s&topic=%i&ln=%s' %\
+ url = CFG_SITE_URL + '/yourbaskets/display?category=%s&topic=%i&ln=%s' %\
(CFG_WEBBASKET_CATEGORIES['PRIVATE'],
argd['topic'], argd['ln'])
redirect_to_url(req, url)
else:
(body, errors, warnings) = perform_request_edit(uid=uid,
bskid=argd['bskid'],
topic=argd['topic'],
ln=argd['ln'])
navtrail = '<a class="navtrail" href="%s/youraccount/display?ln=%s">'\
'%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
navtrail_end = create_basket_navtrail(
uid=uid,
category=CFG_WEBBASKET_CATEGORIES['PRIVATE'],
topic=argd['topic'],
group=0,
bskid=argd['bskid'],
ln=argd['ln'])
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
return page(title = _("Edit basket"),
body = body,
navtrail = navtrail + navtrail_end,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def create_basket(self, req, form):
"""Create basket interface"""
argd = wash_urlargd(form, {'new_basket_name': (str, ""),
'new_topic_name': (str, ""),
'create_in_topic': (int, -1),
'topic_number': (int, -1),
'of' : (str, ''),
})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yourbaskets/create_basket",
navmenuid = 'yourbaskets')
_ = gettext_set_language(argd['ln'])
if argd['new_basket_name'] and \
(argd['new_topic_name'] or argd['create_in_topic'] != -1):
topic = perform_request_create_basket(
uid=uid,
new_basket_name=argd['new_basket_name'],
new_topic_name=argd['new_topic_name'],
create_in_topic=argd['create_in_topic'],
ln=argd['ln'])
- url = weburl + '/yourbaskets/display?category=%s&topic=%i&ln=%s'
+ url = CFG_SITE_URL + '/yourbaskets/display?category=%s&topic=%i&ln=%s'
url %= (CFG_WEBBASKET_CATEGORIES['PRIVATE'], int(topic), argd['ln'])
redirect_to_url(req, url)
else:
(body, errors, warnings) = perform_request_create_basket(
uid=uid,
new_basket_name=argd['new_basket_name'],
new_topic_name=argd['new_topic_name'],
create_in_topic=argd['create_in_topic'],
topic_number=argd['topic_number'],
ln=argd['ln'])
navtrail = '<a class="navtrail" href="%s/youraccount/'\
'display?ln=%s">%s</a>'
- navtrail %= (weburl, argd['ln'], _("Your Account"))
+ navtrail %= (CFG_SITE_URL, argd['ln'], _("Your Account"))
if isGuestUser(uid):
body = create_guest_warning_box(argd['ln']) + body
return page(title = _("Create basket"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def display_public(self, req, form):
"""Display public basket. If of is x** then output will be XML"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'of': (str, "hb"),
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
return page_not_authorized(req, "../yourbaskets/display_public",
navmenuid = 'yourbaskets')
if argd['bskid'] == 0:
# No given basket => display list of public baskets
(body, errors, warnings) = perform_request_list_public_baskets(
0, 1, 1,
argd['ln'])
return page(title = _("List of public baskets"),
body = body,
navtrail = '',
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
of = argd['of'])
if len(argd['of']) and argd['of'][0]=='x':
# XML output
req.content_type = "text/xml"
req.send_http_header()
return perform_request_display_public(bskid=argd['bskid'],
of=argd['of'],
ln=argd['ln'])
(body, errors, warnings) = perform_request_display_public(
bskid=argd['bskid'],
ln=argd['ln'])
referer = get_referer(req)
if 'list_public_basket' not in referer:
- referer = weburl + '/yourbaskets/list_public_baskets?ln=' + \
+ referer = CFG_SITE_URL + '/yourbaskets/list_public_baskets?ln=' + \
argd['ln']
navtrail = '<a class="navtrail" href="%s">%s</a>' % \
(referer, _("List of public baskets"))
return page(title = _("Public basket"),
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def list_public_baskets(self, req, form):
"""List of public baskets interface"""
argd = wash_urlargd(form, {'inf_limit': (int, 0),
'order': (int, 1),
'asc': (int, 1),
'of': (str, '')
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
return page_not_authorized(req, "../yourbaskets/unsubscribe",
navmenuid = 'yourbaskets')
(body, errors, warnings) = perform_request_list_public_baskets(
argd['inf_limit'],
argd['order'],
argd['asc'], argd['ln'])
return page(title = _("List of public baskets"),
body = body,
navtrail = '',
uid = uid,
lastupdated = __lastupdated__,
language = argd['ln'],
errors = errors,
warnings = warnings,
req = req,
navmenuid = 'yourbaskets',
of = argd['of'])
def unsubscribe(self, req, form):
"""unsubscribe to basket"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'of': (str, '')
})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
return page_not_authorized(req, "../yourbaskets/unsubscribe",
navmenuid = 'yourbaskets')
perform_request_unsubscribe(uid, argd['bskid'])
- url = weburl + '/yourbaskets/display?category=%s&ln=%s'
+ url = CFG_SITE_URL + '/yourbaskets/display?category=%s&ln=%s'
url %= (CFG_WEBBASKET_CATEGORIES['EXTERNAL'], argd['ln'])
redirect_to_url(req, url)
def subscribe(self, req, form):
"""subscribe to basket"""
argd = wash_urlargd(form, {'bskid': (int, 0),
'of': (str, '')
})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
return page_not_authorized(req, "../yourbaskets/subscribe",
navmenuid = 'yourbaskets')
errors = perform_request_subscribe(uid, argd['bskid'])
if len(errors):
return page(errors=errors,
uid=uid,
language=argd['ln'],
body = '',
title = '',
req=req,
navmenuid = 'yourbaskets')
- url = weburl + '/yourbaskets/display?category=%s&ln=%s'
+ url = CFG_SITE_URL + '/yourbaskets/display?category=%s&ln=%s'
url %= (CFG_WEBBASKET_CATEGORIES['EXTERNAL'], argd['ln'])
redirect_to_url(req, url)
diff --git a/modules/webcomment/doc/admin/webcomment-admin-guide.webdoc b/modules/webcomment/doc/admin/webcomment-admin-guide.webdoc
index f57142539..4b0d01f44 100644
--- a/modules/webcomment/doc/admin/webcomment-admin-guide.webdoc
+++ b/modules/webcomment/doc/admin/webcomment-admin-guide.webdoc
@@ -1,25 +1,25 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebComment Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>FIXME</p>
diff --git a/modules/webcomment/lib/webcomment.py b/modules/webcomment/lib/webcomment.py
index 86da99649..7fe1f29d8 100644
--- a/modules/webcomment/lib/webcomment.py
+++ b/modules/webcomment/lib/webcomment.py
@@ -1,1050 +1,1050 @@
# -*- coding: utf-8 -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Comments and reviews for records """
__revision__ = "$Id$"
# non CDS Invenio imports:
import time
import math
# CDS Invenio imports:
from invenio.dbquery import run_sql
from invenio.config import CFG_SITE_LANG, \
CFG_WEBALERT_ALERT_ENGINE_EMAIL,\
CFG_SITE_ADMIN_EMAIL,\
- weburl,\
+ CFG_SITE_URL,\
CFG_SITE_NAME,\
CFG_WEBCOMMENT_ALLOW_REVIEWS,\
CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS,\
CFG_WEBCOMMENT_ALLOW_COMMENTS,\
CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL,\
CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN,\
CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS,\
CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_REVIEWS_IN_SECONDS
from invenio.webmessage_mailutils import email_quote_txt
from invenio.webuser import get_user_info
from invenio.dateutils import convert_datetext_to_dategui, \
datetext_default, \
convert_datestruct_to_datetext
from invenio.mailutils import send_email
from invenio.messages import wash_language, gettext_set_language
from invenio.urlutils import wash_url_argument
from invenio.webuser import isGuestUser
from invenio.webcomment_config import CFG_WEBCOMMENT_ACTION_CODE
try:
import invenio.template
webcomment_templates = invenio.template.load('webcomment')
except:
pass
def perform_request_display_comments_or_remarks(recID, ln=CFG_SITE_LANG, display_order='od', display_since='all', nb_per_page=100, page=1, voted=-1, reported=-1, reviews=0, uid=-1):
"""
Returns all the comments (reviews) of a specific internal record or external basket record.
@param recID: record id where (internal record IDs > 0) or (external basket record IDs < -100)
@param display_order: hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param display_since: all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb_per_page: number of results per page
@param page: results page
@param voted: boolean, active if user voted for a review, see perform_request_vote function
@param reported: boolean, active if user reported a certain comment/review, perform_request_report function
@param reviews: boolean, enabled if reviews, disabled for comments
@param uid: the id of the user who is reading comments
@return html body.
"""
errors = []
warnings = []
nb_reviews = 0
nb_comments = 0
# wash arguments
recID = wash_url_argument(recID, 'int')
ln = wash_language(ln)
display_order = wash_url_argument(display_order, 'str')
display_since = wash_url_argument(display_since, 'str')
nb_per_page = wash_url_argument(nb_per_page, 'int')
page = wash_url_argument(page, 'int')
voted = wash_url_argument(voted, 'int')
reported = wash_url_argument(reported, 'int')
reviews = wash_url_argument(reviews, 'int')
# vital argument check
(valid, error_body) = check_recID_is_in_range(recID, warnings, ln)
if not(valid):
return (error_body, errors, warnings)
# Query the database and filter results
res = query_retrieve_comments_or_remarks(recID, display_order, display_since, reviews)
res2 = query_retrieve_comments_or_remarks(recID, display_order, display_since, not reviews)
nb_res = len(res)
if reviews:
nb_reviews = nb_res
nb_comments = len(res2)
else:
nb_reviews = len(res2)
nb_comments = nb_res
# checking non vital arguemnts - will be set to default if wrong
#if page <= 0 or page.lower() != 'all':
if page < 0:
page = 1
warnings.append(('WRN_WEBCOMMENT_INVALID_PAGE_NB',))
if nb_per_page < 0:
nb_per_page = 100
warnings.append(('WRN_WEBCOMMENT_INVALID_NB_RESULTS_PER_PAGE',))
if CFG_WEBCOMMENT_ALLOW_REVIEWS and reviews:
if display_order not in ['od', 'nd', 'hh', 'lh', 'hs', 'ls']:
display_order = 'hh'
warnings.append(('WRN_WEBCOMMENT_INVALID_REVIEW_DISPLAY_ORDER',))
else:
if display_order not in ['od', 'nd']:
display_order = 'od'
warnings.append(('WRN_WEBCOMMENT_INVALID_DISPLAY_ORDER',))
# filter results according to page and number of reults per page
if nb_per_page > 0:
if nb_res > 0:
last_page = int(math.ceil(nb_res / float(nb_per_page)))
else:
last_page = 1
if page > last_page:
page = 1
warnings.append(("WRN_WEBCOMMENT_INVALID_PAGE_NB",))
if nb_res > nb_per_page: # if more than one page of results
if page < last_page:
res = res[(page-1)*(nb_per_page) : (page*nb_per_page)]
else:
res = res[(page-1)*(nb_per_page) : ]
else: # one page of results
pass
else:
last_page = 1
# Send to template
avg_score = 0.0
if not CFG_WEBCOMMENT_ALLOW_COMMENTS and not CFG_WEBCOMMENT_ALLOW_REVIEWS: # comments not allowed by admin
errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',))
if reported > 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED',))
elif reported == 0:
warnings.append(('WRN_WEBCOMMENT_ALREADY_REPORTED',))
if CFG_WEBCOMMENT_ALLOW_REVIEWS and reviews:
avg_score = calculate_avg_score(res)
if voted > 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED',))
elif voted == 0:
warnings.append(('WRN_WEBCOMMENT_ALREADY_VOTED',))
body = webcomment_templates.tmpl_get_comments(recID,
ln,
nb_per_page, page, last_page,
display_order, display_since,
CFG_WEBCOMMENT_ALLOW_REVIEWS,
res, nb_comments, avg_score,
warnings,
border=0,
reviews=reviews,
total_nb_reviews=nb_reviews,
uid=uid)
return (body, errors, warnings)
def perform_request_vote(cmt_id, client_ip_address, value, uid=-1):
"""
Vote positively or negatively for a comment/review
@param cmt_id: review id
@param value: +1 for voting positively
-1 for voting negatively
@return integer 1 if successful, integer 0 if not
"""
cmt_id = wash_url_argument(cmt_id, 'int')
client_ip_address = wash_url_argument(client_ip_address, 'str')
value = wash_url_argument(value, 'int')
uid = wash_url_argument(uid, 'int')
if cmt_id > 0 and value in [-1, 1] and check_user_can_vote(cmt_id, client_ip_address, uid):
action_date = convert_datestruct_to_datetext(time.localtime())
action_code = CFG_WEBCOMMENT_ACTION_CODE['VOTE']
query = """INSERT INTO cmtACTIONHISTORY (id_cmtRECORDCOMMENT,
id_bibrec, id_user, client_host, action_time,
action_code)
VALUES (%i, NULL ,%i, inet_aton('%s'), '%s', '%s')"""
query %= (cmt_id, uid, client_ip_address, action_date, action_code)
run_sql(query)
return query_record_useful_review(cmt_id, value)
else:
return 0
def check_user_can_comment(recID, client_ip_address, uid=-1):
""" Check if a user hasn't already commented within the last seconds
time limit: CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS
@param recID: record id
@param client_ip_address: IP => use: str(req.get_remote_host(apache.REMOTE_NOLOOKUP))
@param uid: user id, as given by invenio.webuser.getUid(req)
"""
recID = wash_url_argument(recID, 'int')
client_ip_address = wash_url_argument(client_ip_address, 'str')
uid = wash_url_argument(uid, 'int')
max_action_time = time.time() - CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_COMMENTS_IN_SECONDS
max_action_time = convert_datestruct_to_datetext(time.localtime(max_action_time))
action_code = CFG_WEBCOMMENT_ACTION_CODE['ADD_COMMENT']
query = """SELECT id_bibrec
FROM cmtACTIONHISTORY
WHERE id_bibrec=%i AND
action_code='%s' AND
action_time>'%s'
""" % (recID, action_code, max_action_time)
if uid < 0:
query += " AND client_host=inet_aton('%s')" % client_ip_address
else:
query += " AND id_user=%i" % uid
res = run_sql(query)
return len(res) == 0
def check_user_can_review(recID, client_ip_address, uid=-1):
""" Check if a user hasn't already reviewed within the last seconds
time limit: CFG_WEBCOMMENT_TIMELIMIT_PROCESSING_REVIEWS_IN_SECONDS
@param cmt_id: comment id
@param client_ip_address: IP => use: str(req.get_remote_host(apache.REMOTE_NOLOOKUP))
@param uid: user id, as given by invenio.webuser.getUid(req)
"""
action_code = CFG_WEBCOMMENT_ACTION_CODE['ADD_REVIEW']
query = """SELECT id_bibrec
FROM cmtACTIONHISTORY
WHERE id_bibrec=%i AND
action_code='%s'
""" % (recID, action_code)
if uid < 0:
query += " AND client_host=inet_aton('%s')" % client_ip_address
else:
query += " AND id_user=%i" % uid
res = run_sql(query)
return len(res) == 0
def check_user_can_vote(cmt_id, client_ip_address, uid=-1):
""" Checks if a user hasn't already voted
@param cmt_id: comment id
@param client_ip_address: IP => use: str(req.get_remote_host(apache.REMOTE_NOLOOKUP))
@param uid: user id, as given by invenio.webuser.getUid(req)
"""
cmt_id = wash_url_argument(cmt_id, 'int')
client_ip_address = wash_url_argument(client_ip_address, 'str')
uid = wash_url_argument(uid, 'int')
query = """SELECT id_cmtRECORDCOMMENT
FROM cmtACTIONHISTORY
WHERE id_cmtRECORDCOMMENT=%i""" % cmt_id
if uid < 0:
query += " AND client_host=inet_aton('%s')" % client_ip_address
else:
query += " AND id_user=%i" % uid
res = run_sql(query)
return (len(res) == 0)
def perform_request_report(cmt_id, client_ip_address, uid=-1):
"""
Report a comment/review for inappropriate content.
Will send an email to the administrator if number of reports is a multiple of CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN
@param cmt_id: comment id
@return integer 1 if successful, integer 0 if not
"""
cmt_id = wash_url_argument(cmt_id, 'int')
if cmt_id <= 0:
return 0
(query_res, nb_abuse_reports) = query_record_report_this(cmt_id)
if query_res == 0:
return 0
if not(check_user_can_report(cmt_id, client_ip_address, uid)):
return 0
action_date = convert_datestruct_to_datetext(time.localtime())
action_code = CFG_WEBCOMMENT_ACTION_CODE['REPORT_ABUSE']
query = """INSERT INTO cmtACTIONHISTORY (id_cmtRECORDCOMMENT, id_bibrec,
id_user, client_host, action_time, action_code)
VALUES (%i, NULL, %i, inet_aton('%s'), '%s', '%s')"""
query %= (cmt_id, uid, client_ip_address, action_date, action_code)
run_sql(query)
if nb_abuse_reports % CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN == 0:
(cmt_id2,
id_bibrec,
id_user,
cmt_body,
cmt_date,
cmt_star,
cmt_vote, cmt_nb_votes_total,
cmt_title,
cmt_reported) = query_get_comment(cmt_id)
(user_nb_abuse_reports,
user_votes,
user_nb_votes_total) = query_get_user_reports_and_votes(int(id_user))
(nickname, user_email, last_login) = query_get_user_contact_info(id_user)
from_addr = '%s Alert Engine <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL)
to_addr = CFG_SITE_ADMIN_EMAIL
subject = "An error report has been sent from a user"
body = '''
The following comment has been reported a total of %(cmt_reported)s times.
Author: nickname = %(nickname)s
email = %(user_email)s
user_id = %(uid)s
This user has:
total number of reports = %(user_nb_abuse_reports)s
%(votes)s
Comment: comment_id = %(cmt_id)s
record_id = %(id_bibrec)s
date written = %(cmt_date)s
nb reports = %(cmt_reported)s
%(review_stuff)s
body =
---start body---
%(cmt_body)s
---end body---
Please go to the WebComment Admin interface %(comment_admin_link)s to delete this message if necessary. A warning will be sent to the user in question.''' % \
{ 'cfg-report_max' : CFG_WEBCOMMENT_NB_REPORTS_BEFORE_SEND_EMAIL_TO_ADMIN,
'nickname' : nickname,
'user_email' : user_email,
'uid' : id_user,
'user_nb_abuse_reports' : user_nb_abuse_reports,
'user_votes' : user_votes,
'votes' : CFG_WEBCOMMENT_ALLOW_REVIEWS and \
"total number of positive votes\t= %s\n\t\t\t\ttotal number of negative votes\t= %s" % \
(user_votes, (user_nb_votes_total - user_votes)) or "\n",
'cmt_id' : cmt_id,
'id_bibrec' : id_bibrec,
'cmt_date' : cmt_date,
'cmt_reported' : cmt_reported,
'review_stuff' : CFG_WEBCOMMENT_ALLOW_REVIEWS and \
"star score\t\t= %s\n\t\t\treview title\t\t= %s" % (cmt_star, cmt_title) or "",
'cmt_body' : cmt_body,
- 'comment_admin_link' : weburl + "/admin/webcomment/webcommentadmin.py",
+ 'comment_admin_link' : CFG_SITE_URL + "/admin/webcomment/webcommentadmin.py",
'user_admin_link' : "user_admin_link" #! FIXME
}
#FIXME to be added to email when websession module is over:
#If you wish to ban the user, you can do so via the User Admin Panel %(user_admin_link)s.
send_email(from_addr, to_addr, subject, body)
return 1
def check_user_can_report(cmt_id, client_ip_address, uid=-1):
""" Checks if a user hasn't already reported a comment
@param cmt_id: comment id
@param client_ip_address: IP => use: str(req.get_remote_host(apache.REMOTE_NOLOOKUP))
@param uid: user id, as given by invenio.webuser.getUid(req)
"""
cmt_id = wash_url_argument(cmt_id, 'int')
client_ip_address = wash_url_argument(client_ip_address, 'str')
uid = wash_url_argument(uid, 'int')
query = """SELECT id_cmtRECORDCOMMENT
FROM cmtACTIONHISTORY
WHERE id_cmtRECORDCOMMENT=%i""" % cmt_id
if uid < 0:
query += " AND client_host=inet_aton('%s')" % client_ip_address
else:
query += " AND id_user=%i" % uid
res = run_sql(query)
return (len(res) == 0)
def query_get_user_contact_info(uid):
"""
Get the user contact information
@return tuple (nickname, email, last_login), if none found return ()
Note: for the moment, if no nickname, will return email address up to the '@'
"""
query1 = """SELECT nickname, email,
DATE_FORMAT(last_login, '%%Y-%%m-%%d %%H:%%i:%%s')
FROM user WHERE id=%s"""
params1 = (uid,)
res1 = run_sql(query1, params1)
if res1:
return res1[0]
else:
return ()
def query_get_user_reports_and_votes(uid):
"""
Retrieve total number of reports and votes of a particular user
@param uid: user id
@return tuple (total_nb_reports, total_nb_votes_yes, total_nb_votes_total)
if none found return ()
"""
query1 = """SELECT nb_votes_yes,
nb_votes_total,
nb_abuse_reports
FROM cmtRECORDCOMMENT
WHERE id_user=%s"""
params1 = (uid,)
res1 = run_sql(query1, params1)
if len(res1) == 0:
return ()
nb_votes_yes = nb_votes_total = nb_abuse_reports = 0
for cmt_tuple in res1:
nb_votes_yes += int(cmt_tuple[0])
nb_votes_total += int(cmt_tuple[1])
nb_abuse_reports += int(cmt_tuple[2])
return (nb_abuse_reports, nb_votes_yes, nb_votes_total)
def query_get_comment(comID):
"""
Get all fields of a comment
@param comID: comment id
@return tuple (comID, id_bibrec, id_user, body, date_creation, star_score, nb_votes_yes, nb_votes_total, title, nb_abuse_reports)
if none found return ()
"""
query1 = """SELECT id,
id_bibrec,
id_user,
body,
DATE_FORMAT(date_creation, '%%Y-%%m-%%d %%H:%%i:%%s'),
star_score,
nb_votes_yes,
nb_votes_total,
title,
nb_abuse_reports
FROM cmtRECORDCOMMENT
WHERE id=%s"""
params1 = (comID,)
res1 = run_sql(query1, params1)
if len(res1)>0:
return res1[0]
else:
return ()
def query_record_report_this(comID):
"""
Increment the number of reports for a comment
@param comID: comment id
@return tuple (success, new_total_nb_reports_for_this_comment) where
success is integer 1 if success, integer 0 if not
if none found, return ()
"""
#retrieve nb_abuse_reports
query1 = "SELECT nb_abuse_reports FROM cmtRECORDCOMMENT WHERE id=%s"
params1 = (comID,)
res1 = run_sql(query1, params1)
if len(res1)==0:
return ()
#increment and update
nb_abuse_reports = int(res1[0][0]) + 1
query2 = "UPDATE cmtRECORDCOMMENT SET nb_abuse_reports=%s WHERE id=%s"
params2 = (nb_abuse_reports, comID)
res2 = run_sql(query2, params2)
return (int(res2), nb_abuse_reports)
def query_record_useful_review(comID, value):
"""
private funciton
Adjust the number of useful votes and number of total votes for a comment.
@param comID: comment id
@param value: +1 or -1
@return integer 1 if successful, integer 0 if not
"""
# retrieve nb_useful votes
query1 = "SELECT nb_votes_total, nb_votes_yes FROM cmtRECORDCOMMENT WHERE id=%s"
params1 = (comID,)
res1 = run_sql(query1, params1)
if len(res1)==0:
return 0
# modify and insert new nb_useful votes
nb_votes_yes = int(res1[0][1])
if value >= 1:
nb_votes_yes = int(res1[0][1]) + 1
nb_votes_total = int(res1[0][0]) + 1
query2 = "UPDATE cmtRECORDCOMMENT SET nb_votes_total=%s, nb_votes_yes=%s WHERE id=%s"
params2 = (nb_votes_total, nb_votes_yes, comID)
res2 = run_sql(query2, params2)
return int(res2)
def query_retrieve_comments_or_remarks (recID, display_order='od', display_since='0000-00-00 00:00:00',
ranking=0):
"""
Private function
Retrieve tuple of comments or remarks from the database
@param recID: record id
@param display_order: hh = highest helpful score
lh = lowest helpful score
hs = highest star score
ls = lowest star score
od = oldest date
nd = newest date
@param display_since: datetime, e.g. 0000-00-00 00:00:00
@param ranking: boolean, enabled if reviews, disabled for comments
@param full_reviews_p: boolean, filter out empty reviews (with score only) if False
@return tuple of comment where comment is
tuple (nickname, date_creation, body, id) if ranking disabled or
tuple (nickname, date_creation, body, nb_votes_yes, nb_votes_total, star_score, title, id)
Note: for the moment, if no nickname, will return email address up to '@'
"""
display_since = calculate_start_date(display_since)
order_dict = { 'hh' : "cmt.nb_votes_yes/(cmt.nb_votes_total+1) DESC, cmt.date_creation DESC ",
'lh' : "cmt.nb_votes_yes/(cmt.nb_votes_total+1) ASC, cmt.date_creation ASC ",
'ls' : "cmt.star_score ASC, cmt.date_creation DESC ",
'hs' : "cmt.star_score DESC, cmt.date_creation DESC ",
'od' : "cmt.date_creation ASC ",
'nd' : "cmt.date_creation DESC "
}
# Ranking only done for comments and when allowed
if ranking and recID > 0:
try:
display_order = order_dict[display_order]
except:
display_order = order_dict['od']
else:
# in case of recID > 0 => external record => no ranking!
ranking = 0
try:
if display_order[-1] == 'd':
display_order = order_dict[display_order]
else:
display_order = order_dict['od']
except:
display_order = order_dict['od']
query = """SELECT user.nickname,
cmt.id_user,
DATE_FORMAT(cmt.date_creation, '%%Y-%%m-%%d %%H:%%i:%%s'),
cmt.body,
%(ranking)s cmt.id
FROM %(table)s cmt LEFT JOIN user ON
user.id=cmt.id_user
WHERE %(id_bibrec)s=%(recID)i
%(ranking_only)s
%(display_since)s
ORDER BY %(display_order)s"""
params = { 'ranking' : ranking and ' cmt.nb_votes_yes, cmt.nb_votes_total, cmt.star_score, cmt.title, ' or '',
'ranking_only' : ranking and ' AND cmt.star_score>0 ' or ' AND cmt.star_score=0 ',
'id_bibrec' : recID > 0 and 'cmt.id_bibrec' or 'cmt.id_bibrec_or_bskEXTREC',
'table' : recID > 0 and 'cmtRECORDCOMMENT' or 'bskRECORDCOMMENT',
'recID' : recID,
'display_since' : display_since=='0000-00-00 00:00:00' and ' ' or 'AND cmt.date_creation>=\'%s\' ' % display_since,
'display_order' : display_order
}
res = run_sql(query % params)
if res:
return res
return ()
def query_add_comment_or_remark(reviews=0, recID=0, uid=-1, msg="", note="", score=0, priority=0, client_ip_address=''):
"""
Private function
Insert a comment/review or remarkinto the database
@param recID: record id
@param uid: user id
@param msg: comment body
@param note: comment title
@param score: review star score
@param priority: remark priority #!FIXME
@return integer >0 representing id if successful, integer 0 if not
"""
current_date = calculate_start_date('0d')
#change utf-8 message into general unicode
msg = msg.decode('utf-8')
note = note.decode('utf-8')
#change general unicode back to utf-8
msg = msg.encode('utf-8')
note = note.encode('utf-8')
query = """INSERT INTO cmtRECORDCOMMENT (id_bibrec,
id_user,
body,
date_creation,
star_score,
nb_votes_total,
title)
VALUES (%s, %s, %s, %s, %s, %s, %s)"""
params = (recID, uid, msg, current_date, score, 0, note)
res = run_sql(query, params)
if res:
action_code = CFG_WEBCOMMENT_ACTION_CODE[reviews and 'ADD_REVIEW' or 'ADD_COMMENT']
action_time = convert_datestruct_to_datetext(time.localtime())
query2 = """INSERT INTO cmtACTIONHISTORY (id_cmtRECORDCOMMENT,
id_bibrec, id_user, client_host, action_time, action_code)
VALUES ('', %i, %i, inet_aton('%s'), '%s', '%s')"""
params2 = (recID, uid, client_ip_address, action_time, action_code)
run_sql(query2%params2)
return int(res)
def calculate_start_date(display_since):
"""
Private function
Returns the datetime of display_since argument in MYSQL datetime format
calculated according to the local time.
@param display_since = all= no filtering
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit number
@return string of wanted datetime.
If 'all' given as argument, will return datetext_default
datetext_default is defined in miscutils/lib/dateutils and
equals 0000-00-00 00:00:00 => MySQL format
If bad arguement given, will return datetext_default
"""
# time type and seconds coefficients
time_types = {'d':0, 'w':0, 'm':0, 'y':0}
## verify argument
# argument wrong size
if (display_since==(None or 'all')) or (len(display_since) > 2):
return datetext_default
try:
nb = int(display_since[0])
except:
return datetext_default
if str(display_since[1]) in time_types:
time_type = str(display_since[1])
else:
return datetext_default
## calculate date
# initialize the coef
if time_type == 'w':
time_types[time_type] = 7
else:
time_types[time_type] = 1
start_time = time.localtime()
start_time = (start_time[0] - nb*time_types['y'],
start_time[1] - nb*time_types['m'],
start_time[2] - nb*time_types['d'] - nb*time_types['w'],
start_time[3],
start_time[4],
start_time[5],
start_time[6],
start_time[7],
start_time[8])
return convert_datestruct_to_datetext(start_time)
def count_comments(recID):
"""
Returns the number of comments made on a record.
"""
recID = int(recID)
query = """SELECT count(id) FROM cmtRECORDCOMMENT
WHERE id_bibrec=%i AND star_score=0"""
return run_sql(query % recID)[0][0]
def count_reviews(recID):
"""
Returns the number of reviews made on a record.
"""
recID = int(recID)
query = """SELECT count(id) FROM cmtRECORDCOMMENT
WHERE id_bibrec=%i AND star_score>0"""
return run_sql(query % recID)[0][0]
def get_first_comments_or_remarks(recID=-1,
ln=CFG_SITE_LANG,
nb_comments='all',
nb_reviews='all',
voted=-1,
reported=-1):
"""
Gets nb number comments/reviews or remarks.
In the case of comments, will get both comments and reviews
Comments and remarks sorted by most recent date, reviews sorted by highest helpful score
@param recID: record id
@param ln: language
@param nb: number of comment/reviews or remarks to get
@param voted: 1 if user has voted for a remark
@param reported: 1 if user has reported a comment or review
@return if comment, tuple (comments, reviews) both being html of first nb comments/reviews
if remark, tuple (remakrs, None)
"""
warnings = []
errors = []
voted = wash_url_argument(voted, 'int')
reported = wash_url_argument(reported, 'int')
## check recID argument
if type(recID) is not int:
return ()
if recID >= 1: #comment or review. NB: suppressed reference to basket (handled in webbasket)
if CFG_WEBCOMMENT_ALLOW_REVIEWS:
res_reviews = query_retrieve_comments_or_remarks(recID=recID, display_order="hh", ranking=1)
nb_res_reviews = len(res_reviews)
## check nb argument
if type(nb_reviews) is int and nb_reviews < len(res_reviews):
first_res_reviews = res_reviews[:nb_reviews]
else:
first_res_reviews = res_reviews
if CFG_WEBCOMMENT_ALLOW_COMMENTS:
res_comments = query_retrieve_comments_or_remarks(recID=recID, display_order="od", ranking=0)
nb_res_comments = len(res_comments)
## check nb argument
if type(nb_comments) is int and nb_comments < len(res_comments):
first_res_comments = res_comments[:nb_comments]
else:
first_res_comments = res_comments
else: #error
errors.append(('ERR_WEBCOMMENT_RECID_INVALID', recID)) #!FIXME dont return error anywhere since search page
# comment
if recID >= 1:
comments = reviews = ""
if reported > 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED_GREEN_TEXT',))
elif reported == 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_NOT_RECORDED_RED_TEXT',))
if CFG_WEBCOMMENT_ALLOW_COMMENTS: # normal comments
comments = webcomment_templates.tmpl_get_first_comments_without_ranking(recID, ln, first_res_comments, nb_res_comments, warnings)
if CFG_WEBCOMMENT_ALLOW_REVIEWS: # ranked comments
#calculate average score
avg_score = calculate_avg_score(res_reviews)
if voted > 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_RECORDED_GREEN_TEXT',))
elif voted == 0:
warnings.append(('WRN_WEBCOMMENT_FEEDBACK_NOT_RECORDED_RED_TEXT',))
reviews = webcomment_templates.tmpl_get_first_comments_with_ranking(recID, ln, first_res_reviews, nb_res_reviews, avg_score, warnings)
return (comments, reviews)
# remark
else:
return(webcomment_templates.tmpl_get_first_remarks(first_res_comments, ln, nb_res_comments), None)
def calculate_avg_score(res):
"""
private function
Calculate the avg score of reviews present in res
@param res: tuple of tuple returned from query_retrieve_comments_or_remarks
@return a float of the average score rounded to the closest 0.5
"""
c_star_score = 6
avg_score = 0.0
nb_reviews = 0
for comment in res:
if comment[c_star_score] > 0:
avg_score += comment[c_star_score]
nb_reviews += 1
if nb_reviews == 0:
return 0.0
avg_score = avg_score / nb_reviews
avg_score_unit = avg_score - math.floor(avg_score)
if avg_score_unit < 0.25:
avg_score = math.floor(avg_score)
elif avg_score_unit > 0.75:
avg_score = math.floor(avg_score) + 1
else:
avg_score = math.floor(avg_score) + 0.5
if avg_score > 5:
avg_score = 5.0
return avg_score
def perform_request_add_comment_or_remark(recID=0,
uid=-1,
action='DISPLAY',
ln=CFG_SITE_LANG,
msg=None,
score=None,
note=None,
priority=None,
reviews=0,
comID=-1,
client_ip_address=None):
"""
Add a comment/review or remark
@param recID: record id
@param uid: user id
@param action: 'DISPLAY' to display add form
'SUBMIT' to submit comment once form is filled
'REPLY' to reply to an existing comment
@param ln: language
@param msg: the body of the comment/review or remark
@param score: star score of the review
@param note: title of the review
@param priority: priority of remark (int)
@param reviews: boolean, if enabled will add a review, if disabled will add a comment
@param comID: if replying, this is the comment id of the commetn are replying to
@return html add form if action is display or reply
html successful added form if action is submit
"""
warnings = []
errors = []
actions = ['DISPLAY', 'REPLY', 'SUBMIT']
_ = gettext_set_language(ln)
## check arguments
check_recID_is_in_range(recID, warnings, ln)
if uid <= 0:
errors.append(('ERR_WEBCOMMENT_UID_INVALID', uid))
return ('', errors, warnings)
user_contact_info = query_get_user_contact_info(uid)
nickname = ''
if user_contact_info:
if user_contact_info[0]:
nickname = user_contact_info[0]
# show the form
if action == 'DISPLAY':
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
return (webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings), errors, warnings)
elif not reviews and CFG_WEBCOMMENT_ALLOW_COMMENTS:
return (webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings), errors, warnings)
else:
errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',))
elif action == 'REPLY':
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
errors.append(('ERR_WEBCOMMENT_REPLY_REVIEW',))
return (webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings), errors, warnings)
elif not reviews and CFG_WEBCOMMENT_ALLOW_COMMENTS:
if comID > 0:
comment = query_get_comment(comID)
if comment:
user_info = get_user_info(comment[2])
if user_info:
date_creation = convert_datetext_to_dategui(str(comment[4]))
msg = _("%(x_name)s wrote on %(x_date)s:")% {'x_name': user_info[2], 'x_date': date_creation}
msg += "\n\n" + comment[3]
msg = email_quote_txt(text=msg)
return (webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings), errors, warnings)
else:
errors.append(('ERR_WEBCOMMENT_COMMENTS_NOT_ALLOWED',))
# check before submitting form
elif action == 'SUBMIT':
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
if note.strip() in ["", "None"] and not CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS:
warnings.append(('WRN_WEBCOMMENT_ADD_NO_TITLE',))
if score == 0 or score > 5:
warnings.append(("WRN_WEBCOMMENT_ADD_NO_SCORE",))
if msg.strip() in ["", "None"] and not CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS:
warnings.append(('WRN_WEBCOMMENT_ADD_NO_BODY',))
# if no warnings, submit
if len(warnings) == 0:
if reviews:
if check_user_can_review(recID, client_ip_address, uid):
success = query_add_comment_or_remark(reviews, recID=recID, uid=uid, msg=msg,
note=note, score=score, priority=0,
client_ip_address=client_ip_address)
else:
warnings.append('WRN_WEBCOMMENT_CANNOT_REVIEW_TWICE')
success = 1
else:
if check_user_can_comment(recID, client_ip_address, uid):
success = query_add_comment_or_remark(reviews, recID=recID, uid=uid, msg=msg,
note=note, score=score, priority=0,
client_ip_address=client_ip_address)
else:
warnings.append('WRN_WEBCOMMENT_TIMELIMIT')
success = 1
if success > 0:
if CFG_WEBCOMMENT_ADMIN_NOTIFICATION_LEVEL > 0:
notify_admin_of_new_comment(comID=success)
return (webcomment_templates.tmpl_add_comment_successful(recID, ln, reviews, warnings), errors, warnings)
else:
errors.append(('ERR_WEBCOMMENT_DB_INSERT_ERROR'))
# if are warnings or if inserting comment failed, show user where warnings are
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
return (webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, msg, score, note, warnings), errors, warnings)
else:
return (webcomment_templates.tmpl_add_comment_form(recID, uid, nickname, ln, msg, warnings), errors, warnings)
# unknown action send to display
else:
warnings.append(('WRN_WEBCOMMENT_ADD_UNKNOWN_ACTION',))
if reviews and CFG_WEBCOMMENT_ALLOW_REVIEWS:
return (webcomment_templates.tmpl_add_comment_form_with_ranking(recID, uid, ln, msg, score, note, warnings), errors, warnings)
else:
return (webcomment_templates.tmpl_add_comment_form(recID, uid, ln, msg, warnings), errors, warnings)
return ('', errors, warnings)
def notify_admin_of_new_comment(comID):
"""
Sends an email to the admin with details regarding comment with ID = comID
"""
comment = query_get_comment(comID)
if len(comment) > 0:
(comID2,
id_bibrec,
id_user,
body,
date_creation,
star_score, nb_votes_yes, nb_votes_total,
title,
nb_abuse_reports) = comment
else:
return
user_info = query_get_user_contact_info(id_user)
if len(user_info) > 0:
(nickname, email, last_login) = user_info
if not len(nickname) > 0:
nickname = email.split('@')[0]
else:
nickname = email = last_login = "ERROR: Could not retrieve"
from invenio.search_engine import print_record
record = print_record(recID=id_bibrec, format='hs')
review_stuff = '''
Star score = %s
Title = %s''' % (star_score, title)
out = '''
The following %(comment_or_review)s has just been posted (%(date)s).
AUTHOR:
Nickname = %(nickname)s
Email = %(email)s
User ID = %(uid)s
RECORD CONCERNED:
Record ID = %(recID)s
Record =
<!-- start record details -->
%(record_details)s
<!-- end record details -->
%(comment_or_review_caps)s:
%(comment_or_review)s ID = %(comID)s %(review_stuff)s
Body =
<!-- start body -->
%(body)s
<!-- end body -->
ADMIN OPTIONS:
-To delete comment go to %(weburl)s/admin/webcomment/webcommentadmin.py/delete?comid=%(comID)s
+To delete comment go to %(siteurl)s/admin/webcomment/webcommentadmin.py/delete?comid=%(comID)s
''' % \
{ 'comment_or_review' : star_score>0 and 'review' or 'comment',
'comment_or_review_caps': star_score>0 and 'REVIEW' or 'COMMENT',
'date' : date_creation,
'nickname' : nickname,
'email' : email,
'uid' : id_user,
'recID' : id_bibrec,
'record_details' : record,
'comID' : comID2,
'review_stuff' : star_score>0 and review_stuff or "",
'body' : body.replace('<br />','\n'),
- 'weburl' : weburl
+ 'siteurl' : CFG_SITE_URL
}
from_addr = '%s WebComment <%s>' % (CFG_SITE_NAME, CFG_WEBALERT_ALERT_ENGINE_EMAIL)
to_addr = CFG_SITE_ADMIN_EMAIL
subject = "A new comment/review has just been posted"
send_email(from_addr, to_addr, subject, out)
def check_recID_is_in_range(recID, warnings=[], ln=CFG_SITE_LANG):
"""
Check that recID is >= 0
Append error messages to errors listi
@param recID: record id
@param warnings: the warnings list of the calling function
@return tuple (boolean, html) where boolean (1=true, 0=false)
and html is the body of the page to display if there was a problem
"""
# Make errors into a list if needed
if type(warnings) is not list:
errors = [warnings]
try:
recID = int(recID)
except:
pass
if type(recID) is int:
if recID > 0:
from invenio.search_engine import record_exists
success = record_exists(recID)
if success == 1:
return (1,"")
else:
warnings.append(('ERR_WEBCOMMENT_RECID_INEXISTANT', recID))
return (0, webcomment_templates.tmpl_record_not_found(status='inexistant', recID=recID, ln=ln))
elif recID == 0:
warnings.append(('ERR_WEBCOMMENT_RECID_MISSING',))
return (0, webcomment_templates.tmpl_record_not_found(status='missing', recID=recID, ln=ln))
else:
warnings.append(('ERR_WEBCOMMENT_RECID_INVALID', recID))
return (0, webcomment_templates.tmpl_record_not_found(status='invalid', recID=recID, ln=ln))
else:
warnings.append(('ERR_WEBCOMMENT_RECID_NAN', recID))
return (0, webcomment_templates.tmpl_record_not_found(status='nan', recID=recID, ln=ln))
def check_int_arg_is_in_range(value, name, errors, gte_value, lte_value=None):
"""
Check that variable with name 'name' >= gte_value and optionally <= lte_value
Append error messages to errors list
@param value: variable value
@param name: variable name
@param errors: list of error tuples (error_id, value)
@param gte_value: greater than or equal to value
@param lte_value: less than or equal to value
@return boolean (1=true, 0=false)
"""
# Make errors into a list if needed
if type(errors) is not list:
errors = [errors]
if type(value) is not int or type(gte_value) is not int:
errors.append(('ERR_WEBCOMMENT_PROGRAMNING_ERROR',))
return 0
if type(value) is not int:
errors.append(('ERR_WEBCOMMENT_ARGUMENT_NAN', value))
return 0
if value < gte_value:
errors.append(('ERR_WEBCOMMENT_ARGUMENT_INVALID', value))
return 0
if lte_value:
if type(lte_value) is not int:
errors.append(('ERR_WEBCOMMENT_PROGRAMNING_ERROR',))
return 0
if value > lte_value:
errors.append(('ERR_WEBCOMMENT_ARGUMENT_INVALID', value))
return 0
return 1
def get_mini_reviews(recid, ln=CFG_SITE_LANG):
"""
Returns the web controls to add reviews to a record from the
detailed record pages mini-panel.
@param recid the id of the displayed record
@param ln the user's language
"""
if CFG_WEBCOMMENT_ALLOW_SHORT_REVIEWS:
action = 'SUBMIT'
else:
action = 'DISPLAY'
reviews = query_retrieve_comments_or_remarks(recid, ranking=1)
return webcomment_templates.tmpl_mini_review(recid, ln, action=action,
avg_score=calculate_avg_score(reviews),
nb_comments_total=len(reviews))
diff --git a/modules/webcomment/lib/webcomment_regression_tests.py b/modules/webcomment/lib/webcomment_regression_tests.py
index 9976ffe7d..ad31b672e 100644
--- a/modules/webcomment/lib/webcomment_regression_tests.py
+++ b/modules/webcomment/lib/webcomment_regression_tests.py
@@ -1,86 +1,86 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebComment Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class WebCommentWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebComment web pages whether they are up or not."""
def test_your_baskets_pages_availability(self):
"""webcomment - availability of comments pages"""
- baseurl = weburl + '/record/10/comments/'
+ baseurl = CFG_SITE_URL + '/record/10/comments/'
_exports = ['', 'display', 'add', 'vote', 'report']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_webcomment_admin_interface_availability(self):
"""webcomment - availability of WebComment Admin interface pages"""
- baseurl = weburl + '/admin/webcomment/webcommentadmin.py/'
+ baseurl = CFG_SITE_URL + '/admin/webcomment/webcommentadmin.py/'
_exports = ['', 'comments', 'delete', 'users']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_webcomment_admin_guide_availability(self):
"""webcomment - availability of WebComment Admin Guide"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/admin/webcomment-admin-guide',
+ test_web_page_content(CFG_SITE_URL + '/help/admin/webcomment-admin-guide',
expected_text="WebComment Admin Guide"))
return
def test_webcomment_mini_review_availability(self):
"""webcomment - availability of mini-review panel on detailed record page"""
- url = weburl + '/record/12'
+ url = CFG_SITE_URL + '/record/12'
error_messages = test_web_page_content(url,
expected_text="(Not yet reviewed)")
test_suite = make_test_suite(WebCommentWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/webcomment/lib/webcomment_templates.py b/modules/webcomment/lib/webcomment_templates.py
index 4d0d6aa65..b83e16a4e 100644
--- a/modules/webcomment/lib/webcomment_templates.py
+++ b/modules/webcomment/lib/webcomment_templates.py
@@ -1,1419 +1,1419 @@
# -*- coding: utf-8 -*-
## $Id$
## Comments and reviews for records.
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""HTML Templates for commenting features """
__revision__ = "$Id$"
# CDS Invenio imports
from invenio.webuser import get_user_info
from invenio.dateutils import convert_datetext_to_dategui
from invenio.webmessage_mailutils import email_quoted_txt2html
-from invenio.config import weburl, \
+from invenio.config import CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL,\
CFG_WEBCOMMENT_ALLOW_REVIEWS, \
CFG_WEBCOMMENT_ALLOW_COMMENTS
from invenio.messages import gettext_set_language
class Template:
"""templating class, refer to webcomment.py for examples of call"""
def tmpl_get_first_comments_without_ranking(self, recID, ln, comments, nb_comments_total, warnings):
"""
@param recID: record id
@param ln: language
@param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks
@param nb_comments_total: total number of comments for this record
@param warnings: list of warning tuples (warning_msg, arg1, arg2, ...)
@return html of comments
"""
# load the right message language
_ = gettext_set_language(ln)
# naming data fields of comments
c_nickname = 0
c_user_id = 1
c_date_creation = 2
c_body = 3
c_id = 4
warnings = self.tmpl_warnings(warnings, ln)
# comments
comment_rows = ''
for comment in comments:
if comment[c_nickname]:
nickname = comment[c_nickname]
display = nickname
else:
(uid, nickname, display) = get_user_info(comment[c_user_id])
messaging_link = self.create_messaging_link(nickname, display, ln)
comment_rows += """
<tr>
<td>"""
- report_link = '%s/record/%s/comments/report?ln=%s&amp;comid=%s' % (weburl, recID, ln, comment[c_id])
- reply_link = '%s/record/%s/comments/add?ln=%s&amp;comid=%s&amp;action=REPLY' % (weburl, recID, ln, comment[c_id])
+ report_link = '%s/record/%s/comments/report?ln=%s&amp;comid=%s' % (CFG_SITE_URL, recID, ln, comment[c_id])
+ reply_link = '%s/record/%s/comments/add?ln=%s&amp;comid=%s&amp;action=REPLY' % (CFG_SITE_URL, recID, ln, comment[c_id])
comment_rows += self.tmpl_get_comment_without_ranking(ln=ln, nickname=messaging_link,
date_creation=comment[c_date_creation],
body=comment[c_body],
report_link=report_link, reply_link=reply_link)
comment_rows += """
<br />
<br />
</td>
</tr>"""
# write button
write_button_label = _("Write a comment")
- write_button_link = '%s/record/%s/comments/add' % (weburl, recID)
+ write_button_link = '%s/record/%s/comments/add' % (CFG_SITE_URL, recID)
write_button_form = '<input type="hidden" name="ln" value="%s"/>' % ln
write_button_form = self.createhiddenform(action=write_button_link, method="get", text=write_button_form, button=write_button_label)
# output
if nb_comments_total > 0:
out = warnings
comments_label = len(comments) > 1 and _("Showing the latest %i comments:") % len(comments) \
or ""
out += """
<table>
<tr>
<td class="blocknote">%(comment_title)s</td>
</tr>
</table>
%(comments_label)s<br />
<table border="0" cellspacing="5" cellpadding="5" width="100%%">
%(comment_rows)s
</table>
%(view_all_comments_link)s
<br />
<br />
%(write_button_form)s<br />""" % \
{'comment_title': _("Discuss this document"),
'comments_label': comments_label,
'nb_comments_total' : nb_comments_total,
'recID': recID,
'comment_rows': comment_rows,
'tab': '&nbsp;'*4,
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
's': nb_comments_total>1 and 's' or "",
'view_all_comments_link': nb_comments_total>0 and '''<a href="%s/record/%s/comments/display">View all %s comments</a>''' \
- % (weburl, recID, nb_comments_total) or "",
+ % (CFG_SITE_URL, recID, nb_comments_total) or "",
'write_button_form': write_button_form,
'nb_comments': len(comments)
}
else:
out = """
<!-- comments title table -->
<table>
<tr>
<td class="blocknote">%(discuss_label)s:</td>
</tr>
</table>
%(detailed_info)s
<br />
%(form)s
<br />""" % {'form': write_button_form,
'discuss_label': _("Discuss this document"),
'detailed_info': _("Start a discussion about any aspect of this document.")
}
return out
def tmpl_record_not_found(self, status='missing', recID="", ln=CFG_SITE_LANG):
"""
Displays a page when bad or missing record ID was given.
@param status: 'missing' : no recID was given
'inexistant': recID doesn't have an entry in the database
'nan' : recID is not a number
'invalid' : recID is an error code, i.e. in the interval [-99,-1]
@param return: body of the page
"""
_ = gettext_set_language(ln)
if status == 'inexistant':
body = _("Sorry, the record %s does not seem to exist.") % (recID,)
elif status in ('nan', 'invalid'):
body = _("Sorry, %s is not a valid ID value.") % (recID,)
else:
body = _("Sorry, no record ID was provided.")
body += "<br /><br />"
- link = "<a href=\"%s?ln=%s\">%s</a>." % (weburl, ln, CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME))
+ link = "<a href=\"%s?ln=%s\">%s</a>." % (CFG_SITE_URL, ln, CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME))
body += _("You may want to start browsing from %s") % link
return body
def tmpl_get_first_comments_with_ranking(self, recID, ln, comments=None, nb_comments_total=None, avg_score=None, warnings=[]):
"""
@param recID: record id
@param ln: language
@param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks
@param nb_comments_total: total number of comments for this record
@param avg_score: average score of all reviews
@param warnings: list of warning tuples (warning_msg, arg1, arg2, ...)
@return html of comments
"""
# load the right message language
_ = gettext_set_language(ln)
# naming data fields of comments
c_nickname = 0
c_user_id = 1
c_date_creation = 2
c_body = 3
c_nb_votes_yes = 4
c_nb_votes_total = 5
c_star_score = 6
c_title = 7
c_id = 8
warnings = self.tmpl_warnings(warnings, ln)
#stars
if avg_score > 0:
avg_score_img = 'stars-' + str(avg_score).split('.')[0] + '-' + str(avg_score).split('.')[1] + '.png'
else:
avg_score_img = "stars-0-0.png"
# voting links
- useful_dict = { 'weburl' : weburl,
+ useful_dict = { 'siteurl' : CFG_SITE_URL,
'recID' : recID,
'ln' : ln,
'yes_img' : 'smchk_gr.gif', #'yes.gif',
'no_img' : 'iconcross.gif' #'no.gif'
}
- link = '<a href="%(weburl)s/record/%(recID)s/reviews/vote?ln=%(ln)s&amp;comid=%%(comid)s' % useful_dict
+ link = '<a href="%(siteurl)s/record/%(recID)s/reviews/vote?ln=%(ln)s&amp;comid=%%(comid)s' % useful_dict
useful_yes = link + '&amp;com_value=1">' + _("Yes") + '</a>'
useful_no = link + '&amp;com_value=-1">' + _("No") + '</a>'
#comment row
comment_rows = ' '
for comment in comments:
if comment[c_nickname]:
nickname = comment[c_nickname]
display = nickname
else:
(uid, nickname, display) = get_user_info(comment[c_user_id])
messaging_link = self.create_messaging_link(nickname, display, ln)
comment_rows += '''
<tr>
<td>'''
- report_link = '%s/record/%s/reviews/report?ln=%s&amp;comid=%s' % (weburl, recID, ln, comment[c_id])
+ report_link = '%s/record/%s/reviews/report?ln=%s&amp;comid=%s' % (CFG_SITE_URL, recID, ln, comment[c_id])
comment_rows += self.tmpl_get_comment_with_ranking(ln=ln, nickname=messaging_link,
date_creation=comment[c_date_creation],
body=comment[c_body],
nb_votes_total=comment[c_nb_votes_total],
nb_votes_yes=comment[c_nb_votes_yes],
star_score=comment[c_star_score],
title=comment[c_title], report_link=report_link)
comment_rows += '''
%s %s / %s<br />''' % (_("Was this review helpful?"), useful_yes % {'comid':comment[c_id]}, useful_no % {'comid':comment[c_id]})
comment_rows += '''
<br />
</td>
</tr>'''
# write button
- write_button_link = '''%s/record/%s/reviews/add''' % (weburl, recID)
+ write_button_link = '''%s/record/%s/reviews/add''' % (CFG_SITE_URL, recID)
write_button_form = ' <input type="hidden" name="ln" value="%s"/>' % ln
write_button_form = self.createhiddenform(action=write_button_link, method="get", text=write_button_form, button=_("Write a review"))
if nb_comments_total > 0:
avg_score_img = str(avg_score_img)
avg_score = str(avg_score)
nb_comments_total = str(nb_comments_total)
score = '<b>'
score += _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \
- {'x_nb_score': '</b><img src="' + weburl + '/img/' + avg_score_img + '" alt="' + avg_score + '" />',
+ {'x_nb_score': '</b><img src="' + CFG_SITE_URL + '/img/' + avg_score_img + '" alt="' + avg_score + '" />',
'x_nb_reviews': nb_comments_total}
useful_label = _("Readers found the following %s reviews to be most helpful.")
useful_label %= len(comments) > 1 and len(comments) or ""
- view_all_comments_link ='<a href="%s/record/%s/reviews/display?ln=%s&amp;do=hh">' % (weburl, recID, ln)
+ view_all_comments_link ='<a href="%s/record/%s/reviews/display?ln=%s&amp;do=hh">' % (CFG_SITE_URL, recID, ln)
view_all_comments_link += _("View all %s reviews") % nb_comments_total
view_all_comments_link += '</a><br />'
out = warnings + """
<!-- review title table -->
<table>
<tr>
<td class="blocknote">%(comment_title)s:</td>
</tr>
</table>
%(score_label)s<br />
%(useful_label)s
<!-- review table -->
<table style="border: 0px; border-collapse: separate; border-spacing: 5px; padding: 5px; width: 100%%">
%(comment_rows)s
</table>
%(view_all_comments_link)s
%(write_button_form)s<br />
""" % \
{ 'comment_title' : _("Rate this document"),
'score_label' : score,
'useful_label' : useful_label,
'recID' : recID,
'view_all_comments' : _("View all %s reviews") % (nb_comments_total,),
'write_comment' : _("Write a review"),
'comment_rows' : comment_rows,
'tab' : '&nbsp;'*4,
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'view_all_comments_link': nb_comments_total>0 and view_all_comments_link or "",
'write_button_form' : write_button_form
}
else:
out = '''
<!-- review title table -->
<table>
<tr>
<td class="blocknote">%s:</td>
</tr>
</table>
%s<br />
%s
<br />''' % (_("Rate this document"),
_("Be the first to review this document."),
write_button_form)
return out
def tmpl_get_comment_without_ranking(self, ln, nickname, date_creation, body, reply_link=None, report_link=None):
"""
private function
@param ln: language
@param nickname: nickname
@param date_creation: date comment was written
@param body: comment body
@param reply_link: if want reply and report, give the http links
@param repot_link: if want reply and report, give the http links
@return html table of comment
"""
# load the right message language
_ = gettext_set_language(ln)
date_creation = convert_datetext_to_dategui(date_creation)
out = ''
final_body = email_quoted_txt2html(body)
title = _('%(x_name)s wrote on %(x_date)s:') % {'x_name': nickname,
'x_date': '<i>' + date_creation + '</i>'}
links = ''
if reply_link:
links += '<a href="' + reply_link +'">' + _("Reply") +'</a>'
if report_link:
links += ' | '
if report_link:
links += '<a href="' + report_link +'">' + _("Report abuse") + '</a>'
out += """
<div style="margin-bottom:20px;background:#F9F9F9;border:1px solid #DDD">%(title)s<br />
<blockquote>
%(body)s
</blockquote>
<br />
<div style="float:right">%(links)s</div>
</div>""" % \
- {'title' : '<div style="background-color:#EEE;padding:2px;"><img src="%s/img/user-icon-1-24x24.png" alt="" />&nbsp;%s</div>' % (weburl, title),
+ {'title' : '<div style="background-color:#EEE;padding:2px;"><img src="%s/img/user-icon-1-24x24.png" alt="" />&nbsp;%s</div>' % (CFG_SITE_URL, title),
'body' : final_body,
'links' : links}
return out
def tmpl_get_comment_with_ranking(self, ln, nickname, date_creation, body, nb_votes_total, nb_votes_yes, star_score, title, report_link=None):
"""
private function
@param ln: language
@param nickname: nickname
@param date_creation: date comment was written
@param body: comment body
@param nb_votes_total: total number of votes for this review
@param nb_votes_yes: number of positive votes for this record
@param star_score: star score for this record
@param title: title of review
@return html table of review
"""
# load the right message language
_ = gettext_set_language(ln)
if star_score > 0:
star_score_img = 'stars-' + str(star_score) + '-0.png'
else:
star_score_img = 'stars-0-0.png'
out = ""
date_creation = convert_datetext_to_dategui(date_creation)
reviewed_label = _("Reviewed by %(x_nickname)s on %(x_date)s") % {'x_nickname': nickname, 'x_date':date_creation}
useful_label = _("%(x_nb_people)i out of %(x_nb_total)i people found this review useful") % {'x_nb_people': nb_votes_yes,
'x_nb_total': nb_votes_total}
links = ''
if report_link:
links += '<a href="' + report_link +'">' + _("Report abuse") + '</a>'
_body = ''
if body != '':
_body = '''
<blockquote>
%s
</blockquote>''' % email_quoted_txt2html(body)
out += '''
<div style="background:#F9F9F9;border:1px solid #DDD">
<div style="background-color:#EEE;padding:2px;">
- <img src="%(weburl)s/img/%(star_score_img)s" alt="%(star_score)s" style="margin-right:10px;"/><b>%(title)s</b><br />
+ <img src="%(siteurl)s/img/%(star_score_img)s" alt="%(star_score)s" style="margin-right:10px;"/><b>%(title)s</b><br />
%(reviewed_label)s<br />
%(useful_label)s
</div>
%(body)s
</div>
-%(abuse)s''' % {'weburl' : weburl,
+%(abuse)s''' % {'siteurl' : CFG_SITE_URL,
'star_score_img': star_score_img,
'star_score' : star_score,
'title' : title,
'reviewed_label': reviewed_label,
'useful_label' : useful_label,
'body' : _body,
'abuse' : links
}
return out
def tmpl_get_comments(self, recID, ln,
nb_per_page, page, nb_pages,
display_order, display_since,
CFG_WEBCOMMENT_ALLOW_REVIEWS,
comments, total_nb_comments,
avg_score,
warnings,
border=0, reviews=0,
total_nb_reviews=0,
nickname='', uid=-1, note='',score=5):
"""
Get table of all comments
@param recID: record id
@param ln: language
@param nb_per_page: number of results per page
@param page: page number
@param display_order: hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param display_since: all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param CFG_WEBCOMMENT_ALLOW_REVIEWS: is ranking enable, get from config.py/CFG_WEBCOMMENT_ALLOW_REVIEWS
@param comments: tuple as returned from webcomment.py/query_retrieve_comments_or_remarks
@param total_nb_comments: total number of comments for this record
@param avg_score: average score of reviews for this record
@param warnings: list of warning tuples (warning_msg, color)
@param border: boolean, active if want to show border around each comment/review
@param reviews: boolean, enabled for reviews, disabled for comments
"""
# load the right message language
_ = gettext_set_language(ln)
# naming data fields of comments
if reviews:
c_nickname = 0
c_user_id = 1
c_date_creation = 2
c_body = 3
c_nb_votes_yes = 4
c_nb_votes_total = 5
c_star_score = 6
c_title = 7
c_id = 8
discussion = 'reviews'
- comments_link = '<a href="%s/record/%s/comments/">%s</a> (%i)' % (weburl, recID, _('Comments'), total_nb_comments)
+ comments_link = '<a href="%s/record/%s/comments/">%s</a> (%i)' % (CFG_SITE_URL, recID, _('Comments'), total_nb_comments)
reviews_link = '<b>%s (%i)</b>' % (_('Reviews'), total_nb_reviews)
add_comment_or_review = self.tmpl_add_comment_form_with_ranking(recID, uid, nickname, ln, '', score, note, warnings, show_title_p=True)
else:
c_nickname = 0
c_user_id = 1
c_date_creation = 2
c_body = 3
c_id = 4
discussion = 'comments'
comments_link = '<b>%s (%i)</b>' % (_('Comments'), total_nb_comments)
- reviews_link = '<a href="%s/record/%s/reviews/">%s</a> (%i)' % (weburl, recID, _('Reviews'), total_nb_reviews)
+ reviews_link = '<a href="%s/record/%s/reviews/">%s</a> (%i)' % (CFG_SITE_URL, recID, _('Reviews'), total_nb_reviews)
add_comment_or_review = self.tmpl_add_comment_form(recID, uid, nickname, ln, note, warnings)
# voting links
- useful_dict = { 'weburl' : weburl,
+ useful_dict = { 'siteurl' : CFG_SITE_URL,
'recID' : recID,
'ln' : ln,
'do' : display_order,
'ds' : display_since,
'nb' : nb_per_page,
'p' : page,
'reviews' : reviews,
'discussion' : discussion
}
- useful_yes = '<a href="%(weburl)s/record/%(recID)s/%(discussion)s/vote?ln=%(ln)s&amp;comid=%%(comid)s&amp;com_value=1&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(weburl)s/record/%(recID)s/%(discussion)s/display">' + _("Yes") + '</a>'
+ useful_yes = '<a href="%(siteurl)s/record/%(recID)s/%(discussion)s/vote?ln=%(ln)s&amp;comid=%%(comid)s&amp;com_value=1&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/record/%(recID)s/%(discussion)s/display">' + _("Yes") + '</a>'
useful_yes %= useful_dict
- useful_no = '<a href="%(weburl)s/record/%(recID)s/%(discussion)s/vote?ln=%(ln)s&amp;comid=%%(comid)s&amp;com_value=-1&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(weburl)s/record/%(recID)s/%(discussion)s/display">' + _("No") + '</a>'
+ useful_no = '<a href="%(siteurl)s/record/%(recID)s/%(discussion)s/vote?ln=%(ln)s&amp;comid=%%(comid)s&amp;com_value=-1&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/record/%(recID)s/%(discussion)s/display">' + _("No") + '</a>'
useful_no %= useful_dict
warnings = self.tmpl_warnings(warnings, ln)
- link_dic = { 'weburl' : weburl,
+ link_dic = { 'siteurl' : CFG_SITE_URL,
'module' : 'comments',
'function' : 'index',
'discussion': discussion,
'arguments' : 'do=%s&amp;ds=%s&amp;nb=%s' % (display_order, display_since, nb_per_page),
'arg_page' : '&amp;p=%s' % page,
'page' : page,
'rec_id' : recID}
## comments table
comments_rows = ''
for comment in comments:
if comment[c_nickname]:
_nickname = comment[c_nickname]
display = _nickname
else:
(uid, _nickname, display) = get_user_info(comment[c_user_id])
messaging_link = self.create_messaging_link(_nickname, display, ln)
# do NOT delete the HTML comment below. It is used for parsing... (I plead unguilty!)
comments_rows += """
<!-- start comment row -->
<tr>
<td>"""
if not reviews:
- report_link = '%(weburl)s/record/%(recID)s/comments/report?ln=%(ln)s&amp;comid=%%(comid)s&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(weburl)s/record/%(recID)s/comments/display' % useful_dict % {'comid':comment[c_id]}
- reply_link = '%(weburl)s/record/%(recID)s/comments/add?ln=%(ln)s&amp;action=REPLY&amp;comid=%%(comid)s' % useful_dict % {'comid':comment[c_id]}
+ report_link = '%(siteurl)s/record/%(recID)s/comments/report?ln=%(ln)s&amp;comid=%%(comid)s&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/record/%(recID)s/comments/display' % useful_dict % {'comid':comment[c_id]}
+ reply_link = '%(siteurl)s/record/%(recID)s/comments/add?ln=%(ln)s&amp;action=REPLY&amp;comid=%%(comid)s' % useful_dict % {'comid':comment[c_id]}
comments_rows += self.tmpl_get_comment_without_ranking(ln, messaging_link, comment[c_date_creation], comment[c_body], reply_link, report_link)
else:
- report_link = '%(weburl)s/record/%(recID)s/reviews/report?ln=%(ln)s&amp;comid=%%(comid)s&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(weburl)s/record/%(recID)s/reviews/display' % useful_dict % {'comid': comment[c_id]}
+ report_link = '%(siteurl)s/record/%(recID)s/reviews/report?ln=%(ln)s&amp;comid=%%(comid)s&amp;do=%(do)s&amp;ds=%(ds)s&amp;nb=%(nb)s&amp;p=%(p)s&amp;referer=%(siteurl)s/record/%(recID)s/reviews/display' % useful_dict % {'comid': comment[c_id]}
comments_rows += self.tmpl_get_comment_with_ranking(ln, messaging_link, comment[c_date_creation], comment[c_body], comment[c_nb_votes_total], comment[c_nb_votes_yes], comment[c_star_score], comment[c_title])
helpful_label = _("Was this review helpful?")
report_abuse_label = "(" + _("Report abuse") + ")"
comments_rows += """
<table>
<tr>
<td>%(helpful_label)s %(tab)s</td>
<td> %(yes)s </td>
<td> / </td>
<td> %(no)s </td>
<td class="reportabuse">%(tab)s%(tab)s<a href="%(report)s">%(report_abuse_label)s</a></td>
</tr>
</table>""" \
% {'helpful_label': helpful_label,
'yes' : useful_yes % {'comid':comment[c_id]},
'no' : useful_no % {'comid':comment[c_id]},
'report' : report_link % {'comid':comment[c_id]},
'report_abuse_label': report_abuse_label,
'tab' : '&nbsp;'*2}
# do NOT remove HTML comment below. It is used for parsing...
comments_rows += """
</td>
</tr>
<!-- end comment row -->"""
## page links
page_links = ''
# Previous
if page != 1:
link_dic['arg_page'] = 'p=%s' % (page - 1)
- page_links += '<a href=\"%(weburl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">&lt;&lt;</a> ' % link_dic
+ page_links += '<a href=\"%(siteurl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">&lt;&lt;</a> ' % link_dic
else:
page_links += ' %s ' % ('&nbsp;'*(len(_('Previous'))+7))
# Page Numbers
for i in range(1, nb_pages+1):
link_dic['arg_page'] = 'p=%s' % i
link_dic['page'] = '%s' % i
if i != page:
page_links += '''
- <a href=\"%(weburl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">%(page)s</a> ''' % link_dic
+ <a href=\"%(siteurl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">%(page)s</a> ''' % link_dic
else:
page_links += ''' <b>%s</b> ''' % i
# Next
if page != nb_pages:
link_dic['arg_page'] = 'p=%s' % (page + 1)
page_links += '''
- <a href=\"%(weburl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">&gt;&gt;</a> ''' % link_dic
+ <a href=\"%(siteurl)s/record/%(rec_id)s/%(discussion)s/%(function)s?%(arguments)s&amp;%(arg_page)s\">&gt;&gt;</a> ''' % link_dic
else:
page_links += '%s' % ('&nbsp;'*(len(_('Next'))+7))
## stuff for ranking if enabled
if reviews:
if avg_score > 0:
avg_score_img = 'stars-' + str(avg_score).split('.')[0] + '-' + str(avg_score).split('.')[1] + '.png'
else:
avg_score_img = "stars-0-0.png"
ranking_average = '<br /><b>'
ranking_average += _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \
- {'x_nb_score': '</b><img src="' + weburl + '/img/' + avg_score_img + '" alt="' + str(avg_score) + '" />',
+ {'x_nb_score': '</b><img src="' + CFG_SITE_URL + '/img/' + avg_score_img + '" alt="' + str(avg_score) + '" />',
'x_nb_reviews': str(total_nb_reviews)}
ranking_average += '<br />'
else:
ranking_average = ""
- write_button_link = '''%s/record/%s/%s/add''' % (weburl, recID, discussion)
+ write_button_link = '''%s/record/%s/%s/add''' % (CFG_SITE_URL, recID, discussion)
write_button_form = '<input type="hidden" name="ln" value="%s"/>'
write_button_form = self.createhiddenform(action=write_button_link,
method="get",
text=write_button_form,
button = reviews and _('Write a review') or _('Write a comment'))
if reviews:
total_label = _("There is a total of %s reviews")
else:
total_label = _("There is a total of %s comments")
total_label %= total_nb_comments
review_or_comment_first = ''
if reviews == 0 and total_nb_comments == 0:
review_or_comment_first = _("Start a discussion about any aspect of this document.")
elif reviews == 1 and total_nb_reviews == 0:
review_or_comment_first = _("Be the first to review this document.")
# do NOT remove the HTML comments below. Used for parsing
body = '''
%(comments_and_review_tabs)s
<!-- start comments table -->
<table style="border: %(border)spx solid black; width: 95%%; margin:10px;font-size:small">
%(comments_rows)s
</table>
<!-- end comments table -->
%(review_or_comment_first)s
<br />''' % \
{ 'record_label': _("Record"),
'back_label': _("Back to search results"),
'total_label': total_label,
'write_button_form' : write_button_form,
'write_button_form_again' : total_nb_comments>3 and write_button_form or "",
'comments_rows' : comments_rows,
'total_nb_comments' : total_nb_comments,
'comments_or_reviews' : reviews and _('review') or _('comment'),
'comments_or_reviews_title' : reviews and _('Review') or _('Comment'),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'module' : "comments",
'recid' : recID,
'ln' : ln,
'border' : border,
'ranking_avg' : ranking_average,
'comments_and_review_tabs' : CFG_WEBCOMMENT_ALLOW_REVIEWS and \
CFG_WEBCOMMENT_ALLOW_COMMENTS and \
'%s | %s <br />' % \
(comments_link, reviews_link) or '',
'review_or_comment_first' : review_or_comment_first
}
# form is not currently used. reserved for an eventual purpose
#form = """
# Display <select name="nb" size="1"> per page
# <option value="all">All</option>
# <option value="10">10</option>
# <option value="25">20</option>
# <option value="50">50</option>
# <option value="100" selected="selected">100</option>
# </select>
# comments per page that are <select name="ds" size="1">
# <option value="all" selected="selected">Any age</option>
# <option value="1d">1 day old</option>
# <option value="3d">3 days old</option>
# <option value="1w">1 week old</option>
# <option value="2w">2 weeks old</option>
# <option value="1m">1 month old</option>
# <option value="3m">3 months old</option>
# <option value="6m">6 months old</option>
# <option value="1y">1 year old</option>
# </select>
# and sorted by <select name="do" size="1">
# <option value="od" selected="selected">Oldest first</option>
# <option value="nd">Newest first</option>
# %s
# </select>
# """ % \
# (reviews==1 and '''
# <option value=\"hh\">most helpful</option>
# <option value=\"lh\">least helpful</option>
# <option value=\"hs\">highest star ranking</option>
# <option value=\"ls\">lowest star ranking</option>
# </select>''' or '''
# </select>''')
#
- #form_link = "%(weburl)s/%(module)s/%(function)s" % link_dic
+ #form_link = "%(siteurl)s/%(module)s/%(function)s" % link_dic
#form = self.createhiddenform(action=form_link, method="get", text=form, button='Go', recid=recID, p=1)
pages = """
<div>
%(v_label)s %(comments_or_reviews)s %(results_nb_lower)s-%(results_nb_higher)s <br />
%(page_links)s
</div>
""" % \
{'v_label': _("Viewing"),
'page_links': _("Page:") + page_links ,
'comments_or_reviews': reviews and _('review') or _('comment'),
'results_nb_lower': len(comments)>0 and ((page-1) * nb_per_page)+1 or 0,
'results_nb_higher': page == nb_pages and (((page-1) * nb_per_page) + len(comments)) or (page * nb_per_page)}
if nb_pages > 1:
#body = warnings + body + form + pages
body = warnings + body + pages + add_comment_or_review
else:
body = warnings + body + add_comment_or_review
return '<div style="margin-left:10px;margin-right:10px;">' + body + '</div>'
def create_messaging_link(self, to, display_name, ln=CFG_SITE_LANG):
"""prints a link to the messaging system"""
- link = "%s/yourmessages/write?msg_to=%s&amp;ln=%s" % (weburl, to, ln)
+ link = "%s/yourmessages/write?msg_to=%s&amp;ln=%s" % (CFG_SITE_URL, to, ln)
if to:
return '<a href="%s" class="maillink">%s</a>' % (link, display_name)
else:
return display_name
def createhiddenform(self, action="", method="get", text="", button="confirm", cnfrm='', **hidden):
"""
create select with hidden values and submit button
@param action: name of the action to perform on submit
@param method: 'get' or 'post'
@param text: additional text, can also be used to add non hidden input
@param button: value/caption on the submit button
@param cnfrm: if given, must check checkbox to confirm
@param **hidden: dictionary with name=value pairs for hidden input
@return html form
"""
output = """
<form action="%s" method="%s">""" % (action, method.lower().strip() in ['get','post'] and method or 'get')
output += """
<table style="width:90%">
<tr>
<td style="vertical-align: top">
"""
output += text + '\n'
if cnfrm:
output += """
<input type="checkbox" name="confirm" value="1" />"""
for key in hidden.keys():
if type(hidden[key]) is list:
for value in hidden[key]:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, value)
else:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, hidden[key])
output += """
</td>
</tr>
<tr>
<td>"""
output += """
<input class="adminbutton" type="submit" value="%s" />""" % (button, )
output += """
</td>
</tr>
</table>
</form>"""
return output
def tmpl_warnings(self, warnings, ln=CFG_SITE_LANG):
"""
Prepare the warnings list
@param warnings: list of warning tuples (warning_msg, arg1, arg2, etc)
@return html string of warnings
"""
red_text_warnings = ['WRN_WEBCOMMENT_FEEDBACK_NOT_RECORDED',
'WRN_WEBCOMMENT_ALREADY_VOTED']
green_text_warnings = ['WRN_WEBCOMMENT_FEEDBACK_RECORDED']
from invenio.errorlib import get_msgs_for_code_list
span_class = 'important'
out = ""
if type(warnings) is not list:
warnings = [warnings]
if len(warnings) > 0:
warnings_parsed = get_msgs_for_code_list(warnings, 'warning', ln)
for (warning_code, warning_text) in warnings_parsed:
if not warning_code.startswith('WRN'):
#display only warnings that begin with WRN to user
continue
if warning_code in red_text_warnings:
span_class = 'important'
elif warning_code in green_text_warnings:
span_class = 'exampleleader'
else:
span_class = 'important'
out += '''
<span class="%(span_class)s">%(warning)s</span><br />''' % \
{ 'span_class' : span_class,
'warning' : warning_text }
return out
else:
return ""
def tmpl_add_comment_form(self, recID, uid, nickname, ln, msg, warnings):
"""
Add form for comments
@param recID: record id
@param uid: user id
@param ln: language
@param msg: comment body contents for when refreshing due to warning
@param warnings: list of warning tuples (warning_msg, color)
@return html add comment form
"""
_ = gettext_set_language(ln)
- link_dic = { 'weburl' : weburl,
+ link_dic = { 'siteurl' : CFG_SITE_URL,
'module' : 'comments',
'function' : 'add',
'arguments' : 'ln=%s&amp;action=%s' % (ln, 'SUBMIT'),
'recID' : recID}
# FIXME a cleaner handling of nicknames is needed.
if not nickname:
(uid, nickname, display) = get_user_info(uid)
if nickname:
note = _("Note: Your nickname, %s, will be displayed as author of this comment") % ('<i>' + nickname + '</i>')
else:
(uid, nickname, display) = get_user_info(uid)
link = '<a href="%s/youraccount/edit">' % CFG_SITE_SECURE_URL
note = _("Note: you have not %(x_url_open)sdefined your nickname%(x_url_close)s. %(x_nickname)s will be displayed as the author of this comment.") % \
{'x_url_open': link,
'x_url_close': '</a>',
'x_nickname': ' <br /><i>' + display + '</i>'}
#from invenio.search_engine import print_record
#record_details = print_record(recID=recID, format='hb', ln=ln)
warnings = self.tmpl_warnings(warnings, ln)
form = """<div><h2>%(add_comment)s</h2>
<textarea name="msg" cols="80" rows="20" style="width:90%%">%(msg)s</textarea>
<br />
<span class="reportabuse">%(note)s</span>
</div>
""" % {'msg': msg,
'note': note,
#'record': record_details,
'record_label': _("Article") + ":",
'comment_label': _("Comment") + ":",
'add_comment': _('Add comment')}
- form_link = "%(weburl)s/record/%(recID)s/comments/%(function)s?%(arguments)s" % link_dic
+ form_link = "%(siteurl)s/record/%(recID)s/comments/%(function)s?%(arguments)s" % link_dic
form = self.createhiddenform(action=form_link, method="post", text=form, button='Add comment')
return warnings + form
def tmpl_add_comment_form_with_ranking(self, recID, uid, nickname, ln, msg, score, note,
warnings, show_title_p=False):
"""
Add form for reviews
@param recID: record id
@param uid: user id
@param ln: language
@param msg: comment body contents for when refreshing due to warning
@param score: review score
@param note: review title
@param warnings: list of warning tuples (warning_msg, color)
@param show_title_p: if True, prefix the form with "Add Review" as title
@return html add review form
"""
_ = gettext_set_language(ln)
- link_dic = { 'weburl' : weburl,
+ link_dic = { 'siteurl' : CFG_SITE_URL,
'module' : 'comments',
'function' : 'add',
'arguments' : 'ln=%s&amp;action=%s' % (ln, 'SUBMIT'),
'recID' : recID}
warnings = self.tmpl_warnings(warnings, ln)
#from search_engine import print_record
#record_details = print_record(recID=recID, format='hb', ln=ln)
if nickname:
note_label = _("Note: Your nickname, %s, will be displayed as the author of this review.")
note_label %= ('<i>' + nickname + '</i>')
else:
(uid, nickname, display) = get_user_info(uid)
link = '<a href="%s/youraccount/edit">' % CFG_SITE_SECURE_URL
note_label = _("Note: you have not %(x_url_open)sdefined your nickname%(x_url_close)s. %(x_nickname)s will be displayed as the author of this comment.") % \
{'x_url_open': link,
'x_url_close': '</a>',
'x_nickname': ' <br /><i>' + display + '</i>'}
selected0 = ''
selected1 = ''
selected2 = ''
selected3 = ''
selected4 = ''
selected5 = ''
if score == 0:
selected0 = ' selected="selected"'
elif score == 1:
selected1 = ' selected="selected"'
elif score == 2:
selected2 = ' selected="selected"'
elif score == 3:
selected3 = ' selected="selected"'
elif score == 4:
selected4 = ' selected="selected"'
elif score == 5:
selected5 = ' selected="selected"'
form = """%(add_review)s
<table style="width: 100%%">
<tr>
<td style="padding-bottom: 10px;">%(rate_label)s:
<select name=\"score\" size=\"1\">
<option value=\"0\"%(selected0)s>-%(select_label)s-</option>
<option value=\"5\"%(selected5)s>***** (best)</option>
<option value=\"4\"%(selected4)s>****</option>
<option value=\"3\"%(selected3)s>***</option>
<option value=\"2\"%(selected2)s>**</option>
<option value=\"1\"%(selected1)s>* (worst)</option>
</select>
</td>
</tr>
<tr>
<td>%(title_label)s:</td>
</tr>
<tr>
<td style="padding-bottom: 10px;">
<input type="text" name="note" maxlength="250" style="width:90%%" value="%(note)s" />
</td>
</tr>
<tr>
<td>%(write_label)s:</td>
</tr>
<tr>
<td>
<textarea name="msg" cols="80" rows="20" style="width:90%%">%(msg)s</textarea>
</td>
</tr>
<tr>
<td class="reportabuse">%(note_label)s</td></tr>
</table>
""" % {'article_label': _('Article'),
'rate_label': _("Rate this article"),
'select_label': _("Select a score"),
'title_label': _("Give a title to your review"),
'write_label': _("Write your review"),
'note_label': note_label,
'note' : note!='' and note or "",
'msg' : msg!='' and msg or "",
#'record' : record_details
'add_review': show_title_p and ('<h2>'+_('Add review')+'</h2>') or '',
'selected0': selected0,
'selected1': selected1,
'selected2': selected2,
'selected3': selected3,
'selected4': selected4,
'selected5': selected5
}
- form_link = "%(weburl)s/record/%(recID)s/reviews/%(function)s?%(arguments)s" % link_dic
+ form_link = "%(siteurl)s/record/%(recID)s/reviews/%(function)s?%(arguments)s" % link_dic
form = self.createhiddenform(action=form_link, method="post", text=form, button=_('Add Review'))
return warnings + form
def tmpl_add_comment_successful(self, recID, ln, reviews, warnings):
"""
@param recID: record id
@param ln: language
@return html page of successfully added comment/review
"""
_ = gettext_set_language(ln)
- link_dic = { 'weburl' : weburl,
+ link_dic = { 'siteurl' : CFG_SITE_URL,
'module' : 'comments',
'function' : 'display',
'arguments' : 'ln=%s&amp;do=od' % ln,
'recID' : recID,
'discussion': reviews==1 and 'reviews' or 'comments'}
- link = "%(weburl)s/record/%(recID)s/%(discussion)s/%(function)s?%(arguments)s" % link_dic
+ link = "%(siteurl)s/record/%(recID)s/%(discussion)s/%(function)s?%(arguments)s" % link_dic
if warnings:
out = self.tmpl_warnings(warnings, ln) + '<br /><br />'
else:
if reviews:
out = _("Your review was successfully added.") + '<br /><br />'
else:
out = _("Your comment was successfully added.") + '<br /><br />'
out += '<a href="%s">' % link
out += _('Back to record') + '</a>'
return out
def tmpl_create_multiple_actions_form(self,
form_name="",
form_action="",
method="get",
action_display={},
action_field_name="",
button_label="",
button_name="",
content="",
**hidden):
""" Creates an HTML form with a multiple choice of actions and a button to select it.
@param form_action: link to the receiver of the formular
@param form_name: name of the HTML formular
@param method: either 'GET' or 'POST'
@param action_display: dictionary of actions.
action is HTML name (name of action)
display is the string provided in the popup
@param action_field_name: html name of action field
@param button_label: what's written on the button
@param button_name: html name of the button
@param content: what's inside te formular
@param **hidden: dictionary of name/value pairs of hidden fields.
"""
output = """
<form action="%s" method="%s">""" % (form_action, method)
output += """
<table>
<tr>
<td style="vertical-align: top" colspan="2">
"""
output += content + '\n'
for key in hidden.keys():
if type(hidden[key]) is list:
for value in hidden[key]:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, value)
else:
output += """
<input type="hidden" name="%s" value="%s" />""" % (key, hidden[key])
output += """
</td>
</tr>
<tr>
<td style="text-align:right;">"""
if type(action_display) is dict and len(action_display.keys()):
output += """
<select name="%s">""" % action_field_name
for (key, value) in action_display.items():
output += """
<option value="%s">%s</option>""" % (key, value)
output += """
</select>"""
output += """
</td>
<td style="text-align:left;">
<input class="adminbutton" type="submit" value="%s" name="%s"/>""" % (button_label, button_name)
output += """
</td>
</tr>
</table>
</form>"""
return output
def tmpl_admin_index(self, ln):
"""
Index page
"""
# load the right message language
_ = gettext_set_language(ln)
out = '<ol>'
if CFG_WEBCOMMENT_ALLOW_COMMENTS or CFG_WEBCOMMENT_ALLOW_REVIEWS:
if CFG_WEBCOMMENT_ALLOW_COMMENTS:
- out += '<li><a href="%(weburl)s/admin/webcomment/webcommentadmin.py/comments?ln=%(ln)s&amp;reviews=0">%(reported_cmt_label)s</a></li>' % \
- {'weburl': weburl, 'ln': ln, 'reported_cmt_label': _("View all reported comments")}
+ out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/comments?ln=%(ln)s&amp;reviews=0">%(reported_cmt_label)s</a></li>' % \
+ {'siteurl': CFG_SITE_URL, 'ln': ln, 'reported_cmt_label': _("View all reported comments")}
if CFG_WEBCOMMENT_ALLOW_REVIEWS:
- out += '<li><a href="%(weburl)s/admin/webcomment/webcommentadmin.py/comments?ln=%(ln)s&amp;reviews=1">%(reported_rev_label)s</a></li>' % \
- {'weburl': weburl, 'ln': ln, 'reported_rev_label': _("View all reported reviews")}
+ out += '<li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/comments?ln=%(ln)s&amp;reviews=1">%(reported_rev_label)s</a></li>' % \
+ {'siteurl': CFG_SITE_URL, 'ln': ln, 'reported_rev_label': _("View all reported reviews")}
out += """
- <li><a href="%(weburl)s/admin/webcomment/webcommentadmin.py/delete?ln=%(ln)s&amp;comid=-1">%(delete_label)s</a></li>
- <li><a href="%(weburl)s/admin/webcomment/webcommentadmin.py/users?ln=%(ln)s">%(view_users)s</a></li>
- <li><a href="%(weburl)s/help/admin/webcomment-admin-guide">%(guide)s</a></li>
- """ % {'weburl' : weburl,
+ <li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/delete?ln=%(ln)s&amp;comid=-1">%(delete_label)s</a></li>
+ <li><a href="%(siteurl)s/admin/webcomment/webcommentadmin.py/users?ln=%(ln)s">%(view_users)s</a></li>
+ <li><a href="%(siteurl)s/help/admin/webcomment-admin-guide">%(guide)s</a></li>
+ """ % {'siteurl' : CFG_SITE_URL,
'delete_label': _("Delete a specific comment/review (by ID)"),
'view_users': _("View all users who have been reported"),
'ln' : ln,
'guide' : _("Guide")}
else:
out += _("Comments and reviews are disabled") + '<br />'
out += '</ol>'
from invenio.bibrankadminlib import addadminbox
return addadminbox('<b>%s</b>'%_("Menu"), [out])
def tmpl_admin_delete_form(self, ln, warnings):
"""
@param warnings: list of warning_tuples where warning_tuple is (warning_message, text_color)
see tmpl_warnings, color is optional
"""
# load the right message language
_ = gettext_set_language(ln)
warnings = self.tmpl_warnings(warnings, ln)
out = '''
<br />
%s<br />
<br />'''%_("Please enter the ID of the comment/review so that you can view it before deciding whether to delete it or not")
form = '''
<table>
<tr>
<td>%s</td>
<td><input type=text name="comid" size="10" maxlength="10" value="" /></td>
</tr>
<tr>
<td><br /></td>
<tr>
</table>
<br />
''' %_("Comment ID:")
- form_link = "%s/admin/webcomment/webcommentadmin.py/delete?ln=%s" % (weburl, ln)
+ form_link = "%s/admin/webcomment/webcommentadmin.py/delete?ln=%s" % (CFG_SITE_URL, ln)
form = self.createhiddenform(action=form_link, method="get", text=form, button=_('View Comment'))
return warnings + out + form
def tmpl_admin_users(self, ln, users_data):
"""
@param users_data: tuple of ct, i.e. (ct, ct, ...)
where ct is a tuple (total_number_reported, total_comments_reported, total_reviews_reported, total_nb_votes_yes_of_reported,
total_nb_votes_total_of_reported, user_id, user_email, user_nickname)
sorted by order of ct having highest total_number_reported
"""
_ = gettext_set_language(ln)
u_reports = 0
u_comment_reports = 1
u_reviews_reports = 2
u_nb_votes_yes = 3
u_nb_votes_total = 4
u_uid = 5
u_email = 6
u_nickname = 7
if not users_data:
return self.tmpl_warnings([(_("There have been no reports so far."), 'green')])
user_rows = ""
for utuple in users_data:
com_label = _("View all %s reported comments") % utuple[u_comment_reports]
com_link = '''<a href="%s/admin/webcomment/webcommentadmin.py/comments?ln=%s&amp;uid=%s&amp;reviews=0">%s</a><br />''' % \
- (weburl, ln, utuple[u_uid], com_label)
+ (CFG_SITE_URL, ln, utuple[u_uid], com_label)
rev_label = _("View all %s reported reviews") % utuple[u_reviews_reports]
rev_link = '''<a href="%s/admin/webcomment/webcommentadmin.py/comments?ln=%s&amp;uid=%s&amp;reviews=1">%s</a>''' % \
- (weburl, ln, utuple[u_uid], rev_label)
+ (CFG_SITE_URL, ln, utuple[u_uid], rev_label)
if not utuple[u_nickname]:
user_info = get_user_info(utuple[u_uid])
nickname = user_info[2]
else:
nickname = utuple[u_nickname]
if CFG_WEBCOMMENT_ALLOW_REVIEWS:
review_row = """
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>"""
review_row %= (utuple[u_nb_votes_yes],
utuple[u_nb_votes_total] - utuple[u_nb_votes_yes],
utuple[u_nb_votes_total])
else:
review_row = ''
user_rows += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(nickname)s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(email)s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(uid)s</td>%(review_row)s
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray; font-weight: bold;">%(reports)s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%(com_link)s%(rev_link)s</td>
</tr>""" % { 'nickname' : nickname,
'email' : utuple[u_email],
'uid' : utuple[u_uid],
'reports' : utuple[u_reports],
'review_row': review_row,
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'ln' : ln,
'com_link' : CFG_WEBCOMMENT_ALLOW_COMMENTS and com_link or "",
'rev_link' : CFG_WEBCOMMENT_ALLOW_REVIEWS and rev_link or ""
}
out = "<br />"
out += _("Here is a list, sorted by total number of reports, of all users who have had a comment reported at least once.")
out += """
<br />
<br />
<table class="admin_wvar" style="width: 100%%;">
<thead>
<tr class="adminheaderleft">
<th>"""
out += _("Nickname") + '</th>\n'
out += '<th>' + _("Email") + '</th>\n'
out += '<th>' + _("User ID") + '</th>\n'
if CFG_WEBCOMMENT_ALLOW_REVIEWS > 0:
out += '<th>' + _("Number positive votes") + '</th>\n'
out += '<th>' + _("Number negative votes") + '</th>\n'
out += '<th>' + _("Total number votes") + '</th>\n'
out += '<th>' + _("Total number of reports") + '</th>\n'
out += '<th>' + _("View all user's reported comments/reviews") + '</th>\n'
out += """
</tr>
</thead>
<tbody>%s
</tbody>
</table>
""" % user_rows
return out
def tmpl_admin_select_comment_checkbox(self, cmt_id):
""" outputs a checkbox named "comidXX" where XX is cmt_id """
return '<input type="checkbox" name="comid%i" />' % int(cmt_id)
def tmpl_admin_user_info(self, ln, nickname, uid, email):
""" prepares informations about a user"""
_ = gettext_set_language(ln)
out = """
%(nickname_label)s: %(messaging)s<br />
%(uid_label)s: %(uid)i<br />
%(email_label)s: <a href="mailto:%(email)s">%(email)s</a>"""
out %= {'nickname_label': _("Nickname"),
'messaging': self.create_messaging_link(uid, nickname, ln),
'uid_label': _("User ID"),
'uid': int(uid),
'email_label': _("Email"),
'email': email}
return out
def tmpl_admin_review_info(self, ln, reviews, nb_reports, cmt_id, rec_id):
""" outputs information about a review """
_ = gettext_set_language(ln)
if reviews:
reported_label = _("This review has been reported %i times")
else:
reported_label = _("This comment has been reported %i times")
reported_label %= int(nb_reports)
out = """
%(reported_label)s<br />
-<a href="%(weburl)s/record/%(rec_id)i?ln=%(ln)s">%(rec_id_label)s</a><br />
+<a href="%(siteurl)s/record/%(rec_id)i?ln=%(ln)s">%(rec_id_label)s</a><br />
%(cmt_id_label)s"""
out %= {'reported_label': reported_label,
'rec_id_label': _("Record") + ' #' + str(rec_id),
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
'rec_id': int(rec_id),
'cmt_id_label': _("Comment") + ' #' + str(cmt_id),
'ln': ln}
return out
def tmpl_admin_comments(self, ln, uid, comID, comment_data, reviews):
"""
@param comment_data: same type of tuple as that
which is returned by webcomment.py/query_retrieve_comments_or_remarks i.e.
tuple of comment where comment is
tuple (nickname,
date_creation,
body,
id) if ranking disabled or
tuple (nickname,
date_creation,
body,
nb_votes_yes,
nb_votes_total,
star_score,
title,
id)
"""
_ = gettext_set_language(ln)
comments = []
comments_info = []
checkboxes = []
users = []
for (cmt_tuple, meta_data) in comment_data:
if reviews:
comments.append(self.tmpl_get_comment_with_ranking(ln,
cmt_tuple[0],#nickname
cmt_tuple[2],#date_creation
cmt_tuple[3],#body
cmt_tuple[5],#nb_votes_total
cmt_tuple[4],#nb_votes_yes
cmt_tuple[6],#star_score
cmt_tuple[7]))#title
else:
comments.append(self.tmpl_get_comment_without_ranking(ln,
cmt_tuple[0],#nickname
cmt_tuple[2],#date_creation
cmt_tuple[3],#body
None, #reply_link
None)) #report_link
users.append(self.tmpl_admin_user_info(ln,
meta_data[0], #nickname
meta_data[1], #uid
meta_data[2]))#email
comments_info.append(self.tmpl_admin_review_info(ln,
reviews,
meta_data[5], # nb abuse reports
meta_data[3], # cmt_id
meta_data[4]))# rec_id
checkboxes.append(self.tmpl_admin_select_comment_checkbox(meta_data[3]))
- form_link = "%s/admin/webcomment/webcommentadmin.py/del_com?ln=%s" % (weburl, ln)
+ form_link = "%s/admin/webcomment/webcommentadmin.py/del_com?ln=%s" % (CFG_SITE_URL, ln)
out = """
<table class="admin_wvar" style="width:100%%;">
<thead>
<tr class="adminheaderleft">
<th>%(review_label)s</th>
<th>%(written_by_label)s</th>
<th>%(review_info_label)s</th>
<th>%(select_label)s</th>
</tr>
</thead>
<tbody>""" % {'review_label': reviews and _("Review") or _("Comment"),
'written_by_label': _("Written by"),
'review_info_label': _("General informations"),
'select_label': _("Select")}
for i in range (0, len(comments)):
out += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintd" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
</tr>""" % (comments[i], users[i], comments_info[i], checkboxes[i])
out += """
</tbody>
</table>"""
if reviews:
action_display = {
'delete': _('Delete selected reviews'),
'unreport': _('Suppress selected abuse report')
}
else:
action_display = {
'delete': _('Delete selected comments'),
'unreport': _('Suppress selected abuse report')
}
form = self.tmpl_create_multiple_actions_form(form_name="admin_comment",
form_action=form_link,
method="post",
action_display=action_display,
action_field_name='action',
button_label=_("OK"),
button_name="okbutton",
content=out)
if uid > 0:
header = '<br />'
if reviews:
header += _("Here are the reported reviews of user %s") % uid
else:
header += _("Here are the reported comments of user %s") % uid
header += '<br /><br />'
if comID > 0:
header = '<br />' +_("Here is comment/review %s")% comID + '<br /><br />'
if uid > 0 and comID > 0:
header = '<br />' + _("Here is comment/review %(x_cmtID)s written by user %(x_user)s") % {'x_cmtID': comID, 'x_user': uid}
header += '<br/ ><br />'
if uid == 0 and comID == 0:
header = '<br />'
if reviews:
header += _("Here are all reported reviews sorted by the most reported")
else:
header += _("Here are all reported comments sorted by the most reported")
header += "<br /><br />"
return header + form
def tmpl_admin_del_com(self, del_res, ln=CFG_SITE_LANG):
"""
@param del_res: list of the following tuple (comment_id, was_successfully_deleted),
was_successfully_deleted is boolean (0=false, >0=true
"""
_ = gettext_set_language(ln)
table_rows = ''
for deltuple in del_res:
table_rows += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
</tr>""" % (deltuple[0], deltuple[1]>0 and _("Yes") or "<span class=\"important\">" +_("No") + "</span>")
out = """
<table class="admin_wvar">
<tr class="adminheaderleft">
<td style="padding-right:10px;">%s</td>
<td>%s</td>
</tr>%s
<table>""" % (_("comment ID"), _("successfully deleted"), table_rows)
return out
def tmpl_admin_suppress_abuse_report(self, del_res, ln=CFG_SITE_LANG):
"""
@param del_res: list of the following tuple (comment_id, was_successfully_deleted),
was_successfully_deleted is boolean (0=false, >0=true
"""
_ = gettext_set_language(ln)
table_rows = ''
for deltuple in del_res:
table_rows += """
<tr>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
<td class="admintdleft" style="padding: 5px; border-bottom: 1px solid lightgray;">%s</td>
</tr>""" % (deltuple[0], deltuple[1]>0 and _("Yes") or "<span class=\"important\">" +_("No") + "</span>")
out = """
<table class="admin_wvar">
<tr class="adminheaderleft">
<td style ="padding-right: 10px;">%s</td>
<td>%s</td>
</tr>%s
<table>""" % (_("comment ID"), _("successfully suppressed abuse report"), table_rows)
return out
def tmpl_mini_review(self, recID, ln=CFG_SITE_LANG, action='SUBMIT',
avg_score=0, nb_comments_total=0):
"""Display the mini version of reviews (only the grading part)"""
_ = gettext_set_language(ln)
- url = '%s/record/%s/reviews/add?ln=%s&amp;action=%s' % (weburl, recID, ln, action)
+ url = '%s/record/%s/reviews/add?ln=%s&amp;action=%s' % (CFG_SITE_URL, recID, ln, action)
if avg_score > 0:
score = _("Average review score: %(x_nb_score)s based on %(x_nb_reviews)s reviews") % \
{'x_nb_score': '<b>%.1f</b>' % avg_score,
'x_nb_reviews': nb_comments_total}
else:
score = '(' +_("Not yet reviewed") + ')'
if avg_score == 5:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', 'full'
elif avg_score >= 4.5:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', 'half'
elif avg_score >= 4:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'full', ''
elif avg_score >= 3.5:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', 'half', ''
elif avg_score >= 3:
s1, s2, s3, s4, s5 = 'full', 'full', 'full', '', ''
elif avg_score >= 2.5:
s1, s2, s3, s4, s5 = 'full', 'full', 'half', '', ''
elif avg_score >= 2:
s1, s2, s3, s4, s5 = 'full', 'full', '', '', ''
elif avg_score >= 1.5:
s1, s2, s3, s4, s5 = 'full', 'half', '', '', ''
elif avg_score == 1:
s1, s2, s3, s4, s5 = 'full', '', '', '', ''
else:
s1, s2, s3, s4, s5 = '', '', '', '', ''
out = '''
<small class="detailedRecordActions">%(rate)s:</small><br /><br />
<div style="margin:auto;width:160px;">
<span style="display:none;">Rate this document:</span>
<div class="star %(s1)s" ><a href="%(url)s&amp;score=1">1</a>
<div class="star %(s2)s" ><a href="%(url)s&amp;score=2">2</a>
<div class="star %(s3)s" ><a href="%(url)s&amp;score=3">3</a>
<div class="star %(s4)s" ><a href="%(url)s&amp;score=4">4</a>
<div class="star %(s5)s" ><a href="%(url)s&amp;score=5">5</a></div></div></div></div></div>
<div style="clear:both">&nbsp;</div>
</div>
<small>%(score)s</small>
''' % {'url': url,
'score': score,
'rate': _("Rate this document"),
's1': s1,
's2': s2,
's3': s3,
's4': s4,
's5': s5
}
return out
diff --git a/modules/webcomment/lib/webcomment_webinterface.py b/modules/webcomment/lib/webcomment_webinterface.py
index bcf3a6f26..d0d5e2e68 100644
--- a/modules/webcomment/lib/webcomment_webinterface.py
+++ b/modules/webcomment/lib/webcomment_webinterface.py
@@ -1,427 +1,427 @@
# -*- coding: utf-8 -*-
## $Id$
## Comments and reviews for records.
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Comments and reviews for records: web interface """
__lastupdated__ = """$Date$"""
__revision__ = """$Id$"""
import urllib
from invenio.webcomment import check_recID_is_in_range, \
perform_request_display_comments_or_remarks,\
perform_request_add_comment_or_remark,\
perform_request_vote,\
perform_request_report
from invenio.config import CFG_SITE_LANG, \
- weburl, \
+ CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_WEBCOMMENT_ALLOW_COMMENTS,\
CFG_WEBCOMMENT_ALLOW_REVIEWS
from invenio.webuser import getUid, page_not_authorized, isGuestUser, collect_user_info
from invenio.webpage import page, pageheaderonly, pagefooteronly
from invenio.search_engine import create_navtrail_links, \
guess_primary_collection_of_a_record, \
get_colID, check_user_can_view_record
from invenio.urlutils import get_client_ip_address, \
redirect_to_url, \
wash_url_argument, make_canonical_urlargd
from invenio.messages import wash_language, gettext_set_language
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
websearch_templates = invenio.template.load('websearch')
class WebInterfaceCommentsPages(WebInterfaceDirectory):
"""Defines the set of /comments pages."""
_exports = ['', 'display', 'add', 'vote', 'report', 'index']
def __init__(self, recid=-1, reviews=0):
self.recid = recid
self.discussion = reviews # 0:comments, 1:reviews
def index(self, req, form):
"""
Redirects to display function
"""
return self.display(req, form)
def display(self, req, form):
"""
Display comments (reviews if enabled) associated with record having id recid where recid>0.
This function can also be used to display remarks associated with basket having id recid where recid<-99.
@param ln: language
@param recid: record id, integer
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param voted: boolean, active if user voted for a review, see vote function
@param reported: boolean, active if user reported a certain comment/review, see report function
@param reviews: boolean, enabled for reviews, disabled for comments
@return the full html page.
"""
argd = wash_urlargd(form, {'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'voted': (int, -1),
'reported': (int, -1),
})
_ = gettext_set_language(argd['ln'])
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
- weburl + user_info['uri']}, {})
+ CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
check_warnings = []
(ok, problem) = check_recID_is_in_range(self.recid, check_warnings, argd['ln'])
if ok:
(body, errors, warnings) = perform_request_display_comments_or_remarks(recID=self.recid,
display_order=argd['do'],
display_since=argd['ds'],
nb_per_page=argd['nb'],
page=argd['p'],
ln=argd['ln'],
voted=argd['voted'],
reported=argd['reported'],
reviews=self.discussion,
uid=uid)
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(self.recid)),
self.recid,
ln=argd['ln'])
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x,y: cmp(x[1],y[1]))
link_ln = ''
if argd['ln'] != CFG_SITE_LANG:
link_ln = '?ln=%s' % argd['ln']
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (weburl, self.recid, tab_id, link_ln), \
+ '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id in ['comments', 'reviews'],
unordered_tabs[tab_id]['enabled']) \
for (tab_id, order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
body = webstyle_templates.detailed_record_container(body,
self.recid,
tabs,
argd['ln'])
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req, self.recid, argd['ln'])
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid), ln=argd['ln'])
- navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (weburl, self.recid, argd['ln'])
+ navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
navtrail += ' &gt; <a class="navtrail">%s</a>' % (self.discussion==1 and _("Reviews") or _("Comments"))
return pageheaderonly(title=title,
navtrail=navtrail,
uid=uid,
verbose=1,
req=req,
language=argd['ln'],
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(argd['ln']) + \
body + \
websearch_templates.tmpl_search_pageend(argd['ln']) + \
pagefooteronly(lastupdated=__lastupdated__, language=argd['ln'], req=req)
else:
return page(title=_("Record Not Found"),
body=problem,
uid=uid,
verbose=1,
req=req,
language=argd['ln'],
warnings=check_warnings, errors=[],
navmenuid='search')
# Return the same page wether we ask for /record/123 or /record/123/
__call__ = index
def add(self, req, form):
"""
Add a comment (review) to record with id recid where recid>0
Also works for adding a remark to basket with id recid where recid<-99
@param ln: languange
@param recid: record id
@param action: 'DISPLAY' to display add form
'SUBMIT' to submit comment once form is filled
'REPLY' to reply to an already existing comment
@param msg: the body of the comment/review or remark
@param score: star score of the review
@param note: title of the review
@param comid: comment id, needed for replying
@return the full html page.
"""
argd = wash_urlargd(form, {'action': (str, "DISPLAY"),
'msg': (str, ""),
'note': (str, ''),
'score': (int, 0),
'comid': (int, -1),
})
_ = gettext_set_language(argd['ln'])
actions = ['DISPLAY', 'REPLY', 'SUBMIT']
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
- weburl + user_info['uri']}, {})
+ CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
client_ip_address = get_client_ip_address(req)
check_warnings = []
(ok, problem) = check_recID_is_in_range(self.recid, check_warnings, argd['ln'])
if ok:
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req,
self.recid,
argd['ln'])
navtrail = create_navtrail_links(cc=guess_primary_collection_of_a_record(self.recid))
- navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (weburl, self.recid, argd['ln'])
+ navtrail += ' &gt; <a class="navtrail" href="%s/record/%s?ln=%s">'% (CFG_SITE_URL, self.recid, argd['ln'])
navtrail += title
navtrail += '</a>'
- navtrail += '&gt; <a class="navtrail" href="%s/record/%s/%s/?ln=%s">%s</a>' % (weburl,
+ navtrail += '&gt; <a class="navtrail" href="%s/record/%s/%s/?ln=%s">%s</a>' % (CFG_SITE_URL,
self.recid,
self.discussion==1 and 'reviews' or 'comments',
argd['ln'],
self.discussion==1 and _('Reviews') or _('Comments'))
if argd['action'] not in actions:
argd['action'] = 'DISPLAY'
# is page allowed to be viewed
if uid == -1 or (not CFG_WEBCOMMENT_ALLOW_COMMENTS and not CFG_WEBCOMMENT_ALLOW_REVIEWS):
return page_not_authorized(req, "../comments/add",
navmenuid='search')
# if guest, must log in first
if isGuestUser(uid):
- referer = "%s/record/%s/%s/add?ln=%s&amp;comid=%s&amp;action=%s&amp;score=%s" % (weburl,
+ referer = "%s/record/%s/%s/add?ln=%s&amp;comid=%s&amp;action=%s&amp;score=%s" % (CFG_SITE_URL,
self.recid,
self.discussion == 1 and 'reviews' or 'comments',
argd['ln'],
argd['comid'],
argd['action'],
argd['score'])
msg = _("Before you add your comment, you need to %(x_url_open)slogin%(x_url_close)s first.") % {
'x_url_open': '<a href="%s/youraccount/login?referer=%s">' % \
(CFG_SITE_SECURE_URL, urllib.quote(referer)),
'x_url_close': '</a>'}
return page(title=_("Login"),
body=msg,
navtrail=navtrail,
uid=uid,
language=CFG_SITE_LANG,
verbose=1,
req=req,
navmenuid='search')
# user logged in
else:
(body, errors, warnings) = perform_request_add_comment_or_remark(recID=self.recid,
ln=argd['ln'],
uid=uid,
action=argd['action'],
msg=argd['msg'],
note=argd['note'],
score=argd['score'],
reviews=self.discussion,
comID=argd['comid'],
client_ip_address=client_ip_address)
if self.discussion:
title = _("Add Review")
else:
title = _("Add Comment")
return page(title=title,
body=body,
navtrail=navtrail,
uid=uid,
language=CFG_SITE_LANG,
verbose=1,
errors=errors,
warnings=warnings,
req=req,
navmenuid='search')
# id not in range
else:
return page(title=_("Record Not Found"),
body=problem,
uid=uid,
verbose=1,
req=req,
warnings=check_warnings, errors=[],
navmenuid='search')
def vote(self, req, form):
"""
Vote positively or negatively for a comment/review.
@param comid: comment/review id
@param com_value: +1 to vote positively
-1 to vote negatively
@param recid: the id of the record the comment/review is associated with
@param ln: language
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param referer: http address of the calling function to redirect to (refresh)
@param reviews: boolean, enabled for reviews, disabled for comments
"""
argd = wash_urlargd(form, {'comid': (int, -1),
'com_value': (int, 0),
'recid': (int, -1),
'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'referer': (str, None)
})
client_ip_address = get_client_ip_address(req)
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
- weburl + user_info['uri']}, {})
+ CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
success = perform_request_vote(argd['comid'], client_ip_address, argd['com_value'], uid)
if argd['referer']:
argd['referer'] += "?ln=%s&amp;do=%s&amp;ds=%s&amp;nb=%s&amp;p=%s&amp;voted=%s&amp;" % (
argd['ln'], argd['do'], argd['ds'], argd['nb'], argd['p'], success)
redirect_to_url(req, argd['referer'])
else:
#Note: sent to comments display
referer = "%s/record/%s/%s?&amp;ln=%s&amp;voted=1"
- referer %= (weburl, self.recid, self.discussion == 1 and 'reviews' or 'comments', argd['ln'])
+ referer %= (CFG_SITE_URL, self.recid, self.discussion == 1 and 'reviews' or 'comments', argd['ln'])
redirect_to_url(req, referer)
def report(self, req, form):
"""
Report a comment/review for inappropriate content
@param comid: comment/review id
@param recid: the id of the record the comment/review is associated with
@param ln: language
@param do: display order hh = highest helpful score, review only
lh = lowest helpful score, review only
hs = highest star score, review only
ls = lowest star score, review only
od = oldest date
nd = newest date
@param ds: display since all= no filtering by date
nd = n days ago
nw = n weeks ago
nm = n months ago
ny = n years ago
where n is a single digit integer between 0 and 9
@param nb: number of results per page
@param p: results page
@param referer: http address of the calling function to redirect to (refresh)
@param reviews: boolean, enabled for reviews, disabled for comments
"""
argd = wash_urlargd(form, {'comid': (int, -1),
'recid': (int, -1),
'do': (str, "od"),
'ds': (str, "all"),
'nb': (int, 100),
'p': (int, 1),
'referer': (str, None)
})
client_ip_address = get_client_ip_address(req)
uid = getUid(req)
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
- weburl + user_info['uri']}, {})
+ CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
success = perform_request_report(argd['comid'], client_ip_address, uid)
if argd['referer']:
argd['referer'] += "?ln=%s&amp;do=%s&amp;ds=%s&amp;nb=%s&amp;p=%s&amp;reported=%s&amp;" % (argd['ln'], argd['do'], argd['ds'], argd['nb'], argd['p'], str(success))
redirect_to_url(req, argd['referer'])
else:
#Note: sent to comments display
referer = "%s/record/%s/%s/display?ln=%s&amp;voted=1"
- referer %= (weburl, self.recid, self.discussion==1 and 'reviews' or 'comments', argd['ln'])
+ referer %= (CFG_SITE_URL, self.recid, self.discussion==1 and 'reviews' or 'comments', argd['ln'])
redirect_to_url(req, referer)
diff --git a/modules/webcomment/lib/webcommentadminlib.py b/modules/webcomment/lib/webcommentadminlib.py
index 6cffbf4af..f3fbac742 100644
--- a/modules/webcomment/lib/webcommentadminlib.py
+++ b/modules/webcomment/lib/webcommentadminlib.py
@@ -1,243 +1,243 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
-from invenio.config import CFG_SITE_LANG, weburl
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL
from invenio.webcomment import query_get_comment
from invenio.urlutils import wash_url_argument
from invenio.dbquery import run_sql
from invenio.messages import gettext_set_language, wash_language
from invenio.webuser import get_user_info
import invenio.template
webcomment_templates = invenio.template.load('webcomment')
def getnavtrail(previous = '', ln=CFG_SITE_LANG):
"""Get the navtrail"""
previous = wash_url_argument(previous, 'str')
ln = wash_language(ln)
_ = gettext_set_language(ln)
- navtrail = """<a class="navtrail" href="%s/help/admin">%s</a> """ % (weburl, _("Admin Area"))
+ navtrail = """<a class="navtrail" href="%s/help/admin">%s</a> """ % (CFG_SITE_URL, _("Admin Area"))
navtrail = navtrail + previous
return navtrail
def perform_request_index(ln=CFG_SITE_LANG):
"""
"""
return webcomment_templates.tmpl_admin_index(ln=ln)
def perform_request_delete(comID=-1, ln=CFG_SITE_LANG):
"""
"""
warnings = []
ln = wash_language(ln)
comID = wash_url_argument(comID, 'int')
if comID is not None:
if comID <= 0:
if comID != -1:
warnings.append(("WRN_WEBCOMMENT_ADMIN_INVALID_COMID",))
return (webcomment_templates.tmpl_admin_delete_form(ln, warnings),None, warnings)
comment = query_get_comment(comID)
if comment:
c_star_score = 5
if comment[c_star_score] > 0:
reviews = 1
else:
reviews = 0
return (perform_request_comments(ln=ln, comID=comID, reviews=reviews), None, warnings)
else:
warnings.append(('WRN_WEBCOMMENT_ADMIN_COMID_INEXISTANT', comID))
return (webcomment_templates.tmpl_admin_delete_form(ln, warnings), None, warnings)
else:
return (webcomment_templates.tmpl_admin_delete_form(ln, warnings), None, warnings)
def perform_request_users(ln=CFG_SITE_LANG):
"""
"""
ln = wash_language(ln)
users_data = query_get_users_reported()
return webcomment_templates.tmpl_admin_users(ln=ln, users_data=users_data)
def query_get_users_reported():
"""
Get the users who have been reported at least one.
@return tuple of ct, i.e. (ct, ct, ...)
where ct is a tuple (total_number_reported, total_comments_reported, total_reviews_reported,
total_nb_votes_yes_of_reported, total_nb_votes_total_of_reported, user_id, user_email, user_nickname)
sorted by order of ct having highest total_number_reported
"""
query1 = "SELECT c.nb_abuse_reports, c.nb_votes_yes, c.nb_votes_total, u.id, u.email, u.nickname, c.star_score " \
"FROM user AS u, cmtRECORDCOMMENT AS c " \
"WHERE c.id_user=u.id AND c.nb_abuse_reports > 0 " \
"ORDER BY u.id "
res1 = run_sql(query1)
if type(res1) is None:
return ()
users = {}
for cmt in res1:
uid = int(cmt[3])
if users.has_key(uid):
users[uid] = (users[uid][0]+int(cmt[0]), int(cmt[6])>0 and users[uid][1] or users[uid][1]+1, int(cmt[6])>0 and users[uid][2]+1 or users[uid][2],
users[uid][3]+int(cmt[1]), users[uid][4]+int(cmt[2]), int(cmt[3]), cmt[4], cmt[5])
else:
users[uid] = (int(cmt[0]), int(cmt[6])==0 and 1 or 0, int(cmt[6])>0 and 1 or 0, int(cmt[1]), int(cmt[2]), int(cmt[3]), cmt[4], cmt[5])
users = users.values()
users.sort()
users.reverse()
users = tuple(users)
return users
def perform_request_comments(ln=CFG_SITE_LANG, uid="", comID="", reviews=0):
"""
"""
ln = wash_language(ln)
uid = wash_url_argument(uid, 'int')
comID = wash_url_argument(comID, 'int')
reviews = wash_url_argument(reviews, 'int')
comments = query_get_comments(uid, comID, reviews, ln)
return webcomment_templates.tmpl_admin_comments(ln=ln, uid=uid,
comID=comID,
comment_data=comments,
reviews=reviews)
def query_get_comments(uid, cmtID, reviews, ln):
"""
private function
tuple of comment where comment is
tuple (nickname, uid, date_creation, body, id) if ranking disabled or
tuple (nickname, uid, date_creation, body, nb_votes_yes, nb_votes_total, star_score, title, id)
"""
qdict = {'id': 0, 'id_bibrec': 1, 'uid': 2, 'date_creation': 3, 'body': 4,
'nb_abuse_reports': 5, 'nb_votes_yes': 6, 'nb_votes_total': 7,
'star_score': 8, 'title': 9, 'email': -2, 'nickname': -1}
query = """SELECT c.id, c.id_bibrec, c.id_user,
c.date_creation, c.body,
c.nb_abuse_reports,
%s
u.email, u.nickname
FROM cmtRECORDCOMMENT c LEFT JOIN user u
ON c.id_user = u.id
%s
ORDER BY c.nb_abuse_reports DESC, c.nb_votes_yes DESC, c.date_creation
"""
select_fields = reviews and 'c.nb_votes_yes, c.nb_votes_total, c.star_score, c.title,' or ''
where_clause = "WHERE " + (reviews and 'c.star_score>0' or 'c.star_score=0')
if uid:
where_clause += ' AND c.id_user=%i' % uid
if cmtID:
where_clause += ' AND c.id=%i' % cmtID
else:
where_clause += ' AND c.nb_abuse_reports>0'
res = run_sql(query % (select_fields, where_clause))
output = []
for qtuple in res:
nickname = qtuple[qdict['nickname']] or get_user_info(qtuple[qdict['uid']], ln)[2]
if reviews:
comment_tuple = (nickname,
qtuple[qdict['uid']],
qtuple[qdict['date_creation']],
qtuple[qdict['body']],
qtuple[qdict['nb_votes_yes']],
qtuple[qdict['nb_votes_total']],
qtuple[qdict['star_score']],
qtuple[qdict['title']],
qtuple[qdict['id']])
else:
comment_tuple = (nickname,
qtuple[qdict['uid']],
qtuple[qdict['date_creation']],
qtuple[qdict['body']],
qtuple[qdict['id']])
general_infos_tuple = (nickname,
qtuple[qdict['uid']],
qtuple[qdict['email']],
qtuple[qdict['id']],
qtuple[qdict['id_bibrec']],
qtuple[qdict['nb_abuse_reports']])
out_tuple = (comment_tuple, general_infos_tuple)
output.append(out_tuple)
return tuple(output)
def perform_request_del_com(ln=CFG_SITE_LANG, comIDs=[]):
"""
private function
Delete the comments and say whether successful or not
@param ln: language
@param comIDs: list of comment ids
"""
ln = wash_language(ln)
comIDs = wash_url_argument(comIDs, 'list')
# map ( fct, list, arguments of function)
comIDs = map(wash_url_argument, comIDs, ('int '*len(comIDs)).split(' ')[:-1])
if not comIDs:
comIDs = map(coerce, comIDs, ('0 '*len(comIDs)).split(' ')[:-1])
return webcomment_templates.tmpl_admin_del_com(del_res=comIDs, ln=ln)
del_res=[]
for comID in comIDs:
del_res.append((comID, query_delete_comment(comID)))
return webcomment_templates.tmpl_admin_del_com(del_res=del_res, ln=ln)
def suppress_abuse_report(ln=CFG_SITE_LANG, comIDs=[]):
"""
private function
suppress the abuse reports for the given comIDs.
@param ln: language
@param comIDs: list of ids to suppress attached reports.
"""
ln = wash_language(ln)
comIDs = wash_url_argument(comIDs, 'list')
# map ( fct, list, arguments of function)
comIDs = map(wash_url_argument, comIDs, ('int '*len(comIDs)).split(' ')[:-1])
if not comIDs:
comIDs = map(coerce, comIDs, ('0 '*len(comIDs)).split(' ')[:-1])
return webcomment_templates.tmpl_admin_del_com(del_res=comIDs, ln=ln)
del_res=[]
for comID in comIDs:
del_res.append((comID, query_suppress_abuse_report(comID)))
return webcomment_templates.tmpl_admin_suppress_abuse_report(del_res=del_res, ln=ln)
def query_suppress_abuse_report(comID):
""" suppress abuse report for a given comment
@return integer 1 if successful, integer 0 if not
"""
query = "UPDATE cmtRECORDCOMMENT SET nb_abuse_reports=0 WHERE id=%i"
params = comID
res = run_sql(query%params)
return int(res)
def query_delete_comment(comID):
"""
delete comment with id comID
@return integer 1 if successful, integer 0 if not
"""
query1 = "DELETE FROM cmtRECORDCOMMENT WHERE id=%s"
params1 = (comID,)
res1 = run_sql(query1, params1)
return int(res1)
diff --git a/modules/webcomment/web/admin/webcommentadmin.py b/modules/webcomment/web/admin/webcommentadmin.py
index 7129e57b8..02a7a07f4 100644
--- a/modules/webcomment/web/admin/webcommentadmin.py
+++ b/modules/webcomment/web/admin/webcommentadmin.py
@@ -1,228 +1,228 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Comments and reviews administrative interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
from invenio.webcommentadminlib import *
from invenio.bibrankadminlib import check_user
from invenio.webpage import page, create_error_box
-from invenio.config import weburl,CFG_SITE_LANG,CFG_SITE_NAME
+from invenio.config import CFG_SITE_URL,CFG_SITE_LANG,CFG_SITE_NAME
from invenio.dbquery import Error
from invenio.webuser import getUid, page_not_authorized
from invenio.urlutils import wash_url_argument, redirect_to_url
from invenio.messages import wash_language, gettext_set_language
def index(req, ln=CFG_SITE_LANG):
"""
Menu of admin options
@param ln: language
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = getnavtrail()
- navtrail_previous_links +=' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % weburl
+ navtrail_previous_links +=' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL
navtrail_previous_links += _("WebComment Admin") + '</a>'
try:
uid = getUid(req)
except Error:
return page(title=_("Internal Error"),
body = create_error_box(req, verbose=0, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req)
(auth_code, auth_msg) = check_user(req, 'cfgwebcomment')
if (auth_code != 'false'):
return page(title=_("WebComment Admin"),
body=perform_request_index(ln=ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links)
def delete(req, ln=CFG_SITE_LANG, comid=""):
"""
Delete a comment by giving its comment id
@param ln: language
@param comid: comment id
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = getnavtrail()
- navtrail_previous_links += ' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % weburl
+ navtrail_previous_links += ' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL
navtrail_previous_links += _("WebComment Admin") + '</a>'
try:
uid = getUid(req)
except Error:
return page(title=_("Internal Error"),
body = create_error_box(req, verbose=0, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req)
(auth_code, auth_msg) = check_user(req,'cfgwebcomment')
if (auth_code != 'false'):
(body, errors, warnings) = perform_request_delete(ln=ln, comID=comid)
return page(title=_("Delete Comment"),
body=body,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req = req,
errors = errors,
warnings = warnings,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links)
def comments(req, ln=CFG_SITE_LANG, uid="", comid="", reviews=0):
"""
View reported comments, filter by either user or a specific comment (only one given at a time)
@param ln: language
@param uid: user id
@param comid: comment id
@param reviews: boolean enabled for reviews, disabled for comments
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = getnavtrail()
- navtrail_previous_links += ' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % weburl
+ navtrail_previous_links += ' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL
navtrail_previous_links += _("WebComment Admin") + '</a>'
try:
auid = getUid(req)
except Error:
return page(title=_("Internal Error"),
body = create_error_box(req, verbose=0, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req)
(auth_code, auth_msg) = check_user(auid, 'cfgwebcomment')
if (auth_code != 'false'):
return page(title=_("View all reported comments"),
body=perform_request_comments(ln=ln, uid=uid, comID=comid, reviews=reviews),
uid=auid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links)
def users(req, ln=CFG_SITE_LANG):
"""
View a list of all the users that have been reported, sorted by most reported
@param ln: language
"""
ln = wash_language(ln)
_ = gettext_set_language(ln)
navtrail_previous_links = getnavtrail()
- navtrail_previous_links += ' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % weburl
+ navtrail_previous_links += ' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL
navtrail_previous_links += _("WebComment Admin") + '</a>'
try:
uid = getUid(req)
except Error:
return page(title=_("Internal Error"),
body = create_error_box(req, verbose=0, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req)
(auth_code, auth_msg) = check_user(req,'cfgwebcomment')
if (auth_code != 'false'):
return page(title=_("View all reported users"),
body=perform_request_users(ln=ln),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links)
def del_com(req, ln=CFG_SITE_LANG, action="delete", **hidden):
"""
private funciton
Delete a comment
@param ln: language
@param **hidden: ids of comments to delete sent as individual variables comidX=on, where X is id
"""
ln = wash_language(ln)
action = wash_url_argument(action, 'str')
_ = gettext_set_language(ln)
navtrail_previous_links = getnavtrail()
- navtrail_previous_links += ' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % weburl
+ navtrail_previous_links += ' &gt; <a class="navtrail" href="%s/admin/webcomment/webcommentadmin.py/">' % CFG_SITE_URL
navtrail_previous_links += _("WebComment Admin") + '</a>'
try:
uid = getUid(req)
except Error:
return page(title=_("Internal Error"),
body = create_error_box(req, verbose=0, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req)
(auth_code, auth_msg) = check_user(req,'cfgwebcomment')
if (auth_code != 'false'):
comIDs = []
args = hidden.keys()
for var in args:
try:
comIDs.append(int(var.split('comid')[1]))
except:
pass
if action == 'delete':
body = perform_request_del_com(ln=ln, comIDs=comIDs)
title = _("Delete comments")
elif action == 'unreport':
body = suppress_abuse_report(ln=ln, comIDs=comIDs)
title = _("Suppress abuse reports")
else:
- redirect_to_url(req, weburl + '/admin/webcomment/webcommentadmin.py')
+ redirect_to_url(req, CFG_SITE_URL + '/admin/webcomment/webcommentadmin.py')
return page(title=title,
body=body,
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__,
req=req)
else:
return page_not_authorized(req=req, text=auth_msg, navtrail=navtrail_previous_links)
diff --git a/modules/webhelp/web/admin/admin.webdoc b/modules/webhelp/web/admin/admin.webdoc
index 87a5d5596..4f9dca2f0 100644
--- a/modules/webhelp/web/admin/admin.webdoc
+++ b/modules/webhelp/web/admin/admin.webdoc
@@ -1,493 +1,493 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(Admin Area)_ -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>Welcome to the Admin Area of the <CFG_SITE_NAME>. You'll find here
pointers to the available runtime admin-level interfaces and
admin-level guides on how to configure and run the CDS Invenio
system.</p>
<p>CDS Invenio comes as a suite of several more or less independent
modules. You'll find brief descriptions for each admin module below.
(More background information on each module may be read in the <a
-href="<WEBURL>/help/hacking/modules-overview">modules overview</a> article.)
+href="<CFG_SITE_URL>/help/hacking/modules-overview">modules overview</a> article.)
</p>
<h3>Admin HOWTO guides</h3>
-<p><a href="<WEBURL>/help/admin/howto">Admin HOWTO Guides</a> give you
+<p><a href="<CFG_SITE_URL>/help/admin/howto">Admin HOWTO Guides</a> give you
you both short and not-so-short recipes and thoughts on some of the
most frequently encountered administrative tasks. They tend to answer
various admin-level questions of a rather general level. The specific
tasks are better addressed by module-specific guides and interfaces
presented below.
</p>
<h3>Data acquisition related modules</h3>
<p>The metadata input into a running CDS Invenio system can be done in two
ways: <em>(i) admin-oriented batch mode</em>,
i.e. <strong>BibHarvest</strong> to get data from OAI repositories,
<strong>BibConvert</strong> to convert any input data into XML MARC,
and <strong>BibUpload</strong> to upload XML MARC files into CDS Invenio;
and <em>(ii) author-oriented interactive mode</em>,
i.e. <strong>WebSubmit</strong> to submit documents via Web. Once the
data are uploaded in CDS Invenio, you may want to modify them via
<strong>BibEdit</strong> to edit the metadata.
</p>
<table border="1" cellpadding="2">
<tr>
<th class="searchboxheader">Admin Module</th>
<th class="searchboxheader">Admin Description</th>
<th class="searchboxheader">Admin Interface</th>
<th class="searchboxheader">Admin Guide</th>
</tr>
<tr>
<td>
<strong>BibHarvest Admin</strong>
</td>
<td>
Enables you to configure OAI metadata harvestor for eventual
periodical batch upload of data. For example, you can define from
where to harvest, with what periodicity, how to transform data
before uploading them into CDS Invenio, etc. Also, enables you to
define your OAI sets in case you want to export your own data.
</td>
<td>
- <a href="<WEBURL>/admin/bibharvest/bibharvestadmin.py">BibHarvest Admin Interface</a>
+ <a href="<CFG_SITE_URL>/admin/bibharvest/bibharvestadmin.py">BibHarvest Admin Interface</a>
<p>
- <a href="<WEBURL>/admin/bibharvest/oaiarchiveadmin.py">OAI Repository Admin Interface</a>
+ <a href="<CFG_SITE_URL>/admin/bibharvest/oaiarchiveadmin.py">OAI Repository Admin Interface</a>
</td>
<td>
<a href="bibharvest-admin-guide">BibHarvest Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibConvert Admin</strong>
</td>
<td>
Explains how to use bibliographic data convertor. Useful for batch
upload of data. For example, when migrating the metadata from
your old system, or when integrating metadata acquisitions from
non-OAI sources, or just about any line-based
not-so-well-structured metadata.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="bibconvert-admin-guide">BibConvert Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibMatch Admin</strong>
</td>
<td>
Tools for matching XML MARC files against the repository content.
Useful when importing third-party metadata files.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="bibmatch-admin-guide">BibMatch Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibUpload Admin</strong>
</td>
<td>
Enables you to configure eventual local special operations to be
done on the data being uploaded.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="bibupload-admin-guide">BibUpload Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebSubmit Admin</strong>
</td>
<td>
Enables you to configure the submit interface and logic for various document types.
For example, you can define which metadata fields should be submitted for various
doctypes, what to do with the inputted values before uploading,
possible peer review and approval strategy, etc.
</td>
<td>
- <a href="<WEBURL>/admin/websubmit/websubmitadmin.py">WebSubmit Admin Interface</a>
+ <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py">WebSubmit Admin Interface</a>
</td>
<td>
<a href="websubmit-admin-guide">WebSubmit Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>ElmSubmit Admin</strong>
</td>
<td>
Enables you to configure the submission of documents by electronic mail.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="elmsubmit-admin-guide">ElmSubmit Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibEdit Admin</strong>
</td>
<td>
Enables you to directly manipulate bibliographic data, edit a single
record, do global replacements, and other cataloguing tasks.
</td>
<td>
- <a href="<WEBURL>/admin/bibedit/bibeditadmin.py">BibEdit Admin Interface</a>
+ <a href="<CFG_SITE_URL>/admin/bibedit/bibeditadmin.py">BibEdit Admin Interface</a>
</td>
<td>
<a href="bibedit-admin-guide">BibEdit Admin Guide</a>
</td>
</tr>
</table>
<h3>Data provision related modules</h3>
<p>The metadata output from a running CDS Invenio system to the end-user
is covered by several modules: <strong>BibIndex</strong> to index the
metadata, <strong>BibRank</strong> to eventually rank them,
<strong>BibFormat</strong> to format them for the output,
<strong>WebSearch</strong> to provide search interfaces and search
engine.
</p>
<table border="1">
<tr>
<th class="searchboxheader">Admin Module</th>
<th class="searchboxheader">Admin Description</th>
<th class="searchboxheader">Admin Interface</th>
<th class="searchboxheader">Admin Guide</th>
</tr>
<tr>
<td>
<strong>BibIndex Admin</strong>
</td>
<td>
Enables you to configure "word files", i.e. to define which
bibliographic fields are indexed into which word indexes. The word
indexes are then used by the search interface. For example, you can
define that the logical author index is constructed from physical
<code>100 $a</code> and <code>700 $a</code> bibliographic tags, you
can force reindexing of the fulltext index, etc.
</td>
<td>
- <a href="<WEBURL>/admin/bibindex/bibindexadmin.py">Manage indexes</a>
+ <a href="<CFG_SITE_URL>/admin/bibindex/bibindexadmin.py">Manage indexes</a>
<p>
- <a href="<WEBURL>/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a>
+ <a href="<CFG_SITE_URL>/admin/bibindex/bibindexadmin.py/field">Manage logical fields</a>
</td>
<td>
<a href="bibindex-admin-guide">BibIndex Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibRank Admin</strong>
</td>
<td>
Enables you to configure various ranking methods to be used by the search engine.
You can rebalance existing ranking sets, create new ranking methods, etc.
</td>
<td>
- <a href="<WEBURL>/admin/bibrank/bibrankadmin.py">BibRank Admin Interface</a>
+ <a href="<CFG_SITE_URL>/admin/bibrank/bibrankadmin.py">BibRank Admin Interface</a>
</td>
<td>
<a href="bibrank-admin-guide">BibRank Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibClassify Admin</strong>
</td>
<td>
Enables you to automatically classify documents according to
keyword taxonomies and thesauri.
</td>
<td>
<small class="note">command-line configuration</small>
</td>
<td>
<a href="bibclassify-admin-guide">BibClassify Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>BibFormat Admin</strong>
</td>
<td>
Enables you to specify how the bibliographic data is presented to
the end user in the search interface. You can decide that titles should be
presented in bold font, that for each author an automatic link to
author's home page should be created according to some receipt, etc.
</td>
<td>
- <a href="<WEBURL>/admin/bibformat/bibformatadmin.py">BibFormat Admin Interface</a>
+ <a href="<CFG_SITE_URL>/admin/bibformat/bibformatadmin.py">BibFormat Admin Interface</a>
</td>
<td>
<a href="bibformat-admin-guide">BibFormat Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebSearch Admin</strong>
</td>
<td>
Enables you to configure the search interface for various metadata
collections. You can define new collections and organize them in
the tree, you can define various portalboxes that would appear on
the screen, you can define search options and search fields to
present, etc.
</td>
<td>
- <a href="<WEBURL>/admin/websearch/websearchadmin.py">WebSearch Admin Interface</a>
+ <a href="<CFG_SITE_URL>/admin/websearch/websearchadmin.py">WebSearch Admin Interface</a>
</td>
<td>
<a href="websearch-admin-guide">WebSearch Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebStat Admin</strong>
</td>
<td>
Enables you to configure the usage statistics reporting system.
</td>
<td>
<small class="note">command-line configuration</small>
</td>
<td>
<a href="webstat-admin-guide">WebStat Admin Guide</a>
</td>
</tr>
</table>
<h3>Personalization related modules</h3>
<p>CDS Invenio interface can be personalized to suit different needs
of different end-users. This functionality is covered by several
modules: <strong>WebSession</strong> to identify users and their
personal configurations, <strong>WebBasket</strong> to provide
personal baskets or document carts, and <strong>WebAlert</strong> to
set up personal email notification alerts.
</p>
<table border="1">
<tr>
<th class="searchboxheader">Admin Module</th>
<th class="searchboxheader">Admin Description</th>
<th class="searchboxheader">Admin Interface</th>
<th class="searchboxheader">Admin Guide</th>
</tr>
<tr>
<td>
<strong>WebSession Admin</strong>
</td>
<td>
Enables you to inspect the status of guest sessions and to expire
them; the status and details on registered users, etc.
</td>
<td>
<small class="note">not available, but see the guide for the command-line way</small>
</td>
<td>
<a href="websession-admin-guide">WebSession Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebBasket Admin</strong>
</td>
<td>
Enables you to inspect and manipulate user baskets set up on the
system, to make them public/private, etc.
</td>
<td>
<small class="note">not available, but see the guide for the command-line way</small>
</td>
<td>
<a href="webbasket-admin-guide">WebBasket Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebAlert Admin</strong>
</td>
<td>
Enables you to inspect and manipulate user alerts set up on the
system, to run the alert engine, etc.
</td>
<td>
<small class="note">not available, but see the guide for the command-line way</small>
</td>
<td>
<a href="webalert-admin-guide">WebAlert Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebComment Admin</strong>
</td>
<td>
Enables you to manipulate readers comments and reviews,
see which ones were reported as abuse/spam, delete them, etc.
</td>
<td>
- <a href="<WEBURL>/admin/webcomment/webcommentadmin.py">WebComment Admin Interface</a>
+ <a href="<CFG_SITE_URL>/admin/webcomment/webcommentadmin.py">WebComment Admin Interface</a>
</td>
<td>
<a href="webcomment-admin-guide">WebComment Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebMessage Admin</strong>
</td>
<td>
Enables you to configure the messaging system.
</td>
<td>
<small class="note">command-line configuration</small>
</td>
<td>
<a href="webmessage-admin-guide">WebMessage Admin Guide</a>
</td>
</tr>
</table>
<h3>System glue modules</h3>
<p>Modules that provide the necessary glue for those presented above
are: <strong>BibSched</strong> to manage and schedule bibliographic
tasks, <strong>WebAccess</strong> to define role-based access control
system to all CDS Invenio services, and <strong>WebStyle</strong> to
define a common look and feel of CDS Invenio web pages.
</p>
<table border="1">
<tr>
<th class="searchboxheader">Admin Module</th>
<th class="searchboxheader">Admin Description</th>
<th class="searchboxheader">Admin Interface</th>
<th class="searchboxheader">Admin Guide</th>
</tr>
<tr>
<td>
<strong>BibSched Admin</strong>
</td>
<td>
Enables you to inspect bibliographic task queue, to postpone or
reschedule jobs, to make priorities, to run periodical tasks, etc.
</td>
<td>
<small class="note">command-line program</small>
</td>
<td>
<a href="bibsched-admin-guide">BibSched Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebAccess Admin</strong>
</td>
<td>
Enables you to define who has got access or admin rights on various CDS Invenio modules.
For example, you can define that John is the bibliographic
data manager, that Jim can modify the search interface pages, that
Jill is the submission approval editor, etc.
</td>
<td>
- <a href="<WEBURL>/admin/webaccess/webaccessadmin.py">WebAccess Admin Interface - Full functionality</a>
+ <a href="<CFG_SITE_URL>/admin/webaccess/webaccessadmin.py">WebAccess Admin Interface - Full functionality</a>
<br /><small>Full interface. Manage, grant, revoke any right.</small>
<p>
- <a href="<WEBURL>/admin/webaccess/webaccessadmin.py/delegate_startarea">Delegate Rights - With Restrictions</a>
+ <a href="<CFG_SITE_URL>/admin/webaccess/webaccessadmin.py/delegate_startarea">Delegate Rights - With Restrictions</a>
<br /><small>Delegate your rights for some roles.</small>
<p>
- <a href="<WEBURL>/admin/webaccess/webaccessadmin.py/manageaccounts">Manage Accounts</a>
+ <a href="<CFG_SITE_URL>/admin/webaccess/webaccessadmin.py/manageaccounts">Manage Accounts</a>
<br /><small>Enable, disable, and modify accounts.</small>
</td>
<td>
<a href="webaccess-admin-guide">WebAccess Admin Guide</a>
</td>
</tr>
<tr>
<td>
<strong>WebStyle Admin</strong>
</td>
<td>
Enables you to customize default CDS Invenio page style and the CSS style sheet.
</td>
<td>
<small class="note">not available, but see the guide for the command-line way</small>
</td>
<td>
<a href="webstyle-admin-guide">WebStyle Admin Guide</a>
</td>
</tr>
</table>
diff --git a/modules/webhelp/web/admin/howto/howto-marc.webdoc b/modules/webhelp/web/admin/howto/howto-marc.webdoc
index e9f4b2129..a45b42077 100644
--- a/modules/webhelp/web/admin/howto/howto-marc.webdoc
+++ b/modules/webhelp/web/admin/howto/howto-marc.webdoc
@@ -1,2257 +1,2257 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: HOWTO MARC Your Bibliographic Data -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">Admin Area</a> &gt; <a class="navtrail" href="howto">Admin HOWTOs</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">Admin Area</a> &gt; <a class="navtrail" href="howto">Admin HOWTOs</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Why to MARC at all?</h2>
<p>All the bibliographic data in the CDS Invenio system are
internally represented in the <a
href="http://www.loc.gov/marc/bibliographic/">MARC 21</a> format.
There are several good reasons for this:
<ul>
<li>MARC format is <em>the</em> standard in the library world. It is
well established and has been used since 1960s.
<li>MARC is flexible enough to represent any metadata structure you
may need now or in the future. Therefore, CDS Invenio can adapt to your
needs without altering its internal data structure.
<li>MARC technology, albeit developed in the punch card times
(1960s!), can be well combined with recent technologies like XML. In
fact, whenever bibliographic metadata are to be worked with externally
in a file format, CDS Invenio uses recently standardized <a
href="http://www.loc.gov/standards/marcxml/">MARC XML</a> format
provided by the Library of Congress.
</ul>
<h2>Choosing MARC representation of your metadata</h2>
<p>Basically, you are in one of the following three situations:
<ol>
<li><p>You do not care much about internal MARC metadata structure as
far as you can work with "more meaningful" metadata concepts like
<em>author</em>, <em>abstract</em>, <em>title</em>, etc. In this case
we simply recommend you to stick to CDS Invenio defaults that preset for
you the most commonly used metadata fields (in alphabetical order;
non-exhaustive list):
<blockquote>
<pre>
METADATA CONCEPT PROPOSED MARC 21 REPRESENTATION
------------------------ -------------------------------
Abstract 520 $a
Author, first 100 $a
Author(s), additional 700 $a
Collection identifier 980 $a
Email 8560 $f
Imprint 260 $a,b,c; 300 $a
Keywords 6531 $a
Language 041 $a
OAI identifier 909CO $o
Publication info 909C4 $* [many subfields]
References 999C5 $* [many subfields]
Primary report number 037 $a [unique throughout the system!]
Additional report number(s) 088 $a
Series 490 $a,v
Subject 65017 $a
Title 245 $a
URL (e.g. to fulltext) 8564 $u, $z
</pre>
</blockquote>
<p>The advantage of using these CDS Invenio defaults is that you can use
pre-defined configurations of <a href="bibconvert-admin">BibConvert</a>,
<a href="bibformat-admin">BibFormat</a>, and <a
href="bibindex-admin">BibIndex</a>.
<li><p>You do not care much about internal MARC metadata structure, so
you are using CDS Invenio defaults, but you need to introduce a new
metadata concept. For example, you would like to store also the
document shelf number, and you want to make it separately searchable.
In this case you are free to choose any MARC tag of your own, for
example <code>963 $6</code>. After that you would configure CDS Invenio
as follows:
<ul>
<li>configure <a href="bibindex-admin">BibIndex</a> to create a new
logical field called <em>document shelf</em> and associate it with
<code>963 $6</code> physical MARC tag;
<li>run <a href="bibindex-admin">BibIndex</a> to create word tables for
the new searchable index;
<li>configure <a href="websubmit-admin">WebSubmit</a> to let the
submission interface know of the existence of the new field;
<li>configure <a href="websearch-admin">WebSearch</a> to introduce the
new searchable field into collections of your choice;
<li>configure <a href="bibformat-admin">BibFormat</a> to include
document shelf information in the record display on search results
pages.
</ul>
<p>which should give you the functionality you need.
<li><p>You have some constraints on the MARC level, for example you
would like to use MARC markup scheme of your own. You are free to
define your own scheme and even invert the meaning of our default
configurations. For each field you would simply follow the
above-mentioned configuration procedure.
<p>However, when designing your own MARC scheme, you need to think of
two CDS Invenio-related restrictions:
<ul>
<li>There should be no clash in the meaning of the same MARC tag in
two different collections. For example, the tag <code>100</code>
should not mean <em>first author</em> in the Preprints collection and
<em>title</em> in the Videotapes collection. The MARC tags are
considered to be chosen globally, in a collection-independent way.
This means that we cannot have several collections reusing the same
MARC code for their own different purposes. (This should never happen
in well designed database system anyway, but if you have a merge of
various databases coming from various groups of users, and if you do
not have the liberty to remap their MARC tags, this may be a problem.)
<li>Also, our database design assumes that the order of repetitive
subfields inside the same field instance does not matter. For
example, let us consider the tag <code>100</code> with the value
<code>$a Foo $a Bar $a Baz</code>. Then, the question "what is the
second <code>$a</code> of the tag <code>100</code>?" is invalid within
the CDS Invenio MARC paradigm. CDS Invenio would store a tag like that,
but not the order of repetitive subfields themselves. In our MARC
paradigm, we prefer to code that information either (i) into different
subfields within the same field instance (<code>100 $a Foo $b Bar $c
Baz</code>), or (ii) into the same subfield but inside several field
instances (<code>100 $a Foo</code>; <code>100 $a Bar</code>; <code>100
$a Baz</code>), according to what is more appropriate. (We think that
to rely on the order of repetitive subfields inside the same field
instance is a suspicious database design.)
</ul>
<p>These two restrictions were introduced in order to keep CDS Invenio
bibliographic tables both simple and fast. As explained above, we
believe that any good database design will avoid these techniques
anyway.
</ol>
<h2>MARC representation in use at CERN</h2>
<p>MARC schema that is used in the CERN library differs in some ways
from the schema found in the CDS Invenio default configuration and in the
demo records, described under point 1 of the preceding section. This
has got both an advantage and a disadvantage: (i) an advantage that it
permits us to easily test the behaviour of CDS Invenio in the context of
different metadata schemata used by different CDS Invenio installations in
the world, and (ii) a disadvantage that the default schema may be
prone to different extensions by different CDS Invenio installations in
the world, while these extensions could have possibly been avoided by
building local extensions on top of a richer default. (There will
probably always be local tag schema differences due to various local
cataloguing traditions.)
<p>As an example of an extensive MARC schema that could provide a
possible richer default for the CDS Invenio installations in the world, we
are listing below all MARC tags that are in production in the CERN
Library. Please note that in some cases the metadata choice was
dictated by local policy of a Swiss library network that the CERN
Library is participating at, and that it may deliberately differ from
the Library of Congress recommendations in some places to some extent.
(See especially tags 024, 035, 037, 518, 700, 710, 711, 720, 721, 722,
723, 724, 725, 773, 866.)
<p>If this schema is found interesting, we may also provide CERN
BibFormat etc configurations that implement it for CDS Invenio.
<pre>
<protect>
GUIDE TO MARC21 TAGS FOR CERN AND CDSWARE
an attempt to present the actual setup of different MARC tags in use in AL500
at CERN
Maja Gracco
1 September 2004
NOTE:
1. The abbreviations "NR" and "R" are MARC21 standards and stands for
"Not repetitive" and "Repetitive". At CERN the rule is to make a tag repetitive,
when possible [only tags 1xx are not-repetitive] but to make subfields
non-repetitive [there are a couple of exceptions].
2. Subfield codes in AL300 are marked with $$ and subfield codes in AL500
are marked with $.
3. When different indicators are used, the tag will be repeated for each of
them like tag 246
4. [CERN] indicates, that some modifications have been done to the tag to
suit the CERN Library needs, like adding one or more subfield code(s) like
tag "773" or slightly change the content of the field like tag "037"
5. [CDS Invenio/MySQL] indicates, that this tag is exlusively used for CDS Invenio;
this technic is mainly used, when AL500 proposes an alpha-tag like "BAS"
6. To be able to find previous fields/tags mentioned under the title:
"Additional field(s)/tag(s):", you can use the Find-command on your browser
001 CONTROL NUMBER (NR)
This field has no indicators or subfield codes.
It contains the control number assigned by the organization
creating, using or distributing the record. - [ARC,CER,IEX,MAN,MMD]
{Created automatically at input via GUI/Cataloguing module}
NOTE: In MySQL used for CDS Invenio record ID
003 CONTROL NUMBER IDENTIFIER (NR)
This field has no indicators or subfield codes.
It contains the MARC code for the agency whose system control
number is present in field 001. - [ARC,CER,IEX,MAN,MMD]
[Additional field(s)/tag(s)]:
{Not in use in AL300}
005 DATE AND TIME OF LAST TRANSACTION (NR)
This field has no indicators or subfield codes.
It contains 16 characters that specify the date and time
of the last record transaction. - [ARC,CER,IEX,MAN,MMD]
[Additional field(s)/tag(s):]
{Not in use in AL300}
020 INTERNATIONAL STANDARD BOOK NUMBER (R)
Indicators - Both undefined
Subfield Code(s)
$a International Standard Book Number (NR) - [CER]
[Additional field(s)/tag(s): IS]
[IS * -> 020 $a {To be used when the base is not "3n" but monograph}]
022 INTERNATIONAL STANDARD SERIAL NUMBER (R)
Indicators - Both undefined
Subfield Code(s)
$a International Standard Serial Number (NR) - [CER]
[Additional field(s)/tag(s): IS]
[IS * -> 022 $a {To be used when the base is "3n" periodical}]
024 OPEN ARCHIVES INITIATIVE (R) [CERN]
Indicators
First Unspecified type of standard number or code
8
Second - undefined
Subfield Code(s)
$a OAI - [CER]
$p OAI-set indicator - [CER]
[Additional field(s)/tag(s): 909CO]
[909CO $$a -> 0248 $a]
{Not in use in AL300}
030 CODEN DESIGNATION (R)
Indicators - Both undefined
Subfield Code(s)
$a CODEN (NR) - [CER]
[Additional field(s)/tag(s): CD]
[CD * -> 030 $a]
035 SYSTEM CONTROL NUMBER (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a System control number (NR) - [CER,IEX,MAN,MMD,WAI/UDC]
$9 System control number: Inst. (NR) - [CER,"CERN annual report",
"CERN ISOLDE",IEX,MAN,MMD,WAI/UDC]
[Additional field(s)/tag(s): ON,OS,SYSNO,909C0,909C6,909C7]
[ON $c, * -> 909C6 $c -> 035 $a; "CERN annual report" -> $9]
[OS $a-$z -> 035 $a; $9 {Not in use in AL300}]
[909C7 $i -> 035 $a; "CERN ISOLDE" -> $9]
[SYSNO * -> 909C0 $o -> 035 $a; $9 global-local-library as in AL300]
NOTE:
[SYSNO * -> 909C0 $o -> 035 ALL but IEX/DIR]
[SYSNO * -> 035 $a {IEX/DIR: System number}]
[035 $9 {IEX/DIR: Add "PIE" into this field} {Not in use in AL300}]
037 SOURCE OF ACQUISITION (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Electronically retrievable number (NR) - [CER,MAN,MMD]
[Additional field(s)/tag(s): ER]
[ER * -> 037 $a]
041 LANGUAGE CODE (NR)
Indicators - Both undefined
Subfield Code(s)
$a Language code (NR) - [ARC,CER,IEX,MAN,MMD]
[Additional field(s)/tag(s): LN]
[LN * -> 041 $a]
044 COUNTRY OF PUBLISHING/PRODUCING ENTITY CODE (NR)
Indicators - Both undefined
Subfield Code(s)
$a Country of publishing/producing entity code (NR) - [CER base=3n]
[Additional field(s)/tag(s): SW]
[SW $$c -> 044 $a]
080 UNIVERSAL DECIMAL CLASSIFICATION NUMBER (R)
Indicators - Both undefined
Subfield Code(s)
$a Universal Decimal Classification number (NR) - [CER,WAI/UDC]
[Additional field(s)/tag(s): UD]
[UD * -> 080 $a]
088 REPORT NUMBER (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Report number (NR) - [ARC,CER,MAN,MMD]
$9 CERN internal number (NR) - [CER,MMD]
[Additional field(s)/tag(s): RN,RN1,FRAME,909C0,909C1]
[RN * -> 088 $a]
[RN1 * -> 909C0 $r -> 909C1 $a -> 088 $9]
[FRAME * -> 909C0 $r -> 909C1 $a -> 088 $9]
100 MAIN ENTRY--PERSONAL NAME (NR) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [CER,MAN,MMD]
$e Relator term (NR) - [CER,MMD]
$u Affiliation (NR) - [CER]
$v Second and onwards affiliation (R) - [CER]
[Additional field(s)/tag(s): AU1,AU2]
[AU1 * -> 100 $a]
[AU2 * -> 100 $a]
[AU1 $$e -> 100 $e {MMD}]
[AU1 $$2 -> 100 $e]
[AU2 $$2 -> 100 $e]
[AU2 $$u -> 100 $u]
[AU2 $$v -> 100 $v]
110 MAIN ENTRY--CORPORATE NAME (NR) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Corporate name (NR) - [CER,IEX]
$b Subordinate unit (NR) - [IEX]
$g Acronym (NR) - [IEX]
[Additional field(s)/tag(s): ACRO,CA1,DEPT,ORG]
[CA1 * -> 110 $a]
[ORG * -> 110 $a]
[DEPT * -> 110 $b]
[ACRO * -> 110 $g]
Don't use this tag for base=3n use tag 931
111 MAIN ENTRY--MEETING NAME (NR) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Meeting: conference, school, workshop (NR) - [CER,MAN]
$c Location of meeting (NR) - [CER]
$d Date of meeting (NR) - [CER]
$f Year of meetig (NR) - [CER]
$g Conference code (NR) - [CER]
$n Number of part/section/meeting (NR) - [CER]
$w Country (NR) - [CER]
$z Closing date (NR) - [CER]
$9 Opening date (NR) - [CER]
[Additional field(s)/tag(s): CF]
[CF * -> 111 $a]
[CF $$p -> 111 $c]
[CF $$d -> 111 $d]
[CF $$y -> 111 $f]
[CF $$c -> 111 $g]
[CF $$n -> 111 $n]
[Not in use in AL300 -> 111 $w]
[Not in use in AL300 -> 111 $z]
[CF $$o -> 111 $9]
145 MAIN TITLE STATEMENT (NR) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Remainder of title (NR) - [CER]
$b Remainder of subtitle (NR) - [CER]
[Not in use in AL300]
210 ABBREVIATED TITLE (NR)
Indicators - Both undefined
Subfield Code(s)
$a Abbreviated title (NR) - [CER base=3n]
[Additional field(s)/tag(s): TI5]
[TI5 * -> 210 $a]
222 KEY TITLE (R)
Indicators - Both undefined
Subfield Code(s)
$a Key title (NR) - [CER base=3n]
[Additional field(s)/tag(s):]
[Created automatically by the system from tag 245 {Not in use in AL300}]
242 TRANSLATION OF TITLE BY CATALOGING AGENCY (R)
Indicators - Both undefined
Subfield Code(s)
$a Title (NR) - [CER bas=17]
$b Remainder of title (NR) - [CER bas=17]
$y Language code of translated title (NR) - [CER bas=17]
[Additional field(s)/tag(s): RT]
[RT * -> 242_1 $a {MMD} -> 242 $a {CER}]
[242_1 $b {MMD} -> 242 $b {CER}; {Not in use on AL300}]
[RT $$y -> 242_1 $y {MMD} -> 242 $y {CER}]
245 TITLE STATEMENT (NR)
Indicators - Both undefined
Subfield Code(s)
$a Title (NR) - [ARC,CER,IEX,MAN,MMD]
$b Remainder of title (NR) - [ARC,CER,IEX,MAN,MMD]
$k Form (NR) - [MAN]
[Additional field(s)/tag(s): TI,TI1,TI3,TYPE]
[TI * -> 245 $a]
[TI1 * -> 245 $a]
[TI3 * -> 245 $a {CER base=3n}]
[TI $$s -> 245 $b]
[TI1 $$s -> 245 $b]
[TI3 $$f -> 245 $b {CER base=3n}]
[TI3 $$s -> 245 $b {CER base=3n}]
[TI $$s -> 245 $ b]
[TYPE * -> 245 $ k]
246 VARYING FORM OF TITLE:1 (R)
Indicators - Both undefined
Subfield Code(s)
$a Title proper/short title (NR) - [CER not base=3n]
$b Remainder of title (NR) - [CER not base=3n]
$g Miscellaneous information (NR) - [CER not base=3n]
$i Display text (NR) - [CER not base=3n]
$n Number of part/section of a work (R) - [CER not base=3n]
$p Name of part/section of a work (R) - [CER not base=3n]
[Additional field(s)/tag(s): MRT,VO]
[MRT * -> 246 $a]
[MRT $$s -> 246 $b]
[MRT $$e -> 246 $g]
[MRT $$1 -> 246 $i]
[VO $$n -> 245 $n -> 246 $n]
[VO $$t -> 245 $p -> 246 $p]
246 VARYING FORM OF TITLE:2 (R)
Indicators
First - undefined
Second Type of title
1 Parallel title
Subfield Code(s)
$a Title proper/short title (NR) - [CER base=3n,MAN,MMD]
$i Display text (NR) - [CER base=3n]
[Additional field(s)/tag(s): 246_1,TI4,TIF]
[TI4 * -> 246_1 $a]
[TIF * -> 246_1 $a]
[TI4 $$1 -> 246_1 $i {parallel title}]
246 VARYING FORM OF TITLE:3 (R)
Indicators
First - undefined
Second Type of title
3 Other title
Subfield Code(s)
$a Title proper/short title (NR) - [CER base=3n]
$i Display text (NR) - [CER base=3n]
$9 Siglum "sigle" (NR) - [CER base=3n]
[Additional field(s)/tag(s): 246_3,TI4]
[TI4 * -> 246_3 $a]
[TI4 $$1 -> 246_3 $i {cross reference}]
[TI4 $$g -> 246_3 $9]{"sigle"]
250 EDITION STATEMENT (NR)
Indicators - Both undefined
Subfield Code(s)
$a Edition statement (NR) - [CER not base=3n,IEX]
[Additional field(s)/tag(s): TI, TI1]
[TI $$e -> 250 $a]
[TI1 $$e -> 250 $a]
260 PUBLICATION, DISTRIBUTION, ETC. (IMPRINT) (NR) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Place of publication (NR) - [CER base=2n,41-45]
$b Name of publisher (NR) - [CER base=2n,41-45]
$c Date of publication [only year] (NR) - [ARC,CER,IEX,MAN,MMD]
$g Reprinted editions (NR) - [CER base=2n,41-45]
[Additional field(s)/tag(s): IM,YR,909CY]
[IM $$p -> 260 $a {CER base=2n,41-45}]
[IM $$n -> 260 $b {CER base=2n,41-45}]
[IM $$d -> 260 $c]{CER base=2n,41-45}]
[YR * -> 909CY $a -> 260 $c {ARC,CER,IEX,MAN,MMD}]
[NO $$g -> 260 $g {CER base=2n,41-45}]
NOTE: This tag is not used for base=3n [use tag 933]
269 PUBLICATION, DISTRIBUTION, ETC. (IMPRINT) (NR) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Place of publ. (NR) - [ARC,CER not base=2n,41-45,IEX,MAN,MMD]
$b Name of publ. (NR) - [ARC,CER not base=2n,41-45,IEX,MAN,MMD]
$c Complete date (NR) - [ARC,CER not base=2n,41-45,IEX,MAN,MMD]
[Additional field(s)/tag(s):
{Not in use in AL300}]
NOTE: Don't use the following lines for CER base=2n,41-45 !!
[IM $$p -> 260 $a -> 269 $a]
[IM $$n -> 260 $b -> 269 $b]
[IM $$d -> 260 $c -> 269 $c]
270 ADDRESS (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Address or Alternate address (NR) - [CER,IEX]
$b City or Alternate city (NR) - [IEX]
$d Country (NR) - [CER,IEX]
$e Postal code for City or Alt. city (NR)- [IEX]
$k Telephone number (R) - [CER,IEX]
$l Fax number (R) - [CER,IEX]
$m Electronic mail address (NR) - [CER,IEX,MMD]
$p Contact person (NR) - [CER,IEX,MMD]
$s City or Alternate city: Suffix (NR) - [IEX]
$9 Telex (NR) - [CER,IEX]
[Additional field(s)/tag(s):AADDR,ADDR,ATOWN,CONT,CT,CTRY,RE,TELn,
TFAXn,TOWN,TOWN1]
[AADDR * -> 270 $a {alternate address}]
[ADDR * -> 270 $a {address}]
[CT $$a -> 270 $a]
[ATOWN * -> 270 $b {alternate town}]
[TOWN * -> 270 $b]
[CTRY * -> 270 $d {IEX}]
[ATOWN $$l -> 270 $e {alternate town: postal address}]
[TOWN $$l -> 270 $e]
[CT $$t -> 270 $k {CER}]
[TEL1-TEL9 $$c,a,l -> 270 $k {IEX}]
[CT $$f -> 270 $l {CER}]
[TFAX1-TFAX3 $$c,a,l -> 270 $l {IEX}]
[CONT $$e -> 270 $m]
[CT * -> 270 $m {MMD}]
[CT $$e -> 270 $m {CER}]
[CONT * -> 270 $$p]
[CT * -> 270 $p {IEX}]
[CT $$p -> 270 $p {MMD}]
[ATOWN $$t -> 270 $s {alternate town: suffix}]
[TOWN $$t -> 270 $s]
[RE $$z -> 270 $9 {CER}]
[TELEX * -> 270 $9 {IEX}]
300 PHYSICAL DESCRIPTION (R)
Indicators - Both undefined
Subfield Code(s)
$a Pagination (NR) - [ARC,CER,MAN,MMD]
[Additional field(s)/tag(s): IM]
[IM $$c -> 300 $a]
310 CURRENT PUBLICATION FREQUENCY (NR)
Indicators - Both undefined
Subfield Code(s)
$a Current publication frequency (NR) - [CER base=3n]
[Additional field(s)/tag(s): SW]
[SW $$f -> 310 $a]
340 PHYSICAL MEDIUM (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Material base and configuration (NR) - [ARC,CER,MAN,MMD]
$c Materials applied to surface (NR) - [ARC]
$d Information recording technique (NR) - [ARC]
$9 CD-ROM [code concatinated]
[Additional field(s)/tag(s): ME,CDR,909CZ]
[ME * -> 340 $a]
[CDR $$a,$$c,$$m,$$n,$$p,$$t,$$w -> 909CZ $a,$c,$m,$n,$p,$t,$w -> 340 $9]
490 SERIES STATEMENT (R)
Indicators - Both undefined
Subfield Code(s)
$a Series statement (NR) - [CER]
$v Volume/sequential designation (NR) - [CER]
[Additional field(s)/tag(s): SR,SR1]
[SR * -> 490 $a]
[SR1 * -> 490 $a]
[SR $$n -> 490 $v]
[SR1 $$n -> 490 $v]
500 GENERAL NOTE (R)
Indicators - Both undefined
Subfield Code(s)
$a General note (NR) - [ARC,CER,IEX,MAN,MMD]
[Additional field(s)/tag(s): NO,OB,PROG]
[NO * -> 500 $a]
[OB * -> 500 $a {MAN}]
[PROG * -> 500 $a {IEX} Scientific program]
NOTE: Don't use this tag for base=3n use tag 935
502 DISSERTATION NOTE (R)
Indicators - Both undefined
Subfield Code(s)
$a Diploma (NR) - [CER base=14]
$b University (NR) - [CER base=14]
$c Date of year of defense - [CER base=14]
[Additional field(s)/tag(s): NO1]
[NO1 * -> 502 $a]
506 RESTRICTIONS ON ACCESS NOTE (R)
Indicators - Both undefined
Subfield Code(s)
$a Terms governing access (NR) - [ARC,MAN,MMD]
$9 Local information (NR) - [ARC]
[Additional field(s)/tag(s): AV, CX]
[AV * -> 506 $a {ARC,MAN,MMD}]
[AV $$d -> 506 $a {MAN}]
[CX * -> 506 $a {ARC,MAN]
518 DATE/TIME AND PLACE OF AN EVENT NOTE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$d Lectures: date (NR) - [CER]
$g Lectures: conference identification (NR) - [CER]
$h Lectures: starting time (NR) - [CER]
$l Lectures: length of speech (NR) - [CER]
$r Lectures: meeting (NR) - [CER]
[Additional field(s)/tag(s): NO1,909CC]
[NO1 $$d -> 909CC $ d -> 518 $d]
[NO1 $$g -> 909CC $ g -> 518 $g]
[NO1 $$h -> 909CC $ h -> 518 $h]
[NO1 $$l -> 909CC $ l -> 518 $l]
[NO1 * -> 909CC $$r -> 518 $r]
NOTE: This tag is only in use for CER base=10-13,16,19
520 ENGLISH SUMMARY, ETC. (R)
Indicators - Both undefined
Subfield Code(s)
$a Summary, etc. note (NR) - [ARC,CER,IEX,MAN,MMD]
$b Expansion of summary note (NR) - [MMD]
[Additional field(s)/tag(s): AB,RT]
[AB * -> 520 $a]
[RT * -> 520 $a {MMD/BUL} Abs. short Eng]
[AB * -> 520 $b {MMD/BUL} Abs. long Eng]
541 IMMEDIATE SOURCE OF ACQUISITION NOTE (R) [CER]
Indicators - Both undefined
Subfield Code(s)
$a Source of acquisition (NR) - [ARC,CER,MAN]
$d Date of acquisition (NR) - [ARC]
$e Accession number (NR) - [MMD]
$f Owner (NR) - [ARC]
$h Price paid by Bookshop [CER]
$9 Price for the user to pay [CER]
[Additional field(s)/tag(s): OS,SI]
[SI * -> 541 $a]
[SI $$b -> 541 $d]
[OS * -> 541 $e]
[SI $$c -> 541 $f]
546 LANGUAGE NOTE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Language of source (NR) - [MAN]
$g Target language (NR) - [MAN
[Additional field(s)/tag(s): LN,TR,919CJ]
[LN * -> 919CJ $a -> 546 $a]
[TR $$t -> 919CJ $b -> 546 $g]
555 CUMULATIVE INDEX/FINDING AIDS NOTE (R)
Indicators - Both undefined
Subfield Code(s)
$a Cumulative index/finding aids note (NR) - [CER base=3n]
[Additional field(s)/tag(s): PNO]
[PNO $$c -> 555 $a]
583 ACTION NOTE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Action (NR) - [CERN:BOOKSHOP,MAN]
$c Time/date of action (NR) - [CERN:BOOKSHOP,MAN]
$i Mail; Method of action (NR) - [MAN]
$z Note (NR) - [CERN:ALD]
[Additional field(s)/tag(s): ACT,DDLN,MAIL,SENDC,919CG]
[ACT * -> 583 $a]
[SENDC * -> 583 $a]
[DDLN * -> 583 $c]
[MAIL * -> 919CG $a -> 583 $i]
590 FRENCH SUMMARY NOTE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Summary, etc. note in French (NR) - [MMD]
$b Expansion of summary note in French (NR) - [MMD]
[Additional field(s)/tag(s): ABF,RTF]
[ABF * -> 590 $a NOT base=75]
[RTF * -> 590 $a only base=75]
[ABF * -> 590 $b only base=75]
594 TYPE OF DOCUMENT (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Type of document (NR) - [ARDA]
[Additional field(s)/tag(s):]
{Not in use in AL300}
595 INTERNAL NOTE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Internal note (NR) - [ARC,CER,IEX,MAN,MMD]
$d Control field (NR)
$i INSPEC number
$s Subject note (NR) - [MMD]
[Additional field(s)/tag(s): NI,OB]
[NI * -> 595 $a]
[OB * -> 595 $a {MMD}]
[NI $$d -> 595 $d]
[NI $$i -> 595 $i]
[NI $$s -> 595 $s]
NOTE: Don't use this tag for base=3n use tag 937
596 SLAC NOTE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a SLAC note (NR) - [CER]
[Additional field(s)/tag(s): NS]
[NS * -> 596 $a]
597 OBSERVATION IN FRENCH (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Observation in French (NR) - [MMD]
[Additional field(s)/tag(s): OBF]
[OBF * -> 597 $a]
598 COPYRIGHT (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Copyright (NR) - [MMD]
[Additional field(s)/tag(s): CR]
[CR * -> 598 $a]
599 STATISTICS FOR THE CERN BOOKSHOP (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Total number of books bought by the Bookshop (NR) - [CER]
$b Total number of books sold by the Bookshop (NR) - [CER]
$c The values of $a minus the values of $b (NR) - [CER]
{Not in use in AL300}
600 SUBJECT ADDED ENTRY--PERSONAL NAME (R)
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [ARC]
$c Titles and other words associated with a name (NR) - [ARC]
[Additional field(s)/tag(s): NA]
[NA * -> 600 $a]
[NA $$c -> 600 $c]
650 SUBJECT ADDED ENTRY--TOPICAL TERM:1 (R)
Indicators
First Level of subject
1 Primary
Second Subject heading system/thesaurus
7 Source specified in subfield $2
Subfield Code(s)
$a Topical term or geographic name (NR) - [CER,MAN,MMD]
$2 Source of heading or term (NR) - [CER,MAN,MMD]
[Additional field(s)/tag(s): 65017,SU]
NOTE: Only 1st occurrence of SU
[SU * -> 65017 $a]
[SU $2 {Not in use in AL300}]
650 SUBJECT ADDED ENTRY--TOPICAL TERM:2 (R)
Indicators
First Level of subject
2 Secondary
Second Subject heading system/thesaurus
7 Source specified in subfield $2
Subfield Code(s)
$a Topical term or geographic name (NR) - [CER,MAN,MMD]
$2 Source of heading or term (NR) - [CER,MAN,MMD]
[Additional field(s)/tag(s): 65027,SU,SUA]
NOTE: Only 2nd occurrence of SU
[SU * -> 65027 $a]
[SUA * -> 65027 $a {MMD}]
[SU $2 {Not in use in AL300} {CER}]
653 ENGLISH INDEX TERM--UNCONTROLLED:1 (R) [CERN]
Indicators
First Level of index term
1 Primary
Second Undefined
Subfield Code(s)
$a Uncontrolled term (NR) - [ARC,CER,MAN,MMD,WAI/UDC]
$9 Institute of the uncontrolled term (NR) - [CER]
[Additional field(s)/tag(s): 6531,KW]
[KW * -> 6531 $a {Databases (see above) CER only older records}]
[KW a-z -> 6531 $a {CER newer records}]
[KW $9 {Not in use in AL300}]
653 FRENCH INDEX TERM--UNCONTROLLED:2 (R)
Indicators
First Level of index term
2 Secondary [in French]
Second Undefined
Subfield Code(s)
$a Uncontrolled term (NR) - [CER,WAI/UDC]
$9 Institute of the uncontrolled term (NR) - [CER,WAI/UDC]
[Additional field(s)/tag(s): 6532,KW6]
[KW6 * -> 6532 $a]
[KW6 $9 {Not in use in AL300}]
690 SUBJECT INDICATOR (R) [CERN]
Indicators
First Origine of indicator
C CERN
Second indicator undefined
Subfield Code(s)
$a Term (NR) - [ARC,CER,IEX,MAN,MMD]
[Additional field(s)/tag(s): 690C,IN,TYPE]
[IN * -> 690C $a]
[TYPE $$a -> 690C $a {Type of research: Accelerator} {IEX}]
[TYPE $$e -> 690C $a {Type of research: Experimental {IEX}]
[TYPE $$o -> 690C $a {Type of research: Other} {IEX}]
[TYPE $$t -> 690C $a {Type of research: Theoretical} {IEX}]
691 OBSERVATION (NR) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Observation (NR) - [ARC,MAN]
[Additional field(s)/tag(s): OB,919C3]
[OB * -> 919C3 $a -> 691 $a]
[OB1 * -> 919C3 $a -> 691 $a {ARC}]
[OB2 * -> 919C3 $a -> 691 $a {ARC}]
692 BEAM (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$e Elements (NR) - [CER]
$i Isotope (NR) - [CER]
$m Minimum intensity (NR) - [CER]
[Additional field(s)/tag(s): BEAM,909CK]
[BEAM $$e -> 909CK $e -> 692 $e]
[BEAM $$i -> 909CK $i -> 692 $i]
[BEAM $$m -> 909CK $m -> 692 $m]
693 ACCELERATOR/EXPERIMENT (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Accelerator (NR) - [CER,IEX,MMD]
$e Experiment (NR) - [CER,IEX,MAN,MMD]
$f Facility
[Additional field(s)/tag(s): AC,ACCL,AE,EX]
[AC * -> 693 $a {CER,IEX base=50,MMD}]
[ACCL * -> 693 $a {IEX base=71}]
[AE $$a -> 693 $a {CER}]
[AE $$e -> 693 $e {CER}]
[EX * -> 693 $e {CER,IEX,MAN,MMD}]
694 CLASSIFICATION TERMS (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Uncontrolled term (NR) - [CER]
$9 Institute of the uncontrolled term (NR) - [CER]
[Additional field(s)/tag(s): KL]
[KL $a-$z -> 694 $a]
[KL $9 {Not in use in AL300}]
695 THESAURUS TERMS (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Uncontrolled term (NR) - [CER]
$9 Institute of the uncontrolled term (NR) - [CER]
[Additional field(s)/tag(s): KW2,TH]
[KW2 * -> 695 $a]
[KW2 $9 {Not in use in AL300}]
[TH $$a-$$z -> 695 $a]
[TH $9 {Not in use in AL300}]
699 SUBJECT CATEGORY FOR CERN BOOKSHOP (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Uncontrolled term (NR) - [CER]
$9 Institute of the uncontrolled term (NR) - [CER]
{Not in use in AL300}
700 ADDED ENTRY--PERSONAL NAME (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [ARC,CER,MMD]
$e Relator term (NR) - [ARC,CER,MMD]
$u Affiliation (NR) - [CER,IEX,MMD]
$v Affiliation (NR) - [CER]
[Additional field(s)/tag(s): AU,AU2,AU3]
[AU * -> 700 $a]
[AU2 * -> 700 $a]
[AU3 * -> 700 $a]
[AU $$2 -> 700 $e]
[AU2 $$2 -> 700 $e]
[AU3 $$2 -> 700 $e]
[AU $$u -> 700 $u]
[AU $$i -> 700 $u {IEX}]
[AU $$v -> 700 $v]
710 ADDED ENTRY--CORPORATE NAME (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Corporate name (NR) - [ARC,CER,MAN]
$b Subordinate unit (NR) - [CER,MAN]
$g Collaboration (NR) - [CER]
$5 CERN Paper (NR) - [CER,MAN,MMD]
$9 CERN Work (NR) - [CER]
[Additional field(s)/tag(s): CA,CE,CO,DI,GP,909CW]
[CA * -> 710 $a {CER}]
[CE * -> 710 $a {ARC}]
[GP * -> 909CW $a -> 710 $b]
[CO * -> 710 $g]
[CO $$n -> 710 $g]
[DI * -> 710 $5 {MAN}]
[DI $$p -> 710 $5]
[DI $$w -> 710 $9]
Don't use this tag for base=3n use tag 532
711 ADDED ENTRY--MEETING NAME (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Meeting name (NR) - [CER]
$c Location of meeting (NR) - [CER]
$d Date of meeting (NR) - [CER]
$f Date of a work (NR) - [CER]
$g Conference code (NR) - [CER]
$n Number of part/section/meeting (NR) - [CER]
$9 Conference opening date (NR) - [CER]
[Additional field(s)/tag(s): CF2,CF3,KF2]
[CF2 * -> 711 $a]
[CF3 * -> 711 $a]
[KF2 * -> 711 $a]
[CF2 $$p -> 711 $c]
[CF3 $$p -> 711 $c]
[KF2 $$p -> 711 $c]
[CF2 $$d -> 711 $d]
[CF3 $$d -> 711 $d]
[KF2 $$d -> 711 $d]
[CF2 $$y -> 711 $f]
[CF3 $$y -> 711 $f]
[KF2 $$y -> 711 $f]
[CF2 $$c -> 711 $g]
[CF3 $$c -> 711 $g]
[KF2 $$c -> 711 $g]
[CF2 $$n -> 711 $n]
[CF3 $$n -> 711 $n]
[KF2 $$n -> 711 $n]
[Not in use in AL300 -> 111 $w]
[Not in use in AL300 -> 111 $z]
[CF2 $$o -> 711 $9]
[CF3 $$o -> 711 $9]
[KF2 $$o -> 711 $9]
720 AUTHOR AS ON DOCUMENT / AUTHOR IN ARCHIVE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [ARC,CER]
[Additional field(s)/tag(s): PE,919C4,YAU,909CB]
[YAU * -> 909CB $y -> 720 $a {CER}]
[PE * -> 919C4 $a -> 720 $a {ARC}]
721 TRANSLATOR (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [MAN]
$l Words translated (NR) - [MAN]
[Additional field(s)/tag(s): AU,919CH]
[AU $$t -> 919CH $a -> 721 $a]
[AU $$w -> 919CH $l -> 721 $l]
722 REVISOR (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [MAN]
[Additional field(s)/tag(s): TR,919CI]
[TR $$r -> 919CI $a -> 722 $a]
723 RE-READER (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [MAN]
$l Words re-read (NR) - [MAN]
$s Language (NR) - [MAN]
[Additional field(s)/tag(s): 919CK]
[919CK $$a -> 723 $a]
[919CK $$l -> 723 $l]
[919CK $$s -> 723 $s]
724 "COMPOSER" OF MINUTES (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [MAN]
$l Words composed (NR) - [MAN]
$s Language (NR) - [MAN]
[Additional field(s)/tag(s)]
{Not in use before mid-April 2004}
725 "TYPIST" OF MINUTES (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [MAN]
$l Words composed (NR) - [MAN]
[Additional field(s)/tag(s)]
{Not in use before mid-June 2004}
770 SUPPLEMENT/SPECIAL ISSUE ENTRY (R)
Indicators - Both undefined
Subfield Code(s)
$i Display text (NR) - [CER base=3n]
$t Title (NR) - [CER base=3n]
$w Record control number (R) - [CER base=3n]
[Additional field(s)/tag(s): RT]
[RT $$1 -> 770 $i {Has the supplement}]
[RT $$t -> 770 $t]
[RT $$w -> 770 $w]
772 PARENT RECORD ENTRY (R)
Indicators - Both undefined
Subfield Code(s)
$i Display text (NR) - [CER base=3n]
$t Title (NR) - [CER base=3n]
$w Record control number (R) - [CER base=3n]
[Additional field(s)/tag(s): RT]
[RT $$1 -> 772 $i {Supplement to}]
[RT $$t -> 772 $t]
[RT $$w -> 772 $w]
773 HOST ITEM ENTRY (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a "DOI" (NR) - [CER]
$c Pagination (NR) - [ARC,CER,MMD]
$d Complete date (NR) - [CER,MMD]
$n Number [issue] (NR) - [ARC,CER,MMD]
$p Title (NR) - [ARC,CER,MMD]
$u URL (NR) - [MMD]
$v Volume (NR) - [CER,MMD]
$y Year (NR) - [ARC,CER,MMD]
[Additional field(s)/tag(s): PR,909C4]
[The same subfield codes are used as in PR and 909C4]
780 PRECEDING ENTRY (R)
Indicators - Both undefined
Subfield Code(s)
$i Display text (NR) - [CER base=3n]
$t Title (NR) - [CER base=3n]
$w Record control number (R) - [CER base=3n]
[Additional field(s)/tag(s): RT]
[RT $$1 -> 780 $i {Continues}]
[RT $$t -> 780 $t]
[RT $$w -> 780 $w]
785 SUCCEEDING ENTRY (R)
Indicators - Both undefined
Subfield Code(s)
$i Display text (NR) - [CER base=3n]
$t Title (NR) - [CER base=3n]
$w Record control number (R) - [CER base=3n]
[Additional field(s)/tag(s): RT]
[RT $$1 -> 785 $i {Continued by}]
[RT $$t -> 785 $t]
[RT $$w -> 785 $w]
787 NONSPECIFIC RELATIONSHIP ENTRY (R)
Indicators - Both undefined
Subfield Code(s)
$i Display text (NR) - [CER base=3n]
$t Title (NR) - [CER base=3n]
$w Record control number (R) - [CER base=3n]
[Additional field(s)/tag(s): RT]
[RT $$1 -> 787 $i {other forms of relation}]
[RT $$t -> 787 $t]
[RT $$w -> 787 $w]
852 LOCATION (R)
Indicators - Both undefined
Subfield Codes
$a Location (NR) - [ARC,CER,MAN,MMD]
$c Shelving location (NR) - [ARC,CER]
[Additional field(s)/tag(s): LO]
[LO * -> 852 $a {ARC,MAN}]
[LO $$a -> 852 $a {MMD}]
[LO $$b -> 852 $c {ARC,CER}]
856 ELECTRONIC LOCATION AND ACCESS:1 (R)
Indicators
First Access method
4 HTTP
Second Relationship
^ No information provided
Subfield Code(s)
$q Electronic format type (NR) - [IEX,MMD]
$u Uniform Resource Identifier (NR) - [ARC,CER,IEX,MAN,MMD]
$x Nonpublic note (NR) - [CER,MMD]
$y Link text (NR) - [ARC,CER,IEX,MAN,MMD]
[Additional field(s)/tag(s): 8564,EXT,MAP,EDL,ICO]
[EXT $$x -> 8564 $q {$x EDL; MMD/PHO: IF .jpeg $x picture}]
[ICO * -> 8564 $q {MMD [bases80-89][.gif] -> $x icon}]
[EDL $$x -> 8564 $u {base=55, MMD}]
[EXT * -> 8564 $u {IEX}; $$x -> $u]
[MAP $$x -> 8564 $u {base=3n}]
[EXT $$t -> 8564 $x] {MMD}]
[MAP $$k -> 8564 $x {base=3n}]
[EDL $$n -> 8564 $y {base=55, MMD}]
[EXT $$n -> 8564 $y]
[MAP $$n -> 8564 $y {base=3n}]
856 ELECTRONIC LOCATION AND ACCESS:2 (R)
Indicators
First Access method
4 HTTP
Second Relationship
2 Periodicals [TOC]
Subfield Code(s)
$u Uniform Resource Identifier (NR) - [CER base=3n]
$x Nonpublic note (NR) - [CER base=3n]
$y Link text (NR) - [CER base=3n]
[Additional field(s)/tag(s): 85642,TOC]
[TOC $$x -> 85642 $u]
[TOC $$k -> 85642 $x]
[TOC $$n -> 85642 $y]
859 ELECTRONIC MAIL MESSAGE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$f E-mail address (NR) - [CER,IEX,MAN,MMD]
[Additional field(s)/tag(s): 8560,EM,EMAIL]
[EM * -> 8560 $f -> 859 $f]
[EMAIL * -> 8560 $f -> 859 $f {IEX}]
866 TEXTUAL HOLDINGS--BASIC BIBLIOGRAPHIC UNIT (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Alternative holdings statement (NR) - [CER base=3n]
$b Library (NR) - [CER base=3n]
$c Collection (NR) - [CER base=3n]
$g Subscription status code (NR) - [CER base=3n]
$v Volume (NR) - [CER base=3n]
$x Retention code (NR) - [CER base=3n]
$z Public note (NR) - [CER base=3n]
[Additional field(s)/tag(s): HN,852]
[HN $$a -> 866 $a]
[HN $$y -> 866 $a]
[HN $$l -> 852 $b -> 866 $b]
[HN $$c -> 852 $c -> 866 $c]
[HN $$s -> 852 $g -> 866 $g]
[HN $$v -> 866 $v
[HN $$r -> 852 $x -> 866 $x]
[HN $$n -> 852 $z -> 866 $z]
901 AFFILIATION AT CONVERSION AL300/AL500 (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$u Name of institute (NR) - [CER,MAN]
[Additional field(s)/tag(s): AF,909CA]
[AF * -> 909CA $$u -> 901 $u]
902 OTHER INSTITUTES (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Name of other institute (NR) - [CER]
[Additional field(s)/tag(s): OI,909CD]
[OI * -> 909CD $a -> 902 $a]
903 "GREY BOOK" (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Approval (NR) - [CER]
$b Beam (NR) - [CER,IEX]
$d Status date (NR) - [IEX]
$s Status (NR) - [CER,IEX]
[Additional field(s)/tag(s): BEAM,NO1,ST,909CE]
[NO1 * -> 909CE $a -> 903 $a {IN= GREY BOOK; NO1=Approved...}]
[BEAM * -> 909CE $b -> 903 $b {IEX}]
[NO1 * -> 909CE $b -> 903 $b {IN= GREY BOOK; NO1=Beam...}]
[ST $$d -> 909CE $d -> 903 $d {IEX}]
[NO1 * -> 909CE $s -> 903 $s {IN= GREY BOOK; NO1=Status...}]
[ST * -> 909CE $s -> 903 $s {CER,IEX}]
904 BEAMS PER SHIFT (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$s Beams per shift (NR) - [CER]
[Additional field(s)/tag(s): SH,909CF]
[SH $$s -> 909CF $s -> 904 $s]
905 SPOKESMAN (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Address (NR) - [CER]
$k Telephone (NR) - [CER]
$l Fax (NR) - [CER]
$m E-mail (NR) - [CER]
$p Personal name (NR) - [CER,IEX]
$q Private address (NR) - [CER]
[Additional field(s)/tag(s): SPK,909CG]
[SPK $$a -> 909CG $a -> 905 $a]
[SPK $$p -> 909CG $k -> 905 $k]
[SPK $$f -> 909CG $l -> 905 $l]
[SPK $$e -> 909CG $m -> 905 $m]
[SPK $$n -> 909CG $p -> 905 $p]
[SPK * -> 909CG $p -> 905 $p {IEX}]
[SPK $$d -> 909CG $q -> 905 $q]
906 RESPONSIBLE PERSON / REFEREE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Address (NR) - [CER]
$k Telephone (NR) - [CER]
$l Fax (NR) - [CER]
$m E-mail (NR) - [CER]
$p Personal name (NR) - [CER]
$q Private address (NR) - [CER]
[Additional field(s)/tag(s): RESP,909CH]
[RESP $$a -> 909CH $a -> 905 $a]
[RESP $$p -> 909CH $k -> 905 $k]
[RESP $$f -> 909CH $l -> 905 $l]
[RESP $$e -> 909CH $m -> 905 $m]
[RESP $$n -> 909CH $p -> 905 $p]
[RESP $$d -> 909CH $q -> 905 $q]
907 INTC: RESOURCE COORDINATOR (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [CER,IEX]
[Additional field(s)/tag(s): RESP,909CI]
[RESP $$r -> 909CI $a -> 907 $a]
908 INTC: TECHNICAL COORDINATOR (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [CER,IEX]
[Additional field(s)/tag(s): RESP,909CJ]
[RESP $$t -> 909CJ $a -> 908 $a]
909 DEPUTY SPOKESMAN (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$d Personal name (NR) - [IEX]
[Additional field(s)/tag(s): RESP,919C7]
[RESP $$d -> 919C7 $d -> 909 $d]
910 FSGO (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$f Personal name (NR) - [IEX]
[Additional field(s)/tag(s): RESP,919C8]
[RESP $$f -> 919C8 $f -> 910 $f]
911 GLIMOS (R) [CERN] [CERN]
Indicators - Both undefined
Subfield Code(s)
$g Personal name (NR) - [IEX]
[Additional field(s)/tag(s): RESP,919C9]
[RESP $$g -> 919C9 -> 911 $g]
912 REGISTRATION FOR CONFERENCE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Abstracts deadline (NR) - [CER]
$f Fee (NR) - [CER]
$i "By invitation only" (NR) - [CER]
$n Number of participants (NR) - [CER]
$p Paper deadline (NR) - [CER]
$r Registration deadline (NR) - [CER]
[Additional field(s)/tag(s): RE,909CR]
[The same subfield codes are used as in RE and 909CR]
913 CITATION (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$c Citation (NR) - [CER]
$p Unformatted references (NR) - [IEX]
$t Title abbreviation (NR) - [CER]
$u Uniform Resource Identifier (NR) - [CER,IEX]
$v Volume (NR) - [CER]
$y Year (NR) - [CER]
[Additional field(s)/tag(s): CN,909C5]
[CN $$c -> 909C5 $c -> 913 $c]
[CN $$p -> 909C5 $p -> 913 $p]
[CN * -> 909C5 $t -> 913 $t {base=1n,n=1-3}]
[CN * -> 909C5 $u -> 913 $u {CER if IN=GREY BOOK}]
[CN * -> 909C5 $u -> 913 $u {IEX}]
[CN $$v -> 909C5 $v -> 913 $v
[CN $$y -> 909C5 $y -> 913 $y
914 UNIVERSAL DECIMAL CLASSIFICATION (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$u Secondary UDC number (NR) - [WAI/UDC]
$v Library shelving code (NR) - [WAI/UDC]
[Additional field(s)/tag(s): UD,UD2,UD3,909CU]
[UD2 * -> 909CU $u -> 914 $u]
[UD3 * -> 909CU $v -> 914 $v]
[UD $$s -> 909CU $v -> 914 $v]
916 "STATUS WEEK" (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Acquisition of proceedings code (NR) - [CER]
$d Display period for books (NR) - [CER]
$s Status of record (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$w Status week (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$y Year for Annual list (NR) - [CER]
$z CERN paper or CERN work (candidates for Ann. Report)(NR) - [CER]
[Additional field(s)/tag(s): DISP,SW,909CS,909CT]
[SW $$a -> 909CS $a -> 916 $a {NOT bas=3n}]
[DISP * -> 909CT $a -> 916 $d]
[SW $$s -> 909CS $s -> 916 $s]
[SW $$w -> 909CS $w -> 916 $w]
[SW $$y -> 909CS $y -> 916 $y]
917 CABLE
Indicators - Both undefined
Subfield Code(s)
$a Cable (NR) - [IEX]
[Additional field(s)/tag(s): CABLE,919CB]
[CABLE * -> 919CB $$a -> 917 $a]
918 DEPARTMENT INDEX (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Department index (NR) - [IEX]
[Additional field(s)/tag(s): DEPTI,919CC]
[DEPTI * -> 919CC $a -> 918 $a]
919 ORGANIZATION INDEX (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Organization index (NR) - [IEX]
[Additional field(s)/tag(s): ORGI,919CD]
[ORGI * -> 919CD $a -> 919 $a]
920 TOWN INDEX (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Town index (NR) - [IEX]
[Additional field(s)/tag(s): TOWNI,919CE]
[TOWNI * -> 919CE $$a -> 920 $a]
921 MICROCOSM: LOANS (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$d Loan date (NR) - [MMD]
$e Exhibition loan (NR) - [MMD]
$i Borrower institute (NR) - [MMD]
$t Loan to (NR) - [MMD]
$x Exhibition name (NR) - [MMD]
[Additional field(s)/tag(s): LD,EL,ISB,LT,XI,919CL]
[LD $$a -> 919CL $a -> 921 $d]
[EL $$e -> 919CL $e -> 921 $e]
[ISB * -> 919CL $i -> 921 $i]
[LT * -> 919CL $t -> 921 $t]
[XI $$a -> 919CL $x -> 921 $x]
922 MICROCOSM: PHYSICAL VALUES (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$d Diameter (NR) - [MMD]
$h Height (NR) - [MMD]
$i Interactive objects (NR) - [MMD]
$l Length (NR) - [MMD]
$p Depth (NR) - [MMD]
$w Weight (NR) - [MMD]
[Additional field(s)/tag(s): DIA,HI,IA,WI,DE,WE,919CM]
[DIA $$a -> 919CM $d -> 922 $d]
[HI $$a -> 919CM $h -> 922 $h]
[IA * -> 919CM $i -> 922 $i]
[WI $$a -> 919CM $l -> 922 $l]
[DE $$a -> 919CM $p -> 922 $p]
[WE $$a -> 919CM $w -> 922 $w]
923 PLACE OF PHOTO (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$p Place of photo (NR) - [MMD]
$r Requestor (NR) - [MAN,MMD]
[Additional field(s)/tag(s): PL,REQ,919CP]
[PL * -> 919CP $p -> 923 $p]
[REQ * -> 919CP $r -> 923 $r]
924 PHOTOLAB (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a "Tirage" (NR) - [MMD]
[Additional field(s)/tag(s): TIR,919CQ]
[TIR * -> 919CQ $a -> 924 $a]
[TIR $$a -> 919CQ $a -> 924 $a]
925 DATES (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Opening date/Date received (NR) - [ARC,MAN]
$b Closing date/Date completed (NR) - [ARC,MAN]
[Additional field(s)/tag(s): DA,919C0]
[DA $$a -> 919C0 $a -> 925 $a {ARC}]
[DA $$r -> 919C0 $a -> 925 $a {MAN}]
[DA $$b -> 919C0 $b -> 925 $b {ARC}]
[DA $$c -> 919C0 $b -> 925 $b {MAN}]
926 RECIPIENT (NR) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [ARC,CER,MAN]
[Additional field(s)/tag(s): DEST,919C1]
[DEST * -> 919C1 $a -> 926 $a]
927 FILE NUMBER (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a File number (NR) - [ARC,MAN]
[Additional field(s)/tag(s): FN,919C2]
[FN * -> 919C2 $a -> 927 $a]
928 ADDITIONAL RECIPIENT(S) (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Personal name (NR) - [CER,MAN]
[Additional field(s)/tag(s):
{Not in use in AL300}
929 RETENTION (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Retention (NR) - [ARC,MAN]
$d Retention date (NR) - [MAN]
[Additional field(s)/tag(s): RT,919C5]
[RT * -> 919C5 $a -> 929 $a]
[RT $$b -> 919C5 $d -> 929 $d {ARC}]
[RT $$d -> 919C5 $d -> 929 $d {MAN}]
931 PERI: MAIN CORPORATE AUTHOR (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Corporate name (NR) - [CER base=3n]
[Additional field(s)/tag(s): CA,909CQ]
[CA * -> 909CQ $a -> 931 $a]
932 PERI: ADDITIONAL CORPORATE AUTHOR (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Corporate name (NR) - [CER base=3n]
[Additional field(s)/tag(s): CA2,909CQ]
[CA2 * -> 909CQ $g -> 932 $a]
933 PERI: IMPRINT (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Place of publisher (NR) - [CER base=3n]
$b Name of publisher (NR) - [CER base=3n]
[Additional field(s)/tag(s): PIM,260,909CP]
[PIM $$p -> 260 $a -> 909CP $a -> 933 $a]
[PIM $$n -> 260 $b -> 909CP $b -> 933 $b]
934 PERI: IMPRINT OF E-JOURNALS (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Place of publisher (NR) - [CER base=3n]
$b Name of publisher (NR) - [CER base=3n]
$l Link for publisher (NR) - [CER base=3n]
$x Non-public note (NR) - [CER base=3n]
[Additional field(s)/tag(s): PIM2,909CM]
[PIM2 $$p -> 909CM $a -> 934 $a]
[PIM2 $$n -> 909CM $b -> 934 $b]
[PIM2 $$l -> 909CM $l -> 934 $l]
[$x not in use in AL300]
935 PERI: USER NOTE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a User note (NR) - [CER base=3n]
[Additional field(s)/tag(s): PNO,909CX,500]
[PNO $$p -> 500 $a -> 909CX $a -> 935 $a]
936 PERI: E-J USER NOTE (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a E-J user note (NR) - [CER base=3n]
[Additional field(s)/tag(s): PNO2,909CN]
[PNO2 * -> 909CN $a -> 936 $a]
937 PERI: INTERNAL NOTE (R) - [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Internal note (NR) - [CER base=3n]
[Additional field(s)/tag(s): PNI,595,909CV]
[PNI * -> 595 $a -> 909CV $a -> 937 $a]
938 PERI: LOCAL INFORMATION (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Frequncy given as numbers (NR) - [CER base=3n]
$f Impact factor (NR) - [CER base=3n]
$i Index (NR) - [CER base=3n]
$p Title status (NR) - [CER base=3n]
[Additional field(s)/tag(s): SW,909CL]
[SW $$a -> 909CL $a -> 938 $a]
[SW $$o -> 909CL $f -> 938 $f]
[SW $$i -> 909CL $i -> 938 $i]
[SW $$p -> 909CL $p -> 938 $p]
940 LINK TO COMPANY (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$u URL address (NR) - [CERN:BOOKSHOP,MAN]
$y URL note (NR) - [CERN:BOOKSHOP,MAN]
[Additional field(s)/tag(s):]
{Not in use in AL300}
941 RELATED DOCUMENT NUMBER (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a Related document number (NR) - [MAN]
[Additional field(s)/tag(s):]
{Not in use in AL300}
960 BASE (R) [CDS Invenio/MySQL]
Indicators - Both undefined
Subfield Code(s)
$a Base number (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
Taken from AL500 BAS
961 CAT (R) [CDS Invenio/MySQL]
Indicators - Both undefined
Subfield Code(s)
$a Cataloguer (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$b Cataloguer level (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$c Modification date (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$l Library (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$h Hour - (NR) [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$x Creation date(NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
Taken from AL500 CAT
962 ALEPH Linking Field (R) [CDS Invenio/MySQL]
Indicators - Both undefined
Subfield Code(s)
$a - link type
UP link to another BIB type record. A record can have only one
link of this type. "DN" link is automatically built in the
opposite direction.
DN "down" link to another BIB type record. Multiple links
are possible. "UP" link is automatically built in the
opposite direction.
PAR parallel link from BIB record to BIB record.
"PAR" link is automatically built in the opposite direction.
HOL link from HOL record to BIB record. Link is built from
BIB to HOL.
ADM link from ADM record to BIB record. Link is built from
BIB to ADM.
ANA is a link between bibliographic records of different levels.
When an anayltic link is created the system generates UP / DWN
links between the two records and an item link between the source
record and the item that corresponds to it (according to vol.,
part,year and pages) on the ADM record of the second record.
ITM links are created between a Bibliographic record and an ADM
record when there is no relationship between the two Bib records,
for example when two items are bound together. [NR} - [ARC]
{Only used for ARC in AL300}
$b - sysno of the linked document record (NR) - [ARC,CER,MMD]
$l - library where linked record is located (NR) - [ARC,CER,MMD]
$n - note regarding a DN (down record) link - (NR) [ARC,CER]
$m - note regarding an UP (up record) link - [not yet in use at CERN]
$y - analytic link - year link - [not yet in use at CERN]
$v - analytic link - volume link - [not yet in use at CERN]
$p - analytic link - part link - [not yet in use at CERN]
$i - analytic link - issue link - [not yet in use at CERN]
$k - analytic link - pages (NR) - [ARC,CER]
$t - base=3n [for paper version of e-journals]/title - (NR) - [CER,MMD
Taken from AL500 LKR
963 OWNER (NR) [CDS Invenio/MySQL]
Indicators - Both undefined
Subfield Code(s)
$a Owner - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
Taken from AL500 OWN
964 ITEM (NR) [CDS Invenio/MySQL]
Indicators - Both undefined
Subfield Code(s)
$a Owner - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
Taken from AL500 ITM
970 SYSTEM NUMBER (NR) [CDS Invenio/MySQL]
Indicators - Both undefined
Subfield Code(s)
$a AL500 sysno (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
Taken from AL500 SYS
980 COLLECTION INDICATOR (R) [CERN] [CDS Invenio/MySQL]
Indicators - Both undefined
Subfield Code(s)
$a Primary indicator (NR) - [ARC,CER,IEX,MAN,MMD]
$b Secondary indicator (NR) - [ARC,CER,IEX,MAN,MMD]
$c Deleted indicator (NR) - [ARC,CER,IEX,MAN,MMD
Generated and used in MySQL; accepted in AL500
981 SYSTEM NUMBER OF DELETED DOUBLE RECORDS (R) [CERN]
Indicators - Both undefined
Subfield Code(s)
$a System number (NR) - [ARC,CER,IEX,MAN,MMD,WAI]
{Not in use in AL300; to be added for users of MySQL}
999 REFERENCES (R) [CERN] [CDS Invenio/MySQL]
Indicators
First Origine of indicator
C CERN
Second Type
5 References
Subfield Code(s)
$o Order number [contains [ ] line number] (NR)
$m Miscellaneous [contains 1st part of reference] (R)
$t Journal Title abbreviation (NR)
$p Page (NR)
$v Volume (NR)
$y Year (NR)
$n Issue Number (NR)
$u Uniform Resource Identifier (NR)
$r Report Number (NR)
NOT YET IMPLEMENTED
BAS BASE
Indicators - Both undefined
Subfield Code(s)
$a Base number (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
For MySQL use tag 960
[Additional field(s)/tag(s): BA]
[BA * -> BAS $a {used for input}]
[BASE * -> BAS $a {used for bath}]
CAT CAT (R)
Indicators - Both undefined
Subfield Code(s)
$a Cataloguer (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$b Cataloguer level (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$c Modification date (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$l Library (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$h Hour - (NR) [ARC,CER,IEX,MAN,MMD,WAI/UDC]
$x Creation date(NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
For MySQL use tag 961
[Additional field(s)/tag(s): CATZZ]
[CATZZ $$a -> CAT $a]
[CATZZ $$b -> CAT $b]
[CATZZ $$d -> CAT $c]
[{Not in use in AL300} -> CAT $l]
[{Not in use in AL300} -> CAT $h
[CATZZ $$c -> CAT $x]
FMT FORMAT
This field has no indicators or subfield codes.
It contains the Scope of material [2 character code]
Not used in MySQL
{Not in use in AL300}
ITM ITEM (NR)
Indicators - Both undefined
Subfield Code(s)
$a Owner - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
For MySQL use tag 964
{Not in use in AL300}
LKR ALEPH Linking Field (R)
Indicators - Both undefined
Subfield Code(s)
$a - link type
UP link to another BIB type record. A record can have only one
link of this type. "DN" link is automatically built in the
opposite direction.
DN "down" link to another BIB type record. Multiple links
are possible. "UP" link is automatically built in the
opposite direction.
PAR parallel link from BIB record to BIB record.
"PAR" link is automatically built in the opposite direction.
HOL link from HOL record to BIB record. Link is built from
BIB to HOL.
ADM link from ADM record to BIB record. Link is built from
BIB to ADM.
ANA is a link between bibliographic records of different levels.
When an anayltic link is created the system generates UP / DWN
links between the two records and an item link between the source
record and the item that corresponds to it (according to vol.,
part,year and pages) on the ADM record of the second record.
ITM links are created between a Bibliographic record and an ADM
record when there is no relationship between the two Bib records,
for example when two items are bound together. [NR} - [ARC]
{Only used for ARC in AL300}
$b - sysno of the linked document record (NR) - [ARC,CER,MMD]
$l - library where linked record is located (NR) - [ARC,CER,MMD]
$n - note regarding a DN (down record) link - (NR) [ARC,CER]
$m - note regarding an UP (up record) link - [not yet in use at CERN]
$y - analytic link - year link - [not yet in use at CERN]
$v - analytic link - volume link - [not yet in use at CERN]
$p - analytic link - part link - [not yet in use at CERN]
$i - analytic link - issue link - [not yet in use at CERN]
$k - analytic link - pages (NR) - [ARC,CER]
$t - base=3n [for paper version of e-journals]/title - (NR) - [CER,MMD]
For MySQL use tag 962
[Additional field(s)/tag(s): 909CK]
[LKR $$a -> 909CK $a -> LKR $a {ARC}]
[LKR $$b -> 909CK $b -> LKR $b {ARC,CER,MMD}]
[LKR $$l -> 909CK $l -> LKR $l {ARC,CER,MMD}]
[LKR $$d -> 909CK $n -> LKR $n {ARC,CER}]
[LKR $$t -> 909CK $n -> LKR $n {CER base=3n}]
[LKR $$e -> 909CK $k -> LKR $k {ARC,CER}]
[LKR $$t -> 909CK $t -> LKR $t {CER base=3n,MMD}]
OWN OWNER (NR)
Indicators - Both undefined
Subfield Code(s)
$a Owner - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
For MySQL use tag 963
{Not in use in AL300}
SYS SYSTEM NUMBER (NR)
Indicators - Both undefined
Subfield Code(s)
$a AL500 sysno (NR) - [ARC,CER,IEX,MAN,MMD,WAI/UDC]
For MySQL use tag 970
</protect>
</pre>
diff --git a/modules/webhelp/web/admin/howto/howto-migrate.webdoc b/modules/webhelp/web/admin/howto/howto-migrate.webdoc
index bc70117a1..b584cb401 100644
--- a/modules/webhelp/web/admin/howto/howto-migrate.webdoc
+++ b/modules/webhelp/web/admin/howto/howto-migrate.webdoc
@@ -1,118 +1,118 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: HOWTO Migrate Your Old Documents Into CDS Invenio -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">Admin Area</a> &gt; <a class="navtrail" href="howto">Admin HOWTOs</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">Admin Area</a> &gt; <a class="navtrail" href="howto">Admin HOWTOs</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Overview</h2>
<p>CDS Invenio comes as a suite of several independent flexible modules
that enable you to easily convert your data from any existing format
and to upload them into the CDS Invenio system. This document briefly
describes how you proceed.
<h2>Quick instructions for the impatient CDS Invenio admin</h2>
<blockquote>
<pre>
$ cd /tmp
$ cp /my/own/doc/system/datadump.txt .
$ vi dump.cfg
$ bibconvert -cdatadump.cfg < datadump.txt > datadump.xml
$ bibupload datadump.xml
$ bibsched
</pre>
</blockquote>
<h2>Detailed instructions for the patient CDS Invenio admin</h2>
<blockquote>
<dl>
<dt><code>$ cd /tmp</code>
<dd><blockquote>Go to a temporary directory.</blockquote>
<dt><code>$ cp /my/own/doc/system/datadump.txt .</code>
<dd><blockquote>Copy your old data into a text file or some other
format of your choice. Preferably, the data should be well
structured, such as XML. (Anyhow, even a free text format may be
attepmted to be matched!)</blockquote>
<dt><code>$ vi dump.cfg</code>
<dd><blockquote>Describe the format of your data in the <a
href="bibconvert-admin">BibConvert</a> language. (You may
also use XSLT if your dumped file is in the XML format.)
<p>This step will enable you to transform your data into MARCXML format
that the CDS Invenio system internally uses for bibliographic data
handling. (If you have not chosen yet your MARC scheme, please read
the <a href="howto-marc">MARC HOWTO</a>.)
<p>When writing the transformation configuration file, you may want to
add a collection identifier to your records so that they would belong
to a particular WebSearch collection; otherwise they might not be
visible through the search interface. You do this by enriching the
metadata to contain the 980 MARC tags in the output, of the form:
<pre>
&lt;datafield tag="980" ind1=" " ind2=" ">
&lt;subfield code="a">ARTICLE&lt;/subfield>
&lt;/datafield>
</pre>
This will make your record to go to the Articles demo collection, for
example. You will be able to define your collections later via the <a
href="websearch-admin">WebSearch Admin</a>, the important
point for now is only to create appropriate 980 collection identifier
tags for records you are going to upload.
<p>If your dumped file is in the XML format, you can also consult the
example <code>oaimarc2marcxml.xsl</code> stylesheet that is used to
manipulate metadata harvested via OAI.
<p>You may also want to preserve any OAI identifiers in your old
records, if you had any.
</blockquote>
<dt><code>$ bibconvert -cdatadump.cfg < datadump.txt > datadump.xml</code>
<dd><blockquote>Convert the data from your own format into XML MARC,
using the configuration you just wrote in the previous step.
</blockquote>
<dt><code>$ bibupload -i datadump.xml</code>
<dd><blockquote>Upload thusly converted metadata into CDS Invenio
bibliographic databases.</blockquote>
<dt><code>$ bibsched</code>
<dd><blockquote>Watch the progress how the metadata are being
uploaded, indexed, and formatted.</blockquote>
</dl>
<p>Congratulations! At this point you should have successfully
migrated your old data into the CDS Invenio system.
</blockquote>
diff --git a/modules/webhelp/web/admin/howto/howto-run.webdoc b/modules/webhelp/web/admin/howto/howto-run.webdoc
index d89b04109..432c4ff0b 100644
--- a/modules/webhelp/web/admin/howto/howto-run.webdoc
+++ b/modules/webhelp/web/admin/howto/howto-run.webdoc
@@ -1,187 +1,187 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: HOWTO Run Your CDS Invenio Installation -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">Admin Area</a> &gt; <a class="navtrail" href="howto">Admin HOWTOs</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">Admin Area</a> &gt; <a class="navtrail" href="howto">Admin HOWTOs</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Overview</h2>
<p>This HOWTO guide intends to give you ideas on how to run your
CDS Invenio installation and how to take care of its normal operation day
by day.
</p>
<h2>BibSched Periodical Tasks</h2>
<p>Many tasks that manipulate the bibliographic record database can be
set to run in a periodical mode. For example, we want to have the
indexing engine to scan periodically for newly arrived documents to
index them as soon as they enter into the system. It is the role of
the BibSched system to take care of the task scheduling and the task
execution.
</p>
<p>Periodical tasks (such as regular metadata indexing) as well as
one-time tasks (such as a batch upload of newly acquired metadata
file) are not executed straight away but are stored in the BibSched
task queue. BibSched daemon looks periodically in the queue and
launches the tasks according to their order or the date of programmed
runtime. You can consider BibSched to be a kind of cron daemon for
bibliographic tasks.
</p>
<p>This means that after having installed CDS Invenio you will want to
have the BibSched daemon running permanently. To launch BibSched
daemon, do:
<blockquote>
<pre>
$ bibsched start
</pre>
</blockquote>
</p>
<p>To setup indexing, ranking, formatting, and collection cache updating
daemons to run periodically with a sleeping period of, say, 1 hour:
<blockquote>
<pre>
$ bibindex -f50000 -s1h
$ bibreformat -oHB -s1h
$ webcoll -v0 -s1h
$ bibrank -f50000 -s1h
</pre>
</blockquote>
</p>
<p>It is imperative to have the above four tasks permanently in your
BibSched queue so that newly submitted documents will be processed
automatically.
</p>
<p>You may also want to set up periodical recalculating of word
similarity ranking weights every, say, thirty days:
<blockquote>
<pre>
$ bibrank -f50000 -R -wwrd -s30d
</pre>
</blockquote>
</p>
Please consult the section below entitled "Recalculate ranking
weights" for more details.
</p>
<p>Note that the BibSched daemon automatic mode stops as soon as some
of the tasks ends with an error. It it therefore a good idea to
inspect BibSched queue from time to time, say several times per day.
This can be done by running the BibSched command-line monitor:
<blockquote>
<pre>
$ bibsched
</pre>
</blockquote>
</p>
<p>The monitor will permit you to stop/start the daemon mode, to
delete the tasks already submitted, to run some of the tasks manually,
etc. Note also that BibSched daemon writes log and error files on its
operation and on the operation of its tasks. The log and error files
can be found at <code>/opt/cds-invenio/var/log</code>.
</p>
<p>You may want to clean up the old BibSched tasks with the DONE
status, let us say once per month, to make the task table slim and the
bibsched daemon both faster and less memory hungry:
<blockquote>
<pre>
$ echo "DELETE FROM schTASK WHERE status='DONE' AND runtime&lt;DATE_SUB(NOW(), INTERVAL 7 DAY);" | dbexec
</pre>
</blockquote>
</p>
<h2>Recalculate ranking weights</h2>
<p>When you are adding new records to the system, the word frequency
ranking weights for old records aren't recalculated by default in
order to speed up the insertion of new records. This may influence a
bit the precision of word similarity searches. It is therefore
advised to expressely run bibrank in the recalculating mode once in a
while by doing:
<blockquote>
<pre>
$ bibrank -R -wwrd
</pre>
</blockquote>
You may want to do this either (i) periodically, say once per month
(see the previous section), or (ii) depending on the frequency of new
additions to the record database, say when the size grows by 2-3
percent.
</p>
<h2>Guest Users Cleanup</h2>
<p>Guest users create a lot of entries in CDS Invenio tables that are
related to their web sessions, their search history, personal baskets,
etc. This data has to be garbage-collected periodically. You can run this,
say every day, via:
<blockquote>
<pre>
$ inveniogc -s1d
</pre>
</blockquote>
</p>
<h2>Cleaning Up the Filesystem</h2>
<p>Different temporary log and err files are created in <code>/opt/cds-invenio/var/log</code>
and <code>/opt/cds-invenio/var/tmp</code> directory that is good to clean up from time to time. The previous command could be used to clean those files, too, via:
<blockquote>
<pre>
$ inveniogc -s1d -d
</pre>
</blockquote>
</p>
<h2>Alert Engine</h2>
<p>CDS Invenio users may set up an automatic notification email alerts
that would send them documents corresponding to the user profile by
email either daily, weekly, or monthly. It is the job of the alert
engine to do this. The alert engine has to be run every day:
<blockquote>
<pre>
$ alertengine
</pre>
</blockquote>
</p>
<p>You may want to set up an external cron job to call
<code>alertengine</code> each day.
</p>
diff --git a/modules/webhelp/web/admin/howto/howto.webdoc b/modules/webhelp/web/admin/howto/howto.webdoc
index e058cdb58..48900eb0e 100644
--- a/modules/webhelp/web/admin/howto/howto.webdoc
+++ b/modules/webhelp/web/admin/howto/howto.webdoc
@@ -1,50 +1,50 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Admin HOWTOs -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">Admin Area</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">Admin Area</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>The HOWTO guides will give you both short and not-so-short
recipes and thoughts on some of the most frequently encountered
administrative tasks.</p>
<blockquote>
<dl>
<dt><a href="howto-marc">HOWTO MARC</a>
<dd>Describes how to choose the MARC representation of your metadata
and how it will be stored in CDS Invenio.
<dt><a href="howto-migrate">HOWTO Migrate</a>
<dd>Describes how to migrate a bunch of your old data from any format
you might have into CDS Invenio.
<dt><a href="howto-run">HOWTO Run</a>
<dd>Describes how to run your CDS Invenio installation and how to take
care of its normal operation day by day.
</dl>
</blockquote>
<p>Haven't found what you were looking for? <a href="mailto:<CFG_SITE_ADMIN_EMAIL>" title="CDS-Invenio-Suggest-a-HOWTO">Suggest a HOWTO</a>.</
diff --git a/modules/webhelp/web/hacking/coding-style.webdoc b/modules/webhelp/web/hacking/coding-style.webdoc
index 3fc50af46..37eb0bdaf 100644
--- a/modules/webhelp/web/hacking/coding-style.webdoc
+++ b/modules/webhelp/web/hacking/coding-style.webdoc
@@ -1,226 +1,226 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Coding Style -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
A brief description of things we strive at, more or less unsuccessfully.
1. Packaging
We use the classical GNU Autoconf/Automake approach, for tutorial
see e.g. <a href="http://www.amath.washington.edu/~lf/tutorials/autoconf/tutorial_toc.html">Learning the GNU development tools</a> or the <a href="http://sources.redhat.com/autobook/autobook/autobook_toc.html">AutoBook</a>.
2. Modules
CDS Invenio started as a set of pretty independent modules developed by
independent people with independent styles. This was even more
pronounced by the original use of many different languages
(e.g. Python, PHP, Perl). Now the CDS Invenio code base is striving to
use Python everywhere, except in speed-critical parts when a
compiled language such as Common Lisp may come to the rescue in the
near future.
When modifying an existing module, we propose to strictly continue
using whatever coding style the module was originally written into.
When writing new modules, we propose to stick to the
below-mentioned standards.
The code integration across modules is happening, but is slow.
Therefore, don't be surprised to see that there is a lot of room to
refactor.
3. WML/ePerl/etc
This is not so important, because not many lines-of-code were
written in WML/ePerl. We prefer to loosely follow the GNU way, as
always.
4. Python
We aim at following recommendations from <a
href="http://www.python.org/peps/pep-0008.html">PEP 8</a>, although
the existing code surely do not fulfil them here and there.
The code indentation is done via spaces only, please do not use
tabs. One tab counts as four spaces. Emacs users can look into
- our <a href="<WEBURL>/hacking/cdsware.el">cdsware.el</a> for inspiration.
+ our <a href="<CFG_SITE_URL>/hacking/cdsware.el">cdsware.el</a> for inspiration.
All the Python code should be extensively documented via
docstrings, so you can always run pydoc file.py to peruse the
file's documentation in one simple go.
Do not forget to run pylint on your code to check for errors like
uninitialized variables and to improve its quality and conformance
to the coding standard. If you develop in Emacs, run M-x pylint
RET on your buffers frequently. Read and implement pylint
suggestions. (Note that using lambda and friends may lead to false
pylint warnings. You can switch them off by putting block comments
of the form ``# pylint: disable-msg=C0301''.)
Do not forget to run pychecker on your code either. It is another
source code checker that catches some situations better and some
situations worse than pylint. If you develop in Emacs, run C-c C-w
(M-x py-pychecker-run RET) on your buffers frequently. (Note that
using psyco on classes may lead to false pychecker warnings.)
You can check the kwalitee of your code by running ``python
modules/miscutil/lib/kwalitee.py *.py'' on your files. You can
also check the code kwalitee across all the modules by running
``make kwalitee-check'' in the main source directory.
Do not hardcode magic constants in your code. Every magic string or
a number should be put into accompanying file_config.py with
symbol name beginning by cfg_modulename_*.
Clearly separate interfaces from implementation. Document your
interfaces. Do not expose to other modules anything that does not
have to be exposed. Apply principle of least information.
Create as few new library files as possible. Do not create many
nested files in nested modules; rather put all the lib files in one
dir with bibindex_foo and bibindex_bar names.
Use imperative/functional paradigm rather then OO. If you do use
OO, then stick to as simple class hierarchy as possible. Recall
that method calls and exception handling in Python are quite
expensive.
Use rather the good old foo_bar naming convention for symbols (both
variables and function names) instead of fooBar CaMelCaSe
convention. (Except for Class names where UppercaseSymbolNames are
to be used.)
Pay special attention to name your symbols descriptively. Your
code is going to be read and work with by others and its symbols
should be self-understandable without any comments and without
studying other parts of the code. For example, use proper English
words, not abbreviations that can be misspelled in many a way; use
words that go in pair (e.g. create/destroy, start/stop; never
create/stop); use self-understandable symbol names
(e.g. list_of_file_extensions rather than list2); never misname
symbols (e.g. score_list should hold the list of scores and nothing
else - if in the course of development you change the semantics of
what the symbol holds then change the symbol name too). Do not be
afraid to use long descriptive names; good editors such as Emacs
can tab-complete symbols for you.
When hacking module A, pay close attention to ressemble existing
coding convention in A, even if it is legacy-weird and even if we
use a different technique elsewhere. (Unless the whole module A is
going to be refactored, of course.)
Speed-critical parts should be profiled with pyprof. Do not forget
to use tricks like psyco.
The code should be well tested before committed. Testing is an
integral part of the development process. Test along as you
program. The testing process should be automatized via our unit
test and regression test suite infrastructures. Please read the
<a href="test-suite">test suite strategy</a> to know more.
Python promotes writing clear, readable, easily maintainable code.
Write it as such. Recall Albert Einstein's ``Everything should be
made as simple as possible, but not simpler''. Things should be
neither overengineered nor oversimplified.
Recall principles Unix is built upon. As summarized by Eric
S. Reymond's <a href="http://www.catb.org/esr/writings/taoup/html/ch01s06.html#id2877537">TAOUP</a>:
Rule of Modularity: Write simple parts connected by clean interfaces.
Rule of Clarity: Clarity is better than cleverness.
Rule of Composition: Design programs to be connected with other programs.
Rule of Separation: Separate policy from mechanism; separate interfaces from engines.
Rule of Simplicity: Design for simplicity; add complexity only where you must.
Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.
Rule of Transparency: Design for visibility to make inspection and debugging easier.
Rule of Robustness: Robustness is the child of transparency and simplicity.
Rule of Representation: Fold knowledge into data, so program logic can be stupid and robust.
Rule of Least Surprise: In interface design, always do the least surprising thing.
Rule of Silence: When a program has nothing surprising to say, it should say nothing.
Rule of Repair: Repair what you can -- but when you must fail, fail noisily and as soon as possible.
Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.
Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.
Rule of Optimization: Prototype before polishing. Get it working before you optimize it.
Rule of Diversity: Distrust all claims for one true way.
Rule of Extensibility: Design for the future, because it will be here sooner than you think.
or the golden rule that says it all: ``keep it simple''.
For more hints, thoughts, and other ruminations on programming,
see my <a href="https://twiki.cern.ch/twiki/bin/view/CDS/Invenio">CDS Invenio Wiki</a>.
5. PHP
We are moving slowly away out of PHP so that there may be several
practices in place with the PHP code present in CDS Invenio. Usually
this is consistent within modules but inconsistent across modules.
For example, some old code used Emacs' perl-mode, following
traditional K&R C style, while some other old code tried to stick
to <a href="http://pear.php.net/manual/en/standards.php">PEAR recommendations</a>.
6. MySQL
Table naming policy is, roughly and briefly:
- "foo": table names in lowercase, without prefix, used by me
for WebSearch
- "foo_bar": underscores represent M:N relationship between
"foo" and "bar", to tie the two tables together
- "bib*": many tables to hold the metadata and relationships
between them
- "idx*": idx is the table name prefix used by BibIndex
- "rnk*": rnk is the table name prefix used by BibRank
- "fmt*": fmt is the table name prefix used by BibFormat
- "sbm*": sbm is the table name prefix used by WebSubmit
- "sch*": sch is the table name prefix used by BibSched
- "acc*": acc is the table name prefix used by WebAccess
- "bsk*": acc is the table name prefix used by WebBasket
- "msg*": acc is the table name prefix used by WebMessage
- "cls*": acc is the table name prefix used by BibClassify
- "sta*": acc is the table name prefix used by WebStat
- "jrn*": acc is the table name prefix used by WebJournal
- "collection*": many tables to describe collections and search
interface pages
- "user*" : many tables to describe personal features (baskets,
alerts)
- "hst*": tables related to historical versions of metadata and
fulltext files
- end of file -
</pre>
diff --git a/modules/webhelp/web/hacking/common-concepts.webdoc b/modules/webhelp/web/hacking/common-concepts.webdoc
index db00a81d3..aab61952e 100644
--- a/modules/webhelp/web/hacking/common-concepts.webdoc
+++ b/modules/webhelp/web/hacking/common-concepts.webdoc
@@ -1,116 +1,116 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Common Concepts -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
The description of concepts you will encounter here and there in the
CDS Invenio. Our interpretation may differ from the practice found in
other products, so please read this carefully.
1. sysno - (ALEPH|old) system number
Stands for (ALEPH|old) system number only. Which means that, for
outside-CERN CDS Invenio installations, stands for an 'old system
number' whatever it is, if they want to publicise it instead of our
internal auto-incremented CDS Invenio record identifiers.
2. recID - CDS Invenio record identifier
Each record has got an auto-incremented ID in the "bibrec" table
(formerly called "bibitem"). This is the basic "record identifier"
concept in CDS Invenio.
3. docID - eventual fulltext document identifier
Each fulltext file may have eventual docID. This will permit us to
interconnect records (recID) with fulltext files (docID), if we
want to. At the moment there is only one-way connection from recID
to docID via HTTP field 856. This is ugly. I think we may
probably profit by introducing recID-docID relationship in several
ways: file protection, reference extraction, fulltext
indexing... (?!)
4. field - logical field concept such as "reportnumber"
A bibliographic record is composed of 'fields' such as title or
author. Note that we consider 'field' to be a logical concept,
that is compound and may consist of several physical MARC fields.
For example, "report number" field consists of several MARC fields
such as 088 $a, 037 $a, 909C0 $r. Another example: "first report
number" consist of only one MARC field, 037 $a.
5. tag - physical field concept such as "088 $a".
Having defined the concept of 'logical field', let's now turn to
the 'physical field' that denotes basically the concept of 'MARC
field' as defined in MARC-21 standard. In addition to tag, a field
may contain two identifiers to describe the data content, and
subfield codes to denote various parts of the content. See our
HOWTO MARC guide on this.
Thus said, in the implementation of our bibliographic tables
(bibXXx) we have sort of generalized the term 'tag' to stand for:
tag = tag code + identifier1 + identifier2 + subfield code
This convention, while taking some freedom from the MARC-21
standard, enables us to write things like "field: base number, tag:
909C0b, value: 11". If this interpretation is indeed too free with
respect to the standard usage of terms, we may change them in the
future.
6. collection - here we distinguish (i) primary collection concept
and (ii) specific collection concept.
The (i) primary collections are basic organizational structure of
how the records are grouped together in collections. The primary
collections are used in the navigable search interface under the
``Narrow search'' box. The (ii) specific collections present an
orthogonal view on the data organization, that is useful to group
together some records from different primary collections, if they
present a common pattern. The specific collections are used in the
search interface under the ``Focus on'' box.
The primary collections are defined mainly by the collection
identifier ("980 $a,b"); and the specific collections are as
defined by any query that is possible for a search engine to
execute (see also "dbquery" column in the "collection" table).
In the past we used to use the term "catalogue", that is now
deprecated, and that can be interchanged with "collection".
7. doctype - stands for web document type concept, used in WebSubmit
The "document type" is used solely for submission purposes, and
fulltext access purposes ("setlink"-like). For example, a document
type "photo" may be used in many collections such as "Foo Photos",
"Bar PhotoLab", etc. Similarly, one collection can cover several
doctypes. (M:N relationship)
8. baskets, alerts, settings - covering personal features
Denote personal features, for which we previously used the terms
"shelf" and "profile" that are now deprecated.
- end of file -
</pre>
diff --git a/modules/webhelp/web/hacking/directory-organization.webdoc b/modules/webhelp/web/hacking/directory-organization.webdoc
index 842468ae2..e6a8d5cfe 100644
--- a/modules/webhelp/web/hacking/directory-organization.webdoc
+++ b/modules/webhelp/web/hacking/directory-organization.webdoc
@@ -1,199 +1,199 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Directory Organization -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
Please find some notes below on how the source (as well as the target)
directory structure is organized, where the sources get installed to,
and how the visible URLs are organized.
1. CDS Invenio will generally install into the directory taken from
--with-prefix configuration variables. These are discussed in points
2 and 3 below, respectively.
2. The first directory (--with-prefix) specifies general CDS Invenio
install directory, where we'll put CLI binaries, Python and PHP
libraries, manpages, log and cache directories for the running
installation, and any other dirs as needed. They will all live
under one common hood.
For example, configure --with-prefix=/opt/cds-invenio,
and you'll obtain the following principal directories:
/opt/cds-invenio/
/opt/cds-invenio/bin
/opt/cds-invenio/lib
/opt/cds-invenio/lib/php
/opt/cds-invenio/lib/python
/opt/cds-invenio/lib/wml
/opt/cds-invenio/var
/opt/cds-invenio/var/cache
/opt/cds-invenio/var/log
with the obvious meaning:
- bin : for command-line executable binaries and scripts
- lib/php : for our own PHP libraries, see below
- lib/python : for our own Python libraries, see below
- lib/wml : for our own WML libraries, see below
- var : for installation-specific runtime stuff
- var/log : for all sorts of runtime logging, e.g. search.log
- var/cache : for all sorts of runtime caching, e.g. OAI
retention harvesting, collection cache, etc
This scheme copies to some extent the usual Unix filesystem
convention, so it may be easily expanded later according to our
future needs.
3. The second directory (prefix/var/www) contains Web scripts (PHP,
mod_python), HTML documents and images, and so on. This is where
webuser-seen files are located. Basically, the files there contain
only the interface to the functionality that is provided by the
libraries stored under the library directory.
The prefix/var/www directory is further structured according to
whom it provides services. We distinguish user-level, admin-level
and hacker-level access to the site, as reflected by the visible
URL structure.
- a) The user-level access point is provided by the main WEBURL
+ a) The user-level access point is provided by the main CFG_SITE_URL
address and its subdirs. All the user-level documentation is
- available under WEBURL/help/. The module-specific user-level
- documentation is available under WEBURL/help/&lt;module&gt;/.
+ available under CFG_SITE_URL/help/. The module-specific user-level
+ documentation is available under CFG_SITE_URL/help/&lt;module&gt;/.
- b) The admin-level access is provided by WEBURL/admin/ entry
+ b) The admin-level access is provided by CFG_SITE_URL/admin/ entry
point. The admin-level documentation is accessible from the
same place. The admin-level module-specific functionality and
- help is available under WEBURL/admin/&lt;module&gt;/. (If
+ help is available under CFG_SITE_URL/admin/&lt;module&gt;/. (If
it's written in mod_python, it usually points to
- WEBURL/&lt;module&gt;admin.py/ since we configure the server
+ CFG_SITE_URL/&lt;module&gt;admin.py/ since we configure the server
to have all mod_python scripts under the prefix/var/www root
directory.)
- c) The hacker-level documentation is provided by WEBURL/hacking/
+ c) The hacker-level documentation is provided by CFG_SITE_URL/hacking/
entry point. There is no hacker-level functionality possible
via Web, of course, so that unlike admin-level entry point,
the hacker-level entry point provides only a common access to
available hacking documention, etc. The module-specific
- information is available under WEBURL/hacking/&lt;module&gt;/.
+ information is available under CFG_SITE_URL/hacking/&lt;module&gt;/.
4. Let's now return a bit more closely to the role Python and PHP
library directories outside of the Apache tree:
/opt/cds-invenio/lib/php
/opt/cds-invenio/lib/python
Here we put not only (a) libraries that may be reused across CDS
Invenio modules, but also (b) all the "core" functionality of CDS
Invenio that is not directly callable by the end users. The
"callable" functionality is put under "prefix/var/www" in case of
web scripts and documents, and under "bindir" in case of CLI
executables.
As for (a), for example in the PHP CDS Invenio library you'll find
currently the common PHP error handling code that is shared between
BibFormat and WebSubmit; in the Python CDS Invenio library (in fact,
CDS Invenio Pythonic 'module', but we are reserving the word 'module'
to denote 'CDS Invenio module' in this text) you'll find config.py
containing WML-supplied site parameters, dbquery.py containing DB
persistent query module, or webpage.py with templates and functions
to produce mod_python web pages with common look and feel. These
could and should be reused across all our modules. Note that I
created only a small number of "broad" libraries at the moment. In
case we want to reuse more code parts, we'd refactor the code more,
as needed.
As for (b), for example the existing search engine was split into
search.py that only contains three "callable" functions, which goes
into prefix/var/www, while the search engine itself is composed of
search_engine.py and search_engine_config.py living under LIBDIR.
In this way we can easily create "real" CLI search, that will
depend only on the search libraries in LIBDIR, and that will get
installed into BINDIR.
To recap:
- For each CDS Invenio module, I'm differentiating between
"callable" and "core" parts. The former go into
prefix/var/www or BINDIR, the latter into LIBDIR.
- Our PHP/Pythonic libraries contain several sorts of thing:
- the implementation of the "callable" functions
- non-callable internal "core" or "library" code parts, as
stated above. Not shared across CDS Invenio modules.
- utility code meant for reuse across CDS Invenio modules, such
as dbquery.py
- Pythonic config files out of user-supplied WML (non-MySQL)
configuration parameters (see
e.g. search_engine_config.py)
5. The same strategy is reflected in the organization of source
directories inside CDS Invenio CVS. Each CDS Invenio module lives in a
separate directory located under "modules" directory of the
sources. Further on, each module contains usually several
subdirectories that reflect the above-mentioned packaging choice.
For example, in case of WebSearch you'll find:
./modules/websearch
./modules/websearch/bin
./modules/websearch/doc
./modules/websearch/doc/hacking
./modules/websearch/doc/admin
./modules/websearch/lib
./modules/websearch/web
./modules/websearch/web/admin
with the following straightforward meaning:
- bin : for callable CLI binaries and scripts
- doc : for documentation. The user-level documentation is
located in this directory. The admin-level
documentation is located in the "admin" subdir. The
programmer-level documentation is located in the
"hacking" subdir.
- lib : for uncallable "core" functionality, see the comments
above
- web : for callable web scripts and pages. The user- and
admin- level is separated similarly as in the "doc"
directory (see above).
The structure is respected throughout all the CDS Invenio modules, a
notable exception being the MiscUtil module that contains subdirs
like "sql" (for the table creating/dropping SQL commands, etc) or
"demo" (for creation of Atlantis Institute of Science, our demo
site.)
- end of file -
</pre>
diff --git a/modules/webhelp/web/hacking/hacking.webdoc b/modules/webhelp/web/hacking/hacking.webdoc
index 867146bf8..f9c5a1244 100644
--- a/modules/webhelp/web/hacking/hacking.webdoc
+++ b/modules/webhelp/web/hacking/hacking.webdoc
@@ -1,92 +1,92 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Hacking CDS Invenio -->
<!-- WebDoc-Page-Navbar-Name: hacking-test-suite -->
<!-- WebDoc-Page-Navbar-Select: hacking -->
Welcome to the CDS Invenio Developers' corner. Before diving into the
source, make sure you don't miss our <a
-href="<WEBURL>/help/">user-level</a> and <a
-href="<WEBURL>/help/admin/">admin-level</a> documentation as well. And now, back to the source, and happy hacking!
+href="<CFG_SITE_URL>/help/">user-level</a> and <a
+href="<CFG_SITE_URL>/help/admin/">admin-level</a> documentation as well. And now, back to the source, and happy hacking!
<h2>General information, coding practices</h2>
<blockquote>
<dl>
<dt><a href="common-concepts">Common Concepts</a></dt>
<dd>Summarizing common terms you will encounter here and there.</dd>
<dt><a href="coding-style">Coding Style</a></dt>
<dd>A policy we try to follow, for good or bad.</dd>
<dt><a href="release-numbering">Release Numbering</a></dt>
<dd>Presenting the version numbering scheme adopted for CDS Invenio stable and development releases.</dd>
<dt><a href="directory-organization">Directory Organization</a></dt>
<dd>How the source and target directories are organized, where the
sources get installed to, what is the visible URL policy, etc.</dd>
<dt><a href="modules-overview">Modules Overview</a></dt>
<dd>Presenting a summary of various CDS Invenio modules and their relationships.</dd>
<dt><a href="test-suite">Test Suite</a></dt>
<dd>Describes our unit and regression test suites.</dd>
</dl>
</blockquote>
<h2>Module-specific information</h2>
<blockquote>
<dl>
<dt><a href="bibclassify-internals">BibClassify Internals</a></dt>
<dd>Describes information useful to understand how BibClassify works,
the taxonomy extensions we use, how the keyword extraction algorithm works.
</dd>
<dt><a href="bibconvert-internals">BibConvert Internals</a></dt>
<dd>Describes information useful to understand how BibConvert works,
and the BibConvert functions can be reused.</dd>
<dt><a href="bibformat-internals">BibFormat Internals</a></dt>
<dd>Describes information useful to understand how BibFormat works.</dd>
<dt><a href="bibrank-internals">BibRank Internals</a></dt>
<dd>Describes information useful to understand how the various
ranking methods available in bibrank works, and how they can
be tweaked to give various output.</dd>
<dt><a href="miscutil-internals">MiscUtil Internals</a></dt>
<dd>Describes information useful to understand what can be found inside the miscellaneous utilities
module, like database access, error management, date handling library, etc.</dd>
<dt><a href="search-engine-internals">WebSearch Internals</a></dt>
<dd>Describes information useful to understand the search process
internals, like the different search stages, the high- and low-level
API, etc.</dd>
<dt><a href="webaccess-internals">WebAccess Internals</a></dt>
<dd>Describes information useful to understand the access control process
internals, its API, etc.</dd>
</dl>
</blockquote>
diff --git a/modules/webhelp/web/hacking/modules-overview.webdoc b/modules/webhelp/web/hacking/modules-overview.webdoc
index 983c799b9..b796630e1 100644
--- a/modules/webhelp/web/hacking/modules-overview.webdoc
+++ b/modules/webhelp/web/hacking/modules-overview.webdoc
@@ -1,273 +1,273 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Modules Overview -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>CDS Invenio consists of several more or less independent modules with
precisely defined functionality. The general criterion for module
names is to use the ``Bib'' prefix to denote modules that work more
with the bibliographic data, and the ``Web'' prefix to denote modules
that work more with the Web interface. (The difference is of course
blurred in some cases, as in the case of search engine that has got a
web interface but searches bibliographic data.)
</p>
<p>Follows a brief description of what each module does. After
descriptions the module relationship diagram is presented.
</p>
<ul>
<li><strong>BibCheck</strong> permits administrators and library
cataloguers to automate various kind of tests on the metadata to see
whether the metadata comply with quality standards. For example,
that certain metadata fields are of a certain length, that they have
numeric content, that they must not be present when other field
exists, that their content is governed by an authority base depending
on values of other fields, etc. The module can report its findings
or can even automatically correct some kind of errors. (FIXME: not
included in CVS yet.)
</li>
<li><strong>BibClassify</strong> allows automatic extraction of
keywords from fulltext documents, based on the frequency of specific
terms, taken from a controlled vocabulary. Controlled vocabularies
can be expressed as simple text thesauri or as structured, RDF-compliant,
taxonomies, to allow a semantic classification.
</li>
<li><strong>BibConvert</strong> allows metadata conversion from any
structured or semi-structured proprietary format into any other
format, typically the <a
href="http://www.loc.gov/standards/marcxml/">MARC XML</a> that is
natively used in CDS Invenio. Nevertheless the input and output formats
are fully configurable and have been tested on data importations from
more than one hundred data sources. The power of this utility lies in
the fact that no structural attributes of data source are presumed,
but they are defined in an extensive data source
configuration. Inevitably, this leads to a high complexity of the
BibConvert configuration language. Most frequent configurations are
provided with the CDS Invenio distribution, such as a sample
configuration from Qualified Dublin Core into the MARCXML.
<br />In general the BibConvert configuration consists from the source
data descriptions and target data descriptions. The processor then
analyzes and parses the input data and creates the resulting data
structure, similarly as the XSLT processor would do. Typically the
BibConvert is aimed at usage for input data that do not dispose of an
XML representation. The source data is required to be structured or
semi-structured, (i.e. not expressed in natural language that is a
subject of information extraction task) and its processing involves
several steps including record separation and field extraction upto
transformation of source field values and their formatting.
<li><strong>BibEdit</strong> permits one to edit the metadata via a
Web interface.
</li>
<li><strong>BibFormat</strong> is in charge of formatting the
bibliographic metadata in numerous ways. This truly enables the
separation of data content administration and formatting layout.
BibFormat can act in the background and format the records when
needed, or can preformat records for some often used outputs, such as the
brief format used when displaying search results.<br/>
The BibFormat settings can be administered either through a
user-friendly web interface, or directly by editing human-readable
configuration files.
</li>
<li><strong>BibHarvest</strong> represents the <a
href="http://www.openarchives.org/">OAi-PMH</a> compatible harvester
allowing the repository to gather metadata from fellow OAi-compliant
repositories and the OAi-PMH repository management. Repository is
built directly on top of the database and disposes of an OAi
repository manager that allows to perform the administrative tasks on
the repository aside from the principal generic data administration
module. The database can be partially or completely open for
harvesting in the scope of the OAi-PMH protocol. In this case, all
data is provided in raw form, where the semantics of individual tags
is indicated uniquely by the MARC21 naming convention. This is
particularly interesting for institutes that are specialized in
cross-archive and cross-disciplinary services provision, as for
example the ARC service provider.
</li>
<li><strong>BibIndex</strong> module takes care of the indexation of
metadata, references and full text files.  Two kinds of indexes --
word and phrase index -- are being maintained.  The user can define
several logical indexes (e.g. author index, title index, etc.) and
the correspondence of which physical MARC21 metadata tag goes into
which logical field index.  An index consists of two parts: (i) a
forward index listing various words (or phrases) found in the given
field, with the set of record identifiers where the given word can be
found; and (ii) a reverse index listing record identifiers, with the
set of words of the given record that go to the forward index.  Such
a two-part indexing technique allows one to rapidly update only those
words that have changed in the input metadata record.  The indexes
were designed with the aim to provide fast user-response search times
and are faster than native MySQL (full text) indexes.
</li>
<li><strong>BibMatch</strong> permits to filter input XML files
against the database content, attempting to match records via certain
criteria, for example to avoid doubly-inputted records.
</li>
<li><strong>BibRank</strong> permits to set up various ranking
criteria that will be used later by the search engine. For example,
ranking by the word frequency, or by some metadata tag value such as
journal name by means of the journal impact factor knowledge base, or
even by the number of downloads of a particular paper. Note that
BibRank is independent of BibIndex.
</li>
<li><strong>BibSched</strong> The bibliographic task scheduler is
central unit of the system that allows all other modules to access
the bibliographic database in a controlled manner, preventing sharing
violation threats and assuring the coherent execution of the database
update tasks. The module comes with an administrative interface that
allows to monitor the task queue including various possibilities of a
manual intervention, for example to re-schedule queued tasks, change
the task order, etc.
</li>
<li><strong>BibUpload</strong> allows to load the new bibliographic
data into the database. To effectuate this task the data must be a
well-formed XML file that complies with the current metadata tag
selection schema. Usually, the properly structured input files of
BibUpload come from the BibConvert utility.
</li>
<li><strong>ElmSubmit</strong> is an email submission gateway that
permits for automatic document uploads from trusted sources via
email. (Usually web submission or harvesting is preferred.)
</li>
<li><strong>MiscUtil</strong> is a collection of miscellaneous
utilities that other modules are using, like the international
messages, etc.
</li>
<li><strong>WebAccess</strong> module is responsible for granting
access to users for performing various actions within the system.  A
Role-Based Access Control (RBAC) technique is used, where users
belong to several groups according to their role in the system.  Each
user group can be granted to perform certain actions depending on
possible one more action arguments.  WebAccess is presently used
mainly for the administrative interface.  There are basically two
kinds of actions: (i) configuration of administrative modules and
(ii) running administrative tasks.
</li>
<li><strong>WebAlert</strong> module allows the end user to be
alerted whenever a new document matching her personal criteria is
inserted into the database.  The criteria correspond to a typical
user query as if it would be done via the search interface.  For
example, a user may want to get notified whenever a new document
containing certain words, or of a certain subject, is inserted.  A
user may create several alerts with a daily, weekly, or a monthly
frequency.  The results of alert searches are either sent back to the
user by email or can also be stored into her baskets.
</li>
<li><strong>WebBasket</strong> module enables the end user of the
system to store the documents she is interested in in a personal
basket or a personal shelf.  The concept is similar to popular
shopping carts.  One user may own several baskets.  A basket can be
either private or public, allowing a simple document sharing
mechanism within a group.
</li>
<li><strong>WebComment</strong> provides a community-oriented tool to
rank documents by the readers or to share comments on the documents
by the readers. Integrated with the group-aware WebBasket, WebGroup,
WebMessage tools, WebComment is at the heart of the social network
features of the CDS Invenio software.
</li>
<li><strong>WebHelp</strong> presents some global user-level,
admin-level, and hacker-level documenation on CDS Invenio. The
module-specific documentation is included within each particular
module.
</li>
<li><strong>WebMessage</strong> permits the communication between
(possibly anonymous) end users via web message boards, to invite
readers to join the groups, etc.
</li>
<li><strong>WebSearch</strong> module handles user requests to search
for a certain words or phrases in the database.  Two types of
searching can be performed: a word search or a phrase search.  The
system allows for complex boolean queries, regular expression
searching, or a combined metadata, references and full text file
searching in one go.  Users have a possibility to browse for present
index terms.  If no direct match could have been found with the
user-typed query pattern, the system proposes alternative matches as
a search guidance.  The search indexes were designed to provide fast
response times for middle-sized data collections of up to 106
records. 
<br />The metadata corpus is organized into metadata collections that
are directly accessible through the browse function, similarly to the
popular concept of Web Directories.  Orthogonal views on the document
corpus are enabled in the search interface via a concept of virtual
collections: for example, a document may be classified both according
to its type (e.g. preprint, book) and according to its Dewey decimal
classification number.  Such a flexible organization views allows for
the creation of easy navigation schemata to the end users.
</li>
<li><strong>WebSession</strong> is a session and user management
module that permits to differentiate between users. Useful for
personalization of the interface and services like personal baskets
and alerts.
</li>
<li><strong>WebStat</strong> is a configurable system that permits to
gather statistics about the health of the server, the usage of the
system, as well as about some particular system features.
</li>
<li><strong>WebStyle</strong> is a library of design-related modules
that defines look and feel of CDS Invenio pages.
</li>
<li><strong>WebSubmit</strong> is a comprehensive submission system
allowing authorized individuals (authors, secretaries and repository
maintenance staff) to submit individual documents into the
system. The submission system disposes of a flow-control mechanism
that assures the data approval by authorized units. In total there
are several different exploitable submission schemas at a disposal,
including an automated full text document conversion from various
textual and image formats. This module also disposes of information
extraction functionality, focusing on bibliographic entities such as
references, authors, keywords or other implicit metadata.
</li>
</ul>
<p>Relationship between the modules: <br />
-<img src="<WEBURL>/img/hacking/modules-overview-diagram.jpeg" border="0" alt="Modules overview diagram" />
+<img src="<CFG_SITE_URL>/img/hacking/modules-overview-diagram.jpeg" border="0" alt="Modules overview diagram" />
</p>
diff --git a/modules/webhelp/web/hacking/release-numbering.webdoc b/modules/webhelp/web/hacking/release-numbering.webdoc
index d4495e70c..7eafb81d4 100644
--- a/modules/webhelp/web/hacking/release-numbering.webdoc
+++ b/modules/webhelp/web/hacking/release-numbering.webdoc
@@ -1,100 +1,100 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Release Numbering Scheme -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
CDS Invenio uses the classical major.minor.patchlevel release version
numbering scheme that is commonly used in the GNU/Linux world and
elsewhere. Each release is labelled by
major.minor.patchlevel
release version number. For example, a release version 4.0.1 means:
4 - 4th major version, i.e. the whole system has been already
4th times either fully rewritten or at least in its very
essential components. The upgrade from one major version
to another may be rather hard, may require new prerequisite
technologies, full data dump, reload and reindexing, as
well as other major configuration adapatations, possibly
with an important manual intervention.
0 - 0th minor version, i.e. the first minor release of the 4th
major rewrite. (Increments go 4.1, 4.2, ... 4.9, 4.10,
4.11, 4.12, ... until some important rewrite is done,
e.g. the database philosophy dramatically changes, leading
to a non-trivial upgrade, and we have 5.0.) The upgrade
from one minor version to another may be laborious but is
relatively painless, in that some table changes and data
manipulations may be necessary but they are somewhat
smaller in nature, easier to grasp, and possibly done by an
automated script.
1 - 1st patch level to 4.0, fixing bugs in 4.0.0 but not adding
any substantially new functionality. That is, the only new
functionality that is added is that of a `bug fix' nature.
The upgrade from one patch level to another is usually
straightforward.
(Packages often seem to break this last rule, e.g. Linux
kernel adopting new important functionality (such as
ReiserFS) within the stable 2.4.x branch. It can be easily
seen that it is somewhat subjective to judge what is
qualitatively more like a minor new functionality and what
is more like a patch to the existing behaviour. We have
tried to quantify these notions with respect to whether
table structure and/or technology change require small or
large upgrade jobs and eventual manual efforts.)
So, if we have a version 4.3, a bug fix would mean to release 4.3.1,
some minor new functionality and upgrade would mean to release 4.4,
some important database structure rewrite or an imaginary exchange of
Python for Common Lisp would mean to release 5.0, etc.
In addition, the two-branch release policy is adopted:
a) stable branch - releases in the stable branch are numbered with
even minor version number, like 0.2, 0.4, etc. These releases
are usually well tested. The configuration files and features
usually don't change often from one release to another. The
release frequency is low.
b) development branch - releases in the development branch are
number with the odd minor version number, like 0.1, 0.3, etc.
These releases are more experimental and may be less tested than
the stable ones. The configuration files and features change
more rapidly from one release to another. The release frequency
is higher.
It can be seen that the above scheme is somewhat similar to the Linux
kernel version numbering scheme.
Currently, CDS Invenio 0.0.9 represents the stable branch release and
0.1.0 the development branch release. We are going to frequently
update it to provide 0.1.1, 0.1.2, etc as the currently missing admin
functionality is being added into the development branch, until later
on, when some release, say 0.1.8, will achieve a status of
satisfaction, at which point we release it as the next stable version
(0.2 or 1.0), and start a new development branch (0.3 or 1.1).
- end of file -
</pre>
diff --git a/modules/webhelp/web/hacking/test-suite.webdoc b/modules/webhelp/web/hacking/test-suite.webdoc
index 449864b22..e4b58df4e 100644
--- a/modules/webhelp/web/hacking/test-suite.webdoc
+++ b/modules/webhelp/web/hacking/test-suite.webdoc
@@ -1,518 +1,518 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Test Suite Strategy -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<strong>1. <a href="#1">General considerations</a></strong><br />
<strong>2. <a href="#2">Unit testing</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.1 <a href="#2.1">Unit testing philosophy</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.2 <a href="#2.2">Writing unit tests</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.3 <a href="#2.3">Running unit tests</a><br />
<strong>3. <a href="#3">Regression testing</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1 <a href="#3.1">Regression testing philosophy</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2 <a href="#3.2">Writing regression tests</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3 <a href="#3.3">Running regression tests</a><br />
<strong>4. <a href="#4">Conclusions</a></strong><br />
<strong>5. <a href="#5">Additional information</a></strong><br />
<a name="1"></a><h2>1. General considerations</h2>
<p>This documents presents guidelines for unit testing and regression
testing homogenisation throughout all CDS Invenio modules.
<p>Testing is an important coding activity. Most authors believe that
writing test cases should take between 10% and 30% of the project
time. But, even with such a large fraction, don't put too much belief
on such a testing. It cannot find bugs that aren't tested for. So,
while testing is an important activity inherent to safe software
development practices, it cannot become a substitute for pro-active
bug hunting, source code inspection, and bugfree-driven development
approach from the start.
<p>Testing should happen alongside with coding. If you write a
function, immediately load it into your toplevel, evaluate its
definition, and call it for a couple of arguments to make sure the
function works as expected. If not, then change the function
definition, re-evaluate it, re-call it, etc. Dynamic languages with
interactive toplevel such as Common Lisp or Python makes this easy for
you. Dynamic redefinition capabilities (full in Common Lisp, partial
in Python) are very programmer-friendly in this respect. If your test
cases are interesting to be kept, then keep them in a test file.
(It's almost all the time a good idea to store them in the test file,
since you cannot predict whether you won't want to change something in
the future.) We'll see below how to store your tests in a test file.
<p>When testing, it is nice to know some rules of thumb, like: check
your edge cases (e.g. null array), check atypical input values
(e.g. laaarge array instead of typically 5-6 elements only), check
your termination conditions, ask whether your arguments have already
been safe-proofed or whether it is in your mandate to check them,
write a test case for each `if-else' branch of the code to explore all
the possibilites, etc. Another interesting rule of thumb is the bug
frequency distribution. Experience has shown that the bugs tend to
cluster. If you discover a bug, there are chances that other bugs are
in the neighborhood. The famous 80/20 rule of thumb applies here too:
about 80% of bugs are located in about 20% of the code. Another rule
of thumb: if you find a bug caused by some coding practice pattern
thay may be used elsewhere too, look and fix other pattern instances.
</p>
<p>In a nutshell, the best advice to write bug-free code is: <em>think
ahead</em>. Try to prepare in advance for unusual usage scenarios, to
foresee problems before they happen. Don't rely on typical input and
typical usage scenarios. Things have a tendency to become atypical
one day. Recall that testing is necessary, but not sufficient, to
write good code. Therefore, think ahead!
</p>
<a name="2"></a><h2>2. Unit testing</h2>
<a name="2.1"></a><h3>2.1 Unit testing philosophy</h3>
<p>Core functionality, such as the hit set intersection for the search
engine, or the text input manipulating functions of the BibConvert
language, should come with a couple of test cases to assure proper
behaviour of the core functionality. The test cases should cover
typical input (e.g. hit set corresponding to the query for ``ellis''),
as well as the edge cases (e.g. empty/full hit set) and other unusual
situations (e.g. non-UTF-8 accented input for BibConvert functions to
test a situation of different number of bytes per char).
</p>
<p>The test cases should be written for most important core
functionality. Not every function or class in the code is to be
throughly tested. Common sense will tell.
</p>
<p>Unit test cases are free of side-effects. Users should be able to
run them on production database without any harm to their data. This
is because the tests test ``units'' of the code, not the application
as such. If the behaviour of the function you would like to test
depends on the status of the database, or some other parameters that
cannot be passed to the function itself, the unit testing framework is
not suitable for this kind of situation and you should use the
regression testing framework instead (see below).
</p>
<p>For more information on Pythonic unit testing, see the
documentation to the unittest module at <a
href="http://docs.python.org/lib/module-unittest.html">http://docs.python.org/lib/module-unittest.html</a>.
For a tutorial, see for example <a
href="http://diveintopython.org/unit_testing/">http://diveintopython.org/unit_testing/</a>.
</p>
<a name="2.2"></a><h3>2.2 Writing unit tests</h3>
<p>Each core file that is located in the lib directory (such as the
<code>webbasketlib.py</code> in the example above) should come with a
testing file where the test cases are stored. The test file is to be
named identically as the lib file it tests, but with the suffix
<code>_tests</code> (in our example,
<code>webbasketlib_tests.py</code>).
</p>
<p>The test cases are written using Pythonic unittest TestCase class.
An example for testing search engine query parameter washing function:
<blockquote>
<pre>
$ cat /opt/cds-invenio/lib/python/invenio/search_engine_tests.py
[...]
import search_engine
import unittest
class TestWashQueryParameters(unittest.TestCase):
"""Test for washing of search query parameters."""
def test_wash_url_argument(self):
"""search engine washing of URL arguments"""
self.assertEqual(1, search_engine.wash_url_argument(['1'],'int'))
self.assertEqual("1", search_engine.wash_url_argument(['1'],'str'))
self.assertEqual(['1'], search_engine.wash_url_argument(['1'],'list'))
self.assertEqual(0, search_engine.wash_url_argument('ellis','int'))
self.assertEqual("ellis", search_engine.wash_url_argument('ellis','str'))
self.assertEqual(["ellis"], search_engine.wash_url_argument('ellis','list'))
self.assertEqual(0, search_engine.wash_url_argument(['ellis'],'int'))
self.assertEqual("ellis", search_engine.wash_url_argument(['ellis'],'str'))
self.assertEqual(["ellis"], search_engine.wash_url_argument(['ellis'],'list'))
[...]
</pre>
</blockquote>
</p>
<p>In addition, each test file is supposed to define a
<code>create_test_suite()</code> function that will return test suite
with all the tests available in this file:
<blockquote>
<pre>
$ cat /opt/cds-invenio/lib/python/invenio/search_engine_tests.py
[...]
def create_test_suite():
"""Return test suite for the search engine."""
return unittest.TestSuite((unittest.makeSuite(TestWashQueryParameters,'test'),
unittest.makeSuite(TestStripAccents,'test')))
[...]
</pre>
</blockquote>
</p>
<p>This will enable us to later include this file into
<code>testsuite</code> executable:
<blockquote>
<pre>
$ cat ~/src/cds-invenio/modules/miscutil/bin/testsuite.in
[...]
from invenio import search_engine_tests
from invenio import bibindex_engine_tests
def create_all_test_suites():
"""Return all tests suites for all CDS Invenio modules."""
return unittest.TestSuite((search_engine_tests.create_test_suite(),
bibindex_engine_tests.create_test_suite()))
[...]
</pre>
</blockquote>
</p>
<p>In this way, all the test cases defined in the file
<code>search_engine_tests.py</code> will be executed when the global
<code>testcase</code> executable is called.
<p>Note that it may be time-consuming to run all the tests in one go.
If you are interested in running tests only on a certain file (say
<code>search_engine_tests.py</code>), then launch:
<blockquote>
<pre>
$ python /opt/cds-invenio/lib/python/invenio/search_engine_tests.py
</pre>
</blockquote>
</p>
<p>For full-scale examples, you may follow
<code>search_engine_tests.py<code> and other <code>_tests.py</code>
files in the source distribution.
</p>
<a name="2.3"></a><h3>2.3 Running unit tests</h3>
<p>CDS Invenio test suite can be run in the source directory:
<blockquote>
<pre>
$ make test
</pre>
</blockquote>
or anytime after the installation:
<blockquote>
<pre>
$ /opt/cds-invenio/bin/testsuite
</pre>
</blockquote>
The ``testsuite'' executable will run all available unit tests
provided with CDS Invenio.
</p>
<p>The informative output is of the form:
<blockquote>
<pre>
$ make test
CDS Invenio v0.3.2.20040519 test suite results:
===========================================
search engine washing of query patterns ... ok
search engine washing of URL arguments ... ok
search engine stripping of accented letters ... ok
bibindex engine list union ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.121s
OK
</pre>
</blockquote>
In case of problems you will see failures like:
<blockquote>
<pre>
CDS Invenio v0.3.2.20040519 test suite results:
===========================================
search engine washing of query patterns ... FAIL
search engine washing of URL arguments ... ok
search engine stripping of accented letters ... ok
bibindex engine list union ... ok
======================================================================
FAIL: search engine washing of query patterns
----------------------------------------------------------------------
Traceback (most recent call last):
File "/opt/cds-invenio/lib/python/invenio/search_engine_tests.py", line 25, in test_wash_pattern
self.assertEqual("ell*", search_engine.wash_pattern('ell*'))
File "/usr/lib/python2.3/unittest.py", line 302, in failUnlessEqual
raise self.failureException, \
AssertionError: 'ell*' != 'ell'
----------------------------------------------------------------------
Ran 4 tests in 0.091s
FAILED (failures=1)
</pre>
</blockquote>
</p>
<p>The test suite compliance should be checked before each CVS commit.
(And, obviously, double-checked before each CDS Invenio release.)
</p>
<a name="3"></a><h2>3. Regression testing</h2>
<a name="3.1"></a><h3>3.1 Regression testing philosophy</h3>
<p>In addition to the above-mentioned unit testing of important
functions, a regression testing should ensure that the overall
application functionality is behaving well and is not altered by code
changes. This is especially important if a bug had been previously
found. Then a regression test case should be written to assure that
it will never reappear. (It also helps to scan the neighborhood of
the bug, or the whole codebase for occurrences of the same kind of
bug, see the 80/20 thumb rule cited above.)
</p>
<p>Moreover, the regression test suite should be used when the
functionality of the item we would like to test depends on
extra-parametrical status, such as the database content. Also, the
regression framework is suitable for testing the web pages overall
behaviour. (In extreme programming, the regression testing is called
<em>acceptance testing</em>, the name that evolved from previous
<em>functionality testing</em>.)
</p>
<p>Within the framework of the regression test suite, we have liberty
to alter database content, unlike that of the unit testing framework.
We can also simulate the web browser in order to test web
applications.
</p>
<p>As an example of a regression test, we can test whether the web
pages are alive; whether searching for Ellis in the demo site produces
indeed 12 records; whether searching for aoeuidhtns produces no hits
but the box of nearest terms, and with which content; whether
accessing the Theses collection page search prompts an Apache password
prompt; whether the admin interface is really accessible only to
admins or also to guests, etc.
</p>
<p>For more information on regression testing, see for example <a
href="http://c2.com/cgi/wiki?RegressionTesting">http://c2.com/cgi/wiki?RegressionTesting</a>.
</p>
<a name="3.2"></a><h3>3.2 Writing regression tests</h3>
<p>Regression tests are written per application (or sub-module) in
files named like <code>websearch_regression_tests.py</code> or
<code>websubmitadmin_regression_tests.py</code>.
</p>
<p>When writing regression tests, you can assume that the site is in
the fresh demo mode (Atlantis Institute of Fictive Science). You can
also safely write not only database-read-only tests, but you can also
safely insert/update/delete into/from the database whatever values you
need for testing. Users are warned prior to running the regression
test suite about its possibly destructive side-effects. (See below.)
Therefore you can create users, create user groups, attach users to
groups to test the group joining process etc, as needed.
</p>
<p>For testing web pages using GET arguments, you can take advantage
of the following helper function:
<blockquote>
<pre>
$ cat /opt/cds-invenio/lib/python/invenio/testutils.py
[...]
def test_web_page_content(url, username="guest", expected_text="</html>"):
"""Test whether web page URL as seen by user USERNAME contains
text EXPECTED_TEXT. Before doing the tests, login as USERNAME.
(E.g. interesting values are "guest" or "admin".)
Return empty list in case of problems, otherwise list of error
messages that may have been encountered during processing of
page.
"""
</pre>
</blockquote>
For example you can test whether admins can access WebSearch Admin
interface but guests cannot:
<blockquote>
<pre>
-test_web_page_content(weburl + '/admin/websearch/websearchadmin.py',
+test_web_page_content(CFG_SITE_URL + '/admin/websearch/websearchadmin.py',
username='admin')
-test_web_page_content(weburl + '/admin/websearch/websearchadmin.py',
+test_web_page_content(CFG_SITE_URL + '/admin/websearch/websearchadmin.py',
username='guest',
expected_text='Authorization failure')
</pre>
</blockquote>
or you can test whether searching for aoeuidhtns produces nearest
terms box:
<blockquote>
<pre>
-test_web_page_content(weburl + '/search?p=aoeuidhtns',
+test_web_page_content(CFG_SITE_URL + '/search?p=aoeuidhtns',
expected_text='Nearest terms in any collection are')
</pre>
</blockquote>
</p>
<p>For testing web pages using POST argumens or for other more
advanced testing you should use directly <code>mechanize</code> Python
module that simulates the browser. It can post forms, follow links,
go back to previous pages, etc. An example of how to test the login
page functionality:
<blockquote>
<pre>
browser = mechanize.Browser()
browser.open(CFG_SITE_SECURE_URL + "/youraccount/login")
browser.select_form(nr=0)
browser['p_un'] = 'userfoo'
browser['p_pw'] = 'passbar'
browser.submit()
username_account_page_body = browser.response().read()
try:
string.index(username_account_page_body,
"You are logged in as userfoo.")
except ValueError:
self.fail('ERROR: Cannot login as userfoo.')
</pre>
</blockquote>
<p>For full-scale examples, you may follow
<code>websearch_regression_tests.py<code> and other
<code>_regression_tests.py</code> files in the source distribution.
</p>
<a name="3.3"></a><h3>3.3 Running regression test suite</h3>
<p>The regression test suite can be run by invoking:
<blockquote>
<pre>
$ /opt/cds-invenio/bin/regressiontestsuite
</pre>
</blockquote>
similarly to the unit test suite cited above. The notable exception
when compared to running the unit test suite is:
</p>
<ul>
<li><code>regressiontestsuite</code> script assumes the site to be in
demo mode (Atlantis Institute of Fictive Science)
<li><code>regressiontestsuite</code> will pollute the database with
test data as it sees fit for the regression testing purposes.
</ul>
<p>
Therefore beware, <strong>running regression test suite requires clean
demo site and may destroy your data forever</strong>. The user is
warned about this prior to running the suite and is given a chance to
abort the process:
<blockquote>
<pre>
$ /opt/cds-invenio/bin/regressiontestsuite
regressiontestsuite: detected 19 regression test modules
**********************************************************************
** **
** *** I M P O R T A N T W A R N I N G *** **
** **
** The regression test suite needs to be run on a clean demo site **
** that you can obtain by doing: **
** **
** $ inveniocfg --drop-demo-site \ **
** --create-demo-site \ **
** --load-demo-records **
** **
** Note that DOING THE ABOVE WILL ERASE YOUR ENTIRE DATABASE. **
** **
** (In addition, due to the write nature of some of the tests, **
** the demo database may be altered with junk data, so that **
** it is recommended to rebuild the demo site anew afterwards.) **
** **
**********************************************************************
Please confirm by typing "Yes, I know!": NO
Aborted.
</pre>
</blockquote>
</p>
<p>If you choose to continue, the regression test suite will produce
the output similar to the unit test suite that was discussed
previously.
</p>
<a name="4"></a><h2>4. Conclusions</h2>
<p>A uniform testing technique and two test suites (unit test suite,
regression test suite) were discussed. Each programmer should plan to
write the test code alongside the core code development to test the
building blocks of his/her code (unit tests) as well as the overall
application behaviour (regression tests). The guidelines were given
how to do so.
</p>
<a name="5"></a><h2>5. Additional information</h2>
<dl>
<dt>More information can be found on the URLs mentioned above:
<dd>
<pre>
<a href="http://c2.com/cgi/wiki?UnitTest">http://c2.com/cgi/wiki?UnitTest</a>
<a href="http://c2.com/cgi/wiki?RegressionTesting">http://c2.com/cgi/wiki?RegressionTesting</a>
<a href="http://docs.python.org/lib/module-unittest.html">http://docs.python.org/lib/module-unittest.html</a>
<a href="http://diveintopython.org/unit_testing/">http://diveintopython.org/unit_testing/</a>
<a href="http://wwwsearch.sourceforge.net/mechanize/">http://wwwsearch.sourceforge.net/mechanize/</a>
</pre>
</dl>
<dl>
<dt>and elsewhere:
<dd>
<pre>
Steve McConnell: "Code Complete"
FIXME: list of other interesting references, like Kent Beck papers, etc
</pre>
</dl>
diff --git a/modules/webjournal/lib/webjournal.py b/modules/webjournal/lib/webjournal.py
index 31821a1bf..98a5ece19 100644
--- a/modules/webjournal/lib/webjournal.py
+++ b/modules/webjournal/lib/webjournal.py
@@ -1,437 +1,437 @@
# -*- coding: utf-8 -*-
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
from urllib2 import urlopen
import smtplib
import sets
import time
from invenio.bibformat_engine import BibFormatObject, \
format_with_format_template
from invenio.errorlib import register_exception
from invenio.webpage import page
-from invenio.config import weburl, CFG_ETCDIR
+from invenio.config import CFG_SITE_URL, CFG_ETCDIR
from invenio.urlutils import redirect_to_url
from invenio.webuser import collect_user_info
from invenio.webjournal_config import InvenioWebJournalNoIndexTemplateError, \
InvenioWebJournalNoIssueNumberTagError, \
InvenioWebJournalNoArticleTemplateError, \
InvenioWebJournalNoArticleRuleError, \
InvenioWebJournalNoPopupTemplateError, \
InvenioWebJournalReleaseUpdateError, \
InvenioWebJournalIssueNotFoundDBError, \
InvenioWebJournalJournalIdNotFoundDBError, \
InvenioWebJournalArchiveDateWronglyFormedError, \
InvenioWebJournalNoSearchTemplateError
from invenio.webjournal_utils import get_xml_from_config
from invenio.webjournal_utils import get_recid_from_order_CERNBulletin, \
get_article_page_from_cache, \
cache_article_page, \
createhtmlmail, \
put_css_in_file, \
get_monday_of_the_week, \
get_current_issue_time, \
get_all_issue_weeks, \
release_journal_update, \
get_next_journal_issues, \
issue_times_to_week_strings, \
issue_week_strings_to_times, \
release_journal_issue, \
was_alert_sent_for_issue, \
update_DB_for_alert, \
get_current_issue, \
get_current_publication, \
get_list_of_issues_for_publication, \
count_down_to_monday, \
count_week_string_up
from invenio.webjournal_templates import tmpl_webjournal_alert_success_msg, \
tmpl_webjournal_alert_subject_CERNBulletin, \
tmpl_webjournal_alert_plain_text_CERNBulletin, \
tmpl_webjournal_alert_interface, \
tmpl_webjournal_issue_control_interface, \
tmpl_webjournal_issue_control_success_msg, \
tmpl_webjournal_update_an_issue, \
tmpl_webjournal_updated_issue_msg, \
tmpl_webjournal_alert_was_already_sent, \
tmpl_webjournal_admin_interface
def perform_request_index(req, journal_name, issue_number, language, category):
"""
Central logic function for index pages.
Brings together format templates and MARC rules from the config, with
the requested index page, given by the url parameters.
From config:
- page template for index pages -> formatting
- MARC rule list -> Category Navigation
- MARC tag used for issue numbers -> search (later in the format
elements)
Uses BibFormatObject and format_with_format_template to produce the
required HTML.
"""
# init all the values we need from config.xml
config_strings = get_xml_from_config(["index", "rule", "issue_number"],
journal_name)
try:
try:
index_page_template = config_strings["index"][0]
except:
raise InvenioWebJournalNoIndexTemplateError(language, journal_name)
except InvenioWebJournalNoIndexTemplateError, e:
register_exception(req=req)
return e.user_box()
index_page_template_path = 'webjournal/%s' % (index_page_template)
rule_list = config_strings["rule"]
try:
if len(rule_list) == 0:
raise InvenioWebJournalNoArticleRuleError(language, journal_name)
except InvenioWebJournalNoArticleRuleError, e:
register_exception(req=req)
return e.user_box()
try:
try:
issue_number_tag = config_strings["issue_number"][0]
except:
raise InvenioWebJournalNoIssueNumberTagError(language, journal_name)
except InvenioWebJournalNoIssueNumberTagError, e:
register_exception(req=req)
return e.user_box()
# get the current category for index display
current_category_in_list = 0
i = 0
if category != "":
for rule_string in rule_list:
category_from_config = rule_string.split(",")[0]
if category_from_config.lower() == category.lower():
current_category_in_list = i
i+=1
else:
# add the first category to the url string as a default
req.journal_defaults["category"] = rule_list[0].split(",")[0]
# get the important values for the category from the config file
rule_string = rule_list[current_category_in_list].replace(" ", "")
category = rule_string.split(",")[0]
rule = rule_string.split(",")[1]
marc_datafield = rule.split(":")[0]
rule_match = rule.split(":")[1]
marc_tag = marc_datafield[:3]
marc_ind1 = (str(marc_datafield[3]) == "_") and " " or marc_datafield[3]
marc_ind2 = (str(marc_datafield[4]) == "_") and " " or marc_datafield[4]
marc_subfield = marc_datafield[5]
# create a marc record, containing category and issue number
temp_marc = '''<record>
<controlfield tag="001">0</controlfield>
<datafield tag="%s" ind1="%s" ind2="%s">
<subfield code="%s">%s</subfield>
</datafield>
<datafield tag="%s" ind1="%s" ind2="%s">
<subfield code="%s">%s</subfield>
</datafield>
</record>''' % (issue_number_tag[:3],
(issue_number_tag[3] == "_") and " " or issue_number_tag[3],
(issue_number_tag[4] == "_") and " " or issue_number_tag[4],
issue_number_tag[5],
issue_number, marc_tag, marc_ind1,
marc_ind2, marc_subfield, rule_match)
#temp_marc = temp_marc.decode('utf-8').encode('utf-8')
# create a record and get HTML back from bibformat
user_info = collect_user_info(req)
bfo = BibFormatObject(0, ln=language, xml_record=temp_marc, user_info=user_info)
bfo.req = req
html = format_with_format_template(index_page_template_path, bfo)[0]
return html
def perform_request_article(req, journal_name, issue_number, language,
category, number, editor):
"""
Central logic function for article pages.
Loads the format template for article display and displays the requested
article using BibFormat.
'Editor' Mode genereates edit links on the article view page and disables
caching.
"""
# init all the values we need from config.xml
config_strings = get_xml_from_config(["detailed", "rule"], journal_name)
try:
try:
index_page_template = config_strings["detailed"][0]
except:
raise InvenioWebJournalNoArticleTemplateError(language,
journal_name)
except InvenioWebJournalNoArticleTemplateError, e:
register_exception(req=req)
return e.user_box()
index_page_template_path = 'webjournal/%s' % (index_page_template)
rule_list = config_strings["rule"]
try:
if len(rule_list) == 0:
raise InvenioWebJournalNoArticleRuleError(language, journal_name)
except InvenioWebJournalNoArticleRuleError, e:
register_exception(req=req)
return e.user_box()
# get the current category for index display
current_category_in_list = 0
i = 0
if category != "":
for rule_string in rule_list:
category_from_config = rule_string.split(",")[0]
if category_from_config.lower() == category.lower():
current_category_in_list = i
i+=1
rule_string = rule_list[current_category_in_list].replace(" ", "")
rule = rule_string.split(",")[1]
# try to get the page from the cache
recid = get_recid_from_order_CERNBulletin(number, rule, issue_number)
cached_html = get_article_page_from_cache(journal_name, category, recid,
issue_number, language)
if cached_html and editor == "False":
return cached_html
# create a record and get HTML back from bibformat
user_info = collect_user_info(req)
bfo = BibFormatObject(recid, ln=language, user_info=user_info)
bfo.req = req
html_out = format_with_format_template(index_page_template_path,
bfo)[0]
# cache if not in editor mode
if editor == "False":
cache_article_page(html_out, journal_name, category,
recid, issue_number, language)
return html_out
def perform_request_administrate(journal_name, language):
"""
"""
current_issue = get_current_issue(language, journal_name)
current_publication = get_current_publication(journal_name,
current_issue,
language)
issue_list = get_list_of_issues_for_publication(current_publication)
next_issue_number = count_week_string_up(issue_list[-1])
return tmpl_webjournal_admin_interface(journal_name, current_issue,
current_publication, issue_list,
next_issue_number, language)
def perform_request_alert(req, journal_name, issue_number, language,
sent, plain_text, subject, recipients,
html_mail, force):
"""
All the logic for alert emails.
Messages are retrieved from templates. (should be migrated to msg class)
Mails can be edited by an interface form.
Sent in HTML/PlainText or only PlainText if wished so.
"""
subject = tmpl_webjournal_alert_subject_CERNBulletin(journal_name,
issue_number)
plain_text = tmpl_webjournal_alert_plain_text_CERNBulletin(journal_name,
language,
issue_number)
plain_text = plain_text.encode('utf-8')
if sent == "False":
interface = tmpl_webjournal_alert_interface(language, journal_name,
subject, plain_text)
return page(title="alert system", body=interface)
else:
if was_alert_sent_for_issue(issue_number,
journal_name,
language) != False and force == "False":
return tmpl_webjournal_alert_was_already_sent(language, journal_name,
subject, plain_text,
recipients,
html_mail, issue_number)
if html_mail == "html":
html_file = urlopen('%s/journal/?name=%s&ln=en'
- % (weburl, journal_name))
+ % (CFG_SITE_URL, journal_name))
html_string = html_file.read()
html_file.close()
html_string = put_css_in_file(html_string, journal_name)
else:
html_string = plain_text.replace("\n", "<br/>")
message = createhtmlmail(html_string, plain_text,
subject, recipients)
server = smtplib.SMTP("localhost", 25)
server.sendmail('Bulletin-Support@cern.ch', recipients, message)
# todo: has to go to some messages config
update_DB_for_alert(issue_number, journal_name, language)
return tmpl_webjournal_alert_success_msg(language, journal_name)
def perform_request_issue_control(req, journal_name, issue_numbers,
language, add, action):
"""
Central logic for issue control.
Regenerates the flat files current_issue and issue_group that control
the which issue is currently active for the journal.
Todo: move issue control to DB
"""
if action == "cfg" or action == "Refresh" or action == "Add_One":
# find out if we are in update or release
try:
current_issue_time = get_current_issue_time(journal_name)
all_issue_weeks = get_all_issue_weeks(current_issue_time,
journal_name,
language)
except InvenioWebJournalIssueNotFoundDBError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalJournalIdNotFoundDBError, e:
register_exception(req=req)
return e.user_box()
if max(all_issue_weeks) > current_issue_time:
# propose an update
next_issue_week = None
all_issue_weeks.sort()
for issue_week in all_issue_weeks:
if issue_week > current_issue_time:
next_issue_week = issue_week
break
output = tmpl_webjournal_update_an_issue(language,
journal_name,
issue_times_to_week_strings([next_issue_week,])[0],
issue_times_to_week_strings([current_issue_time,])[0])
else:
# propose a release
next_issues = get_next_journal_issues(current_issue_time,
journal_name)
next_issues = issue_times_to_week_strings(next_issues,
language)
if action == "Refresh":
next_issues += issue_numbers
next_issues = list(sets.Set(next_issues))# avoid double entries
elif action == "Add_One":
next_issues += issue_numbers
next_issues = list(sets.Set(next_issues))# avoid double entries
next_issues_times = issue_week_strings_to_times(next_issues,
language)
highest_issue_so_far = max(next_issues_times)
one_more_issue = get_next_journal_issues(highest_issue_so_far,
journal_name,
language,
1)
one_more_issue = issue_times_to_week_strings(one_more_issue,
language)
next_issues += one_more_issue
next_issues = list(sets.Set(next_issues)) # avoid double entries
next_issues.sort()
else:
# get the next (default 2) issue numbers to publish
next_issues = get_next_journal_issues(current_issue_time,
journal_name,
language)
next_issues = issue_times_to_week_strings(next_issues,
language)
output = tmpl_webjournal_issue_control_interface(language,
journal_name,
next_issues)
elif action == "Publish":
publish_issues = issue_numbers
publish_issues = list(sets.Set(publish_issues)) # avoid double entries
publish_issues.sort()
try:
release_journal_issue(publish_issues, journal_name, language)
except InvenioWebJournalJournalIdNotFoundDBError, e:
register_exception(req=req)
return e.user_box()
output = tmpl_webjournal_issue_control_success_msg(language,
publish_issues, journal_name)
elif action == "Update":
try:
try:
update_issue = issue_numbers[0]
except:
raise InvenioWebJournalReleaseUpdateError(language, journal_name)
except InvenioWebJournalReleaseUpdateError, e:
register_exception(req=req)
return e.user_box()
try:
release_journal_update(update_issue, journal_name, language)
except InvenioWebJournalJournalIdNotFoundDBError, e:
register_exception(req=req)
return e.user_box()
output = tmpl_webjournal_updated_issue_msg(language, update_issue,
journal_name)
return page(title="Publish System", body=output)
def perform_request_popup(req, language, journal_name, type, record):
"""
"""
config_strings = get_xml_from_config(["popup"], journal_name)
try:
try:
popup_page_template = config_strings["popup"][0]
except:
raise InvenioWebJournalNoPopupTemplateError(language)
except InvenioWebJournalNoPopupTemplateError, e:
register_exception(req=req)
return e.user_box()
popup_page_template_path = 'webjournal/%s' % popup_page_template
user_info = collect_user_info(req)
bfo = BibFormatObject(record, ln=language, user_info=user_info)
bfo.req = req
html = format_with_format_template(popup_page_template_path, bfo)[0]
return html
def perform_request_search(journal_name, language, req, issue,
archive_year, archive_issue, archive_select,
archive_date, archive_search):
"""
Logic for the search / archive page.
"""
config_strings = get_xml_from_config(["search", "issue_number", "rule"],
journal_name)
try:
try:
search_page_template = config_strings["search"][0]
except:
raise InvenioWebJournalNoSearchTemplateError(journal_name, language)
except InvenioWebJournalNoSearchTemplateError, e:
register_exception(req=req)
return e.user_box()
search_page_template_path = 'webjournal/%s' % (search_page_template)
# just an empty buffer record, since all values are in req.journal_defaults
if archive_select == "False" and archive_search == "False":
temp_marc = '''<record>
<controlfield tag="001">0</controlfield>
</record>'''
user_info = collect_user_info(req)
bfo = BibFormatObject(0, ln=language, xml_record=temp_marc, user_info=user_info)
bfo.req = req
html = format_with_format_template(search_page_template_path, bfo)[0]
return html
elif archive_select == "Go":
- redirect_to_url(req, "%s/journal/?name=%s&issue=%s&ln=%s" % (weburl,
+ redirect_to_url(req, "%s/journal/?name=%s&issue=%s&ln=%s" % (CFG_SITE_URL,
journal_name,
archive_issue,
language))
elif archive_search == "Go":
archive_issue_time = time.strptime(archive_date, "%d/%m/%Y")
archive_issue_time = count_down_to_monday(archive_issue_time)
archive_issue = issue_times_to_week_strings([archive_issue_time,])[0]
- redirect_to_url(req, "%s/journal/?name=%s&issue=%s&ln=%s" % (weburl,
+ redirect_to_url(req, "%s/journal/?name=%s&issue=%s&ln=%s" % (CFG_SITE_URL,
journal_name,
archive_issue,
language))
diff --git a/modules/webjournal/lib/webjournal_config.py b/modules/webjournal/lib/webjournal_config.py
index 80826fd36..27658e124 100644
--- a/modules/webjournal/lib/webjournal_config.py
+++ b/modules/webjournal/lib/webjournal_config.py
@@ -1,679 +1,679 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
import os
-from invenio.config import CFG_SITE_ADMIN_EMAIL, CFG_SITE_SUPPORT_EMAIL, CFG_ETCDIR, weburl, CFG_SITE_LANG
+from invenio.config import CFG_SITE_ADMIN_EMAIL, CFG_SITE_SUPPORT_EMAIL, CFG_ETCDIR, CFG_SITE_URL, CFG_SITE_LANG
from invenio.messages import gettext_set_language
from invenio.webpage import page
from invenio.htmlutils import escape_html
from invenio.messages import gettext_set_language
from invenio.webjournal_utils import parse_url_string
from invenio.webjournal_templates import tmpl_webjournal_error_box,\
tmpl_webjournal_missing_info_box
#from invenio.data_cacher import SQLDataCacher
#
#CFG_JOURNAL_CONFIG_CACHE = {}
#
#def initialize_config_cache():
# """
# """
# journal_id_names = SQLDataCacher("SELECT * FROM jrnJOURNAL", affected_tables=(jrnJOURNAL))
class InvenioWebJournalNoIndexTemplateError(Exception):
"""Exception if no index template is specified in the config."""
def __init__(self, language, journal_name):
"""Initialisation."""
self.journal = journal_name
self.language = language
def __str__(self):
"""String representation."""
return 'Admin did not provide a template for the index page of \
journal: %s. \
The path to such a file should be given in the config.xml of\
this journal under the tag <format_template><index>...\
</index></format_template>' % repr(self.journal)
def user_box(self):
"""
user-friendly error message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Internal configuration error'),
_('There is no format configured for this journals index page'),
'Admin did not provide a template for the index page of journal: %s. \
The path to such a file should be given in the config.xml of\
this journal under the tag <format_template><index>...\
</index></format_template>' % escape_html(self.journal))
class InvenioWebJournalNoArticleTemplateError(Exception):
"""
Exception if an article was called without its order number.
"""
def __init__(self, language, journal_name):
"""
Initialisation.
"""
self.journal = journal_name
self.language = language
def __str__(self):
"""
String representation.
"""
return 'Admin did not provide a template for the article view page of journal: %s. \
The path to such a file should be given in the config.xml of this journal \
under the tag <format_template><detailed>...</detailed></format_template>' % repr(self.journal)
def user_box(self):
"""
user-friendly error message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Internal configuration error'),
_('There is no format configured for this journals index page'),
'Admin did not provide a template for the index page of journal: %s. \
The path to such a file should be given in the config.xml of\
this journal under the tag <format_template><index>...\
</index></format_template>' % escape_html(self.journal))
class InvenioWebJournalNoSearchTemplateError(Exception):
"""
Exception if an article was called without its order number.
"""
def __init__(self, journal_name, language=CFG_SITE_LANG):
"""
Initialisation.
"""
self.journal = journal_name
self.language = language
def __str__(self):
"""
String representation.
"""
return 'Admin did not provide a template for the search page view page of journal: %s. \
The path to such a file should be given in the config.xml of this journal \
under the tag <format_template><search>...</search></format_template>' % repr(self.journal)
def user_box(self):
"""
user-friendly error message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Internal configuration error'),
_('There is no format configured for this journals search page'),
'Admin did not provide a template for the search page of journal: %s. \
The path to such a file should be given in the config.xml of\
this journal under the tag <format_template><search>...\
</search></format_template>' % escape_html(self.journal))
class InvenioWebJournalNoPopupTemplateError(Exception):
"""
Exception if an article was called without its order number.
"""
def __init__(self, language, journal_name):
"""
Initialisation.
"""
self.journal = journal_name
self.language = language
def __str__(self):
"""
String representation.
"""
return 'Admin did not provide a template for the popup view page \
of journal: %s. \
The path to such a file should be given in the config.xml of this \
journal under the tag \
<format_template><popup>...</popup></format_template>' % repr(
self.journal)
def user_box(self):
"""
user-friendly error message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Internal configuration error'),
_('There is no format configured for this journals popup page'),
'Admin did not provide a template for the popup page of journal: %s. \
The path to such a file should be given in the config.xml of\
this journal under the tag <format_template><popup>...\
</popup></format_template>' % escape_html(self.journal))
class InvenioWebJournalNoArticleRuleError(Exception):
"""
Exception if there are no article type rules defined.
"""
def __init__(self, language, journal_name):
"""
Initialisation.
"""
self.journal = journal_name
self.language = language
def __str__(self):
"""
String representation.
"""
return 'The config.xml file for journal: %s does not contain any \
article rules. These rules are needed to associate collections from \
your Invenio installation to navigable article types. A rule should \
have the form of <rule>NameOfArticleType, \
marc_tag:ExpectedContentOfMarcTag' % escape_html(self.journal)
def user_box(self):
"""
user-friendly error message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_("No journal articles"),
_("Problem with the configuration of this journal"),
"The system couldn't find the definitions for different article \
kinds (e.g. News, Sports, etc). If there is nothing defined, \
nothing can be shown and it thus indicates that there is either a \
problem with the setup of this journal or in the Software itself.\
There is nothing you can do at this moment. If you wish you can \
send an inquiry to the responsible developers. We apologize \
for the inconvenience.")
class InvenioWebJournalNoIssueNumberTagError(Exception):
"""
Exception if there is no marc tag for issue number defined.
"""
def __init__(self, language, journal_name):
"""
Initialisation.
"""
self.journal = journal_name
self.language = language
def __str__(self):
"""
String representation.
"""
return 'The config.xml file for journal: %s does not contain a marc tag\
to deduce the issue number from. WebJournal is an issue number based \
system, meaning you have to give some form of numbering system in a \
dedicated marc tag, so the system can see which is the active journal \
publication of the date.' % repr(self.journal)
def user_box(self):
"""
user-friendly error message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_("No journal issues"),
_("Problem with the configuration of this journal"),
"The system couldn't find a definition for an issue \
numbering system. Issue numbers conrol the date of the \
publication you are seing. This indicates that there is an \
error in the setup of this journal or the Software itself. \
There is nothing you can do at the moment. If you wish you \
can send an inquiry to the responsible developers. We \
apologize for the inconvenience.")
class InvenioWebJournalNoArticleNumberError(Exception):
"""
Exception if an article was called without its order number.
"""
def __init__(self, language, journal_name):
"""
Initialisation.
"""
self.journal = journal_name
self.language = language
def __str__(self):
"""
String representation.
"""
return 'In Journal %s an article was called without specifying the order \
of this article in the issue. This parameter is mandatory and should be \
provided by internal links in any case. Maybe this was a bad direct url \
hack. Check where the request came from.' % repr(self.journal)
def user_box(self):
"""
user-friendly error message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Journal article error'),
_('We could not know which article you were looking for'),
'The url you passed did not provide an article number or the \
article number was badly formed. If you \
came to this page through some link on the journal page, please \
report this to the admin. If you got this link through some \
external resource, e.g. an email, you can try to put in a number \
for the article in the url by hand or just visit the front \
- page at %s/journal/?name=%s' % (weburl, self.journal))
+ page at %s/journal/?name=%s' % (CFG_SITE_URL, self.journal))
class InvenioWebJournalNoJournalOnServerError(Exception):
"""
Exception that is thrown if there are no Journal instances on the server
"""
def __init__(self, language):
"""
Initialisation.
"""
self.language = language
def __str__(self):
"""
String representation.
"""
return 'Apparently there are no journals configured on this \
installation of CDS Invenio. You can try to use the sample Invenio \
Atlantis Journal for testing.'
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('No journals available'),
_('We could not provide you any journals'),
_('It seems that there are no journals defined on this server. '
'Please contact support if this is not right.'))
class InvenioWebJournalNoNameError(Exception):
"""
"""
def __init__(self, language):
"""
Initialisation.
"""
self.language = language
def __str__(self):
"""
String representation.
"""
return 'User probably forgot to add the name parameter for the journal\
Maybe you also want to check if dns mappings are configured correctly.'
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return webjournal_missing_info_box(self.language,
_("Select a journal on this server"),
_("We couldn't guess which journal you are looking for"),
_("You did not provide an argument for a journal name. "
"Please select the journal you want to read in the list below."))
class InvenioWebJournalNoCurrentIssueError(Exception):
"""
"""
def __init__(self, language):
"""
Initialisation.
"""
self.language = language
def __str__(self):
"""
String representation.
"""
return 'There seems to be no current issue number stored for this \
journal. Is this the first time you use the journal? Otherwise, check\
configuration.'
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return webjournal_error_box(self.language,
_('No current issue'),
_('We could not find any informtion on the current issue'),
_('The configuration for the current issue seems to be empty. '
'Try providing an issue number or check with support.'))
class InvenioWebJournalIssueNumberBadlyFormedError(Exception):
"""
"""
def __init__(self, language, issue):
"""
Initialisation.
"""
self.language = language
self.issue = issue
def __str__(self):
"""
String representation.
"""
return 'The issue number was badly formed. If this comes from the \
user it is no problem.'
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Issue number badly formed'),
_('We could not read the issue number you provided'),
'The issue number you provided in the url seems to be badly\
formed. Issue numbers have to be in the form of ww/YYYY, so\
e.g. 50/2007. You provided the issue number like so: \
%s.' % escape_html(self.issue))
class InvenioWebJournalArchiveDateWronglyFormedError (Exception):
"""
"""
def __init__(self, language, date):
"""
Initialisation.
"""
self.language = language
self.date = date
def __str__(self):
"""
String representation.
"""
return 'The archive date was badly formed. If this comes from the \
user it is no problem.'
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Archive date badly formed'),
_('We could not read the archive date you provided'),
'The archive date you provided in the form seems to be badly\
formed. Archive dates have to be in the form of dd/mm/YYYY, so\
e.g. 02/12/2007. You provided the archive date like so: \
%s.' % escape_html(self.date))
class IvenioWebJournalNoPopupTypeError(Exception):
"""
Exception that is thrown if a popup is requested without specifying the
type of the popup to call.
"""
def __init__(self, language, journal_name):
"""
Initialisation.
"""
self.language = language
self.journal_name = journal_name
def __str__(self):
"""
String representation.
"""
return 'There was no popup type provided for a popup window on \
journal %s.' % repr(self.journal_name)
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('No popup type'),
_('We could not know what kind of popup you requested'),
'You called a popup window on CDS Invenio without \
specifying the type of the popup. Does this link come \
from a CDS Invenio Journal? If so, please contact \
support.')
class InvenioWebJournalNoPopupRecordError(Exception):
"""
Exception that is thrown if a popup is requested without specifying the
type of the popup to call.
"""
def __init__(self, language, journal_name, recid):
"""
Initialisation.
"""
self.language = language
self.journal_name = journal_name
self.recid = recid
def __str__(self):
"""
String representation.
"""
return 'There was no recid provided to the popup system of webjournal \
or the recid was badly formed. The recid was %s' % repr(self.recid)
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('No popup record'),
_('We could not deduce the popup article you requested'),
'You called a popup window on CDS Invenio without \
specifying a record in which you are interested or the \
record was badly formed. Does this link come \
from a CDS Invenio Journal? If so, please contact \
support.')
class InvenioWebJournalReleaseUpdateError(Exception):
"""
Exception that is thrown if an update release was not successful.
"""
def __init__(self, language, journal_name):
"""
Initialisation.
"""
self.language = language
self.journal_name = journal_name
def __str__(self):
"""
String representation.
"""
return 'There were no updates submitted on a click on the update button.\
This should never happen and must be due to an internal error.'
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Update error'),
_('There was an internal error'),
'We encountered an internal error trying to update the \
journal issue. You can try to launch the update again or \
contact the Administrator. We apologize for the \
inconvenience.')
class InvenioWebJournalReleaseDBError(Exception):
"""
Exception that is thrown if an update release was not successful.
"""
def __init__(self, language):
"""
Initialisation.
"""
self.language = language
def __str__(self):
"""
String representation.
"""
return 'There was an error in synchronizing DB times with the actual \
python time objects. Debug the code in: \
webjournal_utils.issue_times_to_week_strings'
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Journal publishing DB error'),
_('There was an internal error'),
'We encountered an internal error trying to publish the \
journal issue. You can try to launch the publish interface \
again or contact the Administrator. We apologize for the \
inconvenience.')
class InvenioWebJournalIssueNotFoundDBError(Exception):
"""
Exception that is thrown if there was an issue number not found in the
"""
def __init__(self, language, journal_name, issue_number):
"""
Initialisation.
"""
self.language = language
self.journal_name = journal_name
self.issue_number = issue_number
def __str__(self):
"""
String representation.
"""
return 'The issue %s could not be found in the DB for journal %s.' % (self.issue_number, self.journal_name)
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Journal issue error'),
_('We could not find a current issue in the Database'),
'We encountered an internal error trying to get an issue \
number. You can try to refresh the page or \
contact the Administrator. We apologize for the \
inconvenience.')
class InvenioWebJournalJournalIdNotFoundDBError(Exception):
"""
Exception that is thrown if there was an issue number not found in the
"""
def __init__(self, language, journal_name):
"""
Initialisation.
"""
self.language = language
self.journal_name = journal_name
def __str__(self):
"""
String representation.
"""
return 'The id for journal %s was not found in the Database. Make \
sure the entry exists!' % (self.journal_name)
def user_box(self):
"""
user-friendly message with formatting.
"""
_ = gettext_set_language(self.language)
return tmpl_webjournal_error_box(self.language,
_('Journal ID error'),
_('We could not find the id for this journal in the Database'),
'We encountered an internal error trying to get the id \
for this journal. You can try to refresh the page or \
contact the Administrator. We apologize for the \
inconvenience.')
#!!! depreceated !!!#
def webjournal_missing_info_box(language, title, msg_title, msg):
"""
returns a box indicating that the given journal was not found on the
server, leaving the opportunity to select an existing journal from a list.
"""
#params = parse_url_string(req)
#try:
# language = params["ln"]
#except:
# language = CFG_SITE_LANG
_ = gettext_set_language(language)
title = _(title)
box_title = _(msg_title)
box_text = _(msg)
box_list_title = _("Available journals")
find_journals = lambda path: [entry for entry in os.listdir(str(path)) if os.path.isdir(str(path)+str(entry))]
try:
all_journals = find_journals('%s/webjournal/' % CFG_ETCDIR)
except:
all_journals = []
box = '''<div style="text-align: center;">
<fieldset style="width:400px; margin-left: auto; margin-right: auto;background: url('%s/img/blue_gradient.gif') top left repeat-x;">
<legend style="color:#a70509;background-color:#fff;"><i>%s</i></legend>
<p style="text-align:center;">%s</p>
<h2 style="color:#0D2B88;">%s</h2>
<ul class="webjournalBoxList">
%s
</ul>
<br/>
<div style="text-align:right;">Mail<a href="mailto:%s"> the Administrator.</a></div>
</fieldset>
</div>
- ''' % (weburl,
+ ''' % (CFG_SITE_URL,
box_title,
box_text,
box_list_title,
- "".join(['<li><a href="%s/journal/?name=%s">%s</a></li>' % (weburl, journal, journal) for journal in all_journals]),
+ "".join(['<li><a href="%s/journal/?name=%s">%s</a></li>' % (CFG_SITE_URL, journal, journal) for journal in all_journals]),
CFG_SITE_ADMIN_EMAIL)
return page(title=title, body=box)
#!!! depreceated !!!#
def webjournal_error_box(language, title, title_msg, msg):
"""
"""
#params = parse_url_string(req)
#try:
# language = params["ln"]
#except:
# language = CFG_SITE_LANG
_ = gettext_set_language(language)
title = _(title)
title_msg = _(title_msg)
msg = _(msg)
box = '''<div style="text-align: center;">
<fieldset style="width:400px; margin-left: auto; margin-right: auto;background: url('%s/img/red_gradient.gif') top left repeat-x;">
<legend style="color:#a70509;background-color:#fff;"><i>%s</i></legend>
<p style="text-align:center;">%s</p>
<br/>
<div style="text-align:right;">Mail<a href="mailto:%s"> the Developers.</a></div>
</fieldset>
</div>
- ''' % (weburl, title_msg, msg, CFG_SITE_SUPPORT_EMAIL)
+ ''' % (CFG_SITE_URL, title_msg, msg, CFG_SITE_SUPPORT_EMAIL)
return page(title=title, body=box)
diff --git a/modules/webjournal/lib/webjournal_templates.py b/modules/webjournal/lib/webjournal_templates.py
index a0cf4cc2f..9423c3736 100644
--- a/modules/webjournal/lib/webjournal_templates.py
+++ b/modules/webjournal/lib/webjournal_templates.py
@@ -1,447 +1,447 @@
# -*- coding: utf-8 -*-
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
import os
import time
-from invenio.config import CFG_SITE_ADMIN_EMAIL, CFG_SITE_SUPPORT_EMAIL, CFG_ETCDIR, weburl, CFG_SITE_LANG
+from invenio.config import CFG_SITE_ADMIN_EMAIL, CFG_SITE_SUPPORT_EMAIL, CFG_ETCDIR, CFG_SITE_URL, CFG_SITE_LANG
from invenio.messages import gettext_set_language
from invenio.webpage import page
from invenio.webjournal_utils import get_number_of_articles_for_issue, \
get_release_time, \
get_announcement_time, \
get_current_publication
def tmpl_webjournal_missing_info_box(language, title, msg_title, msg):
"""
returns a box indicating that the given journal was not found on the
server, leaving the opportunity to select an existing journal from a list.
"""
_ = gettext_set_language(language)
box_title = msg_title
box_text = msg
box_list_title = _("Available Journals")
# todo: move to DB call
find_journals = lambda path: [entry for entry in os.listdir(str(path)) if os.path.isdir(str(path)+str(entry))]
try:
all_journals = find_journals('%s/webjournal/' % CFG_ETCDIR)
except:
all_journals = []
box = '''
<div style="text-align: center;">
<fieldset style="width:400px; margin-left: auto; margin-right:
auto;background: url('%s/img/blue_gradient.gif') top left repeat-x;">
<legend style="color:#a70509;background-color:#fff;">
<i>%s</i>
</legend>
<p style="text-align:center;">%s</p>
<h2 style="color:#0D2B88;">%s</h2>
<ul class="webjournalBoxList">
%s
</ul>
<br/>
<div style="text-align:right;">
Mail
<a href="mailto:%s"> the Administrator.</a>
</div>
</fieldset>
</div>
- ''' % (weburl,
+ ''' % (CFG_SITE_URL,
box_title,
box_text,
box_list_title,
"".join(['<li><a href="%s/journal/?name=%s">%s</a></li>'
- % (weburl,
+ % (CFG_SITE_URL,
journal,
journal) for journal in all_journals]),
CFG_SITE_ADMIN_EMAIL)
return page(title=title, body=box)
def tmpl_webjournal_error_box(language, title, title_msg, msg):
"""
returns an error box for webjournal errors.
"""
_ = gettext_set_language(language)
title = _(title)
title_msg = _(title_msg)
msg = _(msg)
mail_msg = _("Mail %(x_url_open)sdevelopers%(x_url_close)s") % {'x_url_open' :
'<a href="mailto:%s">' % CFG_SITE_SUPPORT_EMAIL,
'x_url_close' : '</a>'}
box = '''
<div style="text-align: center;">
<fieldset style="width:400px; margin-left: auto; margin-right: auto;
background: url('%s/img/red_gradient.gif') top left repeat-x;">
<legend style="color:#a70509;background-color:#fff;">
<i>%s</i>
</legend>
<p style="text-align:center;">%s</p>
<br/>
<div style="text-align:right;">
%s
</div>
</fieldset>
</div>
- ''' % (weburl, title_msg, msg, mail_msg)
+ ''' % (CFG_SITE_URL, title_msg, msg, mail_msg)
return page(title=title, body=box)
def tmpl_webjournal_regenerate_success(language, journal_name, issue_number):
"""
Success message if a user applied the "regenerate" link. Links back to
the regenerated journal.
"""
_ = gettext_set_language(language)
return page(
title=_("Issue regenerated"),
body = '''
The issue number %s for the journal %s has been successfully
regenerated.
Look at your changes: >> <a href="%s/journal/?name=%s"> %s </a>
- ''' % (issue_number, journal_name, weburl, journal_name, journal_name))
+ ''' % (issue_number, journal_name, CFG_SITE_URL, journal_name, journal_name))
def tmpl_webjournal_regenerate_error(language, journal_name, issue_number):
"""
Failure message for a regeneration try.
"""
_ = gettext_set_language(language)
return page(
title=_("Regeneration Error"),
body = _("The issue could not be correctly regenerated. "
"Please contact your administrator."))
def tmpl_webjournal_feature_record_interface(language, journal_name):
"""
Draws an interface form to feature a specific record from CDS Invenio.
"""
_ = gettext_set_language(language)
interface = '''
<form action="%s/journal/feature_record" name="alert">
<input type="hidden" name="name" value="%s"/>
<p>Featured Record ID:</p>
<input type="text" name="recid" value="" />
<p>Link to the picture that should be displayed</p>
<input type="text" name="url" value="" />
<br/>
<input class="formbutton" type="submit" value="feature" name="featured"/>
</form>
- ''' % (weburl, journal_name)
+ ''' % (CFG_SITE_URL, journal_name)
return page(title=_("Feature a record"), body=interface)
def tmpl_webjournal_feature_record_success(language, journal_name, recid):
"""
Draw a success message for featuring a record and a backlink to the journal
"""
_ = gettext_set_language(language)
title = "Successfully featured record: %s" % recid
msg = '''Return to your journal here >>
- <a href="%s/journal/?name=%s">%s</a>''' % (weburl,
+ <a href="%s/journal/?name=%s">%s</a>''' % (CFG_SITE_URL,
journal_name,
journal_name)
return page(title = title, body = msg)
def tmpl_webjournal_alert_plain_text_CERNBulletin(journal_name, language, issue):
"""
Plain Text message for alert of CERN Bulletin. No multilanguage since the
message should always be in two languages.
"""
current_publication = get_current_publication(journal_name, issue)
plain_text = u'''Dear Subscriber,
The latest issue of the CERN Bulletin, no. %s, has been released.
You can access it at the following URL:
http://bulletin.cern.ch/
Best Wishes,
CERN Bulletin team
----
Cher Abonné,
Le nouveau numéro du CERN Bulletin, no. %s, vient de paraître.
Vous pouvez y accéder à cette adresse :
http://bulletin.cern.ch/fre
Bonne lecture,
L'équipe du Bulletin du CERN
''' % (current_publication, current_publication)
return plain_text
def tmpl_webjournal_alert_subject_CERNBulletin(journal_name, issue):
"""
Subject text for the CERN Bulletin release.
"""
return "CERN bulletin %s released" % get_current_publication(journal_name,
issue)
def tmpl_webjournal_alert_interface(language, journal_name, subject,
plain_text):
"""
Alert eMail interface.
"""
_ = gettext_set_language(language)
interface = '''
<form action="%s/journal/alert" name="alert" method="POST">
<input type="hidden" name="name" value="%s"/>
<p>Recipients:</p>
<input type="text" name="recipients" value="bulletin-alert-eng@cern.ch,bulletin-alert-fre@cern.ch,cern-staff@cern.ch,cern-fellows@cern.ch" />
<p>Subject:</p>
<input type="text" name="subject" value="%s" />
<p>Plain Text Message:</p>
<textarea name="plainText" wrap="soft" rows="25" cols="50">%s</textarea>
<p> Send Homepage as html:
<input type="checkbox" name="htmlMail" value="html" checked="checked" />
</p>
<br/>
<input class="formbutton" type="submit" value="alert" name="sent"/>
- </form>''' % (weburl, journal_name, subject, plain_text)
+ </form>''' % (CFG_SITE_URL, journal_name, subject, plain_text)
return interface
def tmpl_webjournal_alert_was_already_sent(language, journal_name,
subject, plain_text, recipients,
html_mail, issue):
"""
"""
_ = gettext_set_language(language)
interface = '''
<form action="%s/journal/alert" name="alert" method="POST">
<input type="hidden" name="name" value="%s"/>
<input type="hidden" name="recipients" value="%s" />
<input type="hidden" name="subject" value="%s" />
<input type="hidden" name="plainText" value="%s" />
<input type="hidden" name="htmlMail" value="%s" />
<input type="hidden" name="force" value="True" />
<p><em>ATTENTION! </em>The alert email for the issue %s has already been
sent. Are you absolutely sure you want to resend it?</p>
<p>Maybe you forgot to release an update issue? If so, please do this
first <a href="%s/journal/issue_control?name=%s&issue=%s">here</a>.</p>
<p>Be aware, if you go on with this, the whole configured mailing list
will receive this message a second time. Only proceed if you know what
you are doing!</p>
<br/>
<input class="formbutton" type="submit" value="I really want this!" name="sent"/>
</form>
- ''' % (weburl, journal_name, recipients,
- subject, plain_text, html_mail, issue, weburl, journal_name,
+ ''' % (CFG_SITE_URL, journal_name, recipients,
+ subject, plain_text, html_mail, issue, CFG_SITE_URL, journal_name,
issue)
return page(title="Confirmation Required", body=interface)
def tmpl_webjournal_alert_success_msg(language, journal_name):
"""
Success messge for the alert system.
"""
_ = gettext_set_language(language)
title = _("Alert sent successfully!")
body = 'Return to your journal here: >> \
- <a href="%s/journal/?name=%s"> %s </a>' % (weburl, journal_name,
+ <a href="%s/journal/?name=%s"> %s </a>' % (CFG_SITE_URL, journal_name,
journal_name)
return page(title=title, body=body)
def tmpl_webjournal_issue_control_interface(language, journal_name,
active_issues):
"""
"""
_ = gettext_set_language(language)
interface = '''
<div align="center">
<h2>Publishing Interface</h2>
<p>This interface gives you the possibilite to create
your current webjournal publication. Every checked
issue number will be in the current publication. Once
you've made your selection you can publish the new
issue by clicking the Publish button at the end.
</p>
<form action="%s/journal/issue_control" name="publish">
<input type="hidden" name="name" value="%s"/>
<ul>
<p>Issue Numbers to publish::..</p>
%s
<br/>
<p>Add a higher issue number by clicking "Add_One"</p>
<input class="formbutton" type="submit" value="Add_One" name="action_publish"/>
<p>.. or add a custom issue number by typing it here and pressing "Refresh"</p>
<input type="text" value="ww/YYYY" name="issue_number"/>
<input class="formbutton" type="submit" value="Refresh" name="action_publish"/>
<br/>
<br/>
<p>If all issues you want to publish are correctly checked, proceed \
by clicking "Publish".</p>
<input class="formbutton" type="submit" value="Publish" name="action_publish"/>
</form>
</div>
- ''' % (weburl,
+ ''' % (CFG_SITE_URL,
journal_name,
"".join(['<li><input type="checkbox" name="issue_number" value="%s" CHECKED>&nbsp;%s</input></li>'
% (issue, issue) for issue in active_issues]),
)
return interface
def tmpl_webjournal_issue_control_success_msg(language,
active_issues, journal_name):
"""
"""
_ = gettext_set_language(language)
issue_string = "".join([" - %s" % issue for issue in active_issues])
title = '<h2>Bulletin %s created successfully!</h2>' % issue_string
body = '<p>Now you can:</p> \
<p>Return to your journal here: >> \
<a href="%s/journal/?name=%s"> %s </a>\
</p>\
<p>Make additional publications here: >> \
<a href="%s/journal/issue_control?name=%s">Issue Interface</a> \
</p>\
<p>Send an alert email here: >> \
<a href="%s/journal/alert?name=%s"> Send an alert</a> \
- </p>' % (weburl, journal_name,
- journal_name, weburl,
- journal_name, weburl, journal_name)
+ </p>' % (CFG_SITE_URL, journal_name,
+ journal_name, CFG_SITE_URL,
+ journal_name, CFG_SITE_URL, journal_name)
return title + body
def tmpl_webjournal_update_an_issue(language, journal_name, next_issue,
current_issue):
"""
A form that lets a user make an update to an issue number.
"""
_ = gettext_set_language(language)
current_articles = get_number_of_articles_for_issue(current_issue,
journal_name,
language)
next_articles = get_number_of_articles_for_issue(next_issue,
journal_name,
language)
html = '''
<p>The Issue that was released on week %s has pending updates scheduled. The
next update for this issue is %s.</p>
<p><em>Note: If you want to make a new release, please click through all the
pending updates first.</em></p>
<p>Do you want to release the update from issue <br/><br/>
<em>%s</em> (%s) <br/>
<em>to issue %s</em> (%s) <br/><br/>
now?</p>
<form action="%s/journal/issue_control" name="publish">
<input type="hidden" name="name" value="%s"/>
<input type="hidden" name="issue_number" value="%s"/>
<input class="formbutton" type="submit" value="Update" name="action_publish"/>
</form>
''' % (current_issue, next_issue,
current_issue,
",".join(["%s : %s" % (item[0], item[1]) for item in current_articles.iteritems()]),
next_issue,
",".join(["%s : %s" % (item[0], item[1]) for item in next_articles.iteritems()]),
- weburl, journal_name, next_issue)
+ CFG_SITE_URL, journal_name, next_issue)
return html
def tmpl_webjournal_updated_issue_msg(language, update_issue, journal_name):
"""
Prints a success message for the Update release of a journal.
"""
_ = gettext_set_language(language)
title = '<h2>Journal update %s published successfully!</h2>' % update_issue
body = '<p>Now you can:</p> \
<p>Return to your journal here: >> \
<a href="%s/journal/?name=%s"> %s </a>\
</p>\
<p>Go back to the publishing interface: >> \
<a href="%s/journal/issue_control?name=%s">Issue Interface</a> \
</p>\
<p>Send an alert email here: >> \
<a href="%s/journal/alert?name=%s"> Send an alert</a> \
- </p>' % (weburl, journal_name, journal_name,
- weburl, journal_name, weburl, journal_name)
+ </p>' % (CFG_SITE_URL, journal_name, journal_name,
+ CFG_SITE_URL, journal_name, CFG_SITE_URL, journal_name)
return title + body
def tmpl_webjournal_admin_interface(journal_name, current_issue,
current_publication, issue_list,
next_issue_number, language=CFG_SITE_LANG):
"""
Returns an administration interface that shows the current publication and
supports links to all important actions.
"""
_ = gettext_set_language(language)
title = _('Webjournal Administration Interface')
# format the issues
issue_boxes = []
issue_list.append(next_issue_number)
for issue in issue_list:
articles = get_number_of_articles_for_issue(issue,
journal_name,
language)
released_on = get_release_time(issue, journal_name, language)
announced_on = get_announcement_time(issue, journal_name, language)
issue_box = '''
<tr style="%s">
<td class="admintdright" style="vertical-align: middle;"></td>
<td class="admintdleft" style="white-space: nowrap; vertical-align: middle;">
<p>Issue: %s</p>
<p>Publication: %s</p>
</td>
<td class="admintdright" style="vertical-align: middle;">
%s
</td>
<td class="admintdright" style="vertical-align: middle;">
<p>%s</p>
<p>%s</p>
</td>
<td class="admintdright" style="vertical-align: middle;">
<p>Not implemented yet</p>
<p><a href="%s/journal/regenerate?name=%s&issue=%s">&gt;regenerate</a></p>
</td>
<tr>
''' % ((issue==current_issue) and "background:#00FF00;" or "background:#F1F1F1;",
issue, (issue==next_issue_number) and "?" or current_publication,
"\n".join(['<p>%s : %s <a href="%s/journal/?name=%s&issue=%s&category=%s">&gt;edit</a></p>' %
(item[0], item[1],
- weburl, journal_name,
+ CFG_SITE_URL, journal_name,
issue, item[0]) for item in articles.iteritems()]),
(released_on==False) and
- '<em>not released</em><br/><a href="%s/journal/issue_control?name=%s">&gt;release now</a>' % (weburl, journal_name) or
+ '<em>not released</em><br/><a href="%s/journal/issue_control?name=%s">&gt;release now</a>' % (CFG_SITE_URL, journal_name) or
'released on: %s' % time.strftime("%d.%m.%Y", released_on),
(announced_on==False)
- and '<em>not announced</em><br/><a href="%s/journal/alert?name=%s&issue=%s">&gt;announce now</a>' % (weburl, journal_name, issue) or
- 'announced on: %s <br/><a href="%s/journal/alert?name=%s&issue=%s">&gt;re-announce</a>' % (time.strftime("%d.%m.%Y", announced_on), weburl, journal_name, issue),
+ and '<em>not announced</em><br/><a href="%s/journal/alert?name=%s&issue=%s">&gt;announce now</a>' % (CFG_SITE_URL, journal_name, issue) or
+ 'announced on: %s <br/><a href="%s/journal/alert?name=%s&issue=%s">&gt;re-announce</a>' % (time.strftime("%d.%m.%Y", announced_on), CFG_SITE_URL, journal_name, issue),
- weburl, journal_name, issue
+ CFG_SITE_URL, journal_name, issue
)
issue_boxes.append(issue_box)
body = '''
<table class="admin_wvar" width="95%%" cellspacing="0">
<tbody>
<tr>
<th class="adminheaderleft"></th>
<th class="adminheaderleft">Issue / Publication</th>
<th class="adminheaderleft">Articles</th>
<th class="adminheaderleft">Release / Announcement</th>
<th class="adminheaderleft">Cache Status</th>
<tr>
%s
</tbody>
</table>
<p><a href="%s/submit?doctype=BULBN">Submit a Breaking News</a></p>
<p><a href="%s/journal/feature_record?name=%s">Feature a Record</a></p>
<p align="right"><a href="%s/journal/?name=%s">&gt;Go to the Journal</a></p>
''' % ("\n".join([issue_box for issue_box in issue_boxes]),
- weburl, weburl, journal_name, weburl, journal_name)
+ CFG_SITE_URL, CFG_SITE_URL, journal_name, CFG_SITE_URL, journal_name)
return page(title=title, body=body)
diff --git a/modules/webjournal/lib/webjournal_utils.py b/modules/webjournal/lib/webjournal_utils.py
index b9600a8ce..7c2d45f99 100644
--- a/modules/webjournal/lib/webjournal_utils.py
+++ b/modules/webjournal/lib/webjournal_utils.py
@@ -1,1237 +1,1237 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Various utilities for WebJournal, e.g. config parser, etc.
"""
from invenio.bibformat_engine import BibFormatObject
from invenio.errorlib import register_exception
from invenio.search_engine import search_pattern
-from invenio.config import CFG_ETCDIR, weburl, CFG_SITE_ADMIN_EMAIL, CFG_CACHEDIR, CFG_SITE_LANG
+from invenio.config import CFG_ETCDIR, CFG_SITE_URL, CFG_SITE_ADMIN_EMAIL, CFG_CACHEDIR, CFG_SITE_LANG
from invenio.messages import gettext_set_language
from invenio.webpage import page
from invenio.dbquery import run_sql
from xml.dom import minidom
from urllib2 import urlopen
import time
import datetime
import re
import os
import cPickle
############################ MAPPING FUNCTIONS ################################
def get_order_dict_from_recid_list(list, issue_number):
"""
this is a centralized function that takes a list of recid's and brings it in
order using a centralized algorithm. this always has to be in sync with
the reverse function get_recid_from_order(order)
parameters:
list: a list of all recid's that should be brought into order
issue_number: the issue_number for which we are deriving the order
(this has to be one number)
returns:
ordered_records: a dictionary with the recids ordered by keys
"""
ordered_records = {}
for record in list:
temp_rec = BibFormatObject(record)
issue_numbers = temp_rec.fields('773__n')
order_number = temp_rec.fields('773__c')
# todo: the marc fields have to be set'able by some sort of config interface
n = 0
for temp_issue in issue_numbers:
if temp_issue == issue_number:
try:
order_number = int(order_number[n])
except:
# todo: Warning, record does not support numbering scheme
order_number = -1
n+=1
if order_number != -1:
try:
ordered_records[order_number] = record
except:
pass
# todo: Error, there are two records with the same order_number in the issue
else:
ordered_records[max(ordered_records.keys()) + 1] = record
return ordered_records
def get_records_in_same_issue_in_order(recid):
"""
"""
raise ("Not implemented yet.")
def get_recid_from_order(order, rule, issue_number):
"""
takes the order of a record in the journal as passed in the url arguments
and derives the recid using the current issue number and the record
rule for this kind of records.
parameters:
order: the order at which the record appears in the journal as passed
in the url
rule: the defining rule of the journal record category
issue_number: the issue number for which we are searching
returns:
recid: the recid of the ordered record
"""
# get the id list
all_records = list(search_pattern(p="%s and 773__n:%s" %
(rule, issue_number),
f="&action_search=Search"))
ordered_records = {}
for record in all_records:
temp_rec = BibFormatObject(record)
issue_numbers = temp_rec.fields('773__n')
order_number = temp_rec.fields('773__c')
# todo: fields for issue number and order number have to become generic
n = 0
for temp_issue in issue_numbers:
if temp_issue == issue_number:
try:
order_number = int(order_number[n])
except:
# todo: Warning, record does not support numbering scheme
order_number = -1
n+=1
if order_number != -1:
try:
ordered_records[order_number] = record
except:
pass
# todo: Error, there are two records with the same order_number in the issue
else:
ordered_records[max(ordered_records.keys()) + 1] = record
try:
recid = ordered_records[int(order)]
except:
pass
# todo: ERROR, numbering scheme inconsistency
return recid
# todo: move to a template
def please_login(req, journal_name, ln="en", title="", message="", backlink=""):
"""
"""
_ = gettext_set_language(ln)
if title == "":
title_out = _("Please login to perform this action.")
else:
title_out = title
if message == "":
message_out = _("In order to publish webjournal issues you must be logged \
in and be authorized for this kind of task. If you have a \
login, use the link \
below to login.")
else:
message_out = message
if backlink == "":
- backlink_out = "%s/journal/issue_control?name=%s" % (weburl, journal_name)
+ backlink_out = "%s/journal/issue_control?name=%s" % (CFG_SITE_URL, journal_name)
else:
backlink_out = backlink
title_msg = _("We need you to login")
body_out = '''<div style="text-align: center;">
<fieldset style="width:400px; margin-left: auto; margin-right: auto;background: url('%s/img/blue_gradient.gif') top left repeat-x;">
<legend style="color:#a70509;background-color:#fff;"><i>%s</i></legend>
<p style="text-align:center;">%s</p>
<br/>
<p><a href="%s/youraccount/login?referer=%s">Login</a></p>
<br/>
<div style="text-align:right;">Mail<a href="mailto:%s"> the Administrator.</a></div>
</fieldset>
</div>
- ''' % (weburl,
+ ''' % (CFG_SITE_URL,
title_msg,
message_out,
- weburl,
+ CFG_SITE_URL,
backlink_out,
CFG_SITE_ADMIN_EMAIL)
return page(title = title_out,
body = body_out,
description = "",
keywords = "",
language = ln,
req = req)
def get_rule_string_from_rule_list(rule_list, category):
"""
"""
i = 0
current_category_in_list = 0
for rule_string in rule_list:
category_from_config = rule_string.split(",")[0]
if category_from_config.lower() == category.lower():
current_category_in_list = i
i+=1
try:
rule_string = rule_list[current_category_in_list]
except:
rule_string = ""
# todo: exception
return rule_string
def get_category_from_rule_string(rule_string):
"""
"""
pass
def get_rule_string_from_category(category):
"""
"""
pass
######################## TIME / ISSUE FUNCTIONS ###############################
def get_monday_of_the_week(week_number, year):
"""
CERN Bulletin specific function that returns a string indicating the
Monday of each week as: Monday <dd> <Month> <Year>
"""
timetuple = issue_week_strings_to_times(['%s/%s' % (week_number, year), ])[0]
return time.strftime("%A %d %B %Y", timetuple)
def get_issue_number_display(issue_number, journal_name, language=CFG_SITE_LANG):
"""
Returns the display string for a given issue number.
"""
journal_id = get_journal_id(journal_name, language)
issue_display = run_sql("SELECT issue_display FROM jrnISSUE \
WHERE issue_number=%s AND id_jrnJOURNAL=%s", (issue_number,
journal_id))[0][0]
return issue_display
def get_current_issue_time(journal_name, language=CFG_SITE_LANG):
"""
Return the current issue of a journal as a time object.
"""
current_issue = get_current_issue(language, journal_name)
week_number = current_issue.split("/")[0]
year = current_issue.split("/")[1]
current_issue_time = issue_week_strings_to_times(['%s/%s' %
(week_number, year), ])[0]
return current_issue_time
def get_all_issue_weeks(issue_time, journal_name, language):
"""
Function that takes an issue_number, checks the DB for the issue_display
which can contain the other (update) weeks involved with this issue and
returns all issues in a list of timetuples (always for Monday of each
week).
"""
from invenio.webjournal_config import InvenioWebJournalIssueNotFoundDBError
journal_id = get_journal_id(journal_name)
issue_string = issue_times_to_week_strings([issue_time,])[0]
try:
issue_display = run_sql(
"SELECT issue_display FROM jrnISSUE WHERE issue_number=%s \
AND id_jrnJOURNAL=%s",
(issue_string, journal_id))[0][0]
except:
raise InvenioWebJournalIssueNotFoundDBError(language, journal_name,
issue_string)
issue_bounds = issue_display.split("/")[0].split("-")
year = issue_display.split("/")[1]
all_issue_weeks = []
if len(issue_bounds) == 2:
# is the year changing? -> "52-02/2008"
if int(issue_bounds[0]) > int(issue_bounds[1]):
# get everything from the old year
old_year_issues = []
low_bound_time = issue_week_strings_to_times(['%s/%s' %
(issue_bounds[0],
str(int(year)-1)), ])[0]
# if the year changes over the week we always take the higher year
low_bound_date = datetime.date(int(time.strftime("%Y", low_bound_time)),
int(time.strftime("%m", low_bound_time)),
int(time.strftime("%d", low_bound_time)))
week_counter = datetime.timedelta(weeks=1)
date = low_bound_date
# count up the weeks until you get to the new year
while date.year != int(year):
old_year_issues.append(date.timetuple())
#format = time.strftime("%W/%Y", date.timetuple())
date = date + week_counter
# get everything from the new year
new_year_issues = []
for i in range(1, int(issue_bounds[1])+1):
to_append = issue_week_strings_to_times(['%s/%s' % (i, year),])[0]
new_year_issues.append(to_append)
all_issue_weeks += old_year_issues
all_issue_weeks += new_year_issues
else:
for i in range(int(issue_bounds[0]), int(issue_bounds[1])+1):
to_append = issue_week_strings_to_times(['%s/%s' % (i, year),])[0]
all_issue_weeks.append(to_append)
elif len(issue_bounds) == 1:
to_append = issue_week_strings_to_times(['%s/%s' %
(issue_bounds[0], year),])[0]
all_issue_weeks.append(to_append)
else:
return False
return all_issue_weeks
def count_down_to_monday(current_time):
"""
Takes a timetuple and counts it down to the next monday and returns
this time.
"""
next_monday = datetime.date(int(time.strftime("%Y", current_time)),
int(time.strftime("%m", current_time)),
int(time.strftime("%d", current_time)))
counter = datetime.timedelta(days=-1)
while next_monday.weekday() != 0:
next_monday = next_monday + counter
return next_monday.timetuple()
def get_next_journal_issues(current_issue_time, journal_name,
language=CFG_SITE_LANG, number=2):
"""
Returns the <number> next issue numbers from the current_issue_time.
"""
#now = '%s-%s-%s 00:00:00' % (int(time.strftime("%Y", current_issue_time)),
# int(time.strftime("%m", current_issue_time)),
# int(time.strftime("%d", current_issue_time)))
#
now = datetime.date(int(time.strftime("%Y", current_issue_time)),
int(time.strftime("%m", current_issue_time)),
int(time.strftime("%d", current_issue_time)))
week_counter = datetime.timedelta(weeks=1)
date = now
next_issues = []
for i in range(1, number+1):
date = date + week_counter
#date = run_sql("SELECT %s + INTERVAL 1 WEEK", (date,))[0][0]
#date_formated = time.strptime(date, "%Y-%m-%d %H:%M:%S")
#raise '%s %s' % (repr(now), repr(date_formated))
next_issues.append(date.timetuple())
#next_issues.append(date_formated)
return next_issues
def issue_times_to_week_strings(issue_times, language=CFG_SITE_LANG):
"""
Function that approaches a correct python time to MySQL time week string
conversion by looking up and down the time horizon and always rechecking
the python time with the mysql result until a week string match is found.
"""
issue_strings = []
for issue in issue_times:
# do the initial pythonic week view
week = time.strftime("%W/%Y", issue)
week += " Monday"
Limit = 5
counter = 0
success = False
# try going up 5
while success == False and counter <= Limit:
counter += 1
success = get_consistent_issue_week(issue, week)
if success == False:
week = count_week_string_up(week)
else:
break
# try going down 5
counter = 0
while success == False and counter <= Limit:
counter += 1
success = get_consistent_issue_week(issue, week)
if success == False:
week = count_week_string_down(week)
else:
break
from webjournal_config import InvenioWebJournalReleaseDBError
if success == False:
raise InvenioWebJournalReleaseDBError(language)
#check_for_time = run_sql("SELECT STR_TO_DATE(%s, %s)",
# (week, conversion_rule))[0][0]
#while (issue != check_for_time.timetuple()):
# week = str(int(week.split("/")[0]) + 1) + "/" + week.split("/")[1]
# if week[1] == "/":
# week = "0" + week
# #raise repr(week)
# check_for_time = run_sql("SELECT STR_TO_DATE(%s, %s)",
# (week, conversion_rule))[0][0]
issue_strings.append(week.split(" ")[0])
return issue_strings
def count_week_string_up(week):
"""
Function that takes a week string representation and counts it up by one.
"""
week_nr = week.split("/")[0]
year = week.split("/")[1]
if week_nr == "53":
week_nr = "01"
year = str(int(year) + 1)
else:
week_nr = str(int(week_nr) + 1)
if len(week_nr) == 1:
week_nr = "0" + week_nr
return "%s/%s" % (week_nr, year)
def count_week_string_down(week):
"""
Function that takes a week string representation and counts it down by one.
"""
week_nr = week.split("/")[0]
year = week.split("/")[1]
if week_nr == "01":
week_nr = "53"
year = str(int(year)-1)
else:
week_nr = str(int(week_nr)-1)
if len(week_nr) == 1:
week_nr = "0" + week_nr
return "%s/%s" % (week_nr, year)
def get_consistent_issue_week(issue_time, issue_week):
"""
This is the central consistency function between our Python and MySQL dates.
We use mysql times because of a bug in Scientific Linux that does not allow
us to reconvert a week number to a timetuple.
The function takes a week string, e.g. "02/2008" and its according timetuple
from our functions. Then it retrieves the mysql timetuple for this week and
compares the two times. If they are equal our times are consistent, if not,
we return False and some function should try to approach a consisten result
(see example in issue_times_to_week_strings()).
"""
conversion_rule = '%v/%x %W'
mysql_repr = run_sql("SELECT STR_TO_DATE(%s, %s)",
(issue_week, conversion_rule))[0][0]
if mysql_repr.timetuple() == issue_time:
return issue_week
else:
return False
def issue_week_strings_to_times(issue_weeks, language=CFG_SITE_LANG):
"""
Converts a list of issue week strings (WW/YYYY) to python time objects.
"""
issue_times = []
for issue in issue_weeks:
week_number = issue.split("/")[0]
year = issue.split("/")[1]
to_convert = '%s/%s Monday' % (year, week_number)
conversion_rule = '%x/%v %W'
result = run_sql("SELECT STR_TO_DATE(%s, %s)",
(to_convert, conversion_rule))[0][0]
issue_times.append(result.timetuple())
return issue_times
def release_journal_update(update_issue, journal_name, language=CFG_SITE_LANG):
"""
Releases an update to a journal.
"""
journal_id = get_journal_id(journal_name, language)
run_sql("UPDATE jrnISSUE set date_released=NOW() \
WHERE issue_number=%s \
AND id_jrnJOURNAL=%s", (update_issue,
journal_id))
def sort_by_week_number(x, y):
"""
Sorts a list of week numbers.
"""
year_x = x.split("/")[1]
year_y = y.split("/")[1]
if cmp(year_x, year_y) != 0:
return cmp(year_x, year_y)
else:
week_x = x.split("/")[0]
week_y = y.split("/")[0]
return cmp(week_x, week_y)
def release_journal_issue(publish_issues, journal_name, language=CFG_SITE_LANG):
"""
Releases a new issue.
"""
journal_id = get_journal_id(journal_name, language)
if len(publish_issues) > 1:
publish_issues.sort(sort_by_week_number)
low_bound = publish_issues[0]
high_bound = publish_issues[-1]
issue_display = '%s-%s/%s' % (low_bound.split("/")[0],
high_bound.split("/")[0],
high_bound.split("/")[1])
# remember convention: if we are going over a new year, take the higher
else:
issue_display = publish_issues[0]
# produce the DB lines
for publish_issue in publish_issues:
run_sql("INSERT INTO jrnISSUE (id_jrnJOURNAL, issue_number, issue_display) \
VALUES(%s, %s, %s)", (journal_id,
publish_issue,
issue_display))
# set first issue to published
release_journal_update(publish_issues[0], journal_name, language)
def delete_journal_issue(issue, journal_name, language=CFG_SITE_LANG):
"""
Deletes an issue from the DB.
"""
journal_id = get_journal_id(journal_name, language)
run_sql("DELETE FROM jrnISSUE WHERE issue_number=%s \
AND id_jrnJOURNAL=%s",(issue, journal_id))
def was_alert_sent_for_issue(issue, journal_name, language):
"""
"""
journal_id = get_journal_id(journal_name, language)
date_announced = run_sql("SELECT date_announced FROM jrnISSUE \
WHERE issue_number=%s \
AND id_jrnJOURNAL=%s", (issue, journal_id))[0][0]
if date_announced == None:
return False
else:
return date_announced.timetuple()
def update_DB_for_alert(issue, journal_name, language):
"""
"""
journal_id = get_journal_id(journal_name, language)
run_sql("UPDATE jrnISSUE set date_announced=NOW() \
WHERE issue_number=%s \
AND id_jrnJOURNAL=%s", (issue,
journal_id))
def get_number_of_articles_for_issue(issue, journal_name, language=CFG_SITE_LANG):
"""
Function that returns a dictionary with all categories and number of
articles in each category.
"""
config_strings = get_xml_from_config(["rule",], journal_name)
rule_list = config_strings["rule"]
all_articles = {}
for rule in rule_list:
category_name = rule.split(",")[0]
if issue[0] == "0" and len(issue) == 7:
week_nr = issue.split("/")[0]
year = issue.split("/")[1]
issue_nr_alternative = "%s/%s" % (week_nr[1], year)
all_records_of_a_type = list(search_pattern(p='65017a:"%s" and 773__n:%s' %
(category_name, issue),
f="&action_search=Search"))
all_records_of_a_type += list(search_pattern(p='65017a:"%s" and 773__n:%s' %
(category_name, issue_nr_alternative),
f="&action_search=Search"))
else:
all_records_of_a_type = list(search_pattern(p='65017a:"%s" and 773__n:%s' %
(category_name, issue),
f="&action_search=Search"))
all_articles[category_name] = len(all_records_of_a_type)
return all_articles
def get_list_of_issues_for_publication(publication):
"""
Takes a publication string, e.g. 23-24/2008 and splits it down to a list
of single issues.
"""
year = publication.split("/")[1]
issues_string = publication.split("/")[0]
bounds = issues_string.split("-")
issues = []
if len(bounds) == 2:
low_bound = issues_string.split("-")[0]
high_bound = issues_string.split("-")[1]
if int(low_bound) < int(high_bound):
for i in range(int(low_bound), int(high_bound)+1):
issue_nr = str(i)
if len(issue_nr) == 1:
issue_nr = "0" + issue_nr
issues.append("%s/%s" % (issue_nr, year))
else:
for i in range(int(low_bound), 53+1):
issue_nr = str(i)
if len(issue_nr) == 1:
issue_nr = "0" + issue_nr
issues.append("%s/%s" % (issue_nr, str(int(year)-1)))
for i in range(1, int(high_bound) + 1):
issue_nr = str(i)
if len(issue_nr) == 1:
issue_nr = "0" + issue_nr
issues.append("%s/%s" % (issue_nr, year))
else:
issues.append("%s/%s" % (bounds[0], year))
return issues
def get_release_time(issue, journal_name, language=CFG_SITE_LANG):
"""
Gets the date at which an issue was released from the DB.
"""
journal_id = get_journal_id(journal_name, language)
try:
release_date = run_sql("SELECT date_released FROM jrnISSUE \
WHERE issue_number=%s AND id_jrnJOURNAL=%s",
(issue, journal_id))[0][0]
except:
return False
if release_date == None:
return False
else:
return release_date.timetuple()
def get_announcement_time(issue, journal_name, language=CFG_SITE_LANG):
"""
Get the date at which an issue was announced through the alert system.
"""
journal_id = get_journal_id(journal_name, language)
try:
announce_date = run_sql("SELECT date_announced FROM jrnISSUE \
WHERE issue_number=%s AND id_jrnJOURNAL=%s",
(issue, journal_id))[0][0]
except:
return False
if announce_date == None:
return False
else:
return announce_date.timetuple()
######################## GET DEFAULTS FUNCTIONS ###############################
def get_journal_id(journal_name, language=CFG_SITE_LANG):
"""
Get the id for this journal from the DB.
"""
from invenio.webjournal_config import InvenioWebJournalJournalIdNotFoundDBError
try:
journal_id = run_sql("SELECT id FROM jrnJOURNAL WHERE name=%s",
(journal_name,))[0][0]
except:
raise InvenioWebJournalJournalIdNotFoundDBError(language, journal_name)
return journal_id
def guess_journal_name(language):
"""
tries to take a guess what a user was looking for on the server if not
providing a name for the journal.
if there is only one journal on the server, returns the name of which,
otherwise redirects to a list with possible journals.
"""
from invenio.webjournal_config import InvenioWebJournalNoJournalOnServerError
from invenio.webjournal_config import InvenioWebJournalNoNameError
all_journals = run_sql("SELECT * FROM jrnJOURNAL ORDER BY id")
if len(all_journals) == 0:
raise InvenioWebJournalNoJournalOnServerError(language)
elif len(all_journals) == 1:
return all_journals[0][1]
else:
raise InvenioWebJournalNoNameError(language)
def get_current_issue(language, journal_name):
"""
Returns the current issue of a journal as a string.
"""
journal_id = get_journal_id(journal_name, language)
try:
current_issue = run_sql("SELECT issue_number FROM jrnISSUE \
WHERE date_released <= NOW() AND id_jrnJOURNAL=%s \
ORDER BY date_released DESC LIMIT 1", (journal_id,))[0][0]
except:
# start the first journal ever with the day of today
current_issue = time.strftime("%W/%Y", time.localtime())
run_sql("INSERT INTO jrnISSUE \
(id_jrnJOURNAL, issue_number, issue_display) \
VALUES(%s, %s, %s)", (journal_id,
current_issue,
current_issue))
return current_issue
def get_current_publication(journal_name, current_issue, language=CFG_SITE_LANG):
"""
Returns the current publication string (current issue + updates).
"""
journal_id = get_journal_id(journal_name, language)
current_publication = run_sql("SELECT issue_display FROM jrnISSUE \
WHERE issue_number=%s AND \
id_jrnJOURNAL=%s",
(current_issue, journal_id))[0][0]
return current_publication
def get_xml_from_config(xpath_list, journal_name):
"""
wrapper for minidom.getElementsByTagName()
Takes a list of string expressions and a journal name and searches the config
file of this journal for the given xpath queries. Returns a dictionary with
a key for each query and a list of string (innerXml) results for each key.
Has a special field "config_fetching_error" that returns an error when
something has gone wrong.
"""
# get and open the config file
results = {}
config_path = '%s/webjournal/%s/config.xml' % (CFG_ETCDIR, journal_name)
config_file = minidom.Document
try:
config_file = minidom.parse("%s" % config_path)
except:
#todo: raise exception "error: no config file found"
results["config_fetching_error"] = "could not find config file"
return results
for xpath in xpath_list:
result_list = config_file.getElementsByTagName(xpath)
results[xpath] = []
for result in result_list:
try:
result_string = result.firstChild.toxml(encoding="utf-8")
except:
# WARNING, config did not have a value
continue
results[xpath].append(result_string)
return results
def parse_url_string(req):
"""
centralized function to parse any url string given in webjournal.
returns:
args: all arguments in dict form
"""
args = {}
# first get what you can from the argument string
try:
argument_string = req.args#"name=CERNBulletin&issue=22/2007"#req.args
except:
argument_string = ""
try:
arg_list = argument_string.split("&")
except:
# no arguments
arg_list = []
for entry in arg_list:
try:
key = entry.split("=")[0]
except KeyError:
# todo: WARNING, could not parse one argument
continue
try:
val = entry.split("=")[1]
except:
# todo: WARNING, could not parse one argument
continue
try:
args[key] = val
except:
# todo: WARNING, argument given twice
continue
# secondly try to get default arguments
try:
for entry in req.journal_defaults.keys():
try:
args[entry] = req.journal_defaults[entry]
except:
# todo: Error, duplicate entry from args and defaults
pass
except:
# no defaults
pass
return args
######################## EMAIL HELPER FUNCTIONS ###############################
def createhtmlmail (html, text, subject, toaddr):
"""
Create a mime-message that will render HTML in popular
MUAs, text in better ones.
"""
import MimeWriter
import mimetools
import cStringIO
out = cStringIO.StringIO() # output buffer for our message
htmlin = cStringIO.StringIO(html)
txtin = cStringIO.StringIO(text)
writer = MimeWriter.MimeWriter(out)
#
# set up some basic headers... we put subject here
# because smtplib.sendmail expects it to be in the
# message body
#
writer.addheader("Subject", subject)
writer.addheader("MIME-Version", "1.0")
writer.addheader("To", toaddr)
#
# start the multipart section of the message
# multipart/alternative seems to work better
# on some MUAs than multipart/mixed
#
writer.startmultipartbody("alternative")
writer.flushheaders()
#
# the plain text section
#
subpart = writer.nextpart()
subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
#pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
pout = subpart.startbody("text/plain", [("charset", 'utf-8')])
mimetools.encode(txtin, pout, 'quoted-printable')
txtin.close()
#
# start the html subpart of the message
#
subpart = writer.nextpart()
subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
txtin.close()
#
# start the html subpart of the message
#
subpart = writer.nextpart()
subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
#
# returns us a file-ish object we can write to
#
#pout = subpart.startbody("text/html", [("charset", 'us-ascii')])
pout = subpart.startbody("text/html", [("charset", 'utf-8')])
mimetools.encode(htmlin, pout, 'quoted-printable')
htmlin.close()
#
# Now that we're done, close our writer and
# return the message body
#
writer.lastpart()
msg = out.getvalue()
out.close()
print msg
return msg
def put_css_in_file(html_message, journal_name):
"""
Takes an external css file and puts all the content of it in the head
of an HTML file in style tags. (Used for HTML emails)
"""
config_strings = get_xml_from_config(["screen"], journal_name)
try:
css_path = config_strings["screen"][0]
except:
register_exception(req=None,
suffix="No css file for journal %s. Is this right?"
% journal_name)
return
- css_file = urlopen('%s/%s' % (weburl, css_path))
+ css_file = urlopen('%s/%s' % (CFG_SITE_URL, css_path))
css = css_file.read()
css = make_full_paths_in_css(css, journal_name)
html_parted = html_message.split("</head>")
if len(html_parted) > 1:
html = '%s<style type="text/css">%s</style></head>%s' % (html_parted[0],
css,
html_parted[1])
else:
html_parted = html_message.split("<html>")
if len(html_parted) > 1:
html = '%s<html><head><style type="text/css">%s</style></head>%s' % (html_parted[0],
css,
html_parted[1])
else:
return
return html
def make_full_paths_in_css(css, journal_name):
"""
"""
url_pattern = re.compile('''url\(["']?\s*(?P<url>\S*)\s*["']?\)''',
re.DOTALL)
url_iter = url_pattern.finditer(css)
rel_to_full_path = {}
for url in url_iter:
url_string = url.group("url")
url_string = url_string.replace("\"", "")
url_string = url_string.replace("\'", "")
if url_string[:6] != "http://":
- rel_to_full_path[url_string] = '"%s/img/%s/%s"' % (weburl,
+ rel_to_full_path[url_string] = '"%s/img/%s/%s"' % (CFG_SITE_URL,
journal_name,
url_string)
for url in rel_to_full_path.keys():
css = css.replace(url, rel_to_full_path[url])
return css
############################ CACHING FUNCTIONS ################################
def cache_index_page(html, journal_name, category, issue, ln):
"""
Caches the index page main area of a Bulletin
(right hand menu cannot be cached)
"""
issue = issue.replace("/", "_")
category = category.replace(" ", "")
if not (os.path.isdir('%s/webjournal/%s' % (CFG_CACHEDIR, journal_name) )):
os.makedirs('%s/webjournal/%s' % (CFG_CACHEDIR, journal_name))
cached_file = open('%s/webjournal/%s/%s_index_%s_%s.html' % (CFG_CACHEDIR,
journal_name,
issue, category,
ln), "w")
cached_file.write(html)
cached_file.close()
def get_index_page_from_cache(journal_name, category, issue, ln):
"""
Function to get an index page from the cache.
False if not in cache.
"""
issue = issue.replace("/", "_")
category = category.replace(" ", "")
try:
cached_file = open('%s/webjournal/%s/%s_index_%s_%s.html'
% (CFG_CACHEDIR, journal_name, issue, category, ln)).read()
except:
return False
return cached_file
def cache_article_page(html, journal_name, category, recid, issue, ln):
"""
Caches an article view of a journal.
"""
issue = issue.replace("/", "_")
category = category.replace(" ", "")
if not (os.path.isdir('%s/webjournal/%s' % (CFG_CACHEDIR, journal_name) )):
os.makedirs('%s/webjournal/%s' % (CFG_CACHEDIR, journal_name))
cached_file = open('%s/webjournal/%s/%s_article_%s_%s_%s.html'
% (CFG_CACHEDIR, journal_name, issue, category, recid, ln),
"w")
cached_file.write(html)
cached_file.close()
def get_article_page_from_cache(journal_name, category, recid, issue, ln):
"""
Gets an article view of a journal from cache.
False if not in cache.
"""
issue = issue.replace("/", "_")
category = category.replace(" ", "")
try:
cached_file = open('%s/webjournal/%s/%s_article_%s_%s_%s.html'
% (CFG_CACHEDIR, journal_name, issue, category, recid, ln)).read()
except:
return False
return cached_file
def clear_cache_for_article(journal_name, category, recid, issue):
"""
Resets the cache for an article (e.g. after an article has been modified)
"""
issue = issue.replace("/", "_")
category = category.replace(" ", "")
# try to delete the article cached file
try:
os.remove('%s/webjournal/%s/%s_article_%s_%s_en.html' %
(CFG_CACHEDIR, journal_name, issue, category, recid))
except:
pass
try:
os.remove('%s/webjournal/%s/%s_article_%s_%s_fr.html' %
(CFG_CACHEDIR, journal_name, issue, category, recid))
except:
pass
# delete the index page for the category
try:
os.remove('%s/webjournal/%s/%s_index_%s_en.html'
% (CFG_CACHEDIR, journal_name, issue, category))
except:
pass
try:
os.remove('%s/webjournal/%s/%s_index_%s_fr.html'
% (CFG_CACHEDIR, journal_name, issue, category))
except:
pass
# delete the entry in the recid_order_map
# todo: make this per entry
try:
os.remove('%s/webjournal/%s/%s_recid_order_map.dat'
% (CFG_CACHEDIR, journal_name, issue))
except:
pass
return True
def clear_cache_for_issue(journal_name, issue):
"""
clears the cache of a whole issue.
"""
issue = issue.replace("/", "_")
all_cached_files = os.listdir('%s/webjournal/%s/'
% (CFG_CACHEDIR, journal_name))
for cached_file in all_cached_files:
if cached_file[:7] == issue:
try:
os.remove('%s/webjournal/%s/%s'
% (CFG_CACHEDIR, journal_name, cached_file))
except:
return False
return True
def cache_recid_data_dict_CERNBulletin(recid, issue, rule, order):
"""
The CERN Bulletin has a specific recid data dict that is cached
using cPickle.
"""
issue = issue.replace("/", "_")
# get whats in there
if not os.path.isdir('%s/webjournal/CERNBulletin' % CFG_CACHEDIR):
os.makedirs('%s/webjournal/CERNBulletin' % CFG_CACHEDIR)
try:
temp_file = open('%s/webjournal/CERNBulletin/%s_recid_order_map.dat'
% (CFG_CACHEDIR, issue))
except:
temp_file = open('%s/webjournal/CERNBulletin/%s_recid_order_map.dat'
% (CFG_CACHEDIR, issue), "w")
try:
recid_map = cPickle.load(temp_file)
except:
recid_map = ""
temp_file.close()
# add new recid
if recid_map == "":
recid_map = {}
if not recid_map.has_key(rule):
recid_map[rule] = {}
recid_map[rule][order] = recid
# save back
temp_file = open('%s/webjournal/CERNBulletin/%s_recid_order_map.dat'
% (CFG_CACHEDIR, issue), "w")
cPickle.dump(recid_map, temp_file)
temp_file.close()
def get_cached_recid_data_dict_CERNBulletin(issue, rule):
"""
Function to restore from cache the dict Data Type that the CERN Bulletin
uses for mapping between the order of an article and its recid.
"""
issue = issue.replace("/", "_")
try:
temp_file = open('%s/webjournal/CERNBulletin/%s_recid_order_map.dat'
% (CFG_CACHEDIR, issue))
except:
return {}
try:
recid_map = cPickle.load(temp_file)
except:
return {}
try:
recid_dict = recid_map[rule]
except:
recid_dict = {}
return recid_dict
######################### CERN SPECIFIC FUNCTIONS #############################
def get_order_dict_from_recid_list_CERNBulletin(list, issue_number):
"""
special derivative of the get_order_dict_from_recid_list function that
extends the behavior insofar as too return a dictionary in which every
entry is a dict (there can be several number 1 articles) and every dict entry
is a tuple with an additional boolean to indicate if there is a graphical "new"
flag. the dict key on the second level is the upload time in epoch seconds.
e.g.
{1:{10349:(rec, true), 24792:(rec, false)}, 2:{736424:(rec,false)}, 24791:{1:(rec:false}}
the ordering inside an order number is given by upload date. so it is an ordering
1-level -> number
2-level -> date
"""
ordered_records = {}
for record in list:
temp_rec = BibFormatObject(record)
issue_numbers = temp_rec.fields('773__n')
order_number = temp_rec.fields('773__c')
try:
# upload_date = run_sql("SELECT modification_date FROM bibrec WHERE id=%s", (record, ))[0][0]
upload_date = run_sql("SELECT creation_date FROM bibrec WHERE id=%s", (record, ))[0][0]
except:
pass
#return repr(time.mktime(upload_date.timetuple()))
# todo: the marc fields have to be set'able by some sort of config interface
n = 0
for temp_issue in issue_numbers:
if temp_issue == issue_number:
try:
order_number = int(order_number[n])
except:
# todo: Warning, record does not support numbering scheme
order_number = -1
n+=1
if order_number != -1:
try:
if ordered_records.has_key(order_number):
ordered_records[order_number][int(time.mktime(upload_date.timetuple()))] = (record, True)
else:
ordered_records[order_number] = {int(time.mktime(upload_date.timetuple())):(record, False)}
except:
pass
# todo: Error, there are two records with the same order_number in the issue
else:
ordered_records[max(ordered_records.keys()) + 1] = record
return ordered_records
def get_recid_from_order_CERNBulletin(order, rule, issue_number):
"""
same functionality as get_recid_from_order above, but extends it for
the CERN Bulletin in a way so multiple entries for the first article are
possible.
parameters:
order: the order at which the record appears in the journal as passed
in the url
rule: the defining rule of the journal record category
issue_number: the issue number for which we are searching
returns:
recid: the recid of the ordered record
"""
# try to get it from cache
recid_dict = {}
recid_dict = get_cached_recid_data_dict_CERNBulletin(issue_number, rule)
if recid_dict.has_key(order):
recid = recid_dict[order]
return recid
alternative_issue_number = "00/0000"
# get the id list
if issue_number[0] == "0":
alternative_issue_number = issue_number[1:]
all_records = list(search_pattern(p="%s and 773__n:%s" %
(rule, issue_number),
f="&action_search=Search"))
all_records += list(search_pattern(p="%s and 773__n:%s" %
(rule, alternative_issue_number),
f="&action_search=Search"))
else:
all_records = list(search_pattern(p="%s and 773__n:%s" %
(rule, issue_number),
f="&action_search=Search"))
#raise repr(all_records)
ordered_records = {}
new_addition_records = []
for record in all_records:
temp_rec = BibFormatObject(record) # todo: refactor with get_fieldValues from search_engine
issue_numbers = temp_rec.fields('773__n')
order_number = temp_rec.fields('773__c')
#raise "%s:%s" % (repr(issue_numbers), repr(order_number))
# todo: fields for issue number and order number have to become generic
n = 0
for temp_issue in issue_numbers:
if temp_issue == issue_number or temp_issue == alternative_issue_number:
try:
order_number = int(order_number[n])
except:
register_exception(stream="warning", suffix="There \
was an article in the journal that does not support \
a numbering scheme")
order_number = -1000
n+=1
if order_number == -1000:
ordered_records[max(ordered_records.keys()) + 1] = record
elif order_number <= 1:
new_addition_records.append(record)
else:
try:
ordered_records[order_number] = record
except:
register_exception(stream='warning', suffix="There \
were double entries for an order in this journal.")
# process the CERN Bulletin specific new additions
if len(new_addition_records) > 1 and int(order) <= 1:
# if we are dealing with a new addition (order number smaller 1)
ordered_new_additions = {}
for record in new_addition_records:
#upload_date = run_sql("SELECT modification_date FROM bibrec WHERE id=%s", (record, ))[0][0]
upload_date = run_sql("SELECT creation_date FROM bibrec WHERE id=%s", (record, ))[0][0]
ordered_new_additions[int(time.mktime(upload_date.timetuple()))] = record
i = 1
while len(ordered_new_additions) > 0:
temp_key = pop_oldest_article_CERNBulletin(ordered_new_additions)
record = ordered_new_additions.pop(int(temp_key))
ordered_records[i] = record
i -=1
else:
# if we have only one record on 1 just push it through
ordered_records[1] = new_addition_records[0]
try:
recid = ordered_records[int(order)]
except:
register_exception()
cache_recid_data_dict_CERNBulletin(recid, issue_number, rule, order)
return recid
def pop_newest_article_CERNBulletin(news_article_dict):
"""
pop key of the most recent article (highest c-timestamp)
"""
keys = news_article_dict.keys()
keys.sort()
key = keys[len(keys)-1]
return key
def pop_oldest_article_CERNBulletin(news_article_dict):
"""
pop key of the oldest article (lowest c-timestamp)
"""
keys = news_article_dict.keys()
keys.sort()
key = keys[0]
return key
########################### REGULAR EXPRESSIONS ###############################
header_pattern = re.compile('<p\s*(align=justify)??>\s*<strong>(?P<header>.*?)</strong>\s*</p>')
para_pattern = re.compile('<p.*?>(?P<paragraph>.+?)</p>', re.DOTALL)
image_pattern = re.compile(r'''
(<a\s*href=["']?(?P<hyperlink>\S*)["']?>)?# get the link location for the image
\s*# after each tag we can have arbitrary whitespaces
<center># the image is always centered
\s*
<img\s*(class=["']imageScale["'])*?\s*src=(?P<image>\S*)\s*border=1\s*(/)?># getting the image itself
\s*
</center>
\s*
(</a>)?
(<br />|<br />|<br/>)*# the caption can be separated by any nr of line breaks
(
<b>
\s*
<i>
\s*
<center>(?P<caption>.*?)</center># getting the caption
\s*
</i>
\s*
</b>
)?''', re.DOTALL | re.VERBOSE | re.IGNORECASE )
#''',re.DOTALL | re.IGNORECASE | re.VERBOSE | re.MULTILINE)
# (<a\s*href=["']?(?P<hyperlink>\S*)["']?>)?\s*<center>\s*<img\s*(class=["']imageScale["'])*?\s*src=(?P<image>\S*)\s*border=1\s*(/)?>
# \s*</center>\s*(</a>)?(<br />|<br />|<br/>)*(<b>\s*<i>\s*<center>(?P<caption>.*?)</center>\s*</i>\s*</b>)?
#url(["']?(?P<url>\S*)["']?)
diff --git a/modules/webjournal/lib/webjournal_webinterface.py b/modules/webjournal/lib/webjournal_webinterface.py
index 7bced353a..20a7bdb50 100644
--- a/modules/webjournal/lib/webjournal_webinterface.py
+++ b/modules/webjournal/lib/webjournal_webinterface.py
@@ -1,625 +1,625 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebJournal Web Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import time
import os
import urllib
from urllib2 import urlopen
from email import message_from_string
from xml.dom import minidom
from mod_python import apache
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.access_control_engine import acc_authorize_action
-from invenio.config import weburl, CFG_WEBDIR, CFG_SITE_LANG, CFG_ETCDIR
+from invenio.config import CFG_SITE_URL, CFG_WEBDIR, CFG_SITE_LANG, CFG_ETCDIR
from invenio.webpage import page
from invenio.webuser import getUid
from invenio.urlutils import redirect_to_url
from invenio.errorlib import register_exception
from invenio.bibformat_engine import format_with_format_template, BibFormatObject
from invenio.search_engine import search_pattern
from webjournal_config import *
from invenio.webjournal_utils import get_recid_from_order, \
get_recid_from_order_CERNBulletin, \
parse_url_string, \
get_xml_from_config, \
please_login, \
get_current_issue, \
get_rule_string_from_rule_list, \
get_monday_of_the_week, \
cache_index_page, \
get_index_page_from_cache, \
get_article_page_from_cache, \
cache_article_page, \
clear_cache_for_issue
from invenio.webjournal_washer import wash_category, \
wash_issue_number, \
wash_journal_name, \
wash_journal_language, \
wash_article_number, \
wash_popup_type, \
wash_popup_record, \
wash_archive_date
from invenio.webjournal import perform_request_index, \
perform_request_article, \
perform_request_alert, \
perform_request_issue_control, \
perform_request_popup, \
perform_request_administrate, \
perform_request_search
from invenio.webjournal_templates import tmpl_webjournal_regenerate_success, \
tmpl_webjournal_regenerate_error, \
tmpl_webjournal_feature_record_interface, \
tmpl_webjournal_feature_record_success, \
tmpl_webjournal_alert_plain_text_CERNBulletin, \
tmpl_webjournal_alert_subject_CERNBulletin, \
tmpl_webjournal_alert_success_msg, \
tmpl_webjournal_alert_interface
class WebInterfaceJournalPages(WebInterfaceDirectory):
"""Defines the set of /journal pages."""
_exports = ['', 'article', 'issue_control', 'edit_article', 'alert', 'search',
'feature_record', 'popup', 'regenerate', 'administrate']
# profiler
#def index(self, req, form):
# import hotshot
# pr = hotshot.Profile('/tmp/journal_profile')
# return pr.runcall(self.index_bla, req=req, form=form)
def index(self, req, form):
"""
Index page.
Washes all the parameters and stores them in journal_defaults dict
for subsequent format_elements.
Passes on to logic function and eventually returns HTML.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'issue': (str, ""),
'category': (str, ""),
'ln': (str, "")}
)
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
issue_number = wash_issue_number(language, journal_name,
argd['issue'])
category = wash_category(language, argd['category'])
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoCurrentIssueError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalIssueNumberBadlyFormedError, e:
register_exception(req=req)
return e.user_box()
# the journal_defaults will be used by format elements that have no
# direct access to the params here, no more checking needed
req.journal_defaults = {"name": journal_name,
"issue": issue_number,
"ln": language,
"category": category}
html = perform_request_index(req, journal_name, issue_number, language,
category)
return html
def article(self, req, form):
"""
Article page.
Washes all the parameters and stores them in journal_defaults dict
for subsequent format_elements.
Passes on to logic function and eventually returns HTML.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'issue': (str, ""),
'category': (str, ""),
'number': (str, ""),
'ln': (str, ""),
'editor': (str, "False")}
)
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
issue_number = wash_issue_number(language, journal_name,
argd['issue'])
category = wash_category(language, argd['category'])
number = wash_article_number(language, argd['number'], journal_name)
editor = argd['editor']
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoCurrentIssueError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalIssueNumberBadlyFormedError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoArticleNumberError, e:
register_exception(req=req)
return e.user_box()
# automatically make all logged in users of cfgwebjournal editors
if acc_authorize_action(getUid(req), 'cfgwebjournal',
name="%s" % journal_name)[0] == 0:
editor = "True"
# the journal_defaults will be used by format elements that have no
# direct access to the params here, no more checking needed
req.journal_defaults = {"name" : journal_name,
"issue" : issue_number,
"ln" : language,
"category" : category,
"editor" : editor,
"number" : number}
html = perform_request_article(req, journal_name, issue_number,
language, category, number, editor)
return html
def edit_article(self, req, form):
"""
Simple url redirecter to toggle the edit mode on for article pages.
Checks if user is logged in.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'ln': (str, "")})
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
if acc_authorize_action(getUid(req), 'cfgwebjournal',
name="%s" % journal_name)[0] != 0:
return please_login(req, journal_name,
backlink='%s/journal/edit_article?%s'
- % (weburl, urllib.quote(req.args)))
+ % (CFG_SITE_URL, urllib.quote(req.args)))
# todo: use make_canonical_url from urlutils
redirect_to_url(req,
"%s/journal/article?%s&editor=True"
- % (weburl, req.args))
+ % (CFG_SITE_URL, req.args))
def administrate(self, req, form):
"""Index page."""
argd = wash_urlargd(form, {'name': (str, ""),
'ln': (str, "")
})
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
# check for user rights
if acc_authorize_action(getUid(req), 'cfgwebjournal',
name="%s" % journal_name)[0] != 0:
return please_login(req, journal_name,
backlink='%s/journal/administrate?name=%s'
- % (weburl, journal_name))
+ % (CFG_SITE_URL, journal_name))
return perform_request_administrate(journal_name, language)
def feature_record(self, req, form):
"""
Interface to feature a record. Will be saved in a flat file.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'recid': (str, "init"),
'featured': (str, "false"),
'url': (str, "init"),
'ln': (str, "")
})
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
recid = argd['recid']
url = argd['url']
featured = argd['featured']
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
# check for user rights
if acc_authorize_action(getUid(req), 'cfgwebjournal',
name="%s" % journal_name)[0] != 0:
return please_login(req, journal_name,
backlink='%s/journal/feature_record?name=%s'
- % (weburl, journal_name))
+ % (CFG_SITE_URL, journal_name))
if recid == "init":
return tmpl_webjournal_feature_record_interface(language,
journal_name)
else:
# todo: move to DB, maybe?
fptr = open('%s/webjournal/%s/featured_record'
% (CFG_ETCDIR, journal_name), "w")
fptr.write(recid)
fptr.write('\n')
fptr.write(argd['url'])
fptr.close()
return tmpl_webjournal_feature_record_success(language,
journal_name, recid)
def regenerate(self, req, form):
"""
Clears the cache for the issue given.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'issue': (str, ""),
'ln': (str, "")})
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
issue_number = wash_issue_number(language, journal_name,
argd['issue'])
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoCurrentIssueError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalIssueNumberBadlyFormedError, e:
register_exception(req=req)
return e.user_box()
# check for user rights
if acc_authorize_action(getUid(req), 'cfgwebjournal',
name="%s" % journal_name)[0] != 0:
return please_login(req, journal_name,
backlink='%s/journal/regenerate?name=%s'
- % (weburl, journal_name))
+ % (CFG_SITE_URL, journal_name))
# clear cache
success = clear_cache_for_issue(journal_name, issue_number)
if success:
return tmpl_webjournal_regenerate_success(language, journal_name,
issue_number)
else:
return tmpl_webjournal_regenerate_error(language, journal_name,
issue_number)
def alert(self, req, form):
"""
Alert system.
Sends an email alert, in HTML/PlainText or only PlainText to a mailing
list to alert for new journal releases.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'sent': (str, "False"),
'plainText': (str, u''),
'htmlMail': (str, ""),
'recipients': (str, ""),
'subject': (str, ""),
'ln': (str, ""),
'issue': (str, ""),
'force': (str, "False")})
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
issue_number = wash_issue_number(language, journal_name,
argd['issue'])
plain_text = argd['plainText']
html_mail = argd['htmlMail']
recipients = argd['recipients']
subject = argd['subject']
sent = argd['sent']
force = argd['force']
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoCurrentIssueError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalIssueNumberBadlyFormedError, e:
register_exception(req=req)
return e.user_box()
# login
if acc_authorize_action(getUid(req), 'cfgwebjournal',
name="%s" % journal_name)[0] != 0:
return please_login(req, journal_name,
backlink='%s/journal/alert?name=%s'
- % (weburl, journal_name))
+ % (CFG_SITE_URL, journal_name))
html = perform_request_alert(req, journal_name, issue_number, language,
sent, plain_text, subject, recipients,
html_mail, force)
return html
def issue_control(self, req, form):
"""
page that allows full control over creating, backtracing, adding to,
removing from issues.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'add': (str, ""),
'action_publish': (str, "cfg"),
'issue_number': (list, []),
'ln': (str, "")}
)
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
issue_numbers = []
for number in argd['issue_number']:
if number != "ww/YYYY":
issue_numbers.append(wash_issue_number(language,
journal_name,
number))
add = argd['add']
action = argd['action_publish']
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoCurrentIssueError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalIssueNumberBadlyFormedError, e:
register_exception(req=req)
return e.user_box()
# check user rights
if acc_authorize_action(getUid(req), 'cfgwebjournal',
name="%s" % journal_name)[0] != 0:
return please_login(req, journal_name)
html = perform_request_issue_control(req, journal_name, issue_numbers,
language, add, action)
return html
def popup(self, req, form):
"""
simple pass-through function that serves as a checker for popups.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'record': (str, ""),
'type': (str, ""),
'ln': (str, "")
})
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
type = wash_popup_type(language, argd['type'], journal_name)
record = wash_popup_record(language, argd['record'], journal_name)
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
except IvenioWebJournalNoPopupTypeError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoPopupRecordError, e:
register_exception(req=req)
return e.user_box()
html = perform_request_popup(req, language, journal_name, type, record)
return html
def search(self, req, form):
"""
Creates a temporary record containing all the information needed for
the search, meaning list of issue_numbers (timeframe), list of keywords,
list of categories to search in. In this way everything can be configured
globally in the config for the given webjournal and we can reuse the bibformat
for whatever search we want.
"""
argd = wash_urlargd(form, {'name': (str, ""),
'issue': (str, ""),
'archive_year': (str, ""),
'archive_issue': (str, ""),
'archive_select': (str, "False"),
'archive_date': (str, ""),
'archive_search': (str, "False"),
'ln': (str, CFG_SITE_LANG)})
try:
language = wash_journal_language(argd['ln'])
journal_name = wash_journal_name(language, argd['name'])
archive_issue = wash_issue_number(language, journal_name,
argd['archive_issue'])
archive_date = wash_archive_date(language, journal_name,
argd['archive_date'])
issue_number = wash_issue_number(language, journal_name,
argd['issue'])
archive_year = argd['archive_year']
archive_select = argd['archive_select']
archive_search = argd['archive_search']
except InvenioWebJournalNoJournalOnServerError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoNameError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalNoCurrentIssueError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalIssueNumberBadlyFormedError, e:
register_exception(req=req)
return e.user_box()
except InvenioWebJournalArchiveDateWronglyFormedError, e:
register_exception(req=req)
return e.user_box()
req.journal_defaults = {"name" : journal_name,
"issue" : issue_number,
"archive_year" : archive_year,
"archive_issue" : archive_issue,
"archive_select" : archive_select,
"archive_date" : archive_date,
"archive_search" : archive_search,
"language" : language,
}
html = perform_request_search(journal_name, language, req, issue_number,
archive_year, archive_issue,
archive_select, archive_date, archive_search)
return html
#if argd['name'] == "":
# register_exception(stream='warning',
# suffix="User tried to search without providing a journal name.")
# return webjournal_missing_info_box(req, title="Journal not found",
# msg_title="We don't know which journal you are looking for",
# msg='''You were looking for a journal without providing a name.
# Unfortunately we cannot know which journal you are looking for.
# Below you have a selection of journals that are available on this server.
# If you should find your journal there, just click the link,
# otherwise please contact the server admin and ask for existence
# of the journal you are looking for.''')
#else:
# journal_name = argd['name']
# config_strings = get_xml_from_config(["search", "issue_number", "rule"], journal_name)
# try:
# try:
# search_page_template = config_strings["search"][0]
# except:
# raise InvenioWebJournalNoArticleTemplateError(journal_name) # todo: new exception
# except InvenioWebJournalNoArticleTemplateError:
# register_exception(req=req)
# return webjournal_error_box(req,
# "Search Page Template not found",
# "Problem with the configuration for this journal.",
# "The system couldn't find the template for the search result page of this journal. This is a mandatory file and thus indicates that the journal was setup wrong or produced an internal error. If you are neither admin nor developer there is nothing you can do at this point, but send an email request. We apologize for the inconvenience.")
# search_page_template_path = 'webjournal/%s' % (search_page_template)
# try:
# try:
# issue_number_tag = config_strings["issue_number"][0]
# except KeyError:
# raise InvenioWebJournalNoIssueNumberTagError(journal_name)
# except InvenioWebJournalNoIssueNumberTagError:
# register_exception(req=req)
# return webjournal_error_box(req,
# title="No Issues",
# title_msg="Problem with the configuration of this journal",
# msg="The system couldn't find a definition for an issue numbering system. Issue numbers conrol the date of the publication you are seing. This indicates that there is an error in the setup of this journal or the Software itself. There is nothing you can do at the moment. If you wish you can send an inquiry to the responsible developers. We apologize for the inconvenience.")
# rule_list = config_strings["rule"]
# try:
# if len(rule_list) == 0:
# raise InvenioWebJournalNoArticleRuleError()
# except InvenioWebJournalNoArticleRuleError, e:
# register_exception(req=req)
# return webjournal_error_box(req,
# "No searchable Articles",
# "Problem with the configuration of this journal",
# "The system couldn't find the definitions for different article kinds (e.g. News, Sports, etc.). If there is nothing defined, nothing can be shown and it thus indicates that there is either a problem with the setup of this journal or in the Software itself. There is nothing you can do at this moment. If you wish you can send an inquiry to the responsible developers. We apologize for the inconvenience.")
# category_rules = []
# if argd['category'] == []:
# # append all categories
# for rule_string in rule_list:
# marc = {}
# marc["category"] = rule_string.split(",")[0]
# rule = rule_string.split(",")[1]
# marc_datafield = rule.split(":")[0]
# marc["rule_match"] = rule.split(":")[1]
# marc["marc_tag"] = marc_datafield[1:4]
# marc["marc_ind1"] = (marc_datafield[4] == "_") and " " or marc_datafield[4]
# marc["marc_ind2"] = (marc_datafield[5] == "_") and " " or marc_datafield[5]
# marc["marc_subfield"] = marc_datafield[6]
# category_rules.append(marc)
# else:
# # append only categories from the url param
# for single_category in argd['category']:
# rule_string = get_rule_string_from_rule_list(rule_list, single_category)
# marc = {}
# marc["category"] = rule_string.split(",")[0]
# rule = rule_string.split(",")[1]
# marc_datafield = rule.split(":")[0]
# marc["rule_match"] = rule.split(":")[1]
# marc["marc_tag"] = marc_datafield[1:4]
# marc["marc_ind1"] = (marc_datafield[4] == "_") and " " or marc_datafield[4]
# marc["marc_ind2"] = (marc_datafield[5] == "_") and " " or marc_datafield[5]
# marc["marc_subfield"] = marc_datafield[6]
# category_rules.append(marc)
#
# category_fields = "\n".join(['''
# <datafield tag="%s" ind1="%s" ind2="%s">
# <subfield code="%s">%s</subfield>
# </datafield>
# ''' % (marc["marc_tag"],
# marc["marc_ind1"],
# marc["marc_ind2"],
# marc["marc_subfield"],
# marc["rule_match"]) for marc in category_rules])
#
# issue_number_fields = "\n".join(['''
# <datafield tag="%s" ind1="%s" ind2="%s">
# <subfield code="%s">%s</subfield>
# </datafield>
# ''' % (issue_number_tag[:3],
# (issue_number_tag[3] == "_") and " " or issue_number_tag[3],
# (issue_number_tag[4] == "_") and " " or issue_number_tag[4],
# issue_number_tag[5],
# issue_number) for issue_number in argd['issue']])
#
# temp_marc = '''<record>
# <controlfield tag="001">0</controlfield>
# %s
# %s
# </record>''' % (issue_number_fields, category_fields)
#
#
# # create a record and get HTML back from bibformat
# bfo = BibFormatObject(0, ln=argd['ln'], xml_record=temp_marc, req=req) # pass 0 for rn, we don't need it
# html_out = format_with_format_template(search_page_template_path, bfo)[0]
#
# #perform_request_search(cc="News Articles", p="families and 773__n:23/2007")
# #cc = argd['category']
# #p = keyword
# #for issue_number in argd['issue_number']:
# # p += " and 773__n:%s" % issue_number
# ## todo: issue number tag generic from config
# #results = perform_request_search(cc=cc, p=p)
#
# return html_out
diff --git a/modules/webmessage/doc/admin/webmessage-admin-guide.webdoc b/modules/webmessage/doc/admin/webmessage-admin-guide.webdoc
index 2bc19a9eb..64de6bf96 100644
--- a/modules/webmessage/doc/admin/webmessage-admin-guide.webdoc
+++ b/modules/webmessage/doc/admin/webmessage-admin-guide.webdoc
@@ -1,25 +1,25 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebMessage Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>FIXME</p>
diff --git a/modules/webmessage/lib/webmessage_regression_tests.py b/modules/webmessage/lib/webmessage_regression_tests.py
index 2714102aa..bf3dcbf65 100644
--- a/modules/webmessage/lib/webmessage_regression_tests.py
+++ b/modules/webmessage/lib/webmessage_regression_tests.py
@@ -1,53 +1,53 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebMessage Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class WebMessageWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebMessage web pages whether they are up or not."""
def test_your_baskets_pages_availability(self):
"""webmessage - availability of Your Messages pages"""
- baseurl = weburl + '/yourmessages/'
+ baseurl = CFG_SITE_URL + '/yourmessages/'
_exports = ['', 'display', 'write', 'send', 'delete', 'delete_all',
'display_msg']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(WebMessageWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/webmessage/lib/webmessage_templates.py b/modules/webmessage/lib/webmessage_templates.py
index 36b1f9c3e..9f53e8cb5 100644
--- a/modules/webmessage/lib/webmessage_templates.py
+++ b/modules/webmessage/lib/webmessage_templates.py
@@ -1,688 +1,688 @@
# -*- coding: utf-8 -*-
## $Id$
##
## handles rendering of webmessage module
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" templates for webmessage module """
__revision__ = "$Id$"
from invenio.webmessage_mailutils import email_quoted_txt2html, email_quote_txt
from invenio.webmessage_config import CFG_WEBMESSAGE_STATUS_CODE, \
CFG_WEBMESSAGE_SEPARATOR, \
CFG_WEBMESSAGE_MAX_NB_OF_MESSAGES, \
CFG_WEBMESSAGE_RESULTS_FIELD
from invenio.dateutils import convert_datetext_to_dategui, \
datetext_default, \
create_day_selectbox, \
create_month_selectbox, \
create_year_selectbox
from invenio.urlutils import create_html_link, create_url
from invenio.htmlutils import escape_html
-from invenio.config import weburl, CFG_SITE_LANG
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG
from invenio.messages import gettext_set_language
from invenio.webuser import get_user_info
class Template:
"""Templates for WebMessage module"""
def tmpl_display_inbox(self, messages, infos=[], warnings=[], nb_messages=0, no_quota=0, ln=CFG_SITE_LANG):
"""
Displays a list of messages, with the appropriate links and buttons
@param messages: a list of tuples:
[(message_id,
user_from_id,
user_from_nickname,
subject,
sent_date,
status=]
@param infos: a list of informations to print on top of page
@param warnings: a list of warnings to display
@param nb_messages: number of messages user has
@param no_quota: 1 if user has no quota (admin) or 0 else.
@param ln: language of the page.
@return the list in HTML format
"""
_ = gettext_set_language(ln)
dummy = 0
inbox = self.tmpl_warning(warnings, ln)
inbox += self.tmpl_infobox(infos, ln)
if not(no_quota):
inbox += self.tmpl_quota(nb_messages, ln)
inbox += """
<table class="mailbox">
<thead class="mailboxheader">
<tr class="inboxheader">
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>
</thead>
<tfoot>
<tr style="height:0px;">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tfoot>
<tbody class="mailboxbody">""" % (_("Subject"),
_("Sender"),
_("Date"),
_("Action"))
if len(messages) == 0:
inbox += """
<tr class="mailboxrecord" style="height: 100px;">
<td colspan="4" style="text-align: center;">
<b>%s</b>
</td>
</tr>""" %(_("No messages"),)
for (msgid, id_user_from, user_from_nick,
subject, sent_date, status) in messages:
if not(subject):
subject = _("No subject")
subject_link = create_html_link(
- weburl + '/yourmessages/display_msg',
+ CFG_SITE_URL + '/yourmessages/display_msg',
{'msgid': msgid, 'ln': ln},
escape_html(subject))
if user_from_nick:
from_link = '%s'% (user_from_nick)
else:
from_link = get_user_info(id_user_from, ln)[2]
- action_link = create_html_link(weburl + '/yourmessages/write',
+ action_link = create_html_link(CFG_SITE_URL + '/yourmessages/write',
{'msg_reply_id': msgid, 'ln': ln},
_("Reply"))
action_link += ' '
- action_link += create_html_link(weburl + '/yourmessages/delete',
+ action_link += create_html_link(CFG_SITE_URL + '/yourmessages/delete',
{'msgid': msgid, 'ln': ln},
_("Delete"))
s_date = convert_datetext_to_dategui(sent_date, ln)
stat_style = ''
if (status == CFG_WEBMESSAGE_STATUS_CODE['NEW']):
stat_style = ' style="font-weight:bold"'
inbox += """
<tr class="mailboxrecord">
<td%s>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>""" %(stat_style, subject_link, from_link, s_date, action_link)
inbox += """
<tr class="mailboxfooter">
<td colspan="2">
<form name="newMessage" action="%(url_new)s" method="post">
<input type="submit" name="del_all" value="%(write_label)s" class="formbutton" />
</form>
</td>
<td>&nbsp;</td>
<td>
<form name="deleteAll" action="%(url_delete_all)s" method="post">
<input type="submit" name="del_all" value="%(delete_all_label)s" class="formbutton" />
</form>
</td>
</tr>
</tbody>
-</table>""" % {'url_new': create_url(weburl + '/yourmessages/write',
+</table>""" % {'url_new': create_url(CFG_SITE_URL + '/yourmessages/write',
{'ln': ln}),
- 'url_delete_all': create_url(weburl + '/yourmessages/delete_all',
+ 'url_delete_all': create_url(CFG_SITE_URL + '/yourmessages/delete_all',
{'ln': ln}),
'write_label': _("Write new message"),
'delete_all_label': _("Delete All")}
return inbox
def tmpl_write(self,
msg_to="", msg_to_group="",
msg_id=0,
msg_subject="", msg_body="",
msg_send_year=0, msg_send_month=0, msg_send_day=0,
warnings=[],
search_results_list=[],
search_pattern="",
results_field=CFG_WEBMESSAGE_RESULTS_FIELD['NONE'],
ln=CFG_SITE_LANG):
"""
Displays a writing message form with optional prefilled fields
@param msg_to: nick of the user (prefills the To: field)
@param msg_subject: subject of the message (prefills the Subject: field)
@param msg_body: body of the message (prefills the Message: field)
@param msg_send_year: prefills to year field
@param msg_send_month: prefills the month field
@param msg_send_day: prefills the day field
@param warnings: display warnings on top of page
@param search_results_list: list of tuples. (user/groupname, is_selected)
@param search_pattern: pattern used for searching
@param results_field: 'none', 'user' or 'group', see CFG_WEBMESSAGE_RESULTS_FIELD
@param ln: language of the form
@return the form in HTML format
"""
_ = gettext_set_language(ln)
write_box = self.tmpl_warning(warnings)
# escape forbidden character
msg_to = escape_html(msg_to)
msg_to_group = escape_html(msg_to_group)
msg_subject = escape_html(msg_subject)
search_pattern = escape_html(search_pattern)
to_select = self.tmpl_user_or_group_search(search_results_list,
search_pattern,
results_field,
ln)
if msg_id:
msg_subject = _("Re:") + " " + msg_subject
msg_body = email_quote_txt(msg_body)
write_box += """
<form name="write_message" action="%(url_form)s" method="post">
<div style="float: left; vertical-align:text-top; margin-right: 10px;">
<table class="mailbox">
<thead class="mailboxheader">
<tr>
<td class="inboxheader" colspan="2">
<table class="messageheader">
<tr>
<td class="mailboxlabel">%(to_label)s</td>
<td class="mailboxlabel">%(users_label)s</td>
<td style="width:100%%;">
<input class="mailboxinput" type="text" name="msg_to_user" value="%(to_users)s" />
</td>
</tr>
<tr>
<td class="mailboxlabel">&nbsp;</td>
<td class="mailboxlabel">%(groups_label)s</td>
<td style="width:100%%;">
<input class="mailboxinput" type="text" name="msg_to_group" value="%(to_groups)s" />
</td>
</tr>
<tr>
<td class="mailboxlabel">&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td class="mailboxlabel">%(subject_label)s</td>
<td colspan="2">
<input class="mailboxinput" type="text" name="msg_subject" value="%(subject)s" />
</td>
</tr>
</table>
</td>
</tr>
</thead>
<tfoot>
<tr>
<td style="height:0px" colspan="2"></td>
</tr>
</tfoot>
<tbody class="mailboxbody">
<tr>
<td class="mailboxlabel">%(message_label)s</td>
<td>
<textarea name="msg_body" rows="10" cols="50">"""
write_box_part2 = """
</td>
</tr>
<tr>
<td class="mailboxlabel">%(send_later_label)s</td>
<td>
%(day_field)s
%(month_field)s
%(year_field)s
</td>
</tr>
<tr class="mailboxfooter">
<td colspan="2" class="mailboxfoot">
<input type="submit" name="send_button" value="%(send_label)s" class="formbutton"/>
</td>
</tr>
</tbody>
</table>
</div>
<div style="vertical-align:top; margin-left: 5px; float: left;">
%(to_select)s
</div>
</form>
"""
write_box += "%(body)s</textarea>" + write_box_part2
day_field = create_day_selectbox('msg_send_day',
msg_send_day, ln)
month_field = create_month_selectbox('msg_send_month',
msg_send_month, ln)
year_field = create_year_selectbox('msg_send_year', -1, 10,
msg_send_year, ln)
write_box = write_box % {'url_form': create_url(
- weburl + '/yourmessages/send',
+ CFG_SITE_URL + '/yourmessages/send',
{'ln': ln}),
'to_users' : msg_to,
'to_groups': msg_to_group,
'subject' : msg_subject,
'body' : msg_body,
'ln': ln,
'day_field': day_field,
'month_field': month_field,
'year_field': year_field,
'to_select': to_select,
'send_later_label': _("Send later?"),
'to_label': _("To:"),
'users_label': _("Users"),
'groups_label': _("Groups"),
'subject_label': _("Subject:"),
'message_label': _("Message:"),
'send_label': _("SEND")}
return write_box
def tmpl_display_msg(self,
msg_id="",
msg_from_id="",
msg_from_nickname="",
msg_sent_to="",
msg_sent_to_group="",
msg_subject="",
msg_body="",
msg_sent_date="",
msg_received_date=datetext_default,
ln=CFG_SITE_LANG):
"""
Displays a given message
@param msg_id: id of the message
@param msg_from_id: id of user who sent the message
@param msg_from_nickname: nickname of the user who sent the message
@param msg_sent_to: list of users who received the message
(comma separated string)
@param msg_sent_to_group: list of groups who received the message
(comma separated string)
@param msg_subject: subject of the message
@param msg_body: body of the message
@param msg_sent_date: date at which the message was sent
@param msg_received_date: date at which the message had to be received
(if this argument != 0000-00-00 => reminder
@param ln: language of the page
@return the message in HTML format
"""
# load the right message language
_ = gettext_set_language(ln)
sent_to_link = ''
tos = msg_sent_to.split(CFG_WEBMESSAGE_SEPARATOR)
if (tos):
for to in tos[0:-1]:
to_display = to
if to.isdigit():
(dummy, to, to_display) = get_user_info(int(to), ln)
- sent_to_link += create_html_link(weburl + '/yourmessages/write',
+ sent_to_link += create_html_link(CFG_SITE_URL + '/yourmessages/write',
{'msg_to': to, 'ln': ln},
escape_html(to_display))
sent_to_link += CFG_WEBMESSAGE_SEPARATOR
to_display = tos[-1]
to = tos[-1]
if to.isdigit():
(dummy, to, to_display) = get_user_info(int(to), ln)
- sent_to_link += create_html_link(weburl + '/yourmessages/write',
+ sent_to_link += create_html_link(CFG_SITE_URL + '/yourmessages/write',
{'msg_to': to, 'ln': ln},
escape_html(to_display))
group_to_link = ""
groups = msg_sent_to_group.split(CFG_WEBMESSAGE_SEPARATOR)
if (groups):
for group in groups[0:-1]:
group_to_link += create_html_link(
- weburl + '/yourmessages/write',
+ CFG_SITE_URL + '/yourmessages/write',
{'msg_to_group': group, 'ln': ln},
escape_html(group))
group_to_link += CFG_WEBMESSAGE_SEPARATOR
group_to_link += create_html_link(
- weburl + '/yourmessages/write',
+ CFG_SITE_URL + '/yourmessages/write',
{'msg_to_group': groups[-1], 'ln': ln},
escape_html(groups[-1]))
# format the msg so that the '>>' chars give vertical lines
final_body = email_quoted_txt2html(msg_body)
out = """
<table class="mailbox" style="width: 70%%;">
<thead class="mailboxheader">
<tr>
<td class="inboxheader" colspan="2">
<table class="messageheader">
<tr>
<td class="mailboxlabel">%(from_label)s</td>
<td>%(from_link)s</td>
</tr>
<tr>
<td class="mailboxlabel">%(subject_label)s</td>
<td style="width: 100%%;">%(subject)s</td>
</tr>
<tr>
<td class="mailboxlabel">%(sent_label)s</td>
<td>%(sent_date)s</td>
</tr>"""
if (msg_received_date != datetext_default):
out += """
<tr>
<td class="mailboxlabel">%(received_label)s</td>
<td>%(received_date)s</td>
</tr>"""
out += """
<tr>
<td class="mailboxlabel">%(sent_to_label)s</td>
<td>%(sent_to)s</td>
</tr>"""
if (msg_sent_to_group != ""):
out += """
<tr>
<td class="mailboxlabel">%(groups_label)s</td>
<td>%(sent_to_group)s</td>
</tr>"""
out += """
</table>
</td>
</tr>
</thead>
<tfoot>
<tr>
<td></td>
<td></td>
</tr>
</tfoot>
<tbody class="mailboxbody">
<tr class="mailboxrecord">
<td colspan="2">%(body)s</td>
</tr>
<tr class="mailboxfooter">
<td>
<form name="reply" action="%(reply_url)s" method="post">
<input class="formbutton" name="reply" value="%(reply_but_label)s" type="submit" />
</form>
</td>
<td>
<form name="deletemsg" action="%(delete_url)s" method="post">
<input class="formbutton" name="delete" value="%(delete_but_label)s" type="submit" />
</form>
</td>
</tr>
</tbody>
</table>
"""
if msg_from_nickname:
msg_from_display = msg_from_nickname
else:
msg_from_display = get_user_info(msg_from_id, ln)[2]
msg_from_nickname = msg_from_id
return out % {'from_link': create_html_link(
- weburl + '/yourmessages/write',
+ CFG_SITE_URL + '/yourmessages/write',
{'msg_to': msg_from_nickname,
'ln': ln},
msg_from_display),
- 'reply_url': create_url(weburl + '/yourmessages/write',
+ 'reply_url': create_url(CFG_SITE_URL + '/yourmessages/write',
{'msg_reply_id': msg_id,
'ln': ln}),
- 'delete_url': create_url(weburl + '/yourmessages/delete',
+ 'delete_url': create_url(CFG_SITE_URL + '/yourmessages/delete',
{'msgid': msg_id,
'ln': ln}),
'sent_date' : convert_datetext_to_dategui(msg_sent_date, ln),
'received_date': convert_datetext_to_dategui(msg_received_date, ln),
'sent_to': sent_to_link,
'sent_to_group': group_to_link,
'subject' : msg_subject,
'body' : final_body,
'reply_to': msg_from_id,
'ln': ln,
'from_label':_("From:"),
'subject_label':_("Subject:"),
'sent_label': _("Sent on:"),
'received_label':_("Received on:"),
'sent_to_label': _("Sent to:"),
'groups_label': _("Sent to groups:"),
'reply_but_label':_("REPLY"),
'delete_but_label': _("DELETE")}
def tmpl_navtrail(self, ln=CFG_SITE_LANG, title=""):
"""
display the navtrail, e.g.:
Your account > Your messages > title
@param title: the last part of the navtrail. Is not a link
@param ln: language
return html formatted navtrail
"""
_ = gettext_set_language(ln)
- nav_h1 = create_html_link(weburl + '/youraccount/display',
+ nav_h1 = create_html_link(CFG_SITE_URL + '/youraccount/display',
{'ln': ln},
_("Your Account"),
{'class': 'navtrail'})
nav_h2 = ""
if (title != ""):
- nav_h2 += create_html_link(weburl + '/yourmessages/display',
+ nav_h2 += create_html_link(CFG_SITE_URL + '/yourmessages/display',
{'ln': ln},
_("Your Messages"),
{'class': 'navtrail'})
return nav_h1 + ' &gt; ' + nav_h2
return nav_h1
def tmpl_confirm_delete(self, ln=CFG_SITE_LANG):
"""
display a confirm message
@param ln: language
@return html output
"""
_ = gettext_set_language(ln)
out = """
<table class="confirmoperation">
<tr>
<td colspan="2" class="confirmmessage">
%(message)s
</td>
</tr>
<tr>
<td>
<form name="validate" action="delete_all" method="post">
<input type="hidden" name="confirmed" value="1" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="submit" value="%(yes_label)s" class="formbutton" />
</form>
</td>
<td>
<form name="cancel" action="display" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<input type="submit" value="%(no_label)s" class="formbutton" />
</form>
</td>
</tr>
</table>"""% {'message': _("Are you sure you want to empty your whole mailbox?"),
'ln':ln,
'yes_label': _("Yes"),
'no_label': _("No")}
return out
def tmpl_infobox(self, infos, ln=CFG_SITE_LANG):
"""Display len(infos) information fields
@param infos: list of strings
@param ln=language
@return html output
"""
_ = gettext_set_language(ln)
if not((type(infos) is list) or (type(infos) is tuple)):
infos = [infos]
infobox = ""
for info in infos:
infobox += "<div class=\"infobox\">"
lines = info.split("\n")
for line in lines[0:-1]:
infobox += line + "<br />\n"
infobox += lines[-1] + "</div><br />\n"
return infobox
def tmpl_warning(self, warnings, ln=CFG_SITE_LANG):
"""
Display len(warnings) warning fields
@param infos: list of strings
@param ln=language
@return html output
"""
_ = gettext_set_language(ln)
if not((type(warnings) is list) or (type(warnings) is tuple)):
warnings = [warnings]
warningbox = ""
if warnings != []:
warningbox = "<div class=\"warningbox\">\n <b>Warning:</b>\n"
for warning in warnings:
lines = warning.split("\n")
warningbox += " <p>"
for line in lines[0:-1]:
warningbox += line + " <br />\n"
warningbox += lines[-1] + " </p>"
warningbox += "</div><br />\n"
return warningbox
def tmpl_quota(self, nb_messages=0, ln=CFG_SITE_LANG):
"""
Display a quota bar.
@nb_messages: number of messages in inbox.
@ln=language
@return html output
"""
_ = gettext_set_language(ln)
quota = float(CFG_WEBMESSAGE_MAX_NB_OF_MESSAGES)
ratio = float(nb_messages) / quota
out = """
%(quota_label)s<br />
<div class="quotabox">
<div class="quotabar" style="width:%(width)ipx"></div>
</div>""" %{'quota_label' : _("Quota used: %(x_nb_used)i messages out of max. %(x_nb_total)i") % {'x_nb_used': nb_messages,
'x_nb_total': CFG_WEBMESSAGE_MAX_NB_OF_MESSAGES},
'width' : int(ratio * 200)
}
return out
def tmpl_multiple_select(self, select_name, tuples_list, ln=CFG_SITE_LANG):
"""displays a multiple select environment
@param tuples_list: a list of (value, isSelected) tuples
@return HTML output
"""
_ = gettext_set_language(ln)
if not((type(tuples_list) is list) or (type(tuples_list) is tuple)):
tuples_list = [tuples_list]
out = """
%s
<select name="%s" multiple="multiple" style="width:100%%">"""% (_("Please select one or more:"), select_name)
for (value, is_selected) in tuples_list:
out += ' <option value="%s"'% value
if is_selected:
out += " selected=\"selected\""
out += ">%s</option>\n"% value
out += "</select>\n"
return out
def tmpl_user_or_group_search(self,
tuples_list=[],
search_pattern="",
results_field=CFG_WEBMESSAGE_RESULTS_FIELD['NONE'],
ln=CFG_SITE_LANG):
"""
Display a box for user searching
@param tuples_list: list of (value, is_selected) tuples
@param search_pattern: text to display in this field
@param results_field: either 'none', 'user', 'group', look at CFG_WEBMESSAGE_RESULTS_FIELD
@param ln: language
@return html output
"""
_ = gettext_set_language(ln)
multiple_select = ''
add_button = ''
if results_field != CFG_WEBMESSAGE_RESULTS_FIELD['NONE'] and results_field in CFG_WEBMESSAGE_RESULTS_FIELD.values():
if len(tuples_list):
multiple_select = self.tmpl_multiple_select('names_selected', tuples_list)
add_button = '<input type="submit" name="%s" value="%s" class="nonsubmitbutton" />'
if results_field == CFG_WEBMESSAGE_RESULTS_FIELD['USER']:
add_button = add_button % ('add_user', _("Add to users"))
else:
add_button = add_button % ('add_group', _("Add to groups"))
else:
if results_field == CFG_WEBMESSAGE_RESULTS_FIELD['USER']:
multiple_select = _("No matching user")
else:
multiple_select = _("No matching group")
out = """
<table class="mailbox">
<thead class="mailboxheader">
<tr class ="inboxheader">
<td colspan="3">
%(title_label)s
<input type="hidden" name="results_field" value="%(results_field)s" />
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="3"></td></tr>
</tfoot>
<tbody class="mailboxbody">
<tr class="mailboxsearch">
<td>
<input type="text" name="search_pattern" value="%(search_pattern)s" />
</td>
<td>
<input type="submit" name="search_user" value="%(search_user_label)s" class="nonsubmitbutton" />
</td>
<td>
<input type="submit" name="search_group" value="%(search_group_label)s" class="nonsubmitbutton" />
</td>
</tr>
<tr class="mailboxresults">
<td colspan="2">
%(multiple_select)s
</td>
<td>
%(add_button)s
</td>
</tr>
</tbody>
</table>
"""
out = out % {'title_label' : _("Find users or groups:"),
'search_user_label' : _("Find a user"),
'search_group_label' : _("Find a group"),
'results_field' : results_field,
'search_pattern' : search_pattern,
'multiple_select' : multiple_select,
'add_button' : add_button}
return out
def tmpl_account_new_mail(self, nb_new_mail=0, total_mail=0, ln=CFG_SITE_LANG):
"""
display infos about inbox (used by myaccount.py)
@param nb_new_mail: number of new mails
@param ln: language
return: html output.
"""
_ = gettext_set_language(ln)
out = _("You have %(x_nb_new)s new messages out of %(x_nb_total)s messages") % \
{'x_nb_new': '<b>' + str(nb_new_mail) + '</b>',
- 'x_nb_total': create_html_link(weburl + '/yourmessages/',
+ 'x_nb_total': create_html_link(CFG_SITE_URL + '/yourmessages/',
{'ln': ln},
str(total_mail),
{},
False, False)}
return out + '.'
diff --git a/modules/webmessage/lib/webmessage_webinterface.py b/modules/webmessage/lib/webmessage_webinterface.py
index 6079f3576..5343e8aa4 100644
--- a/modules/webmessage/lib/webmessage_webinterface.py
+++ b/modules/webmessage/lib/webmessage_webinterface.py
@@ -1,374 +1,374 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebMessage web interface"""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
-from invenio.config import CFG_SITE_SECURE_URL, weburl, CFG_ACCESS_CONTROL_LEVEL_SITE
+from invenio.config import CFG_SITE_SECURE_URL, CFG_SITE_URL, CFG_ACCESS_CONTROL_LEVEL_SITE
from invenio.webuser import getUid, isGuestUser, page_not_authorized
from invenio.webmessage import perform_request_display, \
perform_request_display_msg, \
perform_request_write, \
perform_request_send, \
perform_request_write_with_search, \
perform_request_delete_msg, \
perform_request_delete_all, \
get_navtrail
from invenio.webmessage_config import CFG_WEBMESSAGE_RESULTS_FIELD
from invenio.webmessage_mailutils import escape_email_quoted_text
from invenio.webpage import page
from invenio.messages import gettext_set_language
from invenio.urlutils import redirect_to_url, make_canonical_urlargd
from invenio.htmlutils import escape_html
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
class WebInterfaceYourMessagesPages(WebInterfaceDirectory):
"""Defines the set of /yourmessages pages."""
_exports = ['', 'display', 'write', 'send', 'delete', 'delete_all',
'display_msg']
def index(self, req, form):
""" The function called by default
"""
- redirect_to_url(req, "%s/yourmessages/display?%s" % (weburl, req.args))
+ redirect_to_url(req, "%s/yourmessages/display?%s" % (CFG_SITE_URL, req.args))
def display(self, req, form):
"""
Displays the Inbox of a given user
@param ln: language
@return the page for inbox
"""
argd = wash_urlargd(form, {})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourmessages/display" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="yourmessages")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourmessages/display%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
_ = gettext_set_language(argd['ln'])
(body, errors, warnings) = perform_request_display(uid=uid,
ln=argd['ln'])
return page(title = _("Your Messages"),
body = body,
navtrail = get_navtrail(argd['ln']),
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
errors = errors,
warnings = warnings,
navmenuid = "yourmessages")
def write(self, req, form):
""" write(): interface for message composing
@param msg_reply_id: if this message is a reply to another, id of the
other
@param msg_to: if this message is not a reply, nickname of the user it
must be delivered to.
@param msg_to_group: name of group to send message to
@param ln: language
@return the compose page
"""
argd = wash_urlargd(form, {'msg_reply_id': (int, 0),
'msg_to': (str, ""),
'msg_to_group': (str, "")})
# Check if user is logged
uid = getUid(req)
_ = gettext_set_language(argd['ln'])
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourmessages/write" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="yourmessages")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourmessages/write%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
# Request the composing page
(body, errors, warnings) = perform_request_write(
uid=uid,
msg_reply_id=argd['msg_reply_id'],
msg_to=argd['msg_to'],
msg_to_group=argd['msg_to_group'],
ln=argd['ln'])
title = _("Write a message")
return page(title = title,
body = body,
navtrail = get_navtrail(argd['ln'], title),
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
errors = errors,
warnings = warnings,
navmenuid = "yourmessages")
def send(self, req, form):
"""
Sends the message
@param msg_to_user: comma separated usernames (str)
@param msg_to_group: comma separated groupnames (str)
@param msg_subject: message subject (str)
@param msg_body: message body (string)
@param msg_send_year: year to send this message on (int)
@param_msg_send_month: month to send this message on (int)
@param_msg_send_day: day to send this message on (int)
@param results_field: value determining which results field to display.
See CFG_WEBMESSAGE_RESULTS_FIELD in
webmessage_config.py
@param names_to_add: list of usernames ['str'] to add to
msg_to_user / group
@param search_pattern: will search for users/groups with this pattern
@param add_values: if 1 users_to_add will be added to msg_to_user
field..
@param *button: which button was pressed
@param ln: language
@return a (body, errors, warnings) formed tuple.
"""
argd = wash_urlargd(form, {'msg_to_user': (str, ""),
'msg_to_group': (str, ""),
'msg_subject': (str, ""),
'msg_body': (str, ""),
'msg_send_year': (int, 0),
'msg_send_month': (int, 0),
'msg_send_day': (int, 0),
'results_field': (str,
CFG_WEBMESSAGE_RESULTS_FIELD['NONE']),
'names_selected': (list, []),
'search_pattern': (str, ""),
'send_button': (str, ""),
'search_user': (str, ""),
'search_group': (str, ""),
'add_user': (str, ""),
'add_group': (str, ""),
})
# Check if user is logged
uid = getUid(req)
_ = gettext_set_language(argd['ln'])
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourmessages/send" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="yourmessages")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourmessages/send%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
if argd['send_button']:
(body, errors, warnings, title, navtrail) = perform_request_send(
uid=uid,
msg_to_user=argd['msg_to_user'],
msg_to_group=argd['msg_to_group'],
msg_subject=escape_html(argd['msg_subject']),
msg_body=escape_email_quoted_text(argd['msg_body']),
msg_send_year=argd['msg_send_year'],
msg_send_month=argd['msg_send_month'],
msg_send_day=argd['msg_send_day'],
ln=argd['ln'])
else:
title = _('Write a message')
navtrail = get_navtrail(argd['ln'], title)
if argd['search_user']:
argd['results_field'] = CFG_WEBMESSAGE_RESULTS_FIELD['USER']
elif argd['search_group']:
argd['results_field'] = CFG_WEBMESSAGE_RESULTS_FIELD['GROUP']
add_values = 0
if argd['add_group'] or argd['add_user']:
add_values = 1
(body, errors, warnings) = perform_request_write_with_search(
uid=uid,
msg_to_user=argd['msg_to_user'],
msg_to_group=argd['msg_to_group'],
msg_subject=escape_html(argd['msg_subject']),
msg_body=escape_email_quoted_text(argd['msg_body']),
msg_send_year=argd['msg_send_year'],
msg_send_month=argd['msg_send_month'],
msg_send_day=argd['msg_send_day'],
names_selected=argd['names_selected'],
search_pattern=argd['search_pattern'],
results_field=argd['results_field'],
add_values=add_values,
ln=argd['ln'])
return page(title = title,
body = body,
navtrail = navtrail,
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
errors = errors,
warnings = warnings,
navmenuid = "yourmessages")
def delete(self, req, form):
"""
Suppress a message
@param msgid: id of message
@param ln: language
@return page
"""
argd = wash_urlargd(form, {'msgid': (int, -1),
})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourmessages/delete" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="yourmessages")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourmessages/delete%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
_ = gettext_set_language(argd['ln'])
# Generate content
(body, errors, warnings) = perform_request_delete_msg(uid,
argd['msgid'],
argd['ln'])
return page(title = _("Your Messages"),
body = body,
navtrail = get_navtrail(argd['ln']),
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
errors = errors,
warnings = warnings,
navmenuid = "yourmessages")
def delete_all(self, req, form):
"""
Empty user's inbox
@param confimed: 1 if message is confirmed
@param ln: language
\return page
"""
argd = wash_urlargd(form, {'confirmed': (int, 0),
})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourmessages/delete_all" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="yourmessages")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourmessages/delete_all%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
_ = gettext_set_language(argd['ln'])
# Generate content
(body, errors, warnings) = perform_request_delete_all(uid,
argd['confirmed'],
argd['ln'])
return page(title = _("Your Messages"),
body = body,
navtrail = get_navtrail(argd['ln']),
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
errors = errors,
warnings = warnings,
navmenuid = "yourmessages")
def display_msg(self, req, form):
"""
Display a message
@param msgid: id of message
@param ln: languae
@return page
"""
argd = wash_urlargd(form, {'msgid': (int, -1),
})
# Check if user is logged
uid = getUid(req)
if CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "%s/yourmessages/display_msg" % \
- (weburl,),
+ (CFG_SITE_URL,),
navmenuid="yourmessages")
elif uid == -1 or isGuestUser(uid):
return redirect_to_url(req, "%s/youraccount/login%s" % (
CFG_SITE_SECURE_URL,
make_canonical_urlargd({
'referer' : "%s/yourmessages/display_msg%s" % (
- weburl,
+ CFG_SITE_URL,
make_canonical_urlargd(argd, {})),
"ln" : argd['ln']}, {})))
_ = gettext_set_language(argd['ln'])
# Generate content
(body, errors, warnings) = perform_request_display_msg(uid,
argd['msgid'],
argd['ln'])
title = _("Read a message")
return page(title = title,
body = body,
navtrail = get_navtrail(argd['ln'], title),
uid = uid,
lastupdated = __lastupdated__,
req = req,
language = argd['ln'],
errors = errors,
warnings = warnings,
navmenuid = "yourmessages")
diff --git a/modules/websearch/doc/admin/websearch-admin-guide.webdoc b/modules/websearch/doc/admin/websearch-admin-guide.webdoc
index 54f4384fb..35de6dfd4 100644
--- a/modules/websearch/doc/admin/websearch-admin-guide.webdoc
+++ b/modules/websearch/doc/admin/websearch-admin-guide.webdoc
@@ -1,584 +1,584 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(WebSearch Admin Guide)_ -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p><table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
WARNING: THIS ADMIN GUIDE IS NOT FULLY COMPLETED
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
This Admin Guide is not yet completed. Moreover, some
admin-level functionality for this module exists only in the form of
manual recipes. We are in the process of developing both the
guide as well as the web admin interface. If you are interested
in seeing some specific things implemented with high priority,
please contact us at <CFG_SITE_SUPPORT_EMAIL>. Thanks for your interest!
</td>
</tr>
</tbody>
</table>
<h2>Contents</h2>
<strong>1. <a href="#1">Overview</a></strong><br />
<strong>2. <a href="#2">Edit Collection Tree</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.1 <a href="#2.1">Add new collection</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.2 <a href="#2.2">Add collection to tree</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.3 <a href="#2.3">Modify existing tree</a><br />
<strong>3. <a href="#3">Edit Collection Parameters</a></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1. <a href="#3.1">Modify collection query</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2. <a href="#3.2">Modify access restrictions</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3. <a href="#3.3">Modify translations</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4. <a href="#3.4">Delete collection</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.5. <a href="#3.5">Modify portalboxes</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.6. <a href="#3.6">Modify search fields</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.7. <a href="#3.7">Modify search options</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.8. <a href="#3.8">Modify sort options</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.9. <a href="#3.9">Modify rank options</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.10. <a href="#3.10">Modify output formats</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.11. <a href="#3.11">Configuration of related external collections</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.12. <a href="#3.12">Detailed record page options</a><br />
<strong>4. <a href="#4">Webcoll Status</a></strong><br />
<strong>5. <a href="#5">Collections Status</a></strong><br />
<strong>6. <a href="#6">Edit Search Engine Parameters</a></strong><br />
<strong>7. <a href="#7">Search Engine Cache</a></strong><br />
<strong>8. <a href="#8">Additional Information</a></strong><br />
<a name="1"></a><h2>1. Overview</h2>
<p>WebSearch Admin interface will help you to configure the search
collections that the end-users see. The WebSearch Admin functionality
can be basically separated into several parts: (i) how to organize
collections into <a href="#2">collection tree</a>; (ii) how to define
and edit <a href="#3">collection parameters</a>; (iii) how to update
collection cache via the <a href="#4">webcoll daemon</a>; and (iv) how
to influence the search engine behaviour and set various <a
href="#5">search engine parameters</a>. These issues will be
subsequently described in the rest of this guide.
<a name="2"></a><h2>2. Edit Collection Tree</h2>
<p>Metadata corpus in CDS Invenio is organized into collections. The
collections are organized in a tree. The collection tree is what the
end-users see when they start to navigate at <a
-href="<WEBURL>"><CFG_SITE_NAME></a>. The collection tree is similar to what
+href="<CFG_SITE_URL>"><CFG_SITE_NAME></a>. The collection tree is similar to what
other sites call Web Directories that organize Web into topical
categories, such as <a href="http://www.google.com/dirhp">Google
Directory</a>.
<p>Note that CDS Invenio permits every collection in the tree to have
either "regular" or "virtual" sons. In other words, every node in the
collection tree may see either regular or virtual branches growing out
of it. This permits to create a tree with very complex, multi-level,
nested structures of regular and virtual branches, if needed, with the
aim to ease navigation to end-users from one branch to another. The
difference between a regular and a virtual branch will be explained in
detail further below in the <a href="#2.2">section 2.2</a>.
<a name="2.1"></a><h3>2.1 Add new collection</h3>
<p>To add a new collection, enter its default name in the default
language of the installation and click on the ADD button
to add it. There are two important actions that you have to perform
after adding a collection:
<ul>
<li>You have to define the set of records that belong to this
collection. This is done by defining a search engine query
that would return all records belonging to this collection.
See hints on <a href="#3.1">modify collection query</a> below.
<li>In order for the collection to appear in the collection
navigation tree, you will have to attach it to some existing
collection in the tree. See hints on <a href="#2.2">add
collection to tree</a> below.
</ul>
<p>After you edit these two things, the collection is fully usable for
the search interface. It will appear in the search interface after
the next run of the <a href="#4">WebColl Daemon</a>.
<p>However, you will probably want to customize further things, like
define collection name translation in various languages, define
collection web page portalboxes, define search options, etc, as
explained in this guide under the section <a href="#3">Edit
Collection Parameters</a>.
<a name="2.2"></a><h3>2.2 Add collection to tree</h3>
<p>To attach a collection to the tree, choose first which collection
do you want to attach, then choose the father collection to attach to,
and then choose the fathership relation type between them (regular,
virtual).
<p>The difference between the regular and the virtual relationship
goes as follows:
<ul>
<li><strong>regular relationship</strong>: If collection A is
composed of B and C, in a way that every document belonging to
A is either B or C, then this schema corresponds to the
regular type of relationship. For example, let A equals to
"Multimedia" and B and C to "Photos" and "Videos",
respectively. The latter collections would then be declared
as regular sons of "Multimedia" and they would appear in the
left-hand-side regular navigation tree entitled "Narrow by
Collection" in the collection tree.
<li><strong>virtual relationship</strong>: In addition to the
regular decomposition of "Multimedia" into "Photos" and
"Videos", it may be advantageous to present a different,
orthogonal point of view on "Multimedia", based not on the
document type as seen above, but rather on the document
creator information. Let us consider that some (large) part
of the multimedia material was created by the "University
Multimedia Service" and some (small) part by an external TV
company such as BBC. It may be advantageous to advertize this
point of view to the end users too, so they they would be able
to easily navigate down to the kind of multimedia material
they are looking for. We can create two more collections
named "University Multimedia Service" and "BBC Pictures and
Videos" and declare them as virtual sons of the "Multimedia"
collection. These collections would then appear in the
right-hand-side virtual navigation tree entitled "Focus on" in
the collection tree.
</ul>
The example presented above would then give us the following picture:
<blockquote>
<pre>
M u l t i m e d i a
Narrow by Collection: Focus on:
-------------------- ---------
[ ] Photos University Multimedia Service
[ ] Videos BBC Pictures and Videos
</pre>
</blockquote>
<p>It is important to note that if a collection A is composed of B and
C as its regular sons, and offers X and Y as its virtual sons, then
every document belonging to A must also belong to either B or C. This
requirement does not apply for X and Y, because X and Y offer only a
"focus-on" orthogonal view on a (possibly small) part of the document
corpus of A. If end-users search the collection A, then they are
actually searching inside B and C, not X and Y. If they want to
search inside X or Y, they have to click upon X or Y first. One can
consider virtual branches as a sort of non-essential searching aid to
the end-user that is activated only when users are interested in a
particular "focus-on" relationship, provided that this "virtual" point
of view on A interests her.
<a name="2.3"></a><h3>2.3 Modify existing tree</h3>
<p>To modify existing tree by WebSearch Admin Interface, click on
icons displayed next to collections. The meaning of icons is as
follows:
<table border="1">
<tr>
<td>
- <img border="0" src="<WEBURL>/img/iconcross.gif">
+ <img border="0" src="<CFG_SITE_URL>/img/iconcross.gif">
</td>
<td>
Remove chosen collection with its subcollections from the collection tree,
but do not delete the collection itself.
(For full deletion of a collection, see <a href="#3.4">section 3.4</a>.)
</td>
</tr>
<tr>
<td>
- <img border="0" src="<WEBURL>/img/arrow_up.gif"> &nbsp;
- <img border="0" src="<WEBURL>/img/arrow_down.gif">
+ <img border="0" src="<CFG_SITE_URL>/img/arrow_up.gif"> &nbsp;
+ <img border="0" src="<CFG_SITE_URL>/img/arrow_down.gif">
</td>
<td>
Move chosen collection up or down among its brothers and sisters, i.e.
change the order of collections inside the same level of the tree.
</td>
</tr>
<tr>
<td>
- <img border="0" src="<WEBURL>/img/move_from.gif">
- <img border="0" src="<WEBURL>/img/move_to.gif">
+ <img border="0" src="<CFG_SITE_URL>/img/move_from.gif">
+ <img border="0" src="<CFG_SITE_URL>/img/move_to.gif">
</td>
<td>
Move chosen collection among branches of the tree.
- Press the first icon (<img border="0" src="<WEBURL>/img/move_from.gif">)
+ Press the first icon (<img border="0" src="<CFG_SITE_URL>/img/move_from.gif">)
to choose a collection to move, and the second icon
- (<img border="0" src="<WEBURL>/img/move_to.gif">)
+ (<img border="0" src="<CFG_SITE_URL>/img/move_to.gif">)
to select a new father collection that the chosen collection should be attached to.
</td>
</tr>
</table>
<a name="3"></a><h2>3. Edit Collection Parameters</h2>
<p>To finalize setting up of a collection, you could and should edit
many parameters, such as define list of records belonging to a
collection, define search fields, define search interface page
portalboxes, etc. In this section we will subsequently describe all
the various possibilities as they are presented in the <a
-href="<WEBURL>/admin/websearch/websearchadmin.py/editcollection?colID=1">Edit
+href="<CFG_SITE_URL>/admin/websearch/websearchadmin.py/editcollection?colID=1">Edit
Collection</a> pages of the WebSearch Admin Interface.
<a name="3.1"></a><h3>3.1 Modify collection query</h3>
<p>The <em>collection query</em> defines which documents belong to the
given collection. It is equal to the search term that retrieves all
documents belonging to the given collection, exactly as you would have
typed it into the search interface. For example, to define a
collection of all papers written by Ellis, you could set up your
collection query to be <code>author:Ellis</code>.
<p>Usually, the collection query is chosen on the basis of the
collection identifier that we store in MARC tag 980. This tag is
indexed in a logical field called <code>collection</code> so that a
collection of Theses could be defined via
<code>collection:THESIS</code>, supposing that every thesis metadata
record has got the text <code>THESIS</code> in MARC tag 980.
(Nitpick: we use the term `collection' in two contexts here: once as a
collection of metadata documents, but also and as a logical field
name. We should have probably called the latter
<code>collectionidentifier</code> or somesuch instead, but we hope the
difference is clear from... the context.)
<p>If a collection does not have any collection query defined, then
its content is defined by means of the content of its descendants
(subcollections). This is the case for composed collections. For
example, the composed collection <em>Articles & Preprints</em> (no
query defined) will be defined as a father of <em>Articles</em>
(query: <code>collection:ARTICLE</code>) and <em>Preprints</em>
(query: <code>collection:PREPRINT</code>). In this case the
collection query for <em>Articles & Preprints</em> can stay empty.
<p>Note that you should avoid defining non-empty collection query in
cases the collection has descendants, since it will prevail and the
descendants may not be taken into account. In the same way, if a
collection doesn't have any query nor any descendants defined, then
its contents will be empty.
<p>To remove the collection query, set the parameter empty.
<a name="3.2"></a><h3>3.2 Modify access restrictions</h3>
<p>Until <em>CDS Invenio-0.92.1</em> there was the possibility to directly
restrict a collection by specifying an Apache group. Users who had an
Apache user and password belonging to the given group would have been able
to access the restricted collection.</p>
<p>Collection restriction managament is now integrated with the wider
<a href="webaccess-admin-guide">Role Based Access Control</a>
facility of CDS Invenio.</p>
<p>In order to restrict access to a collection you just have to create
at least an authorization for the action <code>accrestrcoll</code>
specifying the name of the collection as the parameter</p>
<p>If you have just upgraded your installation from <em>CDS
Invenio-0.92.1</em> you probably have run
<code>collection_restrictions_migration_kit.py</code> tool in order
to migrate to the new framework. For every Apache Group with access to a
restricted collection a <em>role</em> will be created, with proper
authorization to access the restricted collections. Each role will have
a <em>FireRole</em> definition that specifies to allow for the given
Apache group. Trough the WebAccess admin interface you will then be able
to change these definition in order to softly migrate your restriction
to whatever is your need.</p>
<a name="3.3"></a><h3>3.3 Modify translations</h3>
<p>You may define translations of collection names into the languages
of your CDS Invenio installation. Moreover, a collection name may be
different in different contexts (e.g. long name, short name, etc), so
that prior to modifying translations you will be asked to select which
name type you want to change.
<p>The translations aren't mandatory to define. If a translation does
not exist in a language chosen by the end user, the end user will be
shown the collection name in the default language of this
installation.
<p>Note also that the list of available languages depends on the
compile-time configuration (see the general <code>invenio.conf</code>
file).
<a name="3.4"></a><h3>3.4 Delete collection</h3>
<p>The collection to be deleted must be first removed from the
collection tree. Any metametadata associated with the collection
(such as association to portalboxes, association to records belonging
to this collection, etc) will be lost, but the metadata itself will be
preserved (such as portalboxes themselves, records themselves, etc).
In total, association to records, output formats, translations, search
options, sort options, search fields, ranking method, and access
restriction will be lost. Use with care!
<p>It may be a good idea only to remove the collection from the end
users interface, but to keep it "hidden" in a corner they don't see
and that they can't search when they search from Home. To achieve
this, do not delete the collection but simply remove it from the
collection tree so that it won't be attached to any father collection.
In this case the search interface page for this collection will stay
updated, but won't be neither shown in the tree nor searchable from
Home page. It will only be accessible via bookmarked URL, for
example.
<a name="3.5"></a><h3>3.5 Modify portalboxes</h3>
<p>The search interface HTML page for a given collection may be
customized by what we call <em>portalboxes</em>. Portalboxes are used
to show various kinds of information to the end user, such as a text
box with some inline help information about the given collection, an
illustrative picture, etc.
<p>To create a new portalbox, a title and a body must be given, where
the body can contain HTML if necessary.
<p>To add a portalbox to the collection, you must choose an existing
portalbox, the language for which the portalbox should be shown, the
position of the portalbox on the screen, and the ordering score of
portalboxes.
<ul>
<li>The <em>language</em> could be chosen depending on the language
used in the portalbox body. Since a portalbox is not necessarily
bound to one particular language, one portalbox may be reused for
several languages, which is particularly suitable for portalboxes
containing language-independent content such as images.
<li>The <em>position</em> of the portalbox on the screen is chosen
from several predefined positions, such as right-top, before-title,
after-title, before-narrow-by-collection-box, etc. You may present
several portalboxes on the same position in the same language, in
which case they will be shown by the order of decreasing score.
<li>The <em>score</em> defines the order of portalboxes that are to be
presented in the same position and in the same language context.
</ul>
<a name="3.6"></a><h3>3.6 Modify search fields</h3>
<p>The <em>search field</em> is a logical field (such as author,
title, etc) that will be proposed to the end users in Simple and
Advanced Search interface pages. If you do not set any search fields
for a collection, then a default list (author, title, year, etc) will
be shown.
<p>Note that if you want to add a new logical field or modify existing
physical MARC tags for a logical field, you have to use the <a
-href="<WEBURL>/admin/bibindex/">BibIndex Admin</a> interface.
+href="<CFG_SITE_URL>/admin/bibindex/">BibIndex Admin</a> interface.
<a name="3.7"></a><h3>3.7 Modify search options</h3>
<p>The <em>search option</em> is like <a href="#3.6">search field</a>
in a way that it permits the end user to narrow down his search to
some logical field such as "subject", but unlike with the search field
the user is not required to type his query in a free text form;
rather, the search interface proposes to the end user several
interesting predefined values prepared by the administrators that the
end user may choose from. For example, an "author search" concept is
a good example of search field usage, since there is plenty of author
names to be matched, so that the end users would usually type the name
they wish to find in free text form; while a "subject search" concept
is a good example for search option usage, since usually there is a
limited number of subjects in the system given by local subject
classification scheme, that the end users do not necessarily know
about and that they are free to choose from a list. As a rule of
thumb, the search field concept denotes the case of unlimited number
possibilites of distinct values to be matched in a given field
(e.g. author, title, keyword); while the search option concept denotes
the case of only a handful or so distinct values to be matched in a
given field (e.g. subject, division, year).
<p>Search options are shown in the "Advanced Search" interfaces only,
while search fields are shown both in "Simple Search" and "Advanced
Search" interface. (Although if you want to add a search option to
the "Simple Search" interface, you can achieve it by creating
appropriate HTML code in a <a href="#3.5">portalbox</a>.) The search
options order, as well as the order of search option values, may be
defined by means of 'move' arrows in the WebSearch Admin interface.
<p>To add a new search option, a field name must first be chosen (for
example "subject") and then a list of possible field values must be
entered (for example "Mathematics", "Physics", "Chemistry", "Biology",
etc). Note that if you want to add a new logical field or modify
existing physical MARC tags for a logical field, you have to use the
-<a href="<WEBURL>/admin/bibindex/">BibIndex Admin</a> interface.
+<a href="<CFG_SITE_URL>/admin/bibindex/">BibIndex Admin</a> interface.
<a name="3.8"></a><h3>3.8 Modify sort options</h3>
<p>You may define a list of logical fields that the end users will be
able to choose for the sorting purposes. For example, "first author"
or "year". If you don't select anything, a default list (author,
title, year, etc) will be shown.
<p>Note that if you want to add a new logical field or modify existing
physical MARC tags for a logical field, you have to use the <a
-href="<WEBURL>/admin/bibindex/">BibIndex Admin</a> interface.
+href="<CFG_SITE_URL>/admin/bibindex/">BibIndex Admin</a> interface.
<a name="3.9"></a><h3>3.9 Modify rank options</h3>
<p>To enable a certain rank method for a collection, select the method
from the "enable rank method" box and add it. The documents in this
collection will then be included in the ranking sets the next time the
BibRank daemon will run. To disable a method the process is the same,
but select the method from the 'disable rank method' box.
<p>Note that if you want to add new ranking method or modify existing
ranking method, you have to use the <a
-href="<WEBURL>/admin/bibrank/">BibRank Admin</a> interface.
+href="<CFG_SITE_URL>/admin/bibrank/">BibRank Admin</a> interface.
<a name="3.10"></a><h3>3.10 Modify output formats</h3>
<p>Each collection may have several output formats defined. The end
users will be able to choose a format they want to see their search
results list in. Most formats like HTML brief or XML Dublin Core are
interesting for each collection, but some formats like HTML portfolio
are only interesting for Photographs collection, not for Articles
collection. The interface will permit you to choose the formats
appropriate for a given collection. The order of formats can be
changed using the 'move' arrows.
<p>Note that if you want to add new output format ('behaviour') or
modify existing output format, you have to use the <a
-href="<WEBURL>/admin/bibformat/">BibFormat Admin</a> interface.
+href="<CFG_SITE_URL>/admin/bibformat/">BibFormat Admin</a> interface.
<a name="3.11"></a><h3>3.11 Configuration of related external collections</h3>
<p>You can customize each collection to provide your users an
additional source of information external to your repository: in a
<i>book</i> collection you might want for example to provide a link to
<i>Amazon</i> items corresponding to the user's query. Futhermore, for
some external services only, you can set the collection to display the
results directly in CDS Invenio search results page.
<p>The following settings are available:
<dl>
<dt>Disabled</dt>
<dd>The external collection is not shown to the user.<dd>
<dt>See also</dt>
<dd>A link to the external collection listing the items corresponding to user's query is displayed (only once a query has been performed).</dd>
<dt>External search</dt>
<dd>User can ask to perform a search in parallel on your repository and on the external collection. Results are shown in the CDS Invenio search results page. Not available for all external collections.<dd>
<dt>External search checked</dt>
<dd>Same as above, but the external collection is searched by default. Not available for all external collections.</dd>
<dl>
<p>You can also apply the settings to sub-collections, by checking the
"<i>Apply also to daughter collections</i>" checkboxes when you apply
your modifications.
<a name="3.12"></a><h3>3.12 Detailed record page options</h3>
<p>These settings let you define how the detailed view (such as <a
-href="<WEBURL>/record/1"><WEBURL>/record/1</a>) of records in this
+href="<CFG_SITE_URL>/record/1"><CFG_SITE_URL>/record/1</a>) of records in this
collection will look like. <br/>
More details are available in the <a
-href="<WEBURL>/help/admin/webstyle-admin-guide#det_page">WebStyle admin
+href="<CFG_SITE_URL>/help/admin/webstyle-admin-guide#det_page">WebStyle admin
guide</a>.
<p> Please note that since a record might belong to several
collections, conflicts between collection settings might occur. This
is especially true in the case of <i>virtual</i> collections. It is
therefore the settings of the <i>primary collection</i> of the record
which are applied.
<a name="4"></a><h2>4. Webcoll Status</h2>
<p>WebColl is the daemon that normally periodically runs via <a
-href="<WEBURL>/help/admin/bibsched-admin-guide">BibSched</a> and that updates the
+href="<CFG_SITE_URL>/help/admin/bibsched-admin-guide">BibSched</a> and that updates the
collection cache with the collection parameters configured in the
previous section. Alternatively to running webcoll via BibSched, you
can also run it any time you want from the command line, either for
all collections or for selected collection only. See the --help
option.
<p>The WebSearch Admin interface has got a WebColl Status menu that
shows when the collection cache was last updated and when the next
update is scheduled. It warns in case something suspicious was
discovered.
<a name="5"></a><h2>5. Collections Status</h2>
<p>The Collection Status menu of the WebSearch Admin interface shows
the list of all collections and checks if there is anything wrong
regarding configuration of collections, together with the languages
the collection name has been translated into, etc. Here is the
detailed explanation of the functionality:
<blockquote>
<dl>
<dt><strong>ID</strong>
<dd>ID of the collection.
<dt><strong>Name</strong>
<dd>Name of the collection.
<dt><strong>Query</strong>
<dd>The collection definition query. Note that it should be empty if
a collection got subcollections. If not, then a query is needed.
<dt><strong>Subcollections</strong>
<dd>The subcollections that the collection is composed of. Note that
a collection which got defined by a query should not have any
subcollections.
<dt><strong>Restricted</strong>
<dd>A restricted collection can only be accessed by users belonging to
the Apache groups mentioned in this column.
<dt><strong>I18N</strong>
<dd>Show which languages the collection name has been translated into.
<dt><strong>Status</strong>
<dd>If no errors was found, <em>OK</em> is displayed for each
collection. If an error was found, then an error number and short
message are shown. The meaning of the error messages is the
following: <em>1:Query</em> means that the collection was defined via
a query but also via subcollections too; <em>2:Query</em> means that
the collection wasn't defined neither via query nor via
subcollections.
</dl>
</blockquote>
<a name="6"></a><h2>6. Edit Search Engine Parameters</h2>
<a name="7"></a><h2>7. Search Engine Cache</h2>
<a name="8"></a><h2>8. Additional Information</h2>
-<a href="<WEBURL>/help/hacking/search-engine-internals">WebSearch Internals</a>
+<a href="<CFG_SITE_URL>/help/hacking/search-engine-internals">WebSearch Internals</a>
diff --git a/modules/websearch/doc/hacking/search-engine-api.webdoc b/modules/websearch/doc/hacking/search-engine-api.webdoc
index e41d32bcc..29eb934fe 100644
--- a/modules/websearch/doc/hacking/search-engine-api.webdoc
+++ b/modules/websearch/doc/hacking/search-engine-api.webdoc
@@ -1,345 +1,345 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Search Engine API -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="search-engine-internals">WebSearch Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="search-engine-internals">WebSearch Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<protect>
<pre>
CDS Invenio Search Engine can be called from within your Python programs
via both a high-level and low-level API interface.
1. High-level API
Description:
The high-level access to the search engine is provided by
exactly the same function as called from the web interface when
users submit their queries. This should guarantee exactly the
same behaviour, and means that you can pass to the high-level
API all the arguments as you see them in the URL.
There are two things to note: (i) the function does not check
for eventual restricted status of the collection, so the
restricted collections will be searched without asking for a
password; (ii) the output format argument (``of'') should be set
to ``id'' (which is the default value) meaning to return list of
recIDs. The function returns the list of recIDs in this case.
Signature:
def perform_request_search(req=None, cc=CFG_SITE_NAME, c=None, p="", f="", rg=10, sf="", so="d", sp="", rm="", of="id", ot="", as=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="",
d1y=0, d1m=0, d1d=0, d2y=0, d2m=0, d2d=0, dt="", verbose=0, ap=0, ln=CFG_SITE_LANG, ec=None):
"""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'.
ec - external collection list (e.g. ['CiteSeer', 'Google']). The
external collections may have been selected/deselected by the
user.
p - pattern to search for (e.g. "ellis and muon or kaon").
f - field to search within (e.g. "author").
rg - records in groups of (e.g. "10"). Defines how many hits
per collection in the search results page are
displayed.
sf - sort field (e.g. "title").
so - sort order ("a"=ascending, "d"=descending).
sp - sort pattern (e.g. "CERN-") -- in case there are more
values in a sort field, this argument tells which one
to prefer
rm - ranking method (e.g. "jif"). Defines whether results
should be ranked by some known ranking method.
of - output format (e.g. "hb"). Usually starting "h" means
HTML output (and "hb" for HTML brief, "hd" for HTML
detailed), "x" means XML output, "t" means plain text
output, "id" means no output at all but to return list
of recIDs found. (Suitable for high-level API.)
ot - output only these MARC tags (e.g. "100,700,909C0b").
Useful if only some fields are to be shown in the
output, e.g. for library to control some fields.
as - advanced search ("0" means no, "1" means yes). Whether
search was called from within the advanced search
interface.
p1 - first pattern to search for in the advanced search
interface. Much like 'p'.
f1 - first field to search within in the advanced search
interface. Much like 'f'.
m1 - first matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
op1 - first operator, to join the first and the second unit
in the advanced search interface. ("a" add, "o" or,
"n" not).
p2 - second pattern to search for in the advanced search
interface. Much like 'p'.
f2 - second field to search within in the advanced search
interface. Much like 'f'.
m2 - second matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
op2 - second operator, to join the second and the third unit
in the advanced search interface. ("a" add, "o" or,
"n" not).
p3 - third pattern to search for in the advanced search
interface. Much like 'p'.
f3 - third field to search within in the advanced search
interface. Much like 'f'.
m3 - third matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
sc - split by collection ("0" no, "1" yes). Governs whether
we want to present the results in a single huge list,
or splitted by collection.
jrec - jump to record (e.g. "234"). Used for navigation
inside the search results.
recid - display record ID (e.g. "20000"). Do not
search/browse but go straight away to the Detailed
record page for the given recID.
recidb - display record ID bis (e.g. "20010"). If greater than
'recid', then display records from recid to recidb.
Useful for example for dumping records from the
database for reformatting.
sysno - display old system SYS number (e.g. ""). If you
migrate to CDS 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").
"""
Examples:
>>> # import the function:
>>> from invenio.search_engine import perform_request_search
>>> # get all hits in a collection:
>>> perform_request_search(cc="ATLAS Communications")
>>> # search for the word `of' in Theses and Books:
>>> perform_request_search(p="of", c=["Theses","Books"])
>>> # search for `muon or kaon' within title:
>>> perform_request_search(p="muon or kaon", f="title")
>>> # phrase search (not the quotes):
>>> perform_request_search(p='"Ellis, J"', f="author")
>>> # regexp search for a system number
>>> perform_request_search(p1="^CERN.*2003-001$", f1="reportnumber", m1="r")
>>> # moi inside Standards gives no hits...
>>> perform_request_search(p="moi", cc="Standards")
>>> # but it does if we use alternative patterns:
>>> perform_request_search(p="moi", cc="Standards", ap=1)
2. Mid-level API
Description:
The mid-level API is provided by a search_pattern() function
that only searches for the given pattern in the given field
according to the given matching pattern. This function does not
know anything about collection. The function does not wash its
arguments, it expects them to be `clean' already. The pattern
is split into `basic search units' for which a boolean query is
launched. The function returns an instance of the HitSet class.
Note that if you want to obtain the list of recIDs (as with the
high-level API), you can invoke the ``tolist()'' method on a
hitset.
Signature:
def search_pattern(req=None, p=None, f=None, m=None, ap=0, of="id", verbose=0):
"""Search for complex pattern 'p' within field 'f' according to
matching type 'm'. Return hitset of recIDs.
The function uses multi-stage searching algorithm in case of no
exact match found. See the Search Internals document for
detailed description.
The 'ap' argument governs whether an alternative patterns are to
be used in case there is no direct hit for (p,f,m). For
example, whether to replace non-alphanumeric characters by
spaces if it would give some hits. See the Search Internals
document for detailed description. (ap=0 forbits the
alternative pattern usage, ap=1 permits it.)
The 'of' argument governs whether to print or not some
information to the user in case of no match found. (Usually it
prints the information in case of HTML formats, otherwise it's
silent).
The 'verbose' argument controls the level of debugging information
to be printed (0=least, 9=most).
All the parameters are assumed to have been previously washed.
This function is suitable as a mid-level API.
"""
Examples:
>>> # import the function:
>>> from invenio.search_engine import search_pattern
>>> # search for muon or kaon in any field:
>>> search_pattern(p="muon or kaon").tolist()
>>> # the following finds nothing by default...
>>> search_pattern(p="cern-moi").tolist()
>>> # ...but it does find something if we allow alternative patterns:
>>> search_pattern(p="cern-moi", ap=1).tolist()
>>> # wildcard search for a report number:
>>> search_pattern(p="CERN-LHC-PROJECT-REPORT-40*", f="reportnumber").tolist()
>>> # regexp search for a report number with possible trailing subjects:
>>> search_pattern(p="^CERN-LHC-PROJECT-REPORT-40(-|$)", f="reportnumber", m="r").tolist()
3. Low-level API
Description:
The low-level API is provided by search_unit() function that
assumes its arguments to be already the basic search units.
Therefore it does not know anything about boolean queries, etc.
The function returns an instance of the HitSet class. Note that
if you want to obtain the list of recIDs (as with the high-level
API), you can invoke the ``tolist()'' method on a hitset.
Signature:
def search_unit(p, f=None, m=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'.
This function is suitable as a low-level API.
"""
Examples:
>>> # import the function:
>>> from invenio.search_engine import search_unit
>>> # search moi in any field:
>>> search_unit(p="moi").tolist()
>>> # this one will not match:
>>> search_unit(p="muon or kaon").tolist()
>>> # regexp search for a report number with possible trailing subjects:
>>> search_unit(p="^CERN-PS-99-037(-|$)", f="reportnumber", m="r").tolist()
More entry points may be created, but I think this threesome kind of
access to the search engine should cover all your needs.
</pre>
</protect>
diff --git a/modules/websearch/doc/hacking/search-engine-internals.webdoc b/modules/websearch/doc/hacking/search-engine-internals.webdoc
index df535d1e2..3a6bc1a4c 100644
--- a/modules/websearch/doc/hacking/search-engine-internals.webdoc
+++ b/modules/websearch/doc/hacking/search-engine-internals.webdoc
@@ -1,38 +1,38 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebSearch Internals -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p>This page summarizes all the information suitable to dig inside
Search Engine internals.</p>
<blockquote>
<dl>
<dt><a href="search-engine-stages">Search Processing Stages</a> <dd>Explains what
happens after user hits the SEARCH button.
<dt><a href="search-engine-api">Search Engine API</a> <dd>Explains how to call
search engine from your Python programs, should a need arize.
</dl>
</blockquote>
diff --git a/modules/websearch/doc/hacking/search-engine-stages.webdoc b/modules/websearch/doc/hacking/search-engine-stages.webdoc
index 56c4bac29..a4fa51979 100644
--- a/modules/websearch/doc/hacking/search-engine-stages.webdoc
+++ b/modules/websearch/doc/hacking/search-engine-stages.webdoc
@@ -1,163 +1,163 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: Search Processing Stages -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="search-engine-internals">WebSearch Internals</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/hacking">Hacking CDS Invenio</a> &gt; <a class="navtrail" href="search-engine-internals">WebSearch Internals</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
What the search engine does after it receives a user query? Let us
explain the steps it takes for a fictive complex query example in
which a user typed
author:ellis reportnumber:TH-2003-114 -muon +energ* title:"kaon decay"
and selected "Theses" and "Books" collections and the "arrival date"
between the 10th and the 20th of March 2003.
The search engine proceeds as follows.
1. Firstly we classify and break apart our search arguments into a)
basic search units and b+c) additional search options:
a) (p_1, f_1), (p_2, f_2), ..., (p_m, f_m) --- the list of basic
(pattern, field) searching units. The user-typed boolean
query is split into the (p_i, f_i) units according to the
non-significant whitespace. (A whitespace is considered to
be significant when it occurs within quoted expressions for
phrase searching, see Search Tips and user-level
documentation.) In our example, the list of basic search
units is: (author, ellis), (reportnumber, TH-2003-114),
(anyfield, muon), (anyfield, energ*), (title, "kaon decay").
b) c_1, c_2, ..., c_n --- the list of collections the user wanted
to search in. In our example: Theses, Books.
c) l_1, l_2, ..., l_o --- the list of additional limiting search
criteria, such as limit to certain arrival date, certain
language, or certain subject category. The user usually
selects such limits from within Advanced Search interface
and its selection boxes. In our example, the limit is on
the arrival date: (arrivaldate, 20030310->20030320).
The basic search units and additional search arguments are then
dealt with subsequently (from a to c with decreasing priority) in
the following searching stages.
2. For each (p_i, f_i), we verify that at least some hits can be found
regardless of c_j and l_k. In other words, we make sure that p_i
is a known indexed term in f_i. Note that p_i may contain
asterisks and may start and end by a single/double quotes, with the
following special meaning:
foo*bar -- asterisk is a wildcard character, meaning to match
any sequence of characters
"foo and bar" -- double quotes to denote exact phrase matching
'foo and bar' -- single quotes to denote partial phrase matching
The p_i (word or phrase) is then tested for existence in the f_i
(word or phrase) index.
2-1. If p_i was found in the f_i index, we retain the (p_i, f_i)
search unit. We also retain it in the case when p_i wasn't
found in the f_i index but when (p_i, f_i) is joined to the
previous unit or to the next unit by boolean operator OR.
2-2. If p_i wasn't found inside f_i, we then look whether p_i
contains some non-alphanumeric characters, such as a dash or a
slash. If this is so, we try to replace them with a boolean
AND query. In our example, the search unit (reportnumber,
TH-2003-114) would be replaced by a new boolean query for
(reportnumber,TH) and (reportnumber,2003) and
(reportnumber,114). If this new query succeeds, we retain
this new boolean query in place of the old search unit.
2-3. If the preceding step failed, we propose to the end user a
list of nearest indexed terms (words, phrases) around p_i
within f_i index and let the user choose one known indexed
term out of this list. In our example, the phrase "kaon
decay" cannot be found in the title index, so we'll propose
closest titles around "kaon decay ...". Let's suppose that
the user will choose "Kaon decays and the flavour problem" out
of this list.
After all the basic search units (p_i, f_i) have been treated we
may proceed to the following stage 3.
3. At this stage, all search units (p_i, f_i) are known to yield at
least some results. We now continue by trying boolean query as
specified by the user. The execution priority goes from left to
right, the known boolean operators are:
+ for set intersection
- for set difference
| for set union
In our example, we proceed by doing the set intersections of
(author, ellis), (reportnumber,TH), (reportnumber,2003),
(reportnumber,114), followed by set differentiation with (anyfield,
muon), followed by set intersection with (anyfield, energ*) and
(title, "Kaon decays and the flavour problem").
3-1. If this gives some hits, we proceed to stage 4.
3-2. If this does not give any hit, we display the number of hits
found for each search unit, advise the user to combine his
search terms differently, and we stop.
4. At this stage, the boolean query (p_1, f_1), (p_2, f_2), ... ,
(p_n, f_n) is known to yield some results. We now continue by
checking whether these results fall into collections c_j that the
user has chosen. This is done by performing a set intersection of
the results obtained so far with the collection universe for c_j.
4-1. If this gives some hits, we proceed to stage 5.
4-2. If this does not give any hit, we first try to look for the
query in any public collection.
4-2-1. If this gives some hits, we warn the user that no match
could have been found in his c_j choice but that there
are hits in other public collections. We propose a
link to get them and we stop.
4-2-2. If this does not give any hit, then there must have
been some hits in some of the restricted collections.
We display a warning that the restricted collections
must be explicitly selected before searching and we
stop.
5. At this stage, the boolean query (p_i, f_i) within c_j is known to
yield some results. We now proceed by checking additional search
limits l_k imposed by the user. This is done by subsequent set
intersections of the results obtained so far with the universe of
records matching limiting criteria (l_1, l_2, ..., l_o).
5-1. If this gives some hits, we proceed to stage 6.
5-2. If this does not give any hit for a certain l_k, we warn the
user that no match could have been found for his l_o choices
and proceed to stage 6 with the results obtained so far.
6. We are done and may display the results.
</pre>
diff --git a/modules/websearch/doc/search-guide.webdoc b/modules/websearch/doc/search-guide.webdoc
index 596a60d99..7912f6be5 100644
--- a/modules/websearch/doc/search-guide.webdoc
+++ b/modules/websearch/doc/search-guide.webdoc
@@ -1,5251 +1,5251 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(Search Guide)_ -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/<lang:link/>">_(Help Central)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/<lang:link/>">_(Help Central)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<lang>
<en>
<p>Our search engine tries to offer today's typical web searching
experience, as gained with popular search engines such as <a
href="http://google.com/">Google</a>. The nature of bibliographic
searching differs from that of a web page searching, though. We
provide many extensions to enable a complex and precise structured
search, including an combined metadata, fulltext and reference search
in one go. This page lists several tips and tricks that you may find
useful to this effect.</p>
</en>
<fr>
<p>Notre moteur de recherche propose des fonctionnalités similaires à
celles des moteurs de recherche actuellement disponibles sur le Web,
tels que <a href="http://google.com/">Google</a>. La nature des
recherches bibliographiques diffère cependant de la recherche de pages
web. Nous proposons donc des extensions permettant des recherches
structurées complexes et précises, dont une recherche combinée
s'effectuant sur les metadonnées, le contenu des fichiers et les
références de chaque notice. Cette page répertorie les trucs et
astuces pouvant vous être utiles.</p>
</fr>
<de>
<p>Unsere Suchmaschine bietet den heutigen Stand der Web-Such
Technologie, die auch von bekannten Suchmaschinen wie zum Beispiel
<a href="http://google.com/">Google</a> angeboten werden. Im Detail
unterscheidet sich jedoch die bibliographische Suche von einer
Web-Suche. Wir bieten mehrere Erweiterungen an, damit eine komplexe
und genau strukturierte Suche möglich wird, inklusiv einer
kombinierten Metadatensuche, Volltextsuche und Referenzsuche. Diese
Seite stellt Tipps und Tricks vor, die für eine effektive Suche
nützlich sind.</p>
</de>
<es>
<p> El motor de búsqueda de este sistema trata de ofrecer la
tecnología más actual de búsqueda web, desarrollada por buscadores tan
populares como <a href="http://google.com/"> Google </a>. Sin
embargo, la naturaleza de una búsqueda bibliográfica difiere
considerablemente de la de una página Web. La alternativa propuesta
es la de proporcionar numerosas extensiones que hagan
posible búsquedas de estructura compleja y precisa, incluso combinando
metadatos, texto completo y citas bibliográficas en una misma
consulta. Esta página ofrece una serie consejos útiles para conseguir
una búsqueda más eficaz.</p>
</es>
<ca>
<p> El motor de cerca d'aquest sistema tracta d'oferir la tecnologia
més actual de cerca web, desenvolupada per cercadors tant populars com
<a href="http://google.com/"> Google </a>. La naturalesa d'una
cerca bibliogràfica, però, difereix considerablement de la d'una
página Web. L'alternativa proposada és la de proporcionar
nombroses extensions que permetin cerques d'estructura complexa i
precisa, inclosa la combinació de metadades, text complet i
referències bibliogràfiques a una mateixa cerca. Aquesta pàgina
ofereix una sèrie de consells útils per aconseguir una cerca més
eficaç.</p>
</ca>
</lang>
<lang>
<en>
<h3><strong class="headerboxbodylogo">Index</strong></h3>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#simpleadvanced">Simple versus advanced search</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#guidance">Search guidance</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#words-vs-phrases">Searching for words versus phrases</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#boolean">Boolean queries</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#punctuation">Special characters and punctuation</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unicode">International characters</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#wildcard">Word truncation/stemming</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#structured">Structured metadata search</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#regexp">Regular expressions</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#span">Span queries</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#combined">Combined metadata/fulltext/citation search</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto">Frequently asked questions</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-choose-terms">How to wisely choose your search terms (speed-wise)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-search-for-author">How to search for publications by a given author</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-sort-pattern">How to sort according to a certain pattern</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-hepdoc">How to get documents from other servers (Google, SPIRES, KEK)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-fulltext">How to search in fulltext files</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-citations">How to search for citations</a>
</en>
<fr>
<h3><strong class="headerboxbodylogo">Index</strong></h3>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#simpleadvanced">Recherche simple versus recherche avancée</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#guidance">Aide à la recherche</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#words-vs-phrases">Recherche de mots versus recherche de phrases</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#boolean">Requêtes booléennes</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#punctuation">Caractères spéciaux et ponctuation</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unicode">Caractères internationaux</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#wildcard">Troncature des mots/indexation par radicaux</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#structured">Recherche structurée</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#regexp">Expressions régulières</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#span">Requêtes de plages</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#combined">Recherche combinée métadonnées/fulltext/citation</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto">Foire aux questions</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-choose-terms">Comment sélectionner vos termes de recherche de manière intelligente (en termes de vitesse)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-search-for-author">Comment rechercher les publications d'un auteur donné</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-sort-pattern">Comment trier d'après un certain critère</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-hepdoc">Comment obtenir les documents d'autres serveurs (Google, SPIRES, KEK)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-fulltext">Comment rechercher le contenu des fichiers</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-citations">Comment rechercher les citations</a>
</fr>
<de>
<h3><strong class="headerboxbodylogo">Inhaltsverzeichnis</strong></h3>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#simpleadvanced">Einfache versus erweiterte Suche</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#guidance">Grundlagen</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#words-vs-phrases">Suche nach Wörtern und Wortgruppen</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#boolean">Boolsche Suche</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#punctuation">Spezielle Zeichen und Notation</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unicode">Internationale Zeichen</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#wildcard">Trunkierung</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#structured">Strukturierte Metadatensuche</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#regexp">Regular expressions</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#span">Bereichs-Recherche</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#combined">Kombinierte Metadaten-/Volltext-/Zitatsuche</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto">FAQ</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-choose-terms">Wie wähle ich am geschicktesten meinen Suchbegriff</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-search-for-author">Wie suche ich nach Publikationen eines bestimmten Autors</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-sort-pattern">Wie lasse ich Ergebnisse auf eine bestimmte Weise sortieren</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-hepdoc">Wie bekomme ich Dokumente anderer Server (Google, SPIRES, KEK)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-fulltext">Wie kann ich in verknüpften Volltextdateien suchen</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-citations">Wie kann ich nach Zitaten suchen</a>
</de>
<es>
<h3><strong class="headerboxbodylogo">Índice</strong></h3>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#simpleadvanced">Búsqueda simple versus avanzada</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#guidance">Búsqueda guiada</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#words-vs-phrases">Búsqueda por palabras versus búsqueda por frases</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#boolean">Consultas booleanas</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#punctuation">Caracteres especiales y puntuación</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unicode">Caracteres internacionales</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#wildcard">Truncamientos y búsquedas por raíz</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#structured">Búsqueda por metadatos estructurados</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#regexp">Expresiones regulares</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#span">Consultas por rango</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#combined">Combinación de metadatos/texto completo/cita bibliográfica</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto">Preguntas frecuentes</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-choose-terms">Elegir correctamente los términos de búsqueda (speed-wise)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-search-for-author">¿Cómo localizar publicaciones a partir del autor?</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-sort-pattern">¿Cómo ordenar acorde a cierto patrón de ordenación?</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-hepdoc">¿Cómo obtener documentos de otros servidores? (Google, SPIRES, KEK)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-fulltext">¿Cómo buscar en ficheros a texto completo?</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-citations">¿Cómo buscar citas bibliogáficas?</a>
</es>
<ca>
<h3><strong class="headerboxbodylogo">Índex</strong></h3>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#simpleadvanced">Cerca simple versus avançada</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#guidance">Cerca guiada</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#words-vs-phrases">Cerca per paraules versus cerca per frases</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#boolean">Consultes booleanes</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#punctuation">Caracters especials i puntuació</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unicode">Caracters internacionals</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#wildcard">Truncaments i cercques per arrel</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#structured">Cerca por metadadess estructurades</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#regexp">Expressions regulars</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#span">Consultes per rang</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#combined">Combinació de metadades/text complet/referència bibliogràfica</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto">Preguntes freqüents</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-choose-terms">Escollir correctament els térmes de cerca (speed-wise)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-search-for-author">Com localitzar publicacions a partir de l'autor?</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-sort-pattern">Com ordenar d'acord a un patró d'ordenació?</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-hepdoc">Com obtenir documents d'altres servidors? (Google, SPIRES, KEK)</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-fulltext">Cómo cercar a fitxers a text complet?</a>
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#howto-citations">Cómo cercar referències bibliogàfiques?</a>
</ca>
</lang>
<h3><a name="simpleadvanced">
<lang>
<en>Simple versus advanced search</en>
<fr>Recherche simple versus recherche avancée</fr>
<de>Einfache versus erweiterte Suche</de>
<es>Búsqueda simple versus avanzada</es>
<ca>Cerca simple versus avançada</ca>
</lang>
</a></h3>
<lang>
<en>
<p>The default search mode is <strong>simple search</strong> that
basically provides you with one input box where you can type your
query, followed by a possibility to choose one of the common indexes
to search within. You would usually simply type the keywords you are
interested in and hit return. For example, if you are interested in
documents on <em>standard model</em> that are written by (or mention)
<em>Ellis</em>, you would type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis standard model" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>and on the search results page you could further add/remove keywords
to get more precisely at what you are looking for, as is mentioned <a
href="#boolean">below</a>.</p>
</en>
<fr>
<p>Le mode de recherche par défaut est la <strong>recherche
simple</strong>, qui consiste principalement en un seul champ dans
lequel vous pouvez saisir votre requête. Vous avez également la
possibilité de restreindre la recherche à un champ spécifique. Dans la
majorité des cas, vous n'aurez qu'à saisir les mots-clés qui vous
intéressent et lancer la recherche. Si, par exemple, vous souhaitez
trouver des documents relatifs au <em>standard model</em> écrits (ou
mentionnés) par <em>Ellis</em>, votre requête doit être la suivante:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis standard model" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Depuis la page des résultats vous pourrez également
ajouter/supprimer des mots-clefs afin de parfaire votre recherche,
comme expliqué <a href="#boolean">plus loin</a>.</p>
</fr>
<es>
<p>El modo de búsqueda por defecto es la <strong>búsqueda
simple</strong> que permite, en términos generales, escribir una
consulta en el campo de texto, acompañado de la posibilidad de
seleccionar uno de los índices comunes sobre los que efectuar la
consulta. Se introducen las palabras clave a buscar y se acciona la
consulta mediante la tecla de retorno (o intro). Por ejemplo, si
interesa recuperar documentos sobre <em>modelos estándar</em> que han
sido escritos por (o que mencionan a) <em>Ellis</em>, debemos
escribir:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis modelos estandar" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>y en la página de resultados de la búsqueda podemos volver a añadir o
quitar palabras clave para obtener mayor precisión en los resultados,
tal y como se especifica en más <a href="#boolean">adelante</a>.</p>
</es>
<ca>
<p>La cerca per defecte és la <strong>cerca simple</strong>, que
fonamentalment permet escriure la consulta dins un camp de text,
seguit de la possibilitat d'escollir un dels índexs comuns sobre els
que realitzar la consulta. S'introdueixen les paraules clau a buscar
i s'acciona la consulta mitjançant la tecla de retorn (intro). Per
exemple, si interessa recuperar documents sobre <em>models
estàndard</em> que han estat escrits per (o que fan esment a)
<em>Ellis</em>, haurem d'escriure:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis models est&agrave;ndar" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>i a la pàgina dels resultats de la cerca podem tornar a afegir o
treure paraules clau per tal d'obtenir més precisió als resultats, tal
i com s'especifica més <a href="#boolean">endavant</a>.</p>
</ca>
</lang>
<lang>
<en>
<p>The <strong>advanced search</strong> interface provides you with
explicit tools to play with: you can change the matching type from the
default word matching to phrase searching or the regular matching; you
can use boolean queries in several indexes, etc. For example, to find
all the documents written by <em>Ellis, J</em> spelled exactly that
way that contain either of the words <em>muon</em> or
<em>neutrino</em> in the title and that were published in
<em>2001</em>, you would type:</p>
<blockquote>
-<form action="<WEBURL>/search" method="get">
+<form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="cc" value="<CFG_SITE_NAME>" />
<input type="hidden" name="as" value="1" />
<input type="hidden" name="ln" value="<lang:current />" />
<table>
<tbody>
<tr valign="bottom">
<td>
<select name="m1">
<option value="a">_(All of the words:)_</option>
<option value="o">_(Any of the words:)_</option>
<option value="e" selected="selected">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p1" size="16" value="Ellis, J" /></td>
<td class="searchboxbody">
<select name="f1">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author" selected="selected">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title">_(title)_</option>
<option value="year">_(year)_</option>
</select>
</td>
<td class="searchboxbody">
<select name="op1">
<option value="a">_(AND)_</option>
<option value="o">_(OR)_</option>
<option value="n">_(AND NOT)_</option>
</select>
</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody">
<select name="m2">
<option value="a">_(All of the words:)_</option>
<option value="o" selected="selected">_(Any of the words:)_</option>
<option value="e">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p2" size="16" value="muon neutrino" /></td>
<td class="searchboxbody"><select name="f2">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="year">_(year)_</option>
</select></td>
<td class="searchboxbody">
<select name="op2">
<option value="a">_(AND)_</option>
<option value="o">_(OR)_</option>
<option value="n">_(AND NOT)_</option>
</select>
</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody">
<select name="m3">
<option value="a">_(All of the words:)_</option>
<option value="o">_(Any of the words:)_</option>
<option value="e">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p3" size="16" value="2001" /></td>
<td class="searchboxbody"><select name="f3">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title">_(title)_</option>
<option value="year" selected="selected">_(year)_</option>
</select></td>
<td class="searchboxbody"><input class="formbutton" type="submit" name="action" value="_(Search)_" /><input class="formbutton" type="submit" name="action" value="_(Browse)_" />&nbsp;</td>
</tr>
</tbody>
</table>
</form>
</blockquote>
<p>Note that Simple Search can provide you basically the same
functionality, if you make use of special syntax that is explained in
the text below. The simple-versus-advanced does not refer to the
functionality that is being provided but rather to the amount of
parametrization you can "tweak". We conform to the common
use of the simple/advanced terms as found in other search engines.</p>
<p>Much of what follows will deal with a question on "how a power user
would use the simple search interface". Recall that you can always go
to the Advanced Search for more query assistance.</p>
</en>
<fr>
<p>L'interface de <strong>recherche avancée</strong> vous offre plus
de contrôles: vous pouvez changer le type de correspondance (exacte,
expression régulière, phrase partielle); vous pouvez effectuer des
recherches booléennes dans différents index, etc. Par exemple pour
trouver tous les documents écrits par <em>Ellis, J</em> (écrit
exactement ainsi), qui contiennent les mots <em>muon</em> ou
<em>neutrino</em> dans le titre et qui ont été publiés en 2001, saisissez:</p>
<blockquote>
-<form action="<WEBURL>/search" method="get">
+<form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="cc" value="<CFG_SITE_NAME>" />
<input type="hidden" name="as" value="1" />
<input type="hidden" name="ln" value="<lang:current />" />
<table>
<tbody>
<tr valign="bottom">
<td>
<select name="m1">
<option value="a">_(All of the words:)_</option>
<option value="o">_(Any of the words:)_</option>
<option value="e" selected="selected">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p1" size="16" value="Ellis, J" /></td>
<td class="searchboxbody">
<select name="f1">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author" selected="selected">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title">_(title)_</option>
<option value="year">_(year)_</option>
</select>
</td>
<td class="searchboxbody">
<select name="op1">
<option value="a">_(AND)_</option>
<option value="o">_(OR)_</option>
<option value="n">_(AND NOT)_</option>
</select>
</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody">
<select name="m2">
<option value="a">_(All of the words:)_</option>
<option value="o" selected="selected">_(Any of the words:)_</option>
<option value="e">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p2" size="16" value="muon neutrino" /></td>
<td class="searchboxbody"><select name="f2">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="year">_(year)_</option>
</select></td>
<td class="searchboxbody">
<select name="op2">
<option value="a">_(AND)_</option>
<option value="o">_(OR)_</option>
<option value="n">_(AND NOT)_</option>
</select>
</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody">
<select name="m3">
<option value="a">_(All of the words:)_</option>
<option value="o">_(Any of the words:)_</option>
<option value="e">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p3" size="16" value="2001" /></td>
<td class="searchboxbody"><select name="f3">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title">_(title)_</option>
<option value="year" selected="selected">_(year)_</option>
</select></td>
<td class="searchboxbody"><input class="formbutton" type="submit" name="action" value="_(Search)_" /><input class="formbutton" type="submit" name="action" value="_(Browse)_" />&nbsp;</td>
</tr>
</tbody>
</table>
</form>
</blockquote>
<p>Notez que la recherche simple peut obtenir le même résultat que la
recherche avancée si vous utilisez la syntaxe expliquée plus bas. La
différence entre la recherche simple et la recherche avancée ne se
fait pas sur les fonctionnalités offertes mais plutôt sur le nombre de
paramètres qu'il vous est possible de combiner. Nous nous conformons
en ce sens aux modèles de recherches simples/avancées que l'on trouve
dans d'autre moteurs de recherche.</p>
<p>Ce qui suit explique principalement la syntaxe qu'un utilisateur
expérimenté peut utiliser dans le mode de recherche simple.
Rappelez-vous que vous pouvez toujours utiliser la recherche
avancée pour parvenir à des résultats ciblés sans avoir à utiliser
de syntaxe particulière.</p>
</fr>
<es>
<p>El interfaz de <strong>búsqueda avanzada</strong> ofrece
herramientas específicas con las que configurar la búsqueda: podemos
cambiar el tipo de búsqueda por defecto (de alguna palabra a todas,
buscar por frase exacta, por expresión regular, etc.); nos permite
utilizar las expresiones booleanas combinando varios índices, etc.
Por ejemplo, para recuperar todos los documentos escritos por
<em>Ellis, J</em> -transcrito exactamente de ese modo- y también las
palabras <em>muons</em> o <em>neutrino</em> en el título de la
publicación y <em>2001</em> en el año, escribiremos lo siguiente:</p>
<blockquote>
-<form action="<WEBURL>/search" method="get">
+<form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="cc" value="<CFG_SITE_NAME>" />
<input type="hidden" name="as" value="1" />
<input type="hidden" name="ln" value="<lang:current />" />
<table>
<tbody>
<tr valign="bottom">
<td>
<select name="m1">
<option value="a">_(All of the words:)_</option>
<option value="o">_(Any of the words:)_</option>
<option value="e" selected="selected">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p1" size="16" value="Ellis, J" /></td>
<td class="searchboxbody">
<select name="f1">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author" selected="selected">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title">_(title)_</option>
<option value="year">_(year)_</option>
</select>
</td>
<td class="searchboxbody">
<select name="op1">
<option value="a">_(AND)_</option>
<option value="o">_(OR)_</option>
<option value="n">_(AND NOT)_</option>
</select>
</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody">
<select name="m2">
<option value="a">_(All of the words:)_</option>
<option value="o" selected="selected">_(Any of the words:)_</option>
<option value="e">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p2" size="16" value="muon neutrino" /></td>
<td class="searchboxbody"><select name="f2">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="year">_(year)_</option>
</select></td>
<td class="searchboxbody">
<select name="op2">
<option value="a">_(AND)_</option>
<option value="o">_(OR)_</option>
<option value="n">_(AND NOT)_</option>
</select>
</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody">
<select name="m3">
<option value="a">_(All of the words:)_</option>
<option value="o">_(Any of the words:)_</option>
<option value="e">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p3" size="16" value="2001" /></td>
<td class="searchboxbody"><select name="f3">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title">_(title)_</option>
<option value="year" selected="selected">_(year)_</option>
</select></td>
<td class="searchboxbody"><input class="formbutton" type="submit" name="action" value="_(Search)_" /><input class="formbutton" type="submit" name="action" value="_(Browse)_" />&nbsp;</td>
</tr>
</tbody>
</table>
</form>
</blockquote>
<p>Obsérvese que si utilizamos una sintaxis especial, la Búsqueda
Simple ofrece prácticamente la misma funcionalidad que la avanzada,
tal y como se indica en las siguientes secciones de esta guía. Este
apartado de búsqueda simple-versus-avanzada no se refiere tanto a las
funcionalidades disponibles, sino a la forma de búsqueda que podemos
configurar mediante pequeñas modificaciones. El sistema se ha adaptado
al uso común de los términos de simple/avanzado que podemos encontrar
en otros motores de búsqueda.</p>
<p>En realidad, gran parte de la explicación que sigue a continuación,
responde a la cuestión de "cómo un usuario avanzado puede utilizar la
interficie de búsqueda simple". Recordamos que en cualquier momento
es posible recurrir a la ayuda de la búsqueda avanzada para conseguir
más asistencia.</p>
</es>
<ca>
<p>La interficie de <strong>cerca avançada</strong> ofereix eines
específiques amb les que configurar la cerca: podem canviar el tipus
de cerca a realitzar per defecte (d'alguna paraula a totes les
paraules, cerca per frase exacta, per expressió regular, etc.); ens
permet emprar la combinació booleana a nombrosos índexs, etc. Per
exemple, per localitzar tots els documents escrits per <em>Ellis,
J</em> -transcrit exactament d'aquesta manera- i també les paraules
<em>muon</em> o <em>neutrino</em> al títol de la publicació i
<em>2001</em> a l'any, escriurem:</p>
<blockquote>
-<form action="<WEBURL>/search" method="get">
+<form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="cc" value="<CFG_SITE_NAME>" />
<input type="hidden" name="as" value="1" />
<input type="hidden" name="ln" value="<lang:current />" />
<table>
<tbody>
<tr valign="bottom">
<td>
<select name="m1">
<option value="a">_(All of the words:)_</option>
<option value="o">_(Any of the words:)_</option>
<option value="e" selected="selected">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p1" size="16" value="Ellis, J" /></td>
<td class="searchboxbody">
<select name="f1">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author" selected="selected">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title">_(title)_</option>
<option value="year">_(year)_</option>
</select>
</td>
<td class="searchboxbody">
<select name="op1">
<option value="a">_(AND)_</option>
<option value="o">_(OR)_</option>
<option value="n">_(AND NOT)_</option>
</select>
</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody">
<select name="m2">
<option value="a">_(All of the words:)_</option>
<option value="o" selected="selected">_(Any of the words:)_</option>
<option value="e">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p2" size="16" value="muon neutrino" /></td>
<td class="searchboxbody"><select name="f2">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="year">_(year)_</option>
</select></td>
<td class="searchboxbody">
<select name="op2">
<option value="a">_(AND)_</option>
<option value="o">_(OR)_</option>
<option value="n">_(AND NOT)_</option>
</select>
</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody">
<select name="m3">
<option value="a">_(All of the words:)_</option>
<option value="o">_(Any of the words:)_</option>
<option value="e">_(Exact phrase:)_</option>
<option value="p">_(Partial phrase:)_</option>
<option value="r">_(Regular expression:)_</option>
</select>
<input type="text" name="p3" size="16" value="2001" /></td>
<td class="searchboxbody"><select name="f3">
<option value="">_(any field)_</option>
<option value="abstract">_(abstract)_</option>
<option value="author">_(author)_</option>
<option value="collection">_(collection)_</option>
<option value="division">_(division)_</option>
<option value="experiment">_(experiment)_</option>
<option value="fulltext">_(fulltext)_</option>
<option value="keyword">_(keyword)_</option>
<option value="reference">_(reference)_</option>
<option value="reportnumber">_(report number)_</option>
<option value="subject">_(subject)_</option>
<option value="title">_(title)_</option>
<option value="year" selected="selected">_(year)_</option>
</select></td>
<td class="searchboxbody"><input class="formbutton" type="submit" name="action" value="_(Search)_" /><input class="formbutton" type="submit" name="action" value="_(Browse)_" />&nbsp;</td>
</tr>
</tbody>
</table>
</form>
</blockquote>
<p>Observis que si fem servir una sintaxi especial, la Cerca Simple
ofereix pràcticament la mateixa funcionalitat que l'avançada, tal i
com s'explica a les següents seccions d'aquesta guia. Aquest apartat
de cerca simple-versus-avançada no es refereix tant a les
funcionalitats disponibles, com a la forma de cerca que podem
configurar mitjançant petites modificacions. El sistema s'ha adaptat
a l'ús comú dels termes simple/avançat que podem trobar a altres
motors de cerca.</p>
<p>En realitat, gran part de l'explicació que segueix a continuació,
respòn a la qüestió de "com un usuari avançat pot utilitzar la
interficie de cerca simple". Recordem que en qualsevol moment és
posible recòrrer a l'ajuda de la cerca avanzada per aconseguir més
assistència.</p>
</ca>
</lang>
<h3><a name="guidance">
<lang>
<en>Search guidance</en>
<fr>Aide à la recherche</fr>
<de>Grundlagen</de>
<es>Búsqueda guiada</es>
<ca>Cerca guiada</ca>
</lang>
</a></h3>
<lang>
<en>
<p>After you submit your query, the search engine will analyze it and
will try to always guide you in case no exact match could be found.
For example, it would print you a list of closest indexed terms in
case of spelling troubles:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="elllis muon" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Alternative choices will be printed in red. The search engine
will similarly warn you when your search terms could not be
found, or when they could but your boolean query couldn't be met. The
search engine will also silently try to search for alternative forms
(e.g. remove punctuation), etc.</p>
<p>Thanks to multiple search stages and the guidance provided at each
stage, it is usually sufficient to simple type what you are looking
for and see what the system says in return. If you aren't satisfied,
you would then add/remove words from your query until the satisfactory
reply.</p>
</en>
<fr>
<p>Une fois votre requête envoyée, le moteur de recherche l'analyse et
tente de vous aider si aucune correspondance exacte ne peut être
trouvée. Il affiche, par exemple, une liste des termes indexés les
plus proches en cas d'orthographe approximative:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="elllis muon" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Les alternatives sont affichées en rouge. Similairement, le moteur
de recherche vous avertit lorsque votre requête ne peut aboutir, ou
lorsque les éléments de recherche existent mais ne correspondent pas à
votre requête booléenne. Le moteur de recherche essaie également de
trouver des formes alternatives (suppression de la ponctuation, etc.)
en arrière-plan.</p>
<p>Grâce aux multiples étapes de la recherche et à l'assistance
fournie à chaque stade, il suffit généralement de saisir les éléments
recherchés et de les adapter ensuite en fonction des réponses données
par le système. Si vous n'êtes pas satisfait du résultat, il vous est
alors possible de corriger votre requête petit à petit.</p>
</fr>
<es>
<p> Gracias al sistema de búsqueda en múltiples etapas y a la ayuda
que se proporciona en cada una de estas etapas, por lo general resulta
suficiente escribir la palabra de búsqueda y revisar los resultados
qué nos devuelve el sistema. Si los resultados no son pertinentes, o
son excesivos o insuficientes, es posible añadir o quitar palabras de
la consulta hasta conseguir resultados más satisfactorios.</p>
<p> Después de recibir una consulta, el motor de búsqueda la analiza
y, en caso de no localizar ninguna coincidencia en la base de datos,
el sistema tratará de guiar al usuario. Un ejemplo de ello son las
listas de los términos indexados que se encuentran más cercanos al
término introducido, en caso de que se produzacan errores ortográficos
o de transcripción al efectuar la consulta:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="elllis muon" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Las opciones alternativas se muestran en rojo. El motor de
búsqueda avisa de forma similar cuando detecta que los términos de
búsqueda no se encuentran, o cuando detecta que sí que existen pero la
fórmula de consulta booleana utilizada no puede localizarlos. Así
mismo, el motor de búsqueda también realiza de forma oculta acciones
alternativas para mejorar los resultados, como por ejemplo eliminar
puntuación, etc.</p>
</es>
<ca>
<p> Gràcies al sistema de cerca en múltiples etapes i a l'ajuda que es
va proporcionant a cadascuna d'aquestes etapes, pel general resulta
suficient teclejar l'objetcte de cerca i observar que ens retorna el
sistema. Si no quedem satisfets, podem afegir/treure paraules de la
nostra consulta fins aconseguir uns resultats satisfactoris.</p>
<p> Després de rebre una consulta, el motor de cerca l'analizarà i
tractarà sempre de guiar a l'usuari en cas de que no localitzi cap
coincidència a la base de dades. Un exemple d'això son les llistes
dels termes indexats que es troben més propers al terme introduït a la
consulta en cas que no es trobin coincidències per errors ortogràfics
o de transcripció:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="elllis muon" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Les opcions alternatives es mostren en vermell. El motor de cerca
avisa de forma similar quan detecta que els termes de cerca no es
troben, o quan detecta que si que existeixen però la fórmula de
consulta booleana emprada no aconsegueix localitzar-los. Així mateix,
el motor de cerca també realitza de forma oculta accions alternatives
per a millorar els resultat,s com, per exemple, eliminar puntuació,
etc.</p>
</ca>
</lang>
<h3><a name="words-vs-phrases">
<lang>
<en>Searching for words versus phrases</en>
<fr>Recherche de mots versus recherche de phrases</fr>
<de>Suche nach Wörtern und Wortgruppen</de>
<es>Búsqueda por palabras versus búsqueda por frases</es>
<ca>Cerca per paraules versus cerca per frases</ca>
</lang>
</a></h3>
<lang>
<en>
<p>The default search mode is a <strong>search for words</strong>. This
means that any whitespace you type is not significant, but is rather
interpreted to mean "add an automatic boolean AND between words", like
Google does. For example, to find all records that contain both the
word <em>ellis</em> and the word <em>muon</em> anywhere in the record,
type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis muon" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
The whitespace would be significant if you include it within quotes.
There are two phrase searching modes:
<ol>
<li>The double quotes instruct the search engine to <strong>search for
exact phrase</strong>. This phrase search mode will match if and
only if the given metadata field is exactly equal to the input
pattern. For example, to find all documents written by <em>Ellis,
J</em> spelled exactly that way, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="&quot;Ellis, J&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</li>
<li>The single quotes instruct the search engine to <strong>search for
partial phrase</strong>. Unlike the exact phrase search, this
mode allows for an extra text appearing before/after given
pattern. This is somewhat similar to the "phrase search mode"
common on Google and other fulltext engines that search for phrase
expressions inside Web pages. For example, to find all the titles
containing the expression <em>muon decay</em> regardless of the
position of the expression in the title, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="'muon decay'"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Now you see how to search for an author spelled sometimes as
<em>Ellis, J</em> and sometimes as <em>Ellis, Jonathan
Richard</em> (and other authors, such as <em>De Lellis, Jim</em>)
at the same time:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="'Ellis, J'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
(See also our specific <a href="#howto-search-for-author">author
searching tips</a>.)
</li>
</ol>
<p>The difference between exact and partial phrase searching modes may
not be obvious upon first look. While the latter is more similar to
what ``phrase search'' usually means in the context of web page search
engines, the former one is usually an order of magnitude faster if you
know the precise values you are looking for.</p>
<p>Another interesting searching mode besides the word and phrase
searches is the <strong>regular expression search</strong>, introduced
by slashes instead of quotes. For example, the above partial phrase
query <code>'muon decay'</code> is fully equivalent to the regular
expression query <code>/muon decay/</code>. The regular expression
syntax is very powerful and permits you to construct very complex
queries. For more information, please consult the <a
href="#regexp">regular expression</a> section of this guide.</p>
</en>
<fr>
<p>Le mode de recherche par défaut est la <strong>recherche de
mots</strong>. Cela signifie que les espaces entre les mots sont
ignorés. Ils sont interpretés dans le sens "ajouter
automatiquement un booléen AND (=<em>ET</em>) entre les mots", à la manière de
Google. Par exemple, pour trouver toutes les notices qui contiennent à la fois le mot
<em>ellis</em> et le mot <em>muon</em>, saisissez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis muon" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Les espaces sont significatifs si la requête est placée entre guillemets.
Il existe également deux modes de recherche de phrases:
<ol>
<li> Les guillemets doubles sont utilisés pour la <strong>recherche
de phrases exactes</strong>. Ce mode de recherche
ne trouvera de résultats que si, et seulement si une
correspondance exacte est trouvée pour le champ donné. Par
exemple, pour trouver tous les documents écrits par <em>Ellis,
J</em> (écrit exactement ainsi), saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="&quot;Ellis, J&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</li>
<li> Les guillemets simples sont utilisés pour la <strong>recherche
de phrases partielles</strong>. Contrairement à la recherche
de phrases exactes, ce mode permet de trouver une phrase même
si celle-ci est contenue dans un texte plus grand. Ce mode
s'apparente au mode de recherche commun à Google
et aux autres moteurs cherchant des phrases à l'intérieur de
pages Web. Par exemple, pour trouver tous les titres contenant
l'expression <em>muon decay</em> sans tenir compte de sa position
dans le titre, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="'muon decay'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Il est ainsi possible de rechercher un auteur dont le nom est
parfois écrit <em>Ellis, J</em> et parfois <em>Ellis, Jonathan
Richard</em> (et d'autres auteurs comme <em>De Lellis, Jim</em>):
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<blockquote>
<input type="hidden" name="ln" value="<lang:current />" />
<input size="40" type="text" name="p" value="'Ellis, J'"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
(Reportez-vous également la section dédiée à
la <a href="#howto-search-for-author">recherche d'auteurs</a>.)
</li>
</ol>
<p>La différence entre les modes de recherche de phrases exactes et
de phrases partielles n'est pas forcément évidente au premier
abord. Alors que le second mode correspond plutôt au mode de recherche
tel qu'on l'entend dans le contexte de la recherche de
pages Web, le premier mode est habituellement bien plus rapide si vous
connaissez la valeur précise de ce que vous cherchez.</p>
<p>Il existe également un autre mode de recherche, celui de
la <strong>recherche d'expressions régulières</strong>, introduit par
des barres obliques ('/') à la place des guillemets. Par
exemple, la requête <code>'muon decay'</code> ci-dessus est
totalement équivalente à la requête par expression
régulière <code>/muon decay/</code>. La syntaxe des expressions
régulières est très puissante et permet de construire des requêtes
complexes. Pour plus d'informations, veuillez consulter la section de
ce guide concernant les <a href="#regexp">expressions régulières</a>.</p>
</fr>
<es>
<p>El modo de búsqueda por defecto es la <strong>búsqueda por
palabras</strong>. Ello significa que se ignoran los espacios en
blanco, pero también que el motor de búsqueda lo interpreta por
defecto como "añade automáticamente una Y booleana entre las palabras
introducidas", del mismo modo que hace Google. Por ejemplo, para
recuperar todos los registros que contengan tanto la palabra
<em>ellis</em> como <em>muon</em> en cualquier parte del
registro, debemos escribir:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis muon" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Si queremos que el espacio en blanco sea significativo, debemos
escribir la sentencia entrecomillada. Existen dos formas de búsqueda
por frases:
<ol>
<li>La introducción de dobles comillas le indica al motor de búsqueda
<strong>buscar la frase exacta</strong>. Esta forma de búsqueda
por frase devolverá resultados sólo si los campos de metadatos
localizados son exactamente iguales al patrón solicitado. Por
ejemplo, para recuperar todos los documentos escritos por
<em>Ellis, J</em> escrito exactamente de ese modo, debemos
escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="&quot;Ellis, J&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</li>
<li>La introducción de comillas simples le indica al motor de búsqueda
<strong>buscar alguna parte de la frase</strong>. A diferencia de
la búsqueda de frase exacta este método permite que exista texto
antes/después del patrón solicitado. Sería algo similar al método
de "búsqueda por frase" que utilizan Google y otros motores de
búsqueda cuando buscan expresiones dentro del texto completo de
las páginas web. Por ejemplo, para localizar todos los títulos
que contengan la expresión <em>muon decay</em> independientemente
de la posición que ocupe la expresión en el título, escribiremos:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="'muon decay'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Observemos como se introduciría la búsqueda de un autor que a
veces aparece escrito como <em>Ellis, J</em> y otras como
<em>Ellis, Jonathan Richard</em> (incluyendo otros autores, como
<em>De Lellis, Jim</em>) en una misma acción de búsqueda:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="'Ellis, J'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
(Para más información, se recomienda ver también la búsqueda
específica por <a href="#howto-search-for-author">autor</a>.)
</li>
</ol>
<p>La diferencia entre buscar por frase exacta o buscar en parte de la
frase puede parecer poco obvia en un primer momento. Sin embargo,
mientras que la búsqueda en parte de la frase se asemeja a la búsqueda
por frase que suele utilizarse en el contexto de los buscadores web,
el uso de la búsqueda por frase exacta resulta mucho más efectiva y
rápida cuanto más se conocen y especifican los valores a recuperar.</p>
<p>Otro interesante modo de búsqueda aparte de la búsqueda por palabra
o por frase es el uso de la <strong>expresión regular de
búsqueda</strong>, que se efectúa a partir del uso de parentesis en
lugar de entrecomillado. Por ejemplo, la búsqueda anterior según el
modo de en parte de la frase <code>'muon decay'</code> es equivalente a
la expresión regular<code>/muon decay/</code>. La sintaxis de las
expresiones regulares es muy potente, y permite construir búsquedas
muy complejas. Para obtener más información, recomendamos la consulta
de la sección <a href="#regexp">expresión regular</a> de esta guia.</p>
</es>
<ca>
<p>El mode de cerca per defecte es la <strong>cerca per
paraules</strong>. Aquest mode implica s'ignorian els espais en
blanc, però també que el motor de cerca els interpreti per defecte com
a "afegeis automàticament una I booleana entre les paraules
introduïdes", de la mateixa forma que fa Google. Per exemple, per
recuperar tots els registres que continguin tant la paraula
<em>ellis</em> com <em>muon</em> a qualsevol part del registre,
haurem d'escriure:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis muon" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Si volem que l'espai en blanc sigui significatiu, hem d'escriure la
sentència entre cometes. Existeixen dues formes de cerca per frase:
<ol>
<li>La introducció de cometes dobles indica al motor de cerca
<strong>cerca la frase exacta</strong>. Aquesta forma de cerca per
frase retornara resultats només en el cas que els camps de metadades
localitzats siguin exactament igual al patró sol·licitat. Per exemple,
per recuperar tots els documents escrits per <em>Ellis, J</em> escrit
exactament d'aquesta manera, hem d'escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="&quot;Ellis, J&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</li>
<li>La introducció de comentes simples indica al motor de cerca
l'acció de <strong>cercar a alguna part de la frase</strong>. A
diferència de la cerca per frase exacta aquest mètode permet que
existeixi text abans/desprès del patró sol•licitat. Aquest métode és
similar al de "cerca per frase" que utilitzen Google i altres motors
de cerca quan cerquen expressions dins del text complet de les pàgines
web. Per exemple, per localitzar tots els títols que continguin
l'expressió <em>muon decay</em> independentment de la posició que
aquesta ocupi dins el títol, escriurem:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="'muon decay'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Observem como s’introduiria la cerca d’un autor que de vegades
apareix escrit com <em>Ellis, J</em> y altres com <em>Ellis,
Jonathan Richard</em> (incloent altres autors com <em>De Lellis,
Jim</em>) en una mateixa acció de cerca:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="'Ellis, J'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
(Per més informació, es recomana veure també la cerca específica
per <a href="#howto-search-for-author">autor</a>.)
</li>
</ol>
<p>La diferència entre cercar per frase exacta o cercar per part de la
frase Pot semblar poc evident a primera vista. Ara bé, mentre que la
cerca per part de la frase s’equipara a la cerca per frase que
acostuma a aplicar-se dins el context dels cercadors web, l’ús de la
cerca per frase exacta resulta molt més efectiva i ràpida com més es
coneguin i especifiquin els valors a recuperar.</p>
<p>Un altra interessant forma de cerca apart de la cerca per paraula o
frase, és l’ús de la <strong>expressió regular de cerca</strong>, que
es realitza a partir del ús de barres inclinades en lloc de cometes.
Per exemple, la cerca anterior segons el mode de ‘part de la frase’ de
l’expressió <code>'muon decay'</code> és equivalent a l’expressió
regular<code>/muon decay/</code>. La sintaxi de les expressions
regulars és molt potent, i permet construir cerques molt complexes.
Per obtenir més informació, recomanem la consulta de la secció <a
href="#regexp">expressió regular</a> d’aquesta guia.</p>
</ca>
</lang>
<h3><a name="boolean">
<lang>
<en>Boolean queries</en>
<fr>Requêtes booléennes</fr>
<de>Boolsche Suche</de>
<es>Búsquedas booleanas</es>
<ca>Cerques booleanes</ca>
</lang>
</a></h3>
<lang>
<en>
We have already seen how whitespace adds a silent boolean AND in the
search for words. The other boolean operators include:
<blockquote>
<table border="1" cellpadding="10">
<tr>
<td rowspan="3" align="center">
<strong>+<br/>AND</strong>
</td>
<td>
<code>ellis +muon</code>
</td>
<td>
matches all records that contain both the word
<em>ellis</em> and the the word <em>muon</em>
</td>
</tr>
<tr>
<td>
<code>ellis muon</code>
</td>
<td>
ditto, syntactic sugar
</td>
</tr>
<tr>
<td>
<code>ellis and muon</code>
</td>
<td>
ditto, syntactic sugar
</td>
</tr>
<tr>
<td rowspan="2" align="center">
<strong>-<br/>NOT</strong>
</td>
<td>
<code>ellis -muon</code>
</td>
<td>
matches all records that contain the word
<em>ellis</em> but that do not contain the word
<em>muon</em>
</td>
</tr>
<tr>
<td>
<code>ellis not muon</code>
</td>
<td>
ditto, syntactic sugar
</td>
</tr>
<tr>
<td rowspan="2" align="center">
<strong>|<br/>OR</strong>
</td>
<td>
<code>ellis |muon</code>
</td>
<td>
matches all records that contain at least one
of the words
</td>
</tr>
<tr>
<td>
<code>ellis or muon</code>
</td>
<td>
ditto, syntactic sugar
</td>
</tr>
</table>
</blockquote>
</en>
<fr>
Nous avons déjà vu comment les espaces ajoutaient silencieusement un <em>ET</em> booléen
dans la recherche par mots. Les autres opérateurs booléens sont:
<blockquote>
<table border="1" cellpadding="10">
<tr>
<td rowspan="3" align="center">
<strong>+<br/>AND</strong>
</td>
<td>
<code>ellis +muon</code>
</td>
<td>
(= ET) cherche toutes les notices contenant à la fois le mot
<em>ellis</em> et le mot <em>muon</em>
</td>
</tr>
<tr>
<td>
<code>ellis muon</code>
</td>
<td>
idem (simplification syntaxique)
</td>
</tr>
<tr>
<td>
<code>ellis and muon</code>
</td>
<td>
idem (simplification syntaxique)
</td>
</tr>
<tr>
<td rowspan="2" align="center">
<strong>-<br/>NOT</strong>
</td>
<td>
<code>ellis -muon</code>
</td>
<td>
(= NON) cherche toutes les notices qui contiennent le mot
<em>ellis</em> mais qui ne contiennent pas le mot
<em>muon</em>
</td>
</tr>
<tr>
<td>
<code>ellis not muon</code>
</td>
<td>
idem (simplification syntaxique)
</td>
</tr>
<tr>
<td rowspan="2" align="center">
<strong>|<br/>OR</strong>
</td>
<td>
<code>ellis |muon</code>
</td>
<td>
(= OU)
cherche toutes les notices qui contiennent au moins l'un des deux mots.
</td>
</tr>
<tr>
<td>
<code>ellis or muon</code>
</td>
<td>
idem (simplification syntaxique)
</td>
</tr>
</table>
</blockquote>
</fr>
<es>
Ya hemos visto como la inclusión de un espacio en blanco es
equivalente a la adición de un operador booleano en la búsqueda por
palabras. El resto de operadores booleanos son:
<blockquote>
<table border="1" cellpadding="10">
<tr>
<td rowspan="3" align="center">
<strong>+<br/>AND</strong>
</td>
<td>
<code>ellis +muon</code>
</td>
<td>
recupera todos los registros que contengan ambas palabras:
<em>ellis</em> y la palabra <em>muon</em>
</td>
</tr>
<tr>
<td>
<code>ellis muon</code>
</td>
<td>
ídem, "syntactic sugar"(*ver nota)
</td>
</tr>
<tr>
<td>
<code>ellis and muon</code>
</td>
<td>
ídem, "syntactic sugar"
</td>
</tr>
<tr>
<td rowspan="2" align="center">
<strong>-<br/>NOT</strong>
</td>
<td>
<code>ellis -muon</code>
</td>
<td>
recupera todos los registros que contengan la palabra
<em>ellis</em> pero que no contenga la palabra <em>muon</em>
</td>
</tr>
<tr>
<td>
<code>ellis not muon</code>
</td>
<td>
ditto, "syntactic sugar"
</td>
</tr>
<tr>
<td rowspan="2" align="center">
<strong>|<br/>OR</strong>
</td>
<td>
<code>ellis |acelerador</code>
</td>
<td>
recupera todos los registros que contengan al menos, una de las dos palabras
</td>
</tr>
<tr>
<td>
<code>ellis or muon</code>
</td>
<td>
ídem, "syntactic sugar"
</td>
</tr>
<tr>
<td colspan="2"><font size="1">(*)Nota de la traducción española: syntactic sugar es una espresión
utilizada en inglés para describir "otra forma similar de llamar a una función"</font></td>
</tr>
</table>
</blockquote>
</es>
<ca>
Hem vist com la inclusió d’un espai en blanc és equivalent a l’adició
d’un operador booleà a la cerca per paraules. La resta d’operadors
booleans són:
<blockquote>
<table border="1" cellpadding="10">
<tr>
<td rowspan="3" align="center">
<strong>+<br/>AND</strong>
</td>
<td>
<code>ellis +muon</code>
</td>
<td>
Recupera tots els registres que contenen ambdues paraules:
<em>ellis</em> i la paraula <em>muon</em>
</td>
</tr>
<tr>
<td>
<code>ellis muon</code>
</td>
<td>
ídem, "syntactic sugar" (*veure nota)
</td>
</tr>
<tr>
<td>
<code>ellis and muon</code>
</td>
<td>
ídem, "syntactic sugar"
</td>
</tr>
<tr>
<td rowspan="2" align="center">
<strong>-<br/>NOT</strong>
</td>
<td>
<code>ellis -muon</code>
</td>
<td>
recupera tots els registres que contenen la paraula <em>ellis</em> però que no contenen la paraula <em>muon</em>
</td>
</tr>
<tr>
<td>
<code>ellis not muon</code>
</td>
<td>
ídem, "syntactic sugar"
</td>
</tr>
<tr>
<td rowspan="2" align="center">
<strong>|<br/>OR</strong>
</td>
<td>
<code>ellis |muon</code>
</td>
<td>
recupera tots els registres que contenen, com a mínim, una de les dues paraules
</td>
</tr>
<tr>
<td>
<code>ellis or muon</code>
</td>
<td>
ídem, "syntactic sugar"
</td>
</tr>
<tr>
<td colspan="2"><sup>(*)</sup><font size="1">Nota de la traducció catalana: syntactic sugar es una espressió
emprada en anglès per a descriure "altre forma similar de cridar una funció"</font></td>
</tr>
</table>
</blockquote>
</ca>
</lang>
<lang>
<en>
<p>Logical operations are automatically chained from left to right.
For example, if you want to search for documents written by Ellis on
muons or kaons, write:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or kaon and ellis"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
which looks for <code>(muon or kaon) and ellis</code>. Note that this
gives different results from:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis and muon or kaon"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
which would search for <code>(ellis and muon) or kaon</code>.
<p>The left-to-right chaining behaviour permits you to easily refine
your search by adding/removing words with and/not or +/- operators.
For example, to exclude the documents on decay from the above search,
append <code>-decay</code>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or kaon and ellis -decay" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
to get a refined list. Keep adding/removing terms until you are
satisfied.
</en>
<fr>
<p>Les opérations logiques sont chaînées de gauche à droite.
Par exemple, si vous cherchez les documents écrits par Ellis sur les muons
ou les kaons, écrivez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or kaon and ellis"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
qui recherche pour <code>(muon or kaon) and ellis</code>. Notez que cela donne un résultat différent de:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis and muon or kaon"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
qui cherche pour <code>(ellis and muon) or kaon</code>
<p>Ce chaînage de gauche à droite permet de facilement améliorer votre
recherche en ajoutant/supprimant des mots avec les opérateurs and/not ou
+/-. Par exemple, pour exclure les documents sur le "decay" de la
recherche ci-dessus, ajoutez <code>-decay</code>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or kaon and ellis -decay" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
pour obtenir une liste plus précise. Ajoutez/supprimez d'autres termes jusqu'à satisfaction.
</fr>
<es>
<p>Los operadores lógicos se disponen de forma automática de izquierda
a derecha. Logical operations are automatically chained from left to
right. Por ejemplo, si queremos buscar documentos escritos por Ellis
sobre muon o kaon, escribiremos:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or kaon and ellis"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
ello nos realizará una búsqueda equivalente a <code>(muon O
kaon) Y ellis</code>. Nótese también que se obtienen
resultados diferentes si introducimos
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis and muon or kaon"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
que si introducimos <code>(ellis AND muon) OR kaon</code>.
<p>La disposición de los elementos de izquierda a derecha permite
refinar fácilmente una búsqueda añadiendo o quitando palabras
utilizanto los operadores AND/NOT +/-.
Por ejemplo, para excluir los documentos sobre desintegración en la
búsqueda anterior, sólo hay que añadir <code>-decay</code>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or kaon and ellis -decay" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
y con ello obtendremos una lista más filtrada. Se pueden añadir o
quitar tantos elementos como sea conveniente hasta conseguir mayor
precisión en la búsqueda.
</es>
<ca>
<p>Els operadors lògics es disposen de forma automàtica d’esquerre
a dreta. Per exemple, si es volen recuperar documents escrits per Ellis
sobre muon o kaon, escriurem:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or kaon and ellis"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
es realitzarà una cerca equivalent a <code>(muon OR kaon)
AND ellis</code>. Notis també que s’obtenen resultats diferents si
s’introdueix
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ellis and muon or kaon"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
que si s’introdueix <code>(ellis AND muon) OR kaon</code>.
<p>La disposició dels elements d’esquerre a dreta permet refinar
fàcilment una cerca afegint o traient paraules utilitzant els
operadors AND/NOT +/-.
Per exemple, per excloure els documents sobre desintegració a la cerca
anterior, només cal afegir <code>-decay</code>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or kaon and ellis -decay" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Amb el que s’obtindria una llista més filtrada. Es poden afegir o
traure tants elements com sigui convenient, fins aconseguir més
precisió a la cerca. </ca>
</lang>
<h3><a name="punctuation">
<lang>
<en>Special characters and punctuation</en>
<fr>Caractères spéciaux et ponctuation</fr>
<de>Spezielle Zeichen und Notation</de>
<es>Caracteres especiales y puntuación</es>
<ca>Caracters especials i puntuació</ca>
</lang>
</a></h3>
<lang>
<en>
<p>When indexing words, an attention is paid to index it both with and
without punctuation, so that you should be able to search for terms
containing special characters, such as <em>C++</em>, verbatim:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="C++" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="O'Shea"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
For example, to find records containing the LaTeX expression
<code>$e^{+}e^{-}$</code> in the title, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="$e^{+}e^{-}$" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
For example, to find document with the report number
<em>hep-ph/0204133</em>, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="hep-ph/0204133" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Note that the search is case-insensitive:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="BlaCK hOlEs" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>Lors de l'indexation des mots, nous veillons à les saisir avec et
sans ponctuation, afin qu'il soit possible de rechercher des termes
contenant des caractères spéciaux, comme dans <em>C++</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="C++" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="O'Shea"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Par exemple, pour trouver les notices contenant l'expression
LaTeX <code>$e^{+}e^{-}$</code> dans le titre, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="$e^{+}e^{-}$" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Par exemple, pour trouver le rapport avec le
numéro <em>hep-ph/0204133</em>, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<blockquote>
<input type="hidden" name="ln" value="<lang:current />" />
<input size="40" type="text" name="p" value="hep-ph/0204133" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Notez que la recherche est insensible à la casse:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="BlaCK hOlEs" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<es>
<p>Cuando se indexan las palabras, se pone especial atención en indexarlas
con y sin puntuación. Ello hace posible la búsqueda de términos que contienen
caracteres especiales, como por ejemplo <em>C++</em>, textualmente:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="C++" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="O'Shea"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Por ejemplo, para recuperar registros que contengan la expresión LaTeX
<code>$e^{+}e^{-}$</code> en el título, debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="$e^{+}e^{-}$" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Por ejemplo, para recuperar un documento con el número de informe
<em>hep-ph/0204133</em>, debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="hep-ph/0204133" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Nótese que esta búsqueda es sensible a las mayúsculas:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="AguJeros NEgRos" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
<ca>
<p>Quan s’indexen les paraules, es posa especial atenció en que siguin
indexades amb i sense puntuació. Això fa possible la cerca de termes
que contenen caràcters especials, com per exemple <em>C++</em>,
textualment:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="C++" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="O'Shea"/>
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Per exemple, per recuperar registres que contenen l’expresió LaTeX
<code>$e^{+}e^{-}$</code> al títol, cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="$e^{+}e^{-}$" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Per exemple, per recuperar un document amb número d'informe
<em>hep-ph/0204133</em>, cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="hep-ph/0204133" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Observis que aquesta cerca és sensible a les majúscules:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="ForaTs NEgRes" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
</lang>
<h3><a name="unicode">
<lang>
<en>International characters</en>
<fr>Caractères internationaux</fr>
<de>Internationale Zeichen</de>
<es>Caracteres internacionales</es>
<ca>Caràcters internacionals</ca>
</lang>
</a></h3>
<lang>
<en>
<p>The search engine works with Unicode UTF-8 so you can type your
query strings in any language stored in the database. For
example, to find the documents written by (or on) Пушкин, type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="пушкин" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Note that you don't have to type accents to find accented results. For example,
type <code>Lemaitre</code> to find papers by <em>Lemaître</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="Lemaitre" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
IMPORTANT NOTE
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
Currently, words that include accented characters can only be retrieved by entering
accented characters in the query.
</td>
</tr>
</tbody>
</table>
</en>
<fr>
<p> Le moteur de recherche prend en compte les caractères Unicode
UTF-8, afin de rendre possible la recherche dans n'importe quelle
langue. Par exemple, pour trouver les documents écrits par (ou
concernant) Пушкин, saisissez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="пушкин" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Notez que les accents sont optionnels. Par exemple, saisissez <code>Lemaitre</code> pour trouver des articles de <em>Lemaître</em>.
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="Lemaitre" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
NOTE IMPORTANTE
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
Actuellement, les mots qui contiennent des accents ne peuvent
être retrouvés qu'en entrant les accents dans la requête.
</td>
</tr>
</tbody>
</table>
</fr>
<es>
<p>El motor de búsqueda se basa en el estandar Unicode UTF-8, lo que
hace posible introducir cadenas de búsqueda en cualquier idioma que se
encuentre en la base de datos. Por ejemplo, para recuperar documentos
escritos por (o sobre) Пушкин, debemos escribir:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="пушкин" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Obsérvese que no es necesario introducir acentos para conseguir registros que contienen palabras acentuadas.
Por ejemplo, escribiremos <code>Lemaitre</code> para buscar artículos de <em>Lemaître</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="Lemaitre" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
NOTA IMPORTANTE
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
En estos momentos, las palabras que incluyen caracteres acentuados sólo pueden
recuperarse entrando los caracteres acentuados en la consulta.
</td>
</tr>
</tbody>
</table>
</es>
<ca>
<p>El motor de cerca es basa en l’estàndard Unicode UTF-8, lo que fa
possible introduir cadenes de cerca en qualsevol llengua que es trobi
a la base de dades. Per exemple, per recuperar documents escrits per
(o sobre) Пушкин, cal escriure:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="пушкин" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Notis que no és necessari introduir accents per aconseguir registres
que contenen paraules accentuades. Per exemple, escriurem <code>Lemaitre</code> para recuperar articles de <em>Lemaître</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="Lemaitre" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
NOTA IMPORTANT
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
En aquests moments, les paraules que inclouen caràcters accentuats només es poden
recuperar entrant els caràcters accentuats a la consulta.
</td>
</tr>
</tbody>
</table>
</ca>
</lang>
<h3><a name="wildcard">
<lang>
<en>Word truncation/stemming</en>
<fr>Troncature des mots/indexation par radicaux</fr>
<de>Trunkierung</de>
<es>Truncamientos y búsquedas por raíz</es>
<ca>Truncaments i cerques per arrel</ca>
</lang>
</a></h3>
<lang>
<en>
<p>The word truncation is supported via asterisk (*) wildcard
character. The wildcard instructs the search engine to match any
number of characters in that place. For example, to find records
that contain words <em>muon</em>, <em>muons</em>, <em>muonic</em>
etc, type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
The wildcard query works both in prefix and infix position. For
example, to get all the words that start by <em>CERN-TH</em> and
end by <em>31</em>, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="CERN-TH*31" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Note that the wildcard will be ignored if you try to apply it to
very short words, such as <em>a*</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="a*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
The wildcard character can be used also in the phrase searching
mode. For example, to find all the documents whose title starts by
"Neutrino mass", type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="&quot;Neutrino mass*&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recall that we have introduced exact and partial phrase search
modes. Actually, a partial phrase search mode launches an exact
search enclosed within wildcards: we could say that <code>'foo bar
baz'</code> equals to <code>"*foo bar baz*"</code>. Now you can
see why the partial phrase search is slow: due to the usage of two
asterisks in front and after the text, each and every title in the
database has to be looked up to determine whether it matches or
not. (There are currently no partial phrase indexes.)
</en>
<fr>
<p>La troncature des mots est symbolisée à l'aide d'un caractère de
remplacement (un astérisque (*)). Le caractère de remplacement
indique au moteur de recherche que le caractère à cet emplacement
peut être de n'importe quel type. Par exemple, pour trouver les
résultats contenant les
mots <em>muon</em>, <em>muons</em>, <em>muonic</em> etc, saisissez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Les requêtes à l'aide de caractères de remplacement fonctionnent
lorsque le caractère est placé en position de préfixe ou
d'infixe. Par exemple, pour obtenir tous les termes commençant
par <em>CERN-TH</em> et finissant par <em>31</em>, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="CERN-TH*31" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Notez que le caractère de remplacement ne sera pas pris en compte
s'il est inséré avant ou après une lettre isolée (a*, par
exemple):
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="a*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Le caractère de remplacement peut également être utilisé dans le
cadre de la recherche par phrase. Par exemple, pour trouver tous
les documents dont le titre commence par "Neutrino mass",
saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="&quot;Neutrino mass*&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Nous vous avons déjà expliqué les différences qui existent entre
les modes de recherche de phrases exactes et de phrases
partielles. On peut dire qu'un mode de recherche de phrases
partielles lance une recherche exacte incluse entre des caractères
de remplacement: <code>'foo bar baz'</code> est ainsi l'équivalent
de <code>'*foo bar baz*'</code>. Vous comprenez maintenant
pourquoi la recherche de phrases partielles est plus lente: en
raison de l'utilisation de deux astérisques au début et à la fin
du texte de recherche, tous les titres de la base de données
doivent être examinés afin de déterminer s'ils correspondent ou
non (il n'existe actuellement pas d'index des phrases
partielles).
</fr>
<es>
<p>El truncamiento de una palabra se efectua a través del uso del
asterisco (*). Este comodín indica al motor de búsqueda que
retorne cualquier número de carácteres en su lugar. Por ejemplo,
para recuperar registros que contengan las palabras
<em>muon</em>, <em>muonic</em>, <em>muons</em>
etc, debemos escribir:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
La búsqueda por comodín puede utilizarse tanto en posición de
prefijo como de sufijo. Por ejemplo, para recuperar todas las
palabras que comiencen por <em>CERN-TH</em> y acaben con
<em>31</em>, debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="CERN-TH*31" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Obsérvese que el comodín se ignorará si tratamos de utilizarlo en
palabras demasiado cortas, como por ejemplo <em>a*</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="a*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
El carácter comodín también puede usarse en el modo de búsqueda por
frase. Por ejemplo, para recuperar todos los documentos cuyo título
comience con "Neutrino mass", debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="&quot;Neutrino mass*&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recordamos que en esta búsqueda hemos introducido las formas de
'frase exacta' y 'parte de la frase'. En realidad, el modo de
búsqueda de 'parte de la frase' lanzará una búsqueda exacta sin
necesidad de utilizar los comodines: tanto podemos utilizar
<code>'foo bar baz'</code> como <code>"*foo bar baz*"</code>.
Esto ayuda a comprender porqué la búsqueda de
palabras en una parte de la frase es lenta: debido al uso de dos
asteriscos delante y detrás del texto, todos y cada de los
registros de la base de datos son comprobados para determinar si se
corresponden a la búsqueda o no. Actualmente no hay ningún índice
de búsqueda que indexe partes de frase.
</es>
<ca>
<p>El truncament d’una paraula es realitza a través de l’ús del símbol
asterisc (*). Aquest comodí indica al motor de cerca que retorni
qualsevol número de caràcters en el seu lloc. Per exemple, per
recuperar registres que continguin les paraules <em>astronomia</em>,
<em>astrònoms</em>, <em>astronòmics</em> etc, cal escriure:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="astronom*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
La cerca por comodí pot utilitzar-se tant en posició de prefix com
de sufix. Per exemple, per recuperar totes les paraules que
comencin per <em>CERN-TH</em> i acabin amb <em>31</em>, cal
escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="CERN-TH*31" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Notis que el comodí s’ignora si es tracta d’utilitzar-lo amb
paraules massa curtes, com per exemple <em>a*</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="a*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
El caràcter comodí també pot utilitzar-se al mode de cerca per
frase. Per exemple, per recuperar tots els documents el títol dels
quals comenci per "Neutrino mass", cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="&quot;Neutrino mass*&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title" selected="selected">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recordem que a aquesta cerca hem introduït les formes de 'frase
exacta' i 'part de la frase'. En realitat, el mode de cerca per
'part de la frase' ens llençarà una cerca exacta sense necessitat
de fer servir els comodins: tant podem utilitzar <code>'foo bar
baz'</code> com <code>"*foo bar baz*"</code>.
Això ajuda a comprendre perquè la cerca de
paraules a una part de la frase és lenta: degut a l’ús de dos
asteriscs davant i darrera del text, tots i cadascun dels registres
de la base de dades es comproven per determinar si es corresponen o
no amb la cerca introduïda. Actualment no hi ha cap índex de cerca
que indexi parts de frase.
</ca>
</lang>
<h3><a name="structured">
<lang>
<en>Structured metadata search</en>
<fr>Recherche structurée</fr>
<de>Strukturierte Metadatensuche</de>
</lang>
</a></h3>
<lang>
<en>
<p>Searching within various bibliograpic fields (such as title,
author) is supported via Google's <code>"site:"</code> like syntax.
If a search term is preceded by a field name and a colon, then the
term is searched for inside this field only. For example, to find
documents containing the word <em>ellis</em> within author index,
type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
To select documents written by <em>Ellis</em> that contain words
like <em>muon</em>, <em>muons</em>, <em>muonic</em> within title,
type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:ellis title:muon*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
To select documents written by the <em>NA60</em> experiment from
the year <em>2001</em>, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="experiment:NA60 year:2001" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
The most common fields you may want to use are
<code>author</code>, <code>title</code>,
<code>reportnumber</code>, <code>abstract</code>,
<code>keyword</code>, <code>year</code>, <code>experiment</code>,
<code>fulltext</code>, and <code>reference</code>.
</en>
<fr>
<p>La recherche au sein de différents champs bibliographiques (titre,
auteur, etc.) est prise en charge de manière similaire à la syntaxe
de Google. Si un élément de recherche est précédé d'un nom de champ
et de deux points, le terme n'est recherché que dans le champ
donné. Par exemple, pour trouver les documents contenant le mot
<em>ellis</em> dans l'index des auteurs, saisissez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Pour sélectionner les documents écrits par <em>Ellis</em> dont le
titre contient des mots tels que <em>muon</em>, <em>muons</em>,
<em>muonic</em>, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:ellis title:muon*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Pour sélectionner les documents écrits par l'expérience
<em>NA60</em> en <em>2001</em>, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="experiment:NA60 year:2001" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Les champs les plus fréquemment utilisés sont les suivants:
<code>author</code> (<em>auteur</em>), <code>title</code> (<em>titre</em>),
<code>reportnumber</code> (<em>num. de rapport</em>),
<code>abstract</code> (<em>résumé</em>), <code>keyword</code> (<em>mot-clef</em>),
<code>year</code> (<em>année</em>), <code>experiment</code> (<em>expérience</em>),
<code>fulltext</code> (<em>contenu</em>), and <code>reference</code>
(<em>référence</em>).
</fr>
<es>
<p>La búsqueda realizada utilizando varios campos bibliográficos
(tales como título, autor) se realiza a través de una sintaxis
similar a la que utiliza Google en sus búsquedas tipo
<code>"site:"</code>. Si un término de búsqueda es precedido por
un nombre de campo seguido por dos puntos este término se buscará
únicamente en el campo especificado. Por ejemplo, para recuperar
documentos que contienen la palabra <em>ellis</em> dentro del
índice de autores, debemos escribir:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Para recuperar documentos escritos por <em>Ellis</em> que contengan
palabras tales como <em>muon</em>, <em>muonic</em>,
<em>muons</em> dentro del título, debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:ellis title:muon*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Para recuperar documentos escritos por el experimento <em>NA60</em>
del año <em>2001</em>, debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="experiment:NA60 year:2001" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Los campos de uso más común son los siguientes:
<code>author</code>, <code>title</code>,
<code>reportnumber</code>, <code>abstract</code>,
<code>keyword</code>, <code>year</code>, <code>experiment</code>,
<code>fulltext</code>, and <code>reference</code>.
</es>
<ca>
<p>La cerca realitzada utilitzant diversos camps bibliogràfics (Tals
com títol, autor) es realitza a través d’una sintaxi similar a
l’emprada per Google en les Seves cerques tipus <code>"site:"</code>.
Si un terme de cerca es precedit per un nom de camp seguit per dos
punts aquest terme es cerca únicament dins el camp especificat. Per
exemple, per recuperar documents que contenen la paraula
<em>ellis</em> dins l’índex d’autors, cal escriure:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Per recuperar documents escrits per <em>Ellis</em> que continguin
paraules tals com <em>muon</em>, <em>muonic</em>,
<em>muons</em> dins el títol, cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:ellis title:muon*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Per recuperar documents escrits per l’experiment <em>NA60</em> de l’any <em>2001</em>, cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="experiment:NA60 year:2001" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Els camps d’ús comú amb aquesta tècnica són:
<code>author</code>, <code>title</code>,
<code>reportnumber</code>, <code>abstract</code>,
<code>keyword</code>, <code>year</code>, <code>experiment</code>,
<code>fulltext</code>, and <code>reference</code>.
</ca>
</lang>
<h3><a name="regexp">
<lang>
<en>Regular expressions</en>
<fr>Expressions régulières</fr>
<es>Expresiones regulares</es>
<ca>Expressions regulars</ca>
</lang>
</a></h3>
<lang>
<en>
<p>The regular expression searching mode is mostly for the power users
acquainted with the traditional Unix/POSIX regexp syntax. In the
Simple Search interface you can trigger it by using slashes instead of
quotes:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="title:/^E.*s$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
while in the Advanced Search interface you can select the matching
type explicitely by using the selection box menu. The above example
will find all the titles that start by the letter <em>E</em>, followed
by any number of any characters, and end by the letter <em>s</em>.
</en>
<fr>
<p>Les expressions régulières sont réservées à l'usage des
utilisateurs avancés, familiers de la syntaxe des regexp
Unix/POSIX. Dans le mode de Recherche Simple, on définit une
expression régulière en substituant le signe / aux guillemets:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="title:/^E.*s$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
alors que dans le mode de Recherche Avancée il est possible de
sélectionner explicitement la recherche par expressions régulières
dans la liste des types de recherches. L'exemple ci-dessus va
trouver tous les titres commençant pas la lettre <em>E</em>, suivi
par n'importe quel nombre de caractères et se terminant par la
lettre s.
</fr>
<es>
<p>El uso del modo de búsqueda por expresión regular está dirigida
sobre todo a usuarios avanzados que conocen la sintaxis tradicional
Unix/POSIX regexp. En el interfaz de consulta simple se puede forzar
este modo usando barras inclinadas en lugar de comillas:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="title:/^E.*s$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
mientras que en el modo de búsqueda Avanzado se puede seleccionar
explícitamente este modo de búsqueda seleccionandolo en el menú
desplegable. En el ejemplo anterior se recuperarían todos los títulos
que comienzan por la letra <em>E</em>, seguida de cualquier número o
cualquier carácter, y que terminen por la letra <em>s</em>.
</es>
<ca>
<p>L’ ús del mode de cerca per expressió regular està dirigida
sobretot a usuaris avançats que coneixen la sintaxi tradicional
Unix/POSIX regexp. A l’interficie de consulta simple es pot forçar
aquest mode utilitzant les Barres inclinades enlloc de comentes:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="title:/^E.*s$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Al mode de cerca Avançada es pot seleccionar explícitament aquest mode
de cerca seleccionant-lo al menú desplegable. a l’exemple anterior es
recuperarien tots els títols que comencen per la lletra <em>E</em>,
seguida de qualsevol número o qualsevol caràcter, i que acabin per la
lletra <em>s</em>.
</ca>
</lang>
<lang>
<en>
<p>Another example could be an author search for an author expressed
in the database as either <em>Ellis, J</em> or <em>Ellis, John</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:/^Ellis, (J|John)$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>Autre exemple: la recherche d'un auteur dont le prénom peut être
abrégé ou entier, comme <em>Ellis, J</em> ou <em>Ellis, John</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:/^Ellis, (J|John)$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<es>
<p>Otro ejemplo similar es el de la búsqueda por autor de un autor que
se ha introducido en la base de datos como <em>Ellis, J</em> o bien
como <em>Ellis, John</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:/^Ellis, (J|John)$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
<ca>
<p>Un altre exemple similar és el de la cerca per autor d’un autor que
ha estat introduït a la base de dades com <em>Ellis, J</em> o bé com
<em>Ellis, John</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:/^Ellis, (J|John)$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
</lang>
<lang>
<en>
<p>The regular expression search enables you to formulate very
specific word proximity queries. For example, let us find all titles
containing words <em>dense</em> and <em>matter</em> that are separated
by at most one word that doesn't contain the letter <em>l</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="title:/dense ([^ l]* )?matter/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>Une expression régulière vous permet de définir des paramètres de
proximité entre plusieurs termes. Par exemple, rechercher tous les
titres contenant les mots <em>dense</em> et <em>matter</em> séparés
par un seul mot ne contenant pas la lettre <em>l</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="title:/dense ([^ l]* )?matter/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<es>
<p>La búsqueda por expresión regular permite formular consultas de
palabras muy específicas por su proximidad. Por ejemplo, permite
recuperar registros cuyos títulos contienen las palabras
<em>dense</em> y <em>matter</em> y que estén separadas al menos por
una palabra que no contenga la letra <em>l</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="title:/dense ([^ l]* )?matter/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
<ca>
<p>La cerca per expressió regular permet formular consultes de
paraules Molt específiques per proximitat. Per exemple, permet
recuperar registres Els títols dels quals contenen les paraules
<em>dense</em> i <em>matter</em> i que estiguin separades, al menys,
per una paraula que no contingui la lletra <em>l</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="title:/dense ([^ l]* )?matter/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
</lang>
<lang>
<en>
<p>Note that you can also use character intervals such as
<code>[a-k]</code> and occurrence counts such as <code>{3}</code>.
For example, let us find all preprints that do not follow the year
cataloguing policy, that is <em>YYYY</em> to denote year, optionally
followed by <em>?</em> or by another <em>-YYYY</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="collection:PREPRINT -year:/^[0-9]{4}([\?\-]|\-[0-9]{4})?$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
You can use also character classes such <protect><code>[:alnum:]</code></protect>, so
that the above query is equivalent to:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="collection:PREPRINT -year:/^[[:digit:]]{4}([\?\-]|\-[[:digit:]]{4})?$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>A noter que vous pouvez utiliser des intervalles
comme <code>[a-k]</code> ou <code>[0-9]</code> et définir la longueur
du mot, <code>{3}</code> p.ex., chercher l'ensemble des "Preprints"
dont la date ne suit pas la règle de catalogage, soit <em>YYYY</em>
pour spécifier une l'année de publication, éventuellement suivi
par <em>?</em> ou une autre année <em>-YYYY</em> (p.ex. 2003-2004 sera
exclus) :</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="collection:PREPRINT -year:/^[0-9]{4}([\?\-]|\-[0-9]{4})?$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Il est aussi possible d'utiliser des "familles" de caractères, tel
que <protect><code>[:alnum:]</code></protect>, de telle sorte que la
requête ci-dessus est équivalente à:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="collection:PREPRINT -year:/^[[:digit:]]{4}([\?\-]|\-[[:digit:]]{4})?$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<es>
<p>Obsérvese que también pueden utilizarse carácteres de intervalo
como <code>[a-k]</code> y contadores de ocurrencia como
<code>{3}</code>. Por ejemplo, podemos recuperar todos los pre-prints
que no se corresponden con la política de catalogación <em>AAAA</em>
para describir los años, opcionalmente seguida por <em>?</em> o por
otro <em>-AAAA</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="collection:PREPRINT -year:/^[0-9]{4}([\?\-]|\-[0-9]{4})?$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
También pueden utilizarse otras clases de caracteres como
<protect><code>[:alnum:]</code></protect>, el cual en el ejemplo anterior sería
equivalente a:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="collection:PREPRINT -year:/^[[:digit:]]{4}([\?\-]|\-[[:digit:]]{4})?$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
<ca>
<p>Observis que també poden emprar-se caràcters d’interval com
<code>[a-k]</code> i contadors de concurrència com <code>{3}</code>.
Per ejemple, podem recuperar tots els pre-prints que no es
corresponguin amb la política de catalogació <em>AAAA</em> per
descriure anys, opcionalment seguida per <em>?</em> o per altre
<em>-AAAA</em>:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="collection:PREPRINT -year:/^[0-9]{4}([\?\-]|\-[0-9]{4})?$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
També poden emprar-se altres classe de caràcters com
<protect><code>[:alnum:]</code></protect>, que a l’exemple anterior seria
equivalent a:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="collection:PREPRINT -year:/^[[:digit:]]{4}([\?\-]|\-[[:digit:]]{4})?$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
</lang>
<lang>
<en>
<p>To learn more about POSIX regular expressions, please consult the
<a href="http://en.wikipedia.org/wiki/Regular_expression">Wikipedia
regexp article</a> and the <a
href="http://dev.mysql.com/doc/mysql/en/Regexp.html">MySQL regexp
documentation</a>.</p>
</en>
<fr>
<p>Pour plus d'informations sur les expressions régulières, voir les articles de
<a href="http://en.wikipedia.org/wiki/Regular_expression">Wikipedia</a>
et <a href="http://dev.mysql.com/doc/mysql/fr/Regexp.html">MySQL</a> à
ce sujet.</p>
</fr>
<es>
<p>Para conocer más acerca de las expresiones regulares POSIX, se
recomienda la consulta de <a
href="http://en.wikipedia.org/wiki/Regular_expression">Wikipedia
regexp article</a> y <a
href="http://dev.mysql.com/doc/mysql/en/Regexp.html">MySQL regexp
documentation</a>.</p>
</es>
<ca>
<p>Per conèixer més sobre les expressions regulars POSIX, es recomana
la Consulta de <a
href="http://en.wikipedia.org/wiki/Regular_expression">Wikipedia
regexp article</a> i <a
href="http://dev.mysql.com/doc/mysql/en/Regexp.html">MySQL regexp
documentation</a>.</p>
</ca>
</lang>
<h3><a name="span">
<lang>
<en>Span queries</en>
<fr>Requêtes de plages</fr>
<de>Bereichs-Recherche</de>
<es>Consultas por rango</es>
<ca>Consultes per rang</ca>
</lang>
</a></h3>
<lang>
<en>
<p>The span query is provided via a <code>-></code> sign. For
example, to search for all documents on <em>muon decay</em> published
between <em>1983</em> and <em>1992</em>, type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="30" type="text" name="p" value="muon decay year:1983->1992" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
To find all documents by authors with names ranging from <em>Ellis,
J</em> to <em>Ellis, Qqq</em>, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="30" type="text" name="p" value="author:&quot;Ellis, J&quot;->&quot;Ellis, Qqq&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>La requête de plages est disponible via le signe ->. Par exemple,
pour rechercher tous les documents relatifs au <em>muon decay</em>
publiés entre 1983 et 1992, saisissez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="30" type="text" name="p" value="muon decay year:1983->1992" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Pour trouver tous les documents dont l'orthographe du nom de l'auteur est comprise entre <em>Ellis,
J</em> to <em>Ellis, Qqq</em>, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="30" type="text" name="p" value="author:&quot;Ellis, J&quot;->&quot;Ellis, Qqq&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<es>
<p>La consulta por rango se realiza a través del signo
<code>-></code>. Por ejemplo, para recuperar todos los documentos
sobre <em>par motor</em> publicados entre <em>1983</em> y
<em>1992</em>, debemos escribir:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="30" type="text" name="p" value="destintegraci&oacute;n muon a&ntilde;o:1983->1992" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Para recuperar todos los documentos de autores cuyos nombres estén
dentro del rango que va de <em>Ellis, J</em> a <em>Ellis, Qqq</em>,
debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="30" type="text" name="p" value="autor:&quot;Ellis, J&quot;->&quot;Ellis, Qqq&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
<ca>
<p>La consulta por rang es realitza a través del signe
<code>-></code>. Per exemple, per recuperar tots els documents sobre
<em>par motor</em> publicats entre <em>1983</em> i <em>1992</em>, cal
escriure:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="30" type="text" name="p" value="destintegraci&oacute; muon any:1983->1992" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Per recuperar tots els documents d’autors els noms dels quals estiguin
dins El rang que va des de <em>Ellis, J</em> fins <em>Ellis, Qqq</em>,
cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="30" type="text" name="p" value="autor:&quot;Ellis, J&quot;->&quot;Ellis, Qqq&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
</lang>
<h3><a name="combined">
<lang>
<en>Combined metadata/fulltext/citation search</en>
<fr>Recherche combinée métadonnées/fulltext/citation</fr>
<de>Kombinierte Metadaten-/Volltext-/Zitatsuche</de>
<es>Combinación de metadatos/texto completo/cita bibliográfica</es>
<ca>Combinació de metadades/text complet/referència bibliogràfica</ca>
</lang>
</a></h3>
<lang>
<en>
<p>All the syntax mentioned above can be combined together in one
query. For example, to find documents that have the word
<em>ellis</em> inside author fields, that do not contain words like
<em>muon</em>, 'muonic' etc in any field, that contain the phrase
(or the substring, to be more precise) 'dense quark matter' inside
abstract fields, and that were published in year starting by digits
'200', type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:ellis -muon* +abstract:'dense quark matter' year:200*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Note that the default "any field" global index does contain only the metadata terms,
not the citation nor fulltext terms. You have to explicitely mention <code>fulltext</code>
or <code>reference</code> index to search there. For example, to find the term <em>Higgs</em>
in either metadata, references or fulltext files, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="higgs or reference:higgs or fulltext:higgs" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
This permits an interesting combination of metadata, fulltext and citation search in
the same query. For example, to get all documents written by
<em>Lin</em> whose fulltext files contain the words
<em>Schwarzschild</em> and <em>AdS</em>, and who cite journal
<em>Adv. Theor. Math. Phys.</em>, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:lin fulltext:Schwarzschild fulltext:AdS reference:&quot;Adv. Theor. Math. Phys.&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>Les syntaxes détaillées ci-dessus peuvent être combinées dans une
seule requête. Par exemple, pour trouver les documents dont le
champ d'auteur contient le mot <em>ellis</em>, dont aucun des
champs ne contient des mots tels que <em>muon</em>, 'muonic'
etc. et dont le champ de résumé contient l'expression (ou
sous-chaîne, pour être plus précis) 'dense quark matter', et qui
ont été publiés dans les années 2000 et plus, saisissez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:ellis -muon* +abstract:'dense quark matter' year:200*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Notez que la recherche dans "tous les champs" ne s'effectue que sur les métadonnées, et non pas sur les citations, ni le contenu des documents. Il vous faut explicitement indique une recherche dans les champs <code>fulltext</code>
ou <code>reference</code> pour chercher dans ces données. Par exemple, pour trouver le terme <em>Higgs</em> dans les métadonnées, les références et le contenu des fichiers, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="higgs or reference:higgs or fulltext:higgs" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Ceci permet de combiner la recherche dans les métadonnées, le
contenu des fichiers et les citations dans une seule requête. Par
exemple, pour trouver tous les documents écrits par
<em>Lin</em> dont les fichiers contiennent les mots
<em>Schwarzschild</em> et <em>AdS</em>, et citant le journal
<em>Adv. Theor. Math. Phys.</em>, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:lin fulltext:Schwarzschild fulltext:AdS reference:&quot;Adv. Theor. Math. Phys.&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<es>
<p>Toda la sintaxis mencionada anteriormente se puede combinar en una
misma consulta. Por ejemplo, para recuperar documentos que contienen
la palabra <em>ellis</em> dentro del campo autor, y que no contienen
palabras como <em>muon</em>, 'muonic', etc., en ningún otro
campo, y que contienen (o subcadena de palabras, para ser más
precisos) ‘dense quark matter’ dentro del campo de resumen, y que han sido
publicados dentro de los años que comienzan por los dígitos '200',
debe escribirse:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:ellis -muon* +abstract:'dense quark matter' year:200*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Obsérvese que la opción de índice global “cualquier campo” por defecto
contiene únicamente términos de metadatos, no del texto completo ni de
las citas bibliográficass. Es necesario especificar explícitamente
las opciones de buscar en el índice <code>texto completo</code> o de
<code>cita bibliográfica</code> si se desea buscar en estos campos.
Por ejemplo, para recuperar el término <em>Higgs</em> tanto dentro de
los metadatos, las citas o el texto completo, debe escribirse:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="higgs or reference:higgs or fulltext:higgs" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Ello permite una interesante combinación de metadatos, texto completo
y citas dentro de una misma búsqueda. Por ejemplo, para recuperar
todos los documentos escritos por <em>Lin</em> que en el texto
completo contengan las palabras <em>Schwarzschild</em> y <em>AdS</em>,
que citen la revista <em>Adv. Theor. Math. Phys.</em>, debe
escribirse:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:lin fulltext:Schwarzschild fulltext:AdS reference:&quot;Adv. Theor. Math. Phys.&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
<ca>
<p>Tota la sintaxi mencionada anteriorment es pot combinar en una
mateixa consulta. Per exemple, per recuperar documents que contenen
la paraula <em>ellis</em> dins el camp d’autor, i que no contenen
paraules com <em>muon</em>, 'muonic', etc., a cap altre
camp, i que contenen la frase (o subcadena de paraules, per ser més
precisos) ‘dense quark matter’ dins el camp de resum, i que han estat
publicats dins els anys que comencen pels dígits '200', cal escriure:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:ellis -muon* +abstract:'dense quark matter' year:200*" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Observis que l’opció d’índex global “qualsevol camp” per defecte conté
només els termes de metadades, no del text complet ni de les
referències bibliogràfiques. Cal especificar explícitament les
opcions de cercar a l’índex <code>text complet</code> o de
<code>referència bibliogràfica</code> si volem cercar per aquests
camps. Per exemple, per recuperar el terme <em>Higgs</em> tant dins
les metadades, les referències o el text complet, cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="higgs or reference:higgs or fulltext:higgs" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Això permet una interessant combinació de metadades, text complet i
referències dins una mateixe cerca. Per exemple, per recuperar
tots els documents escrits per <em>Lin</em> que en el text complet
continguin les paraules <em>Schwarzschild</em> and <em>AdS</em>,
que citi la revista <em>Adv. Theor. Math. Phys.</em>, cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:lin fulltext:Schwarzschild fulltext:AdS reference:&quot;Adv. Theor. Math. Phys.&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
</lang>
<h3><a name="howto">
<lang>
<en>Frequently asked questions</en>
<fr>Foire aux questions</fr>
<de>FAQ</de>
<es>Preguntas frecuentes</es>
<ca>Preguentes freqüents</ca>
</lang>
</a></h3>
<h4><a name="howto-choose-terms">
<lang>
<en>How to wisely choose your search terms (speed-wise)</en>
<fr>Comment sélectionner vos termes de recherche de manière intelligente (en termes de vitesse)</fr>
<de>Wie wähle ich am geschicktesten meinen Suchbegriff</de>
<es>Escoger correctamente los términos de búsqueda (speed-wise)</es>
<ca>Escollir correctament els térmes de cerca (speed-wise)</ca>
</lang>
</a></h4>
<lang>
<en>
<ul>
<li>Whenever possible, prefer word searches instead of phrase searches.
Search rather for <code>black hole</code> than for <code>"black hole"</code>.</li>
<li>Avoid common terms such as <code>and</code>, <code>of</code>, or <code>CERN</code>.</li>
<li>If you are searching for a specific metadata information, such
as a <em>report number</em>, choose corresponding index.</li>
<li>If you are looking for a specific document collection, such
as <em>Theses</em>, choose the
- <a href="<WEBURL>/?c=Theses">Theses</a> collection first, and start your search from there.</li>
+ <a href="<CFG_SITE_URL>/?c=Theses">Theses</a> collection first, and start your search from there.</li>
</ul>
</en>
<fr>
<ul>
<li>Dans la mesure du possible, recherchez plutôt des termes que des
phrases. Recherchez plutôt <code>black hole</code>
que <code>"black hole"</code>.</li>
<li>Evitez des termes courants tels
que <code>et</code>, <code>de</code> ou <code>CERN</code>.</li>
<li>Si vous recherchez une information spécifique au niveau des
métadonnées, telle qu'un numéro de rapport, sélectionnez l'index
correspondant.</li>
<li>Si vous recherchez une collection spécifique de documents
(Thèses, par exemple), commencez la recherche dans la collection
- <a href="<WEBURL>/?c=Theses&amp;ln=fr">Thèses</a>.</li>
+ <a href="<CFG_SITE_URL>/?c=Theses&amp;ln=fr">Thèses</a>.</li>
</ul>
</fr>
<es>
<ul>
<li>Siempre que sea posible, es preferible realizar búsquedas por
palabra antes que por frase. Es preferible buscar por <code>agujero
negro</code> que por <code>"agujero negro"</code>.</li>
<li>Se aconseja evitar vocablos comunes tales como <code>y</code>,
<code>de</code>, o siglas como <code>CERN</code>.</li>
<li>Si la búsqueda versa sobre información específica en los
metadatos, como por ejemplo, <em>número de registro</em>, seleccionar
el índice correspondiente.</li>
<li>Si la búsqueda versa sobre una colección específica de documentos,
como por ejemplo, <em>Tesis</em>, elegir en primer lugar la colección
-<a href="<WEBURL>/?c=Theses">Tesis</a>, e iniciar la búsqueda a partir
+<a href="<CFG_SITE_URL>/?c=Theses">Tesis</a>, e iniciar la búsqueda a partir
de aquí.</li>
</ul>
</es>
<ca>
<ul>
<li>Sempre que sigui possible, es preferible realitzar cerques per
paraula abans que per frase. Es preferible una cerca per <code>forat
negre</code> que per <code>"forat negre"</code>.</li>
<li>S’aconsella evitar mots comuns tals com les
preposicions<code>i</code>, <code>de</code>, o sigles com
<code>CERN</code>.</li>
<li>Si la cerca versa sobre informació específica a les metadades, com
per exemple, <em>número de registre</em>, cal seleccionar l’índex
corresponent.</li>
<li>Si la cerca versa sobre una col•lecció específica de documents,
com per exemple, <em>Tesis</em>, cal escollir en primer lloc la
-col•lecció <a href="<WEBURL>/?c=Theses">Tesis</a>, e iniciar la cerca
+col•lecció <a href="<CFG_SITE_URL>/?c=Theses">Tesis</a>, e iniciar la cerca
a partir de aquí.</li>
</ul>
</ca>
</lang>
<h4><a name="howto-search-for-author">
<lang>
<en>How to search for publications by a given author</en>
<fr>Comment rechercher les publications d'un auteur donné</fr>
<de>Wie suche ich nach Publikationen eines bestimmten Autors</de>
<es>¿Como localizar publicaciones a partir del autor?</es>
<ca>Com localitzar publicacions a partir de l'autor?</ca>
</lang>
</a></h4>
<lang>
<en>
<p>You can search for an author in many ways, each having its own
advantages and disadvantages.</p>
<ol>
<li>First of all, note that searching for words isn't usually what you
would want here. If you choose to search for the words <code>Ellis
J</code> within the author index, it means that two queries (for the
words <code>Ellis</code> and <code>J</code>) are effected first and a
boolean AND is performed next:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="Ellis J" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Such a query would match also a document whose first author is
<em>Ellis, R</em> and the second author <em>Finch, A J</em>, which is
probably not what you wanted. While the search is very fast and you
would have found the results for the author you were looking for, such
a technique could have returned you many false positives, as the one
cited above. Instead of searching for words, a more suitable
technique to apply in this case is to search for phrases which will
permit you to achieve higher search precisions.</p>
</li>
<li>The author names are usually stored in a form containing initials
only, such as <em>Ellis, J</em>. To get the list of publications of
an author whose name is spelled exactly that way, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:&quot;Ellis, J&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>Il existe différentes méthodes de recherche d'un auteur. Chaque
méthode a ses avantages et ses inconvénients.</p>
<ol>
<li>Tout d'abord, notez que les recherches basées sur des termes ne
sont généralement pas adaptées. Si vous recherchez les mots
<code>Ellis J</code> dans le champ de l'auteur, deux requêtes (pour
les mots <code>Ellis</code> et <code>J</code>) sont d'abord
effectuées. Le booléen ET n'est utilisé que par la suite:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="Ellis J" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Une telle requête inclut également les documents dont le premier
auteur est <em>Ellis, R</em> et le second <em>Finch, A J</em>. Or il
est possible qu'il ne s'agisse pas du résultat que vous recherchez. La
recherche est très rapide et peut vous présenter un certain nombre de
résultats correspondant à l'auteur recherché. Il est néanmoins
également possible qu'une telle technique vous renvoie de nombreuses
réponses incorrectes, comme celle susmentionnée. Dans un tel cas, il
est plus adapté de rechercher des phrases plutôt que des
termes. Vous pouvez ainsi affiner votre recherche.</p>
</li>
<li>Les noms d'auteurs sont généralement stockés sous une forme
contenant uniquement des initiales, telle que <em>Ellis, J</em>. Pour
obtenir la liste des publications d'un auteur dont le nom est
orthographié de cette manière, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:&quot;Ellis, J&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<es>
<p>La búsqueda por autor puede realizarse de diversas formas, cada una
de las cuales presenta sus ventajas y desventajas.</p>
<ol>
<li>En primer lugar, es necesario anotar que el modo de búsqueda por
palabras no es el que más adecuado en este tipo de búsqueda. Si se
selecciona la búsqueda por palabras <code>Ellis J</code> dentro del
índice de autores, ello se interpretará como dos consultas (para las
palabras <code>Ellis</code> y <code>J</code>) que se efectuarán en
primer lugar, y una booleana Y que se realizará después.
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="Ellis J" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>La consulta realizada de este modo recuperará también un documento
que presenta como primer autor a <em> Ellis, R </em> y como segundo
autor <em>Finch, A J</em>, lo cual, probablemente, no es lo deseado.
La búsqueda se ha realizado muy rápidamente y se han recuperado
registros sobre el autor requerido, sin embargo, los resultados
recuperados siguiendo este método pueden no ser pertinentes, tal y
como ocurre en el ejemplo citado anteriormente. En lugar de buscar por
palabras, la técnica más conveniente que debe aplicarse en este caso
es la búsquda por frase, que permite conseguir mayor precisión.</p>
</li>
<li>Es habitual que los nombres de autores que se introducen a través
de los formularios contengan únicamente la inicial del nombre, como
por ejemplo, <em>Ellis, J</em>. Para obtener un listado de las
publicaciones de dicho autor con el nombre escrito exactamente de ese
modo, debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:&quot;Ellis, J&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
<ca>
<p>La cerca per autor es pot realitzar de diverses formes, cadascuna
de les quals presenta els seus avantatges i les seves desavantatges.</p>
<ol>
<li>En primer lloc, cal notar que el mode de cerca per paraules no és
el més adient en aquests casos. Si es selecciona la cerca per paraules
<code>Ellis J</code> dins l’índex d’autors, s’interpretarà como a dues
consultes (per a les paraules <code>Ellis</code> i <code>J</code>) que
es realitzaran en primer lloc, i una booleana I que es realitzarà
després.
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="Ellis J" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>La consulta realitzada d’aquesta forma recuperarà també un document
que tingui com a primer autor a <em> Ellis, R </em> i com a segon
autor a <em>Finch, A J</em>, fet que, probablement, no es el desitjat.
La cerca s’ha realitzat amb molta rapidesa i s’han recuperat registres
sobre l’autor requerit, ara bé, els resultats obtinguts seguint aquest
métode poden no ser pertinents, tal i com passa a l’exemple citat
anteriorment. En lloc de cercar per paraules, la tècnica més
convenient que s’ha d’aplicar en aquests casos és la cerca per frase,
que permet aconseguir major grau de precisió.</p>
</li>
<li>Es habitual que els noms d’autors que s’introdueixen a través dels
formularis continguin únicament la inicial del nom, com per exemple,
<em>Ellis, J</em>. Per obtenir un llistat de les publicacions de tal
autor amb el nom escrit exactament d’aquesta manera, cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:&quot;Ellis, J&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
</lang>
<lang>
<en>
<p>This way of searching gives you the highest precision and no false
positives. (Assuming there are no other authors whose names are
spelled <em>Ellis, J</em>, an assumption that is often false<a
href="#author-full-names"><sup>*</sup></a>.) The search is very fast.</p>
</li>
<li> Sometimes an author's first name may be spelled abbreviated on
some documents (such as <em>Ellis, J</em>) and sometimes full on
others (such as <em>Ellis, John</em>; eventually also with the middle
name: <em>Ellis, John Rolfe</em>). To get the list of publications
for all these forms at the same time, you could use a boolean OR
query:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:&quot;Ellis, J&quot; or author:&quot;Ellis, John&quot; or author:&quot;Ellis, John Rolfe&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>This way of searching still keeps the highest precision and no
false positives. (Assuming there are no other authors whose names are
spelled <em>Ellis, J</em> or <em>Ellis, John</em>, an assumption that
is often false<a href="#author-full-names"><sup>*</sup></a>.) The search is
fast.</p>
</li>
<li>To match all of the above forms in a single search term, you can
try to use a wildcard query:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:&quot;Ellis, J*&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>It would match all author names that start by the text
<code>Ellis, J</code>, i.e. not only the wanted forms <em>Ellis,
J</em> and <em>Ellis, John</em>, but also <em>Ellis, Jim</em>, or
<em>Ellis, John Rolfe</em>, or <em>Ellis, Jonathan Richard</em>.</p>
<p>This way of searching returns you more results, which may be
suitable in case you don't know how the names are spelled in the
database. But you also risk the eventuality of getting false
positives. The search is relatively fast.</p>
</li>
<li>Yet another, the most general alternative is to use a partial
phrase matching:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:'Ellis, J'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>It would find not only all the authors mentioned above, but also
the ones whose names contain the expression <code>Ellis, J</code>
anywhere inside the name, such as <em>De Lellis, Jim</em>. It thus
gives you the largest possible number of hits at the largest risk of
false positives. The search is relatively slow.</p>
<p>(Note though that this way of searching may be very handy in case
of compound family names such <em>Pepe-Altarelli, M</em> or <em>'t
Hooft, G</em> where a casual user query for <em>Hooft, G</em> would
match the wanted author, unlike the methods mentioned above.)</p>
</li>
<li>Finally, let us note that you can use the <a href="#regexp">regular
expression syntax</a> to construct any complex author query. A simple
example is to search for an author expressed in the database as either
<em>Ellis, J</em> or <em>Ellis, John</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:/^Ellis, (J|John)$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Please consult <a href="#regexp">regular expression searching tips</a>
to know more about regular expression search possibilities.
</li>
</ol>
<p><a name="author-full-names"><strong><sup>*</sup>NOTE:</strong></a>
If you produce your own list of publications and you notice that
sometimes your first name is spelled abbreviated and sometimes in
full, or if you want to identify your publications among several
authors with the same abbreviation, please contact the <a
href="mailto:<CFG_SITE_ADMIN_EMAIL>">administrators</a> of <CFG_SITE_NAME_INTL> so that
they could work with you on inputting a consistently spelled and
properly formatted first name everywhere. Only the consistent
database content will ensure a proper author searching behaviour.</p>
</en>
<fr>
<p>Cette méthode de recherche vous permet de procéder à une recherche
très précise et d'éviter les réponses incorrectes (si l'on part du
principe, souvent erroné, qu'aucun autre auteur dont le nom est
orthographié <em>Ellis, J</em> existe dans la base de données). La
recherche est très rapide.</p>
</li>
<li> Il arrive que le prénom de l'auteur soit abrégé sur certains
documents (<em>Ellis, J</em>) et complet sur d'autres (<em>Ellis,
John</em>), éventuellement accompagné d'un second prénom (<em>Ellis,
John Rolfe</em>). Pour obtenir la liste de publications correspondant
à l'ensemble de ces formes, vous pouvez utiliser une requête booléenne
OU:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:&quot;Ellis, J&quot; or author:&quot;Ellis, John&quot; or author:&quot;Ellis, John Rolfe&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Cette méthode de recherche vous permet toujours de procéder à une
recherche très précise et d'éviter les réponses incorrectes (si l'on
part du principe, souvent erroné, qu'aucun autre auteur dont le nom
est orthographié <em>Ellis, J</em> ou <em>Ellis, John</em> existe dans la base de
données <a href="#author-full-names"><sup>*</sup></a>). La recherche est rapide.</p>
</li>
<li>Pour rassembler l'ensemble des formes susmentionnées dans une
seule recherche de termes, vous pouvez baser la requête sur un
caractère de remplacement:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:&quot;Ellis, J*&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>L'ensemble des noms d'auteurs qui commencent par
<code>Ellis, J</code>, i.e. non seulement les formes recherchées <em>Ellis,
J</em> et <em>Ellis, John</em>, mais aussi <em>Ellis, Jim</em>, ou
<em>Ellis, John Rolfe</em>, ou <em>Ellis, Jonathan Richard</em> est affiché.</p>
<p>Ce mode de recherche permet d'obtenir plus de résultats. Il peut
être utile lorsque vous ne connaissez pas l'orthographe des noms dans
la base de données. Vous risquez néanmoins d'obtenir des réponses
incorrectes. La recherche est relativement rapide.</p>
</li>
<li>L'alternative la plus fréquemment utilisée reste néanmoins la
recherche de phrases partielles:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:'Ellis, J'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Ce mode permet non seulement de trouver tous les auteurs mentionnés
ci-dessus mais également ceux contenant l'expression <code>Ellis,
J</code> au sein du nom, tels que <em>De Lellis, Jim</em>. Vous
disposez ainsi du plus grand nombre possible de résultats. Le risque
de réponses incorrectes est néanmoins très élevé. La recherche est
relativement lente.</p>
<p>(Notez que cette méthode de recherche peut néanmoins s'avérer très
utile lors de la recherche de noms de famille composés tels que
<em>Pepe-Altarelli, M</em> or <em>'t Hooft, G</em>, où une requête
normale pour <em>Hooft, G</em> permet de trouver l'auteur recherché,
contrairement aux méthodes susmentionnées).</p>
</li>
<li>Finalement, notez que vous pouvez utiliser
les <a href="#regexp">expressions régulières</a> pour construire
n'importe quelle recherche d'auteur, même complexe. Un exemple simple
est la recherche d'un auteur dont le nom est archivé en tant
que <em>Ellis, J</em> ou <em>Ellis, John</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:/^Ellis, (J|John)$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Consultez les astuces sur les <a href="#regexp">expressions
régulières</a> pour en apprendre plus sur les possibilités qu'elles
offrent.
</li>
</ol>
<p><a
name="author-full-names"><strong><sup>*</sup>Remarque:</strong></a> si
vous créez votre propre liste de publications et remarquez que le
prénom est parfois épelé de manière abrégée et d'autres fois de
manière complète ou si vous souhaitez identifier vos publications
parmi celles de différents auteurs disposant du même nom en abrégé,
veuillez <a href="mailto:<CFG_SITE_ADMIN_EMAIL>">contacter les
administrateurs</a> de <CFG_SITE_NAME_INTL> de manière à ce qu'ils vous aident
à saisir les prénoms de manière correcte et cohérente dans l'ensemble
de la base de données. Une base de données au contenu cohérent
garantit des résultats satisfaisants lors de la recherche d'auteurs.</p>
</fr>
<es>
<p>Esta técnica de búsqueda confiere mayor precisión y evita falsos
resultados aparentemente positivos. (La presunción de que no existen
otros autores cuyo nombre se escriba como <em>Ellis, J</em>, en
ocasiones puede no ser cierta<a
href="#author-full-names"><sup>*</sup></a>.) La búsqueda se realizará
con mucha rapidez.</p>
</li>
<li> Algunas veces, en algunos documentos, el nombre propio de un
autor se escribe de forma abreviada, (como <em>Ellis, J</em>) y otras
de forma completa (como <em>Ellis, John</em>; y en algunas ocasiones
también con el nombre compuesto:<em>Ellis, John Rolfe</em>). Para
obtener una lista de publicaciones del autor con todas sus formas en
una misma búsqueda, se puede utilizar el operador booleano O:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:&quot;Ellis, J&quot; or author:&quot;Ellis, John&quot; or author:&quot;Ellis, John Rolfe&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Este modo de búsqueda nos ofrece aún mayor precisión y evita falsos
resultados positivos (o no pertinentes). (La presunción de que no hay
más autores que se escriban como <em>Ellis, J</em> O <em>Ellis,
John</em>, en ocasiones puede no ser cierta <a
href="#author-full-names"><sup>*</sup></a>.) La búsqueda es muy
rápida.</p>
</li>
<li>Para localizar todas las formas introducidas en el ejemplo
anterior con un único término de búsqueda, podemos probar de utilizar
una búsqueda con comodín:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:&quot;Ellis, J*&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Ello recuperará todos los nombres de autor que comienzan con el
texto <code>Ellis, J</code>, no sólo con las formas requeridas en el
ejemplo <em>Ellis, J</em> y <em>Ellis, John</em>, sino también
<em>Ellis, Jim</em>, o <em>Ellis, John Rolfe</em>, o <em>Ellis,
Jonathan Richard</em>.</p>
<p>Esta búsqueda retornará muchos más resultados, pero puede resultar
conveniente en el caso en que se desconozca la forma de introducción
de los nombres en la base de datos. Sin embargo, aumenta el riesgo de
obtener resultados poco pertinentes. La búsqueda es relativamente rápida.</p>
</li>
<li>Aparte de esta última, la alternativa general más utilizada es el
uso de una búsqueda por parte de la frase:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:'Ellis, J'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Esta búsqueda recupera no sólo todos los autores mencionados, sino
también todos aquellos cuyos nombres contengan la expresión
<code>Ellis, J</code> en cualquier parte de su nombre, como por
ejemplo <em>De Lellis, Jim</em>. Ello aumenta enormemente las
posibilidades de obtener registros con un alto riesgo de ser poco
pertinentes. La búsqueda es relativamente lenta.</p>
<p>(Obsérvese que este modo de búsqueda puede resultar muy práctico
para apellidos compuestos tales como <em>Pepe-Altarelli, M</em> o
<em>'t Hooft, G</em> en la que una consulta puntual de usuario por
<em>Hooft, G</em> recuperará el autor requerido, a diferencia de los
métodos mencionados anteriormente.)</p>
</li>
<li>Finalmente, anotar que es posible utilizar la <a
href="#regexp">sintaxis de expresión regular</a> para construir
consultas complejas para buscar por autor. Un ejemplo sencillo es la
búsqueda de un autor introducido en la base de datos tanto como
<em>Ellis, J</em> como <em>Ellis, John</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:/^Ellis, (J|John)$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Se recomienda la consulta de <a href="#regexp">búsqueda por
expresiones regulares</a> para conocer más acerca de las posibilidades
de este modo de búsqueda.
</li>
</ol>
<p><a name="author-full-names"><strong><sup>*</sup>NOTA:</strong></a>
Si es autor de su propia lista de publicaciones y detecta que algunas
veces su nombre se escribe de forma abreviada y a veces completo, o si
desea identificar sus publicaciones entre diversos autores con la
misma forma abreviada, por favor contacte con los<a
href="mailto:<CFG_SITE_ADMIN_EMAIL>">administradores</a> de <CFG_SITE_NAME_INTL> que
trataran de trabajar conjuntamente para implementar una forma
consistente y normalizada de introducción de su nombre en toda la base
de datos. Sólo una base de datos con contenido consistente puede
garantizar una búqueda por autores exitosa.</p>
</es>
<ca>
<p>Aquesta tècnica de cerca confereix major grau de precisió i evita falsos
resultats aparentement positius. (La presumpció de que no existeixen
altres autores els noms dels quals s’escrigui com <em>Ellis, J</em>, en
ocasions pot no ser certa<a
href="#author-full-names"><sup>*</sup></a>.) La cerca es realitzarà
con amb molta rapidesa.</p>
</li>
<li> Algunes vegadess, en alguns documents, el nom propi d’un
autor s’escriu de forma abreujada, (com <em>Ellis, J</em>) i altres
de forma completa (com <em>Ellis, John</em>; i en altres ocasions
també amb el nom compost:<em>Ellis, John Rolfe</em>). Per
obtenir una llista de publicacions de l’autor amb totes les seves formes en
una mateixa cerca, es pot utilitzar l’operador booleà O:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="60" type="text" name="p" value="author:&quot;Ellis, J&quot; or author:&quot;Ellis, John&quot; or author:&quot;Ellis, John Rolfe&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Aquest mode de cerca ens ofereix encara major grau de precisió i
evita falsos resultats positius (o no pertinents). (La presumpció de
que no hi ha més autors que s’escriguin com <em>Ellis, J</em> O
<em>Ellis, John</em>, en ocasions pot no ser certa <a
href="#author-full-names"><sup>*</sup></a>.) La cerca és molt ràpida.</p>
</li>
<li>Per localitzar totes les formes introduïdes a l’exemple anterior
amb un únic terme de cerca, podem provar d’emprar una cerca amb
comodí:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:&quot;Ellis, J*&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Això recuperarà tots els noms d’autor que comencen amb el text
<code>Ellis, J</code>, no només amb les formes requerides a l’exemple
<em>Ellis, J</em> i <em>Ellis, John</em>, sinó també <em>Ellis,
Jim</em>, o <em>Ellis, John Rolfe</em>, o <em>Ellis,Jonathan
Richard</em>.</p>
<p>Aquesta cerca retornarà molts més resultats, però pot resultar
convenient en el cas que es desconegui la forma d’introducció dels
noms a la base de dades. Ara bé, augmenta el risc d’obtenir resultats
poc pertinents. La cerca és relativament ràpida.</p>
</li>
<li>Apart d’aquesta darrera, l’alternativa general més emprada és la
cerca per part de la frase:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:'Ellis, J'" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
<p>Aquesta cerca recupera no només tots els autors mencionats, sinó
també tots aquells els noms dels quals continguin l’expresió
<code>Ellis, J</code> a qualsevol part del seu nom, com per exemple
<em>De Lellis, Jim</em>. Això augmenta enormement les possibilitats
d’obtenir registres amb un alt risc de ser poc pertinents. La cerca
és relativament lenta.</p>
<p>(Observis que aquest mode de cerca pot resultar molt pràctic per a
cognoms compostos, tals com <em>Pepe-Altarelli, M</em> o <em>'t Hooft,
G</em> en els que una consulta d’una consulta puntual de usuario por
<em>Hooft, G</em> recuperarà l’autor requerit, a diferència dels
mètodes mencionats anteriorment.)</p>
</li>
<li>Finalment, anotar que es possible emprar la <a
href="#regexp">sintaxi de expressió regular</a> per construir
consultes complexes per fer cerques per autor. Un exemple senzill és
la cerca d’un autor introduït a la base de dades tant per <em>Ellis,
J</em> com per <em>Ellis, John</em>:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="author:/^Ellis, (J|John)$/" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author" selected="selected">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Es recomana la consulta de <a href="#regexp">cerca per expressions
regulars</a> per conèixer més sobre les possibilitats d’aquest mode de
cerca.
</li>
</ol>
<p><a name="author-full-names"><strong><sup>*</sup>NOTA:</strong></a>
Si es autor de la seva pròpia llista de publicacions i detecta que
algunes vegades el seu nom s’escriu de forma abreujada i de vegades
complet, o si desitja identificar les seves publicacions entre
diversos autors amb la mateixa forma abreujada, sis plau, contacti amb
els <a href="mailto:<CFG_SITE_ADMIN_EMAIL>">administradors</a> de <CFG_SITE_NAME_INTL>
que tractaran de treballar-hi conjuntament per implementar una forma
consistent i normalitzada d’introducció del seu nom a tota la base de
dades. Només una base de dades amb contingut consistent pot garantir
una cerca per autors exitosa.</p>
</ca>
</lang>
<h4><a name="howto-sort-pattern">
<lang>
<en>How to sort according to a certain pattern</en>
<fr>Comment trier d'après un certain critère</fr>
<de>Wie lasse ich Ergebnisse auf eine bestimmte Weise sortieren</de>
<es>¿Cómo ordenar acorde a cierto patrón de ordenación?</es>
<ca>Com ordenar d'acord a cert patró d'ordenació?</ca>
</lang>
</a></h4>
<lang>
<en>
<p>You may select a certain field according to which sort the search
results, for example to sort the results by main title. However,
sometimes you may want to sort by a report number and it happens
that your documents have several of them. For example, the report
numbers <em>hep-ph/0204140</em>, <em>CERN-TH-2002-069</em> and
<em>RM3-TH-02-4</em> all denote <a
- href="<WEBURL>/search?p=CERN-TH-2002-069&amp;f=reportnumber">the
+ href="<CFG_SITE_URL>/search?p=CERN-TH-2002-069&amp;f=reportnumber">the
same document</a>. Now if you sort your search results set
containing this document, the system will take into consideration
the first report number, that may be either of these three.
Sometimes you may want to classify this document under its
<em>hep-ph</em> number, sometimes under its <em>CERN</em> number,
depending on whether you produce a list of CERN or hep-ph
publications. How can you influence the search engine to prefer
one report number rather than the other?</p>
<p>In other words, the search engine by default answers a query
like "sort by first author" or "sort by first report number", but
sometimes you may want to ask the search engine to "sort by first
report number that starts by the text <em>CERN-</em>". The latter
possibility is available via a "silent" sort parameter called
<code>sp</code> (for "sort pattern") that sorts preferentially
according to the given textual pattern if they can be found. The
parameter is "silent" in a way that it is not present in the search
interface, you have to add it manually to your search URL.
For example, to get all CERN-TH publications of the year 2001
sorted by their CERN-TH numbers, you would search for
<code>CERN-TH-2001*</code> within <code>reportnumber</code> index,
and on the search results page, being satisfied with the results,
you would add <code>&amp;sp=CERN-TH</code> to the URL to sort the
results preferentially by CERN-TH report numbers, to get a <a
- href="<WEBURL>/search?p=reportnumber%3A%22CERN-TH-2001*%22&amp;sf=reportnumber&amp;so=a&amp;sp=CERN-TH">nicely
+ href="<CFG_SITE_URL>/search?p=reportnumber%3A%22CERN-TH-2001*%22&amp;sf=reportnumber&amp;so=a&amp;sp=CERN-TH">nicely
sorted list</a> of all CERN-TH 2001 publications.</p>
</en>
<fr>
<p>Il est possible de sélectionner le critère d'après lequel les
résultats de la recherche sont ordrés, par exemple pour trier les
résultats par titre. Cependant, il arrive parfois que l'on cherche
à trier selon le numéro de rapport, alors que certains documents en
ont plusieurs. Par exemple, les numéros de
rapport <em>hep-ph/0204140</em>, <em>CERN-TH-2002-069</em> et
<em>RM3-TH-02-4</em> appartiennent tous
au <a
- href="<WEBURL>/search?p=CERN-TH-2002-069&amp;f=reportnumber&amp;ln=fr">même
+ href="<CFG_SITE_URL>/search?p=CERN-TH-2002-069&amp;f=reportnumber&amp;ln=fr">même
document</a>. Si ce document se trouve dans vos résultats, le
système ne prendra en compte qu' seul de ces numéros pour le tri.
Il arrive que l'on cherche à trier parfois selon la
référence <em>hep-ph</em>, et d'autres fois selon la
référence <em>CERN</em>, selon que l'on veuille produire une liste
des publications CERN ou hep-ph. Comment demander au moteur de
recherche d'utiliser un des numéros plutôt que les autres?</p>
<p>En d'autres termes, par défaut le moteur de recherche répond à
une requête "trier par le premier auteur" ou "trier par le
premier numéro de rapport", mais il arrive qu'il soit nécessaire
de demander "trier par le premier numéro de rapport qui commence
par le texte <em>CERN-</em>". Cette dernière requête peut être
formulée via un paramètre de tri "silencieux"
appelé <code>sp</code> (pour "search pattern", <em>motif de
tri</em>) qui trie de préférence selon le motif textuel qui lui
est fourni. Ce paramètre est "silencieux" dans le sens qu'il
n'est pas disponible dans l'interface web, mais doit être ajouté
manuellement à l'URL
Par exemple, pour obtenir toutes les publications de l'année
2001 triées d'après leur référence CERN-TH, il faut
chercher <code>CERN-TH-2001*</code> dans
l'index <code>reportnumber</code>, et sur la page des résultats
(une fois que ceux-ci sont satisfaisants), ajouter
<code>&amp;sp=CERN-TH</code> à l'URL pour obtenir
la <a
- href="<WEBURL>/search?p=reportnumber%3A%22CERN-TH-2001*%22&amp;sf=reportnumber&amp;so=a&amp;sp=CERN-TH&amp;ln=fr">liste
+ href="<CFG_SITE_URL>/search?p=reportnumber%3A%22CERN-TH-2001*%22&amp;sf=reportnumber&amp;so=a&amp;sp=CERN-TH&amp;ln=fr">liste
triée</a> selon ce paramètre.</p>
</fr>
<es>
<p>Es posible seleccionar un campo determinado en función del tipo de
ordenación de los resultados de búsqueda, por ejemplo para
ordenarse los resultados por el título principal. De este modo, en
ocasiones puede ocurrir que se desee ordenar los documentos por el
código de referencia y que un mismo documento tenga más de uno.
Por ejemplo, los códigos <em>hep-ph/0204140</em>,
<em>CERN-TH-2002-069</em> y <em>RM3-TH-02-4</em> apuntan todos <a
- href="<WEBURL>/search?p=CERN-TH-2002-069&amp;f=reportnumber">al
+ href="<CFG_SITE_URL>/search?p=CERN-TH-2002-069&amp;f=reportnumber">al
mismo documento</a>. Si se ordena el conjunto de resultados de
búsqueda contenidos en el documento, el sistema tendrá en cuenta el
primer número de informe, que puede ser cualquiera de los tres. En
ocasiones se puede desear clasificar un documento según un
determinado código de referencia <em> hep-ph </em>, u otro <em>
CERN> </em> según si se ha publicado en una colección de
publicaciones de CERN o hep-ph. ¿Como se puede influir en el motor
de búsqueda para priorizar un código antes que otro?</p>
<p>En otras palabras, el motor de búsqueda responde por defecto a
una pregunta similar a “ordena por el primer autor” u “ordena por
el primer código de informe”, pero en ocasiones se desea instar al
buscador para que ordene por el “primer código de informe que
comience por el texto <em>CERN-</em>". Esta opción es posible a
través del uso de un paràmetre de ordenación “invisible” denominado
<code>sp</code> (para a "ordenar según un patrón") que permite
ordenar siguiendo un patron de preferencia dado. Este parámetro es
“invisible” desde el punto de vista de que no está presente en el
interfaz de búsqueda, sino que debe ser añadido manualmente en el
URL de la búsqueda.
Por ejemplo, para recuperar todas las publicaciones del CERN-TH del
año 2001 ordenados por su código, debe buscarse por
<code>CERN-TH-2001*</code> dentro del índice <code>código de
informe</code>, y en la página de resultados de búsqueda, cuando
esté satisfecho con los resultados obtenidos, añadir
<code>&amp;sp=CERN-TH</code> en el URL a fin de ordenar los resultados
sgún los códigos de informe CERN-TH, y conseguir una correcta <a
- href="<WEBURL>/search?p=reportnumber%3A%22CERN-TH-2001*%22&amp;sf=reportnumber&amp;so=a&amp;sp=CERN-TH">
+ href="<CFG_SITE_URL>/search?p=reportnumber%3A%22CERN-TH-2001*%22&amp;sf=reportnumber&amp;so=a&amp;sp=CERN-TH">
ordenación del listado</a> de todas las publicaciones CERN-TH del
2001.</p>
</es>
<ca>
<p>Es possible seleccionar un camp determinat en funció del tipus
d’ordenació dels resultats de la cerca, per exemple per ordenar els
resultats pel títol principal. D’aquesta forma, en ocasions pot
passar que es desitgi ordenar els documents pel codi de referència
i que un mateix document en tingui més d’un. Per exemple, els
codis <em>hep-ph/0204140</em>, <em>CERN-TH-2002-069</em> i
<em>RM3-TH-02-4</em> apunten tots <a
- href="<WEBURL>/search?p=CERN-TH-2002-069&amp;f=reportnumber">al
+ href="<CFG_SITE_URL>/search?p=CERN-TH-2002-069&amp;f=reportnumber">al
mateix document</a>. Si s’ordena el conjunt de resultats de cerca
que conté el document, el sistema tindrà en consideració el primer
número d’informe, que pot ser qualsevol dels tres. En ocasions es
desitja classificar un document segons un determinat codi de
referència <em> hep-ph </em>, o altre <em> CERN> </em> segons si ha
estat publicat a una col•lecció de publicacions CERN o hep-ph. Com
es pot influir en el motor de cerca per prioritzar un codi abans de
l’altre?</p>
<p>En altres paraules, el motor de cerca respon per defecte a una
pregunta semblant a “ordena pel primer autor” o “ordena pel primer
codi d’informe”, però en ocasions es pot desitjar instar al
cercador per tal que ordeni pel “primer codi d’informe que comenci
pel text <em>CERN-</em>". Aquesta darrera opció és possible a
través d’un paràmetre d’ordenació “invisible” denominat
<code>sp</code> (per a "ordenar segons un patró") que permet
ordenar segons un patró de preferència donat. Aquest paràmetre és
“invisible” des del punt de vista que no és present a l’interfície
de cerca, sinó que ha d’afegir-se manualment a la URL de cerca.
Per exemple, per recuperar totes les publicacions del CERN-TH de
l’any 2001 ordenats pel seu codi, cal cercar per
<code>CERN-TH-2001*</code> dins l’índex de <code>códi
d’informe</code>, i a la pàgina de resultats de cerca, quan
s’estigui satisfet amb els resultats obtinguts, afegir
<code>&amp;sp=CERN-TH</code> a la URL per tal d’ordenar els resultats
segons els codis d’informe CERN-TH, i aconseguir una correcta <a
- href="<WEBURL>/search?p=reportnumber%3A%22CERN-TH-2001*%22&amp;sf=reportnumber&amp;so=a&amp;sp=CERN-TH">
+ href="<CFG_SITE_URL>/search?p=reportnumber%3A%22CERN-TH-2001*%22&amp;sf=reportnumber&amp;so=a&amp;sp=CERN-TH">
ordenació de la llista </a> de totes les publicacions CERN-TH del
2001.</p>
</ca>
</lang>
<h4><a name="howto-hepdoc">
<lang>
<en>How to get documents from other servers (Google, SPIRES, KEK)</en>
<fr>Comment obtenir les documents d'autres serveurs (Google, SPIRES, KEK)</fr>
<de>Wie bekomme ich Dokumente anderer Server (Google, SPIRES, KEK)</de>
<es>¿Cómo obtener documentos de otros servidores? (Google, SPIRES, KEK)</es>
<ca>Com obtenir documents d'altres servidors? (Google, SPIRES, KEK)</ca>
</lang>
</a></h4>
<lang>
<en>
<p>On the search results page, links to other servers like <a
href="http://google.com/">Google</a>, <a
href="http://www.slac.stanford.edu/spires/hep/">SPIRES</a> or <a
href="http://www-lib.kek.jp/KISS/kiss_prepri.html">KEK</a> are
automatically proposed in a box entitled "Try your search on". You
can simply click on the proposed links to run your query on these
search engines.</p>
<p>Note that the links aren't printed if the search engine doesn't
support it. For example, SPIRES or KEK cannot search for terms within
"any field", so we don't link to them in these cases.</p>
</en>
<fr>
<p>Sur la page des résultats, des liens vers d'autres serveurs tels
que <a href="http://google.com/">Google</a>, <a
href="http://www.slac.stanford.edu/spires/hep/">SPIRES</a> ou <a
href="http://www-lib.kek.jp/KISS/kiss_prepri.html">KEK</a> sont
automatiquement proposés dans une zone appelée "Essayez votre requête
sur&nbsp;...". Il suffit de cliquer sur ces liens pour obtenir les
réponses de ces moteurs.</p>
<p>Notez que ces liens ne sont pas toujours imprimés pour tous les
moteurs de recherche. Par exemple, SPIRES et KEK ne supportent pas
la recherche par mot dans "tous les champs".</p>
</fr>
<ca>
<p>A la pàgina de resultats de cerca, s'ofereixen automàticament
enllaços a altres servidors com <a
href="http://google.com/">Google</a>, <a
href="http://www.slac.stanford.edu/spires/hep/">SPIRES</a> o <a
href="http://www-lib.kek.jp/KISS/kiss_prepri.html">KEK</a> sota el nom
de "Proveu la vostra cerca a...". Amb un simple clic sobre els
enllaços proposats es pot enviar la cerca realitzada en aquests
cercadors.</p>
<p>Observis que els enllaços a altres cercadors no es mostren si el
cercador no suporta la cerca. Per exemple, SPIRES o KEK no poden
cercar termes dintre del camp "qualsevol camp", de forma que en
aquests casos, el sistema no els enllaçarà.</p>
</ca>
<es>
<p>En la página de resultadoss de búsqueda, se oferecen automáticament
enlaces a otros servidores tales como <a
href="http://google.com/">Google</a>, <a
href="http://www.slac.stanford.edu/spires/hep/">SPIRES</a> o <a
href="http://www-lib.kek.jp/KISS/kiss_prepri.html">KEK</a> bajo el
nombre de "Intentar la búsqueda en...". Con un simple cic sobre los
enlaces propuestos se puede enviar la búsqueda realizada en dichos
buscadores.</p>
<p>Obsérvese que los enlaces a otros cercadores no se muestran si el
buscador no soporta el tipo de búsqueda. Por ejemplo, SPIRES o KEK no
pueden buscar términos dentro del campo "cualquier campo", de forma
que en estos casos, el sistema no los enlazará.</p>
</es>
</lang>
<h4><a name="howto-fulltext">
<lang>
<en>How to search in fulltext files</en>
<fr>Comment rechercher le contenu des fichiers</fr>
<de>Wie kann ich in verknüpften Volltextdateien suchen</de>
<es>¿Cómo buscar en ficheros a texto completo?</es>
<ca>Com cercar a fitxers a text complet?</ca>
</lang>
</a></h4>
<lang>
<en>
<p>If a metadata record contains some associated fulltext files, <CFG_SITE_NAME_INTL>
tries to extract the textual information from the files and index it into a separate <code>fulltext</code> index.
To search for all records that contain the term <em>e-</em> in their fulltext files,
type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="fulltext:e-" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recall that fulltext words aren't included in the default global ``any field'' index,
but that you may freely combine a fulltext and metadata search. For example, to find all
articles written by <em>Ellis</em> that contain the word <em>muon</em> either in the
metadata or in the fulltext, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or fulltext:muon and author:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>Si une notice a des fichiers associés, <CFG_SITE_NAME_INTL> tente d'en
extraire les informations textuelles et de les indexer dans l'index
"fulltext". Pour chercher toutes les notices qui contiennent le
terme <em>e-</em> dans leur fichiers, saisissez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="fulltext:e-" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Rappelez-vous que les mots contenus dans les fichiers ne sont pas
inclus dans l'index global ``tous les champs'', mais qu'il vous est
possible de combiner librement la recherche dans le fichier avec la
recherche dans les métadonnées. Par exemple, pour trouver tous les
articles écrits par <em>Ellis</em> qui contiennent le
mot <em>muon</em> dans les métadonnées et/ou dans le texte complet,
saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or fulltext:muon and author:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<ca>
<p>Si un registre de metadades té arxius a text complet associats
<CFG_SITE_NAME_INTL> tractarà de extraure la informació textual dels arxius e
indexar-la a un índex de <code>text complet</code>separat. Per
recuperar tots els documents que contenen el terme <em>e-</em>al text
complet dels seus documents associats, cal escriure:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="fulltext:e-" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recordem que les paraules del text complet no s'inclouen a l'índex
global 'qualsevol camp' predeterminat, però és possible realitzar una
combinació lliure de text complet i metadades dins una mateixa cerca.
Per exemple, per recuperar tots els articles escrits per
<em>Ellis</em> que continguin la paraula <em>muon</em> tant a les
metadades como all text complet, cal escriure:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or fulltext:muon and author:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
<es>
<p>Si un registro de metadatos contiene archivos a texto completo
asociados, <CFG_SITE_NAME_INTL> tratará de extraer la información textual de
los archivos e indexarla en un índice de <code>texto
completo</code>separado. Para recuperar todos los documentos que
contienen el término <em>e-</em> en el texto completo de sus
documentos asociados, debemos escribir:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="fulltext:e-" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recordamos que las palabras del texto completo no se incluyen en el
índice global 'cualquier campo' predeterminado, pero es posible
realizar una combinación libre de texto completo y metadatos en una
misma búsqueda. Por ejemplo, para recuperar todos los artículos
escritos por <em>Ellis</em> que contengan la palabra <em>muon</em>
tanto en los metadatos como en el texto completo, debemos escribir:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="muon or fulltext:muon and author:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
</lang>
<h4><a name="howto-citations">
<lang>
<en>How to search for citations</en>
<fr>Comment rechercher les citations</fr>
<de>Wie kann ich nach Zitaten suchen</de>
<es>¿Como buscar citas bibliográficas?</es>
<ca>Cómo cercar referències bibliogàfiques</ca>
</lang>
</a></h4>
<lang>
<en>
<p>If a metadata record contains an associated fulltext file, <CFG_SITE_NAME_INTL>
tries to extract references automatically from that file and index
them into a separate <code>reference</code> index. To search for
all records that cite <em>Ellis</em> in their reference lists,
type:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="reference:Ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
To search for all records that cite preprint <em>hep-ph/0103062</em>
in their reference lists, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="reference:hep-ph/0103062" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
To search for all records that cite an article from <em>Giddings</em>
and <em>Ross</em> published in <em>Physical Review D</em> in volume
<em>61</em> in year <em>2000</em>, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="70" type="text" name="p" value="reference:giddings reference:ross reference:&quot;Phys. Rev., D&quot; reference:61 reference:2000" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recall that citation terms aren't included in the default global "any field" index,
but that you may freely combine a citation search with a metadata search.
For example, to find all articles on <em>standard model</em> that aren't written by
<em>Ellis</em> but that do cite him, type:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="standard model -author:ellis reference:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</en>
<fr>
<p>Si les métadonnées d'une notice contiennent un texte complet
associé, <CFG_SITE_NAME_INTL> tente d'en extraire automatiquement les
références et les indexe dans l'index <code>references</code>. Pour
chercher tous les documents qui citent <em>Ellis</em>, saisissez:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="reference:Ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Pour chercher toutes les notices qui citent le
preprint <em>hep-ph/0103062</em>, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="reference:hep-ph/0103062" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Pour chercher toutes les notices qui citent un article
de <em>Giddings</em> et <em>Ross</em> publié dans
<em>Physical Review D</em>, volume <em>61</em> en <em>2000</em>, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="70" type="text" name="p" value="reference:giddings reference:ross reference:&quot;Phys. Rev., D&quot; reference:61 reference:2000" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Rappelez-vous que les citations ne sont pas incluses dans l'index
global ``tous les champs'', mais qu'il vous est possible de combiner
librement la recherche des citations avec la recherche dans les
métadonnées. Par exemple, pour trouver tous les articles sur le
<em>standard model</em> qui n'ont pas été écrits par <em>Ellis</em>
mais qui le citent, saisissez:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="standard model -author:ellis reference:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</fr>
<es>
<p>Si un registro de metadatos contiene un fichero de texto asociado,
<CFG_SITE_NAME_INTL> trata de extraer las citas automaticamente del fichero e
indexarlas en un índice separado de <code>citas</code>. Para
recuperar todos los registros que citan a <em>Ellis</em> en sus
listados de citas bibliogáficas, escribiremos:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="reference:Ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Para recuperar todos los registros que citan el pre-print
<em>hep-ph/0103062</em>en sus listados de citas bibliográficas,
escribiremos:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="reference:hep-ph/0103062" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Para recuperar todos los documentos que citan un artículo de
<em>Giddings</em> y <em>Ross</em>publicado en <em>Physical Review
D</em> volumen <em>61</em> y en el año <em>2000</em>, escribiremos:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="70" type="text" name="p" value="reference:giddings reference:ross reference:&quot;Phys. Rev., D&quot; reference:61 reference:20000" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recuerde que los términos de las citaciones no estan incluidos dentro
del índice “cualquier campo” en la búsqueda global predeterminada,
pero puede realizar una combinación libre entre búsqueda por cita
bibliogràfica y por metadatos. Por ejemplo, para recuperar todos los
artículos sobre <em>modelo estándar</em> que no han sido escritos por
<em>Ellis</em> pero lo citan, escribiremos:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="standard model -author:ellis reference:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</es>
<ca>
<p>Si un registre de metadades conté un fitxer de text associat,
<CFG_SITE_NAME_INTL> tracta de extraure les referències automàticament del
fitxer e indexar-les a un índex separat de <code>referències</code>.
Para recuperar tots els registres que citen a <em>Ellis</em> en els
seus llistats de referències bibliogràfiques, escriurem:</p>
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="reference:Ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Per recuperar tots els registres que citen el pre-print
<em>hep-ph/0103062</em>en els seus llistats de referències
bibliogràfiques, escriurem:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="reference:hep-ph/0103062" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Per recuperar tots els documents que citen un article de
<em>Giddings</em> i <em>Ross</em>publicat a <em>Physical Review D</em>
volum <em>61</em> i a l’any <em>2000</em>, escriurem:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="70" type="text" name="p" value="reference:giddings reference:ross reference:&quot;Phys. Rev., D&quot; reference:61 reference:2000" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
Recordi que els termes de les referències no estan inclosos dins
l’índex “qualsevol camp” a la cerca global predeterminada, però pot
realitzar una combinació lliure entre cerca por referència i per
metadades.
Per exemple, per recuperar tots els articles sobre <em>model
estàndar</em> que no han estat escrits per <em>Ellis</em> però el
citen, escriurem:
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<blockquote>
<input size="40" type="text" name="p" value="standard model -author:ellis reference:ellis" />
<select name="f"><option value="">_(any field)_</option>
<option value="title">_(title)_</option>
<option value="author">_(author)_</option></select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</blockquote>
</form>
</ca>
</lang>
diff --git a/modules/websearch/doc/search-tips.webdoc b/modules/websearch/doc/search-tips.webdoc
index c51d4102b..5fa4b95ed 100644
--- a/modules/websearch/doc/search-tips.webdoc
+++ b/modules/websearch/doc/search-tips.webdoc
@@ -1,569 +1,569 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(Search Tips)_ -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/<lang:link/>">_(Help Central)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/<lang:link/>">_(Help Central)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h5><lang>
<en>How to find any value in any field:</en>
<fr>Comment chercher n'importe quel mot dans n'importe quel champ:</fr>
<it>Come cercare dei termini in qualsiasi campo:</it>
</lang></h5>
<table width="100%" border="1" cellpadding="10" cellspacing="0">
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input size="20" type="text" name="p" value="" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Empty search box returns all records in the database.</en>
<fr>Une recherche vide retourne toutes les entrées de la base de donnée.</fr>
<it>Non specificando alcun termine per la ricerca vengono riportati tutti i
record del database.</it>
</lang>
</td>
</tr>
</table>
<h5><lang>
<en>How to find documents in a particular collection:</en>
<fr>Comment trouver un document classé dans une collection spécifique:</fr>
<it>Come trovare documenti appartenenti ad una particolare collezione:</it>
</lang></h5>
<table width="100%" border="1" cellpadding="10" cellspacing="0">
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
<p>
_(Narrow by collection:)_
<br />
- <input type="checkbox" name="c" value="Preprints" checked="checked" />&nbsp;<a href="<WEBURL>/?c=Preprints<lang:link />">Preprints</a>
+ <input type="checkbox" name="c" value="Preprints" checked="checked" />&nbsp;<a href="<CFG_SITE_URL>/?c=Preprints<lang:link />">Preprints</a>
<br />
- <input type="checkbox" name="c" value="Theses" checked="checked" />&nbsp;<a href="<WEBURL>/?c=Theses<lang:link />">Theses</a>
+ <input type="checkbox" name="c" value="Theses" checked="checked" />&nbsp;<a href="<CFG_SITE_URL>/?c=Theses<lang:link />">Theses</a>
</p>
</form>
</td>
<td width="50%">
<lang>
<en>Click on a link below the search box to see and select
subcollections such as Preprints or Theses, or select/deselect
the tick boxes next to a particular collection before doing the
search.</en>
<fr>Avant de lancer votre recherche, cliquez sur un des liens de
l'arborescence située en dessous de la boite de recherche pour
afficher une sous-collection telle que "Preprints" ou "Thèses",
ou sélectionner la case à cocher située à gauche d'une
collection ou du type de document souhaité.</fr>
<it>Fai clic su uno dei collegamenti al di sotto della casella di ricerca
e seleziona delle sotto-collezioni come Preprints o Thesis o
seleziona/deseleziona le caselle di selezione accanto alle collezioni
desiderate prima di effettuare la ricerca.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="NA60" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
<p>
_(Narrow by collection:)_
<br />
- <input type="checkbox" name="c" value="Preprints" checked="checked" />&nbsp;<a href="<WEBURL>/?c=Preprints<lang:link />">Preprints</a>
+ <input type="checkbox" name="c" value="Preprints" checked="checked" />&nbsp;<a href="<CFG_SITE_URL>/?c=Preprints<lang:link />">Preprints</a>
<br />
- <input type="checkbox" name="c" value="Theses" checked="checked" />&nbsp;<a href="<WEBURL>/?c=Theses<lang:link />">Theses</a>
+ <input type="checkbox" name="c" value="Theses" checked="checked" />&nbsp;<a href="<CFG_SITE_URL>/?c=Theses<lang:link />">Theses</a>
</p>
</form>
</td>
<td width="50%">
<lang>
<en>If you want documents from a 'collection' not proposed by default,
such as NA60 documents, then enter it as a search term.</en>
<fr>Pour les documents appartenant à une collection qui n'est pas
proposée par défaut, tels que les documents NA60, entrez le nom de
la collection dans la requête.</fr>
<it>Se desideri dei documenti appartenenti a una 'collezione' non proposta
in maniera predefinita, come i documenti NA60, inseriscila come un termine
della ricerca.</it>
</lang>
</td>
</tr>
</table>
<div align="right"><small><a href="search-guide<lang:link/>#howto-choose-terms">
<lang>
<en>more on how to wisely choose your search terms...</en>
<fr>plus d'informations sur le choix des termes de recherche...</fr>
<it>maggiori informazioni riguardo a come scegliere in maniera appropriata
i termini di rcerca...</it>
</lang></a></small></div>
<h5><lang>
<en>How to search for words/phrases (within titles, abstracts, etc):</en>
<fr>Comment chercher un ou plusieurs mots, une phrase (dans le titre, l'abstract, etc.):</fr>
<it>Come effettuare una ricerca di parole/frasi (all'interno di titoli, abstract, ecc.)</it>
</lang></h5>
<table width="100%" border="1" cellpadding="10" cellspacing="0">
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="higgs boson" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Returns records containing words <em>higgs</em> and
<em>boson</em>.</en>
<fr>Retourne les notices contenant les mots <em>higgs</em> et
<em>boson</em>.</fr>
<it>Restituisce i record contenenti le parole <em>higgs</em> e
<em>boson</em>.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="'higgs boson'" />
<select name="f"><option value="">_(any field)_</option>
<option selected="selected">_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Returns records containing phrase <em>'higgs boson'</em> in
title.</en>
<fr>Retourne les notices contenant la phrase <em>'higgs boson'</em>
dans le titre.</fr>
<it>Restituisce i record contenenti la frase <em>'higgs boson'</em> nel
titolo.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="&quot;higgs boson&quot;" />
<select name="f"><option value="">_(any field)_</option>
<option selected="selected">_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Returns records entitled exactly <em>"Higgs boson"</em> but not
records such as <em>"Overview of Higgs boson
production"</em>.</en>
<fr>Retourne les notices dont le titre est exactement <em>"Higgs
boson"</em>, mais pas les notices telles que <em>"Overview of
Higgs boson production"</em></fr>
<it>Restituisce i record il cui titolo è esattamente <em>"Higgs boson"</em>
ma non i record quali <em>"Overview of Higgs boson
production"</em>.</it>
</lang>
</td>
</tr>
</table>
<div align="right"><small><a href="search-guide<lang:link/>#words-vs-phrases">
<lang>
<en>more on word and phrase searches...</en>
<fr>plus d'informations sur la recherche de mots ou de
phrases...</fr>
<it>maggiori informazioni sulla ricerca per parole e frasi...</it>
</lang>
</a></small></div>
<h5><lang>
<en>How to use truncation:</en>
<fr>Comment utiliser la troncature:</fr>
<it>Come utilizzare la troncatura</it>
</lang></h5>
<table width="100%" border="1" cellpadding="10" cellspacing="0">
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="muon*" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Returns records containing words <em>muon</em>, <em>muons</em>,
<em>muonic</em>, etc.</en>
<fr>Retourne les notices contenant les mots <em>muon</em>,
<em>muons</em>, <em>muonic</em>, etc.</fr>
<it>Restituisce i record contenenti le parole <em>muon</em>, <em>muons</em>,
<em>muonic</em>, ecc.</it>
</lang>
</td>
</tr>
</table>
<div align="right"><small><a href="search-guide<lang:link/>#wildcard">
<lang>
<en>more on truncation...</en>
<fr>plus d'informations au sujet de la troncature ...</fr>
<it>maggiori informazioni sulla troncatura...</it>
</lang>
</a></small></div>
<h5><lang>
<en>How to use boolean operators:</en>
<fr>Comment utiliser les opérateurs booléens:</fr>
<it>Come utilizzare gli operatori booleani:</it>
</lang></h5>
<table width="100%" border="1" cellpadding="10" cellspacing="0">
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="muon kaon" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
_(or)_:
<br />
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="muon AND kaon" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Returns records containing both <em>muon</em> and <em>kaon</em>.</en>
<fr>Retourne les notices contenant à la fois <em>muon</em> et <em>kaon</em>.</fr>
<it>Restituisce i record contenenti sia <em>muon</em> che <em>kaon</em>.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="muon OR kaon" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Returns records containing either <em>muon</em> or <em>kaon</em>.</en>
<fr>Retourne les notices contenant soit <em>muon</em>, soit <em>kaon</em>.</fr>
<it>Restituisce i record contenenti <em>muon</em> e/o <em>kaon</em>.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="muon NOT kaon" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Returns records containing <em>muon</em> but not <em>kaon</em>.</en>
<fr>Retourne les notices contenant <em>muon</em>, mais pas <em>kaon</em>.</fr>
<it>Restituisce i record contenenti <em>muon</em> ma non <em>kaon</em>.</it>
</lang>
</td>
</tr>
</table>
<div align="right"><small><a href="search-guide<lang:link/>#boolean">
<lang>
<en>more on Boolean queries...</en>
<fr>plus d'informations au sujet des requêtes booléennes...</fr>
<it>maggiori informazioni sulle richieste di ricerca booleane...</it>
</lang>
</a></small></div>
<h5><lang>
<en>How to find documents from a certain period:</en>
<fr>Comment chercher un document d'une certaine période:</fr>
<it>Come cercare i documenti a partire da un certo periodo:</it>
</lang></h5>
<table width="100%" border="1" cellpadding="10" cellspacing="0">
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="2003" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
<option selected="selected" value="year">_(year)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Type <em>2003</em> and select the <em>_(year)_</em> field.</en>
<fr>Saisissez <em>2003</em> and sélectionnez le champ <em>_(year)_</em>.</fr>
<it>Digita <em>2003</em> e seleziona il campo <em>_(year)_</em>.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="year:2003" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Alternatively, type <code>'_(year)_:'</code> followed by value.</en>
<fr>Alternativement, saisissez <code>'_(year)_:'</code> suivi de l'année.</fr>
<it>In alternativa, digita <code>'_(year)_:'</code> seguito dall'anno.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="year:2003->2004" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>You can enter specific year range.</en>
<fr>Vous pouvez également définir un intervalle temporel.</fr>
<it>Puoi inserire specifici intervalli di anni.</it>
</lang>
</td>
</tr>
</table>
<div align="right"><small><a href="search-guide<lang:link/>#span">
<lang>
<en>more on span queries...</en>
<fr>plus d'informations au sujet des requêtes par périodes...</fr>
<it>maggiori informazioni sulle ricerche tramite intervalli di tempo...</it>
</lang>
</a></small></div>
<h5><lang>
<en>How to search for authors:</en>
<fr>Comment chercher un document par auteur:</fr>
<it>Come effettuare ricerche su autori:</it>
</lang></h5>
<table width="100%" border="1" cellpadding="10" cellspacing="0">
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="Ellis" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Some authors have unique names and their publications can be
retrieved by searching for the surname in the
<code>_(any field)_</code> field.</en>
<fr>Les noms uniques peuvent être recherchés dans le champ
<code>_(any field)_</code>.</fr>
<it>Alcuni autori hanno dei nomi univoci e le loro pubblicazioni possono
essere recuperate ricercando il cognome nel campo
<code>_(any field)_</code>.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="Ellis, J R" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option selected="selected" value="author">_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>For better results, type surname comma initial(s) and select the
_(author)_ field.</en>
<fr>Pour de meilleurs résultats, inscrire le nom accompagné d'une
virgule et de l'initiale du prénom, puis sélectionner le champ de
recherche _(author)_.</fr>
<it>Per migliorare i risultati della ricerca, digita il cognome seguito dalla
virgola, seguita dalla(e) iniziale(i) e seleziona il campo _(author)_.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="Ellis, John Rolfe" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option selected="selected" value="author">_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Sometimes authors are indexed with their full name.</en>
<fr>Parfois, les auteurs peuvent être inscrits avec leur prénom
complet.</fr>
<it>A volte gli autori sono indicizzati tramite il loro nome completo.</it>
</lang>
</td>
</tr>
<tr>
<td width="50%" style="white-space: nowrap">
- <form action="<WEBURL>/search" method="get">
+ <form action="<CFG_SITE_URL>/search" method="get">
<input type="hidden" name="ln" value="<lang:current />" />
<input size="20" type="text" name="p" value="author:&quot;Ellis, J*&quot; year:1990->1993" />
<select name="f"><option value="">_(any field)_</option>
<option>_(title)_</option>
<option>_(author)_</option>
</select>
<input class="formbutton" type="submit" name="action" value="_(Search)_" />
</form>
</td>
<td width="50%">
<lang>
<en>Find papers by J.Ellis written from 1990 until 1993, using
truncation to match all first names beginning with J.</en>
<fr>Rechercher toutes les notices de J.Ellis publiées entre 1990 et
1993, en utilisant la troncature permettant de retrouver tous
les prénoms commençant par J.</fr>
<it>Come ricercare i tutti gli articoli di J.Ellis scritti tra il 1990 e il
1993, utilizzando la troncatura per ricuperare tutti i nomi di battesimo
che cominciano con J.</it>
</lang>
</td>
</tr>
</table>
<div align="right"><small><a href="search-guide<lang:link/>#howto-search-for-author">
<lang>
<en>more on author searches...</en>
<fr>plus d'informations au sujet de la recherche par auteur...</fr>
<it>maggiori informazioni sulla ricerca tramite nome dell'autore...</it>
</lang>
</a></small></div>
<h5><lang>
<en>More information:</en>
<fr>Plus d'informations:</fr>
<it>Ulteriori informazioni:</it>
</lang></h5>
<lang>
<en>Special characters, regular expressions, fulltext searching,
citation searching, and other capabilities are fully explained in
the complete <a href="search-guide<lang:link/>">Search
Guide</a>.</en>
<fr>Des explications additionnelles concernant les caractères
spéciaux, les expressions régulières, la recherche dans le
contenu des fichiers, des citations, ainsi que d'autres
fonctionnalités sont disponibles dans le <a
href="search-guide<lang:link/>">Guide de Recherche</a>.</fr>
<it>Nella <a href="search-guide<lang:link/>">Guida di Ricerca</a> completa sono
spiegate in maniera estesa ulteriori funzionalità quali caratteri speciali,
espressioni regolari, ricerca nel testo integrale,
ricerca tramite citazioni.</it>
</lang>
diff --git a/modules/websearch/lib/search_engine.py b/modules/websearch/lib/search_engine.py
index 870cc68f6..d10934136 100644
--- a/modules/websearch/lib/search_engine.py
+++ b/modules/websearch/lib/search_engine.py
@@ -1,4060 +1,4053 @@
# -*- coding: utf-8 -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable-msg=C0301
"""CDS Invenio Search Engine in mod_python."""
__lastupdated__ = """$Date$"""
__revision__ = "$Id$"
## import general modules:
import cgi
import copy
import string
import os
import re
import time
import urllib
import zlib
## import CDS Invenio stuff:
from invenio.config import \
CFG_CERN_SITE, \
CFG_OAI_ID_FIELD, \
CFG_WEBCOMMENT_ALLOW_REVIEWS, \
CFG_WEBSEARCH_CALL_BIBFORMAT, \
CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX, \
CFG_WEBSEARCH_FIELDS_CONVERT, \
CFG_WEBSEARCH_NB_RECORDS_TO_SORT, \
CFG_WEBSEARCH_SEARCH_CACHE_SIZE, \
CFG_WEBSEARCH_USE_JSMATH_FOR_FORMATS, \
CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_LOGDIR, \
- weburl
+ CFG_SITE_URL
from invenio.search_engine_config import CFG_EXPERIMENTAL_FEATURES, InvenioWebSearchUnknownCollectionError
from invenio.bibrank_record_sorter import get_bibrank_methods, rank_records
from invenio.bibrank_downloads_similarity import register_page_view_event, calculate_reading_similarity_list
from invenio.bibindex_engine_stemmer import stem
from invenio.bibformat import format_record, format_records, get_output_format_content_type, create_excel
from invenio.bibformat_config import CFG_BIBFORMAT_USE_OLD_BIBFORMAT
from invenio.bibrank_downloads_grapher import create_download_history_graph_and_box
from invenio.data_cacher import DataCacher
from invenio.websearch_external_collections import print_external_results_overview, perform_external_collection_search
from invenio.access_control_admin import acc_get_action_id
from invenio.access_control_config import VIEWRESTRCOLL, \
CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS
from invenio.websearchadminlib import get_detailed_page_tabs
from invenio.intbitset import intbitset as HitSet
from invenio.webinterface_handler import wash_urlargd
from invenio.urlutils import make_canonical_urlargd
from invenio.dbquery import DatabaseError
from invenio.access_control_engine import acc_authorize_action
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
webcomment_templates = invenio.template.load('webcomment')
from invenio.bibrank_citation_searcher import calculate_cited_by_list, calculate_co_cited_with_list, get_self_cited_in, get_self_cited_by
from invenio.bibrank_citation_grapher import create_citation_history_graph_and_box
from invenio.dbquery import run_sql, run_sql_cached, get_table_update_time, Error
from invenio.webuser import getUid, collect_user_info
from invenio.webpage import page, pageheaderonly, pagefooteronly, create_error_box
from invenio.messages import gettext_set_language
try:
from mod_python import apache
except ImportError, e:
pass # ignore user personalisation, needed e.g. for command-line
try:
import invenio.template
websearch_templates = invenio.template.load('websearch')
except:
pass
## global vars:
search_cache = {} # will cache results of previous searches
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?
collection_reclist_cache_timestamp = 0
field_i18nname_cache_timestamp = 0
collection_i18nname_cache_timestamp = 0
## precompile some often-used regexp for speed reasons:
re_word = re.compile('[\s]')
re_quotes = re.compile('[\'\"]')
re_doublequote = re.compile('\"')
re_equal = re.compile('\=')
re_logical_and = re.compile('\sand\s', re.I)
re_logical_or = re.compile('\sor\s', re.I)
re_logical_not = re.compile('\snot\s', re.I)
re_operators = re.compile(r'\s([\+\-\|])\s')
re_pattern_wildcards_at_beginning = re.compile(r'(\s)[\*\%]+')
re_pattern_single_quotes = re.compile("'(.*?)'")
re_pattern_double_quotes = re.compile("\"(.*?)\"")
re_pattern_regexp_quotes = re.compile("\/(.*?)\/")
re_pattern_short_words = re.compile(r'([\s\"]\w{1,3})[\*\%]+')
re_pattern_space = re.compile("__SPACE__")
re_pattern_today = re.compile("\$TODAY\$")
re_unicode_lowercase_a = re.compile(unicode(r"(?u)[áàäâãå]", "utf-8"))
re_unicode_lowercase_ae = re.compile(unicode(r"(?u)[æ]", "utf-8"))
re_unicode_lowercase_e = re.compile(unicode(r"(?u)[éèëê]", "utf-8"))
re_unicode_lowercase_i = re.compile(unicode(r"(?u)[íìïî]", "utf-8"))
re_unicode_lowercase_o = re.compile(unicode(r"(?u)[óòöôõø]", "utf-8"))
re_unicode_lowercase_u = re.compile(unicode(r"(?u)[úùüû]", "utf-8"))
re_unicode_lowercase_y = re.compile(unicode(r"(?u)[ýÿ]", "utf-8"))
re_unicode_lowercase_c = re.compile(unicode(r"(?u)[çć]", "utf-8"))
re_unicode_lowercase_n = re.compile(unicode(r"(?u)[ñ]", "utf-8"))
re_unicode_uppercase_a = re.compile(unicode(r"(?u)[ÁÀÄÂÃÅ]", "utf-8"))
re_unicode_uppercase_ae = re.compile(unicode(r"(?u)[Æ]", "utf-8"))
re_unicode_uppercase_e = re.compile(unicode(r"(?u)[ÉÈËÊ]", "utf-8"))
re_unicode_uppercase_i = re.compile(unicode(r"(?u)[ÍÌÏÎ]", "utf-8"))
re_unicode_uppercase_o = re.compile(unicode(r"(?u)[ÓÒÖÔÕØ]", "utf-8"))
re_unicode_uppercase_u = re.compile(unicode(r"(?u)[ÚÙÜÛ]", "utf-8"))
re_unicode_uppercase_y = re.compile(unicode(r"(?u)[Ý]", "utf-8"))
re_unicode_uppercase_c = re.compile(unicode(r"(?u)[ÇĆ]", "utf-8"))
re_unicode_uppercase_n = re.compile(unicode(r"(?u)[Ñ]", "utf-8"))
re_latex_lowercase_a = re.compile("\\\\[\"H'`~^vu=k]\{?a\}?")
re_latex_lowercase_ae = re.compile("\\\\ae\\{\\}?")
re_latex_lowercase_e = re.compile("\\\\[\"H'`~^vu=k]\\{?e\\}?")
re_latex_lowercase_i = re.compile("\\\\[\"H'`~^vu=k]\\{?i\\}?")
re_latex_lowercase_o = re.compile("\\\\[\"H'`~^vu=k]\\{?o\\}?")
re_latex_lowercase_u = re.compile("\\\\[\"H'`~^vu=k]\\{?u\\}?")
re_latex_lowercase_y = re.compile("\\\\[\"']\\{?y\\}?")
re_latex_lowercase_c = re.compile("\\\\['uc]\\{?c\\}?")
re_latex_lowercase_n = re.compile("\\\\[c'~^vu]\\{?n\\}?")
re_latex_uppercase_a = re.compile("\\\\[\"H'`~^vu=k]\\{?A\\}?")
re_latex_uppercase_ae = re.compile("\\\\AE\\{?\\}?")
re_latex_uppercase_e = re.compile("\\\\[\"H'`~^vu=k]\\{?E\\}?")
re_latex_uppercase_i = re.compile("\\\\[\"H'`~^vu=k]\\{?I\\}?")
re_latex_uppercase_o = re.compile("\\\\[\"H'`~^vu=k]\\{?O\\}?")
re_latex_uppercase_u = re.compile("\\\\[\"H'`~^vu=k]\\{?U\\}?")
re_latex_uppercase_y = re.compile("\\\\[\"']\\{?Y\\}?")
re_latex_uppercase_c = re.compile("\\\\['uc]\\{?C\\}?")
re_latex_uppercase_n = re.compile("\\\\[c'~^vu]\\{?N\\}?")
class RestrictedCollectionDataCacher(DataCacher):
def __init__(self):
def cache_filler():
ret = []
try:
viewcollid = acc_get_action_id(VIEWRESTRCOLL)
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""", (viewcollid,))
except Exception:
# database problems, return empty cache
return []
for coll in res:
ret.append(coll[0])
return ret
def timestamp_getter():
return max(get_table_update_time('accROLE_accACTION_accARGUMENT'), get_table_update_time('accARGUMENT'))
DataCacher.__init__(self, cache_filler, timestamp_getter)
def collection_restricted_p(collection):
cache = restricted_collection_cache.get_cache()
return collection in cache
try:
restricted_collection_cache.is_ok_p
except Exception:
restricted_collection_cache = RestrictedCollectionDataCacher()
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 ths record,
or he has view rights to the primary collection this record belongs to.
Returns the same type as acc_authorize_action
"""
def _is_user_in_authorized_author_list_for_recid(user_info, recid):
"""Return True if the user have submitted the given record."""
authorized_emails = []
for tag in CFG_ACC_GRANT_AUTHOR_RIGHTS_TO_EMAILS_IN_TAGS:
authorized_emails.extend(get_fieldvalues(recid, tag))
for email in authorized_emails:
email = email.strip().lower()
if user_info['email'].strip().lower() == email:
return True
return False
record_primary_collection = guess_primary_collection_of_a_record(recid)
if collection_restricted_p(record_primary_collection):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=record_primary_collection)
if auth_code == 0 or _is_user_in_authorized_author_list_for_recid(user_info, recid):
return (0, '')
else:
return (auth_code, auth_msg)
else:
return (0, '')
class IndexStemmingDataCacher(DataCacher):
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_getter():
return get_table_update_time('idxINDEX')
DataCacher.__init__(self, cache_filler, timestamp_getter)
def get_index_stemming_language(index_id):
cache = index_stemming_cache.get_cache()
return cache[index_id]
try:
index_stemming_cache.is_ok_p
except Exception:
index_stemming_cache = IndexStemmingDataCacher()
class FieldI18nNameDataCacher(DataCacher):
def __init__(self):
def cache_filler():
ret = {}
try:
res = run_sql("SELECT f.name,fn.ln,fn.value FROM fieldname AS fn, field AS f WHERE fn.id_field=f.id AND fn.type='ln'") # ln=long name
except Exception:
# database problems, return empty cache
return {}
for f, ln, i18nname in res:
if i18nname:
if not ret.has_key(f):
ret[f] = {}
ret[f][ln] = i18nname
return ret
def timestamp_getter():
return get_table_update_time('fieldname')
DataCacher.__init__(self, cache_filler, timestamp_getter)
def get_field_i18nname(self, f, ln=CFG_SITE_LANG):
out = f
try:
out = self.get_cache()[f][ln]
except KeyError:
pass # translation in LN does not exist
return out
try:
if not field_i18n_name_cache.is_ok_p:
raise Exception
except Exception:
field_i18n_name_cache = FieldI18nNameDataCacher()
class CollectionRecListDataCacher(DataCacher):
def __init__(self):
def cache_filler():
ret = {}
try:
res = run_sql("SELECT name,reclist FROM collection")
except Exception:
# database problems, return empty cache
return {}
for name, reclist in res:
ret[name] = None # this will be filled later during runtime by calling get_collection_reclist(coll)
return ret
def timestamp_getter():
return get_table_update_time('collection')
DataCacher.__init__(self, cache_filler, timestamp_getter)
def get_collection_reclist(self, coll):
cache = self.get_cache()
if not cache[coll]:
# not yet it the cache, so calculate it and fill the cache:
set = HitSet()
query = "SELECT nbrecs,reclist FROM collection WHERE name='%s'" % coll
res = run_sql(query, None, 1)
if res:
try:
set = HitSet(res[0][1])
except:
pass
self.cache[coll] = set
cache[coll] = set
# finally, return reclist:
return cache[coll]
try:
if not collection_reclist_cache.is_ok_p:
raise Exception
except Exception:
collection_reclist_cache = CollectionRecListDataCacher()
class CollectionI18nDataCacher(DataCacher):
def __init__(self):
def cache_filler():
ret = {}
try:
res = run_sql("SELECT c.name,cn.ln,cn.value FROM collectionname AS cn, collection AS c WHERE cn.id_collection=c.id AND cn.type='ln'") # ln=long name
except Exception:
# database problems,
return {}
for c, ln, i18nname in res:
if i18nname:
if not ret.has_key(c):
ret[c] = {}
ret[c][ln] = i18nname
return ret
def timestamp_getter():
return get_table_update_time('collectionname')
DataCacher.__init__(self, cache_filler, timestamp_getter)
def get_coll_i18nname(self, c, ln=CFG_SITE_LANG):
"""Return nicely formatted collection name (of name type 'ln',
'long name') for collection C in language LN."""
cache = self.get_cache()
out = c
try:
out = cache[c][ln]
except KeyError:
pass # translation in LN does not exist
return out
try:
if not collection_i18n_name_cache.is_ok_p:
raise Exception
except Exception:
collection_i18n_name_cache = CollectionI18nDataCacher()
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 = []
query = "SELECT id,name FROM collection ORDER BY name ASC"
res = run_sql(query)
for c_id, c_name in res:
# make a nice printable name (e.g. truncate c_printable for
# long collection names in given language):
c_printable = get_coll_i18nname(c_name, ln)
if len(c_printable)>30:
c_printable = c_printable[:30] + "..."
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 = []
query = "SELECT c.name,cc.id_son FROM collection_collection AS cc, collection AS c "\
" WHERE c.id=cc.id_son AND cc.id_dad='%s' ORDER BY score DESC" % collid
res = run_sql(query)
for c, cid in res:
# make a nice printable name (e.g. truncate c_printable for
# long collection names in given language):
c_printable = get_coll_i18nname(c, ln)
if len(c_printable)>30:
c_printable = c_printable[:30] + "..."
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):
"""Returns first index id where the field code FIELD is indexed.
Returns zero in case there is no table for this index.
Example: field='author', output=4."""
out = 0
res = run_sql("""SELECT w.id FROM idxINDEX AS w, idxINDEX_field AS wf, field AS f
WHERE f.code=%s AND wf.id_field=f.id AND w.id=wf.id_idxINDEX
LIMIT 1""", (field,))
if res:
out = res[0][0]
return out
def get_words_from_pattern(pattern):
"Returns list of whitespace-separated words from pattern."
words = {}
for word in string.split(pattern):
if not words.has_key(word):
words[word] = 1;
return words.keys()
def create_basic_search_units(req, p, f, m=None, of='hb'):
"""Splits search pattern and search field into a list of independently searchable units.
- A search unit consists of '(operator, pattern, field, type, hitset)' tuples where
'operator' is set union (|), set intersection (+) or set exclusion (-);
'pattern' is either a word (e.g. muon*) or a phrase (e.g. 'nuclear physics');
'field' is either a code like 'title' or MARC tag like '100__a';
'type' is the search type ('w' for word file search, 'a' for access file search).
- Optionally, the function accepts the match type argument 'm'.
If it is set (e.g. from advanced search interface), then it
performs this kind of matching. If it is not set, then a guess is made.
'm' can have values: 'a'='all of the words', 'o'='any of the words',
'p'='phrase/substring', 'r'='regular expression',
'e'='exact value'.
- Warnings are printed on req (when not None) in case of HTML output formats."""
opfts = [] # will hold (o,p,f,t,h) units
## check arguments: if matching type phrase/string/regexp, do we have field defined?
if (m=='p' or m=='r' or m=='e') and not f:
m = 'a'
if of.startswith("h"):
print_warning(req, "This matching type cannot be used within <em>any field</em>. I will perform a word search instead." )
print_warning(req, "If you want to phrase/substring/regexp search in a specific field, e.g. inside title, then please choose <em>within title</em> search option.")
## is desired matching type set?
if m:
## A - matching type is known; good!
if m == 'e':
# A1 - exact value:
opfts.append(['+', p, f, 'a']) # '+' since we have only one unit
elif m == 'p':
# A2 - phrase/substring:
opfts.append(['+', "%" + p + "%", f, 'a']) # '+' since we have only one unit
elif m == 'r':
# A3 - regular expression:
opfts.append(['+', p, f, 'r']) # '+' since we have only one unit
elif m == 'a' or m == 'w':
# A4 - all of the words:
p = strip_accents(p) # strip accents for 'w' mode, FIXME: delete when not needed
for word in get_words_from_pattern(p):
opfts.append(['+', word, f, 'w']) # '+' in all units
elif m == 'o':
# A5 - any of the words:
p = strip_accents(p) # strip accents for 'w' mode, FIXME: delete when not needed
for word in get_words_from_pattern(p):
if len(opfts)==0:
opfts.append(['+', word, f, 'w']) # '+' in the first unit
else:
opfts.append(['|', word, f, 'w']) # '|' in further units
else:
if of.startswith("h"):
print_warning(req, "Matching type '%s' is not implemented yet." % m, "Warning")
opfts.append(['+', "%" + p + "%", f, 'a'])
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 and p[0] == "'" and p[-1] == "'":
## B0bis - does 'p' start and end by single quote, and is 'f' defined? => doing ACC search
opfts.append(['+', '%' + p[1:-1] + '%', f, 'a'])
elif f and p[0] == "/" and p[-1] == "/":
## B0ter - does 'p' start and end by a slash, and is 'f' defined? => doing regexp search
opfts.append(['+', p[1:-1], f, 'r'])
elif f and string.find(p, ',') >= 0:
## B1 - does 'p' contain comma, and is 'f' defined? => doing ACC search
opfts.append(['+', p, f, 'a'])
elif f and str(f[0:2]).isdigit():
## B2 - does 'f' exist and starts by two digits? => doing ACC search
opfts.append(['+', p, f, 'a'])
else:
## B3 - doing WRD search, but maybe ACC too
# search units are separated by spaces unless the space is within single or double quotes
# so, let us replace temporarily any space within quotes by '__SPACE__'
p = re_pattern_single_quotes.sub(lambda x: "'"+string.replace(x.group(1), ' ', '__SPACE__')+"'", p)
p = re_pattern_double_quotes.sub(lambda x: "\""+string.replace(x.group(1), ' ', '__SPACE__')+"\"", p)
p = re_pattern_regexp_quotes.sub(lambda x: "/"+string.replace(x.group(1), ' ', '__SPACE__')+"/", p)
# wash argument:
p = re_equal.sub(":", p)
p = re_logical_and.sub(" ", p)
p = re_logical_or.sub(" |", p)
p = re_logical_not.sub(" -", p)
p = re_operators.sub(r' \1', p)
for pi in string.split(p): # iterate through separated units (or items, as "pi" stands for "p item")
pi = re_pattern_space.sub(" ", pi) # replace back '__SPACE__' by ' '
# firstly, determine set operator
if pi[0] == '+' or pi[0] == '-' or pi[0] == '|':
oi = pi[0]
pi = pi[1:]
else:
# okay, there is no operator, so let us decide what to do by default
oi = '+' # by default we are doing set intersection...
# secondly, determine search pattern and field:
if string.find(pi, ":") > 0:
fi, pi = string.split(pi, ":", 1)
else:
fi, pi = f, pi
# look also for old ALEPH field names:
if fi and CFG_WEBSEARCH_FIELDS_CONVERT.has_key(string.lower(fi)):
fi = CFG_WEBSEARCH_FIELDS_CONVERT[string.lower(fi)]
# wash 'pi' argument:
if re_quotes.match(pi):
# B3a - quotes are found => do ACC search (phrase search)
if fi:
if pi[0] == '"' and pi[-1] == '"':
pi = string.replace(pi, '"', '') # remove quote signs
opfts.append([oi, pi, fi, 'a'])
elif pi[0] == "'" and pi[-1] == "'":
pi = string.replace(pi, "'", "") # remove quote signs
opfts.append([oi, "%" + pi + "%", fi, 'a'])
else: # unbalanced quotes, so do WRD query:
opfts.append([oi, pi, fi, 'w'])
else:
# fi is not defined, look at where we are doing exact or subphrase search (single/double quotes):
if pi[0] == '"' and pi[-1] == '"':
opfts.append([oi, pi[1:-1], "anyfield", 'a'])
if of.startswith("h"):
print_warning(req, "Searching for an exact match inside any field may be slow. You may want to search for words instead, or choose to search within specific field.")
else:
# nope, subphrase in global index is not possible => change back to WRD search
pi = strip_accents(pi) # strip accents for 'w' mode, FIXME: delete when not needed
for pii in get_words_from_pattern(pi):
# since there may be '-' and other chars that we do not index in WRD
opfts.append([oi, pii, fi, 'w'])
if of.startswith("h"):
print_warning(req, "The partial phrase search does not work in any field. I'll do a boolean AND searching instead.")
print_warning(req, "If you want to do a partial phrase search in a specific field, e.g. inside title, then please choose 'within title' search option.", "Tip")
print_warning(req, "If you want to do exact phrase matching, then please use double quotes.", "Tip")
elif fi and str(fi[0]).isdigit() and str(fi[0]).isdigit():
# B3b - 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):
# B3c - fi exists but there is no words table for fi => try ACC search
opfts.append([oi, pi, fi, 'a'])
elif fi and pi.startswith('/') and pi.endswith('/'):
# B3d - fi exists and slashes found => try regexp search
opfts.append([oi, pi[1:-1], fi, 'r'])
else:
# B3e - general case => do WRD search
pi = strip_accents(pi) # strip accents for 'w' mode, FIXME: delete when not needed
for pii in get_words_from_pattern(pi):
opfts.append([oi, pii, fi, 'w'])
## sanity check:
for i in range(0, len(opfts)):
try:
pi = opfts[i][1]
if pi == '*':
if of.startswith("h"):
print_warning(req, "Ignoring standalone wildcard word.", "Warning")
del opfts[i]
if pi == '' or pi == ' ':
fi = opfts[i][2]
if fi:
if of.startswith("h"):
print_warning(req, "Ignoring empty <em>%s</em> search term." % fi, "Warning")
del opfts[i]
except:
pass
## return search units:
return opfts
def page_start(req, of, cc, as, ln, uid, title_message=None,
description='', keywords='', recID=-1, tab=''):
"Start page according to given output format."
_ = gettext_set_language(ln)
if not title_message: title_message = _("Search Results")
if not req:
return # we were called from CLI
content_type = get_output_format_content_type(of)
if of.startswith('x'):
if of == 'xr':
# we are doing RSS output
req.content_type = "application/rss+xml"
req.send_http_header()
req.write("""<?xml version="1.0" encoding="UTF-8"?>\n""")
else:
# we are doing XML output:
req.content_type = "text/xml"
req.send_http_header()
req.write("""<?xml version="1.0" encoding="UTF-8"?>\n""")
elif of.startswith('t') or str(of[0:3]).isdigit():
# we are doing plain text output:
req.content_type = "text/plain"
req.send_http_header()
elif of == "id":
pass # nothing to do, we shall only return list of recIDs
elif content_type == 'text/html':
# we are doing HTML output:
req.content_type = "text/html"
req.send_http_header()
if not description:
description = "%s %s." % (cc, _("Search Results"))
if not keywords:
keywords = "%s, WebSearch, %s" % (get_coll_i18nname(CFG_SITE_NAME, ln), get_coll_i18nname(cc, ln))
argd = {}
if req.args:
argd = cgi.parse_qs(req.args)
rssurl = websearch_templates.build_rss_url(argd)
navtrail = create_navtrail_links(cc, as, ln)
navtrail_append_title_p = 1
# FIXME: Find a good point to put this code.
# This is a nice hack to trigger jsMath only when displaying single
# records.
if of.lower() in CFG_WEBSEARCH_USE_JSMATH_FOR_FORMATS:
metaheaderadd = """
<script type='text/javascript'>jsMath = {styles: {'#jsMath_button': 'display: none'}}; </script>
<script src='/jsMath/easy/invenio-jsmath.js' type='text/javascript'></script>
"""
else:
metaheaderadd = ''
if tab != '' or ((of != '' or of.lower() != 'hd') and of != 'hb'):
# 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)
if (of != '' or of.lower() != 'hd') and of != 'hb':
# Export
format_name = of
query = "SELECT name FROM format WHERE code=%s"
res = run_sql(query, (of,))
if res:
format_name = res[0][0]
navtrail += ' &gt; <a class="navtrail" href="%s/record/%s">%s</a> &gt; %s' % \
- (weburl, recID, title_message, format_name)
+ (CFG_SITE_URL, recID, title_message, format_name)
else:
# Discussion, citations, etc. tabs
tab_label = get_detailed_page_tabs(cc, ln=ln)[tab]['label']
navtrail += ' &gt; <a class="navtrail" href="%s/record/%s">%s</a> &gt; %s' % \
- (weburl, recID, title_message, _(tab_label))
+ (CFG_SITE_URL, recID, title_message, _(tab_label))
navtrail_append_title_p = 0
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=\
navtrail_append_title_p,
rssurl=rssurl))
req.write(websearch_templates.tmpl_search_pagestart(ln=ln))
#else:
# req.send_http_header()
def page_end(req, of="hb", ln=CFG_SITE_LANG):
"End page according to given output format: e.g. close XML tags, add HTML footer, etc."
if of == "id":
return [] # empty recID list
if not req:
return # we were called from CLI
if of.startswith('h'):
req.write(websearch_templates.tmpl_search_pageend(ln = ln)) # pagebody end
req.write(pagefooteronly(lastupdated=__lastupdated__, language=ln, req=req))
return "\n"
def create_inputdate_box(name="d1", selected_year=0, selected_month=0, selected_day=0, ln=CFG_SITE_LANG):
"Produces 'From Date', 'Until Date' kind of selection box. Suitable for search options."
_ = gettext_set_language(ln)
box = ""
# day
box += """<select name="%sd">""" % name
box += """<option value="">%s""" % _("any day")
for day in range(1, 32):
box += """<option value="%02d"%s>%02d""" % (day, is_selected(day, selected_day), day)
box += """</select>"""
# month
box += """<select name="%sm">""" % name
box += """<option value="">%s""" % _("any month")
for mm, month in [(1, _("January")), (2, _("February")), (3, _("March")), (4, _("April")), \
(5, _("May")), (6, _("June")), (7, _("July")), (8, _("August")), \
(9, _("September")), (10, _("October")), (11, _("November")), (12, _("December"))]:
box += """<option value="%02d"%s>%s""" % (mm, is_selected(mm, selected_month), month)
box += """</select>"""
# year
box += """<select name="%sy">""" % name
box += """<option value="">%s""" % _("any year")
this_year = int(time.strftime("%Y", time.localtime()))
for year in range(this_year-20, this_year+1):
box += """<option value="%d"%s>%d""" % (year, is_selected(year, selected_year), year)
box += """</select>"""
return box
def create_search_box(cc, colls, p, f, rg, sf, so, sp, rm, of, ot, as,
ln, p1, f1, m1, op1, p2, f2, m2, op2, p3, f3,
m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec,
action=""):
"""Create search box for 'search again in the results page' functionality."""
# load the right message language
_ = gettext_set_language(ln)
# some computations
cc_intl = get_coll_i18nname(cc, ln)
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' : '',
'text' : '*** %s ***' % _("any collection")
})
for val in colls_nice:
# print collection:
if not cx.startswith("Unnamed collection"):
temp.append({ 'value' : val['value'],
'text' : val['text'],
'selected' : (c == re.sub("^[\s\-]*","", val['value']))
})
coll_selects.append(temp)
coll_selects.append([{ 'value' : '',
'text' : '*** %s ***' % _("add another collection")
}] + colls_nice)
else: # we searched in CFG_SITE_NAME, so print 'any collection' heading
coll_selects.append([{ 'value' : '',
'text' : '*** %s ***' % _("any collection")
}] + colls_nice)
sort_fields = [{
'value' : '',
'text' : _("latest first")
}]
query = """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"""
res = run_sql(query)
for code, name in res:
sort_fields.append({
'value' : code,
'text' : name,
})
## ranking methods
ranks = [{
'value' : '',
'text' : "- %s %s -" % (_("OR").lower (), _("rank by")),
}]
for (code, name) in get_bibrank_methods(cc_colID, ln):
# propose found rank methods:
ranks.append({
'value' : code,
'text' : name,
})
formats = []
query = """SELECT code,name FROM format WHERE visibility='1' ORDER BY name ASC"""
res = run_sql(query)
if res:
# propose found formats:
for code, name in res:
formats.append({ 'value' : code,
'text' : name
})
else:
formats.append({'value' : 'hb',
'text' : _("HTML brief")
})
return websearch_templates.tmpl_search_box(
ln = ln,
as = as,
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 = sort_fields,
sf = sf,
so = so,
ranks = ranks,
sc = sc,
rg = rg,
formats = formats,
of = of,
pl = pl,
jrec = jrec,
ec = ec,
)
def create_navtrail_links(cc=CFG_SITE_NAME, as=0, ln=CFG_SITE_LANG, self_p=1, tab=''):
"""Creates navigation trail links, i.e. links to collection
ancestors (except Home collection). If as==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)))
if self_p and cc != CFG_SITE_NAME:
dads.append((cc, get_coll_i18nname(cc, ln)))
return websearch_templates.tmpl_navtrail_links(
as=as, 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_cached("""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_cached("SELECT code,name FROM field ORDER BY name ASC")
fields = [{
'value' : '',
'text' : get_field_i18nname("any field", ln)
}]
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)
})
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):
"""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 = []
# check what type is 'cc':
if type(cc) is list:
for ci in cc:
if collection_reclist_cache.has_key(ci):
# yes this collection is real, so use it:
cc = ci
break
else:
# check once if cc is real:
if not collection_reclist_cache.has_key(cc):
if cc:
raise InvenioWebSearchUnknownCollectionError(cc)
else:
cc = CFG_SITE_NAME # cc is not set, so replace it with Home collection
# check type of 'c' argument:
if type(c) is list:
colls = c
else:
colls = [c]
# remove all 'unreal' collections:
colls_real = []
for coll in colls:
if collection_reclist_cache.has_key(coll):
colls_real.append(coll)
else:
if coll:
raise InvenioWebSearchUnknownCollectionError(coll)
colls = colls_real
# check if some real collections remain:
if len(colls)==0:
colls = [cc]
# 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'
AND c.restricted IS NULL""", (cc,))
l_cc_nonrestricted_sons = []
l_c = colls
for row in res:
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'
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)
# second, let us decide on collection splitting:
if split_colls == 0:
# type A - no sons are wanted
colls_out = colls_out_for_display
# elif split_colls == 1:
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:
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)
return (cc, colls_out_for_display, colls_out)
def strip_accents(x):
"""Strip accents in the input phrase X (assumed in UTF-8) by replacing
accented characters with their unaccented cousins (e.g. é by e).
Return such a stripped X."""
x = re_latex_lowercase_a.sub("a", x)
x = re_latex_lowercase_ae.sub("ae", x)
x = re_latex_lowercase_e.sub("e", x)
x = re_latex_lowercase_i.sub("i", x)
x = re_latex_lowercase_o.sub("o", x)
x = re_latex_lowercase_u.sub("u", x)
x = re_latex_lowercase_y.sub("x", x)
x = re_latex_lowercase_c.sub("c", x)
x = re_latex_lowercase_n.sub("n", x)
x = re_latex_uppercase_a.sub("A", x)
x = re_latex_uppercase_ae.sub("AE", x)
x = re_latex_uppercase_e.sub("E", x)
x = re_latex_uppercase_i.sub("I", x)
x = re_latex_uppercase_o.sub("O", x)
x = re_latex_uppercase_u.sub("U", x)
x = re_latex_uppercase_y.sub("Y", x)
x = re_latex_uppercase_c.sub("C", x)
x = re_latex_uppercase_n.sub("N", x)
# convert input into Unicode string:
try:
y = unicode(x, "utf-8")
except:
return x # something went wrong, probably the input wasn't UTF-8
# asciify Latin-1 lowercase characters:
y = re_unicode_lowercase_a.sub("a", y)
y = re_unicode_lowercase_ae.sub("ae", y)
y = re_unicode_lowercase_e.sub("e", y)
y = re_unicode_lowercase_i.sub("i", y)
y = re_unicode_lowercase_o.sub("o", y)
y = re_unicode_lowercase_u.sub("u", y)
y = re_unicode_lowercase_y.sub("y", y)
y = re_unicode_lowercase_c.sub("c", y)
y = re_unicode_lowercase_n.sub("n", y)
# asciify Latin-1 uppercase characters:
y = re_unicode_uppercase_a.sub("A", y)
y = re_unicode_uppercase_ae.sub("AE", y)
y = re_unicode_uppercase_e.sub("E", y)
y = re_unicode_uppercase_i.sub("I", y)
y = re_unicode_uppercase_o.sub("O", y)
y = re_unicode_uppercase_u.sub("U", y)
y = re_unicode_uppercase_y.sub("Y", y)
y = re_unicode_uppercase_c.sub("C", y)
y = re_unicode_uppercase_n.sub("N", y)
# return UTF-8 representation of the Unicode string:
return y.encode("utf-8")
def wash_index_term(term, max_char_length=50):
"""
Return washed form of the index term TERM that would be suitable
for storing into idxWORD* tables. I.e., lower the TERM, and
truncate it safely to MAX_CHAR_LENGTH UTF-8 characters (meaning,
in principle, 4*MAX_CHAR_LENGTH bytes).
The function works by an internal conversion of TERM, when needed,
from its input Python UTF-8 binary string format into Python
Unicode format, and then truncating it safely to the given number
of TF-8 characters, without possible mis-truncation in the middle
of a multi-byte UTF-8 character that could otherwise happen if we
would have been working with UTF-8 binary representation directly.
Note that MAX_CHAR_LENGTH corresponds to the length of the term
column in idxINDEX* tables.
"""
washed_term = unicode(term, 'utf-8').lower()
if len(washed_term) <= max_char_length:
# no need to truncate the term, because it will fit
# nicely even if it uses four-byte UTF-8 characters
return washed_term.encode('utf-8')
else:
# truncate the term in a safe position:
return washed_term[:max_char_length].encode('utf-8')
def 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 + " "
# get rid of wildcards at the beginning of words:
p = re_pattern_wildcards_at_beginning.sub("\\1", p)
# replace spaces within quotes by __SPACE__ temporarily:
p = re_pattern_single_quotes.sub(lambda x: "'"+string.replace(x.group(1), ' ', '__SPACE__')+"'", p)
p = re_pattern_double_quotes.sub(lambda x: "\""+string.replace(x.group(1), ' ', '__SPACE__')+"\"", p)
p = re_pattern_regexp_quotes.sub(lambda x: "/"+string.replace(x.group(1), ' ', '__SPACE__')+"/", p)
# get rid of extremely short words (1-3 letters with wildcards):
p = re_pattern_short_words.sub("\\1", p)
# replace back __SPACE__ by spaces:
p = re_pattern_space.sub(" ", p)
# replace special terms:
p = re_pattern_today.sub(time.strftime("%Y-%m-%d", time.localtime()), p)
# remove unnecessary whitespace:
p = string.strip(p)
return p
def wash_field(f):
"""Wash field passed by URL."""
# get rid of unnecessary whitespace:
f = string.strip(f)
# wash old-style CDS Invenio/ALEPH 'f' field argument, e.g. replaces 'wau' and 'au' by 'author'
if CFG_WEBSEARCH_FIELDS_CONVERT.has_key(string.lower(f)):
f = CFG_WEBSEARCH_FIELDS_CONVERT[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 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_i18nname(c, ln=CFG_SITE_LANG):
"""Return nicely formatted collection name (of name type 'ln',
'long name') for collection C in language LN."""
global collection_i18nname_cache
global collection_i18nname_cache_timestamp
# firstly, check whether the collectionname table was modified:
if get_table_update_time('collectionname') > collection_i18nname_cache_timestamp:
# yes it was, cache clear-up needed:
collection_i18nname_cache = create_collection_i18nname_cache()
# secondly, read i18n name from either the cache or return common name:
out = c
try:
out = collection_i18nname_cache[c][ln]
except KeyError:
pass # translation in LN does not exist
return out
def get_field_i18nname(f, ln=CFG_SITE_LANG):
"""Return nicely formatted field name (of type 'ln', 'long name')
for field F in language LN."""
global field_i18nname_cache
global field_i18nname_cache_timestamp
# firstly, check whether the fieldname table was modified:
if get_table_update_time('fieldname') > field_i18nname_cache_timestamp:
# yes it was, cache clear-up needed:
field_i18nname_cache = create_field_i18nname_cache()
# secondly, read i18n name from either the cache or return common name:
out = f
try:
out = field_i18nname_cache[f][ln]
except KeyError:
pass # translation in LN does not exist
return out
def get_coll_ancestors(coll):
"Returns a list of ancestors for collection 'coll'."
coll_ancestors = []
coll_ancestor = coll
while 1:
res = run_sql("""SELECT c.name FROM collection AS c
LEFT JOIN collection_collection AS cc ON c.id=cc.id_dad
LEFT JOIN collection AS ccc ON ccc.id=cc.id_son
WHERE ccc.name=%s ORDER BY cc.id_dad ASC LIMIT 1""",
(coll_ancestor,))
if res:
coll_name = res[0][0]
coll_ancestors.append(coll_name)
coll_ancestor = coll_name
else:
break
# ancestors found, return reversed list:
coll_ancestors.reverse()
return coll_ancestors
def get_coll_sons(coll, type='r', public_only=1):
"""Return a list of sons (first-level descendants) of type 'type' for collection 'coll'.
If public_only, then return only non-restricted son collections.
"""
coll_sons = []
query = "SELECT c.name FROM collection AS c "\
"LEFT JOIN collection_collection AS cc ON c.id=cc.id_son "\
"LEFT JOIN collection AS ccc ON ccc.id=cc.id_dad "\
"WHERE cc.type=%s AND ccc.name=%s"
if public_only:
query += " AND c.restricted IS NULL "
query += " ORDER BY cc.score DESC"
res = run_sql(query, (type, coll))
for name in res:
coll_sons.append(name[0])
return coll_sons
def get_coll_real_descendants(coll):
"""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 ORDER BY cc.score DESC""",
(coll,))
for name, dbquery in res:
if dbquery: # this is 'real' collection, so return it:
coll_sons.append(name)
else: # this is 'composed' collection, so recurse:
coll_sons.extend(get_coll_real_descendants(name))
return coll_sons
def get_collection_reclist(coll):
"""Return hitset of recIDs that belong to the collection 'coll'.
But firstly check the last updated date of the collection table.
If it's newer than the cache timestamp, then empty the cache,
since new records could have been added."""
global collection_reclist_cache
global collection_reclist_cache_timestamp
# firstly, check whether the collection table was modified:
if get_table_update_time('collection') > collection_reclist_cache_timestamp:
# yes it was, cache clear-up needed:
collection_reclist_cache = create_collection_reclist_cache()
# secondly, read reclist from either the cache or the database:
if not collection_reclist_cache[coll]:
# not yet it the cache, so calculate it and fill the cache:
query = "SELECT nbrecs,reclist FROM collection WHERE name='%s'" % coll
res = run_sql(query, None, 1)
if res:
try:
set = HitSet(res[0][1])
except:
set = HitSet()
collection_reclist_cache[coll] = set
# finally, return reclist:
return collection_reclist_cache[coll]
def coll_restricted_p(coll):
"Predicate to test if the collection coll is restricted or not."
if not coll:
return 0
res = run_sql("SELECT restricted FROM collection WHERE name=%s", (coll,))
if res and res[0][0] is not None:
return 1
else:
return 0
def coll_restricted_group(coll):
"Return Apache group to which the collection is restricted. Return None if it's public."
if not coll:
return None
res = run_sql("SELECT restricted FROM collection WHERE name=%s", (coll,))
if res:
return res[0][0]
else:
return None
def create_collection_reclist_cache():
"""Creates list of records belonging to collections. Called on startup
and used later for intersecting search results with collection universe."""
global collection_reclist_cache_timestamp
# populate collection reclist cache:
collrecs = {}
try:
res = run_sql("SELECT name,reclist FROM collection")
except Error:
# database problems, set timestamp to zero and return empty cache
collection_reclist_cache_timestamp = 0
return collrecs
for name, reclist in res:
collrecs[name] = None # this will be filled later during runtime by calling get_collection_reclist(coll)
# update timestamp:
try:
collection_reclist_cache_timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
except NameError:
collection_reclist_cache_timestamp = 0
return collrecs
try:
collection_reclist_cache.has_key(CFG_SITE_NAME)
except:
try:
collection_reclist_cache = create_collection_reclist_cache()
except:
collection_reclist_cache = {}
def create_collection_i18nname_cache():
"""Create cache of I18N collection names of type 'ln' (=long name).
Called on startup and used later during the search time."""
global collection_i18nname_cache_timestamp
# populate collection I18N name cache:
names = {}
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 Error:
# database problems, set timestamp to zero and return empty cache
collection_i18nname_cache_timestamp = 0
return names
for c, ln, i18nname in res:
if i18nname:
if not names.has_key(c):
names[c] = {}
names[c][ln] = i18nname
# update timestamp:
try:
collection_i18nname_cache_timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
except NameError:
collection_i18nname_cache_timestamp = 0
return names
try:
collection_i18nname_cache.has_key(CFG_SITE_NAME)
except:
try:
collection_i18nname_cache = create_collection_i18nname_cache()
except:
collection_i18nname_cache = {}
def create_field_i18nname_cache():
"""Create cache of I18N field names of type 'ln' (=long name).
Called on startup and used later during the search time."""
global field_i18nname_cache_timestamp
# populate field I18 name cache:
names = {}
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 Error:
# database problems, set timestamp to zero and return empty cache
field_i18nname_cache_timestamp = 0
return names
for f, ln, i18nname in res:
if i18nname:
if not names.has_key(f):
names[f] = {}
names[f][ln] = i18nname
# update timestamp:
try:
field_i18nname_cache_timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
except NameError:
field_i18nname_cache_timestamp = 0
return names
try:
field_i18nname_cache.has_key(CFG_SITE_NAME)
except:
try:
field_i18nname_cache = create_field_i18nname_cache()
except:
field_i18nname_cache = {}
def browse_pattern(req, colls, p, f, rg, ln=CFG_SITE_LANG):
"""Browse either biliographic phrases or words indexes, and display it."""
# load the right message language
_ = gettext_set_language(ln)
## do we search in words indexes?
if not f:
return browse_in_bibwords(req, p, f)
## is p enclosed in quotes? (coming from exact search)
if p.startswith('"') and p.endswith('"'):
p = p[1:-1]
p_orig = p
## okay, "real browse" follows:
browsed_phrases = get_nearest_terms_in_bibxxx(p, f, rg, 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)
except:
# probably there are no hits at all:
req.write(_("No values found."))
return
## try to check hits in these particular collection selection:
browsed_phrases_in_colls = []
if 0:
for phrase in browsed_phrases:
phrase_hitset = HitSet()
phrase_hitsets = search_pattern("", phrase, f, 'e')
for coll in colls:
phrase_hitset.union_update(phrase_hitsets[coll])
if len(phrase_hitset) > 0:
# okay, this phrase has some hits in colls, so add it:
browsed_phrases_in_colls.append([phrase, len(phrase_hitset)])
## were there hits in collections?
if browsed_phrases_in_colls == []:
if browsed_phrases != []:
#print_warning(req, """<p>No match close to <em>%s</em> found in given collections.
#Please try different term.<p>Displaying matches in any collection...""" % p_orig)
## try to get nbhits for these phrases in any collection:
for phrase in browsed_phrases:
browsed_phrases_in_colls.append([phrase, get_nbhits_in_bibxxx(phrase, f)])
## display results now:
out = websearch_templates.tmpl_browse_pattern(
f=f,
fn=get_field_i18nname(f, ln),
ln=ln,
browsed_phrases_in_colls=browsed_phrases_in_colls,
colls=colls,
)
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):
"""Search for complex pattern 'p' within field 'f' according to
matching type 'm'. Return hitset of recIDs.
The function uses multi-stage searching algorithm in case of no
exact match found. See the Search Internals document for
detailed description.
The 'ap' argument governs whether an alternative patterns are to
be used in case there is no direct hit for (p,f,m). For
example, whether to replace non-alphanumeric characters by
spaces if it would give some hits. See the Search Internals
document for detailed description. (ap=0 forbits the
alternative pattern usage, ap=1 permits it.)
The 'of' argument governs whether to print or not some
information to the user in case of no match found. (Usually it
prints the information in case of HTML formats, otherwise it's
silent).
The 'verbose' argument controls the level of debugging information
to be printed (0=least, 9=most).
All the parameters are assumed to have been previously washed.
This function is suitable as a mid-level API.
"""
_ = gettext_set_language(ln)
hitset_empty = HitSet()
# sanity check:
if not p:
hitset_full = HitSet(trailing_bits=1)
hitset_full.discard(0)
# no pattern, so return all universe
return hitset_full
# search stage 1: break up arguments into basic search units:
if verbose and of.startswith("h"):
t1 = os.times()[4]
basic_search_units = create_basic_search_units(req, p, f, m, of)
if verbose and of.startswith("h"):
t2 = os.times()[4]
print_warning(req, "Search stage 1: basic search units are: %s" % basic_search_units)
print_warning(req, "Search stage 1: execution took %.2f seconds." % (t2 - t1))
# search stage 2: do search for each search unit and verify hit presence:
if verbose and of.startswith("h"):
t1 = os.times()[4]
basic_search_units_hitsets = []
for idx_unit in range(0, len(basic_search_units)):
bsu_o, bsu_p, bsu_f, bsu_m = basic_search_units[idx_unit]
basic_search_unit_hitset = search_unit(bsu_p, bsu_f, bsu_m)
if verbose >= 9 and of.startswith("h"):
print_warning(req, "Search stage 1: pattern %s gave hitlist %s" % (bsu_p, list(basic_search_unit_hitset)))
if len(basic_search_unit_hitset) > 0 or \
ap==0 or \
bsu_o=="|" or \
((idx_unit+1)<len(basic_search_units) and basic_search_units[idx_unit+1][0]=="|"):
# stage 2-1: this basic search unit is retained, since
# either the hitset is non-empty, or the approximate
# pattern treatment is switched off, or the search unit
# was joined by an OR operator to preceding/following
# units so we do not require that it exists
basic_search_units_hitsets.append(basic_search_unit_hitset)
else:
# stage 2-2: no hits found for this search unit, try to replace non-alphanumeric chars inside pattern:
if re.search(r'[^a-zA-Z0-9\s\:]', bsu_p):
if bsu_p.startswith('"') and bsu_p.endswith('"'): # is it ACC query?
bsu_pn = re.sub(r'[^a-zA-Z0-9\s\:]+', "*", bsu_p)
else: # it is WRD query
bsu_pn = re.sub(r'[^a-zA-Z0-9\s\:]+', " ", bsu_p)
if verbose and of.startswith('h') and req:
print_warning(req, "trying (%s,%s,%s)" % (bsu_pn, bsu_f, bsu_m))
basic_search_unit_hitset = search_pattern(req=None, p=bsu_pn, f=bsu_f, m=bsu_m, of="id", ln=ln)
if len(basic_search_unit_hitset) > 0:
# we retain the new unit instead
if of.startswith('h'):
print_warning(req, _("No exact match found for %(x_query1)s, using %(x_query2)s instead...") % \
{'x_query1': "<em>" + cgi.escape(bsu_p) + "</em>",
'x_query2': "<em>" + cgi.escape(bsu_pn) + "</em>"})
basic_search_units[idx_unit][1] = bsu_pn
basic_search_units_hitsets.append(basic_search_unit_hitset)
else:
# stage 2-3: no hits found either, propose nearest indexed terms:
if of.startswith('h'):
if req:
if bsu_f == "recid":
print_warning(req, "Requested record does not seem to exist.")
else:
print_warning(req, create_nearest_terms_box(req.argd, bsu_p, bsu_f, bsu_m, ln=ln))
return hitset_empty
else:
# stage 2-3: no hits found either, propose nearest indexed terms:
if of.startswith('h'):
if req:
if bsu_f == "recid":
print_warning(req, "Requested record does not seem to exist.")
else:
print_warning(req, create_nearest_terms_box(req.argd, bsu_p, bsu_f, bsu_m, ln=ln))
return hitset_empty
if verbose and of.startswith("h"):
t2 = os.times()[4]
for idx_unit in range(0, len(basic_search_units)):
print_warning(req, "Search stage 2: basic search unit %s gave %d hits." %
(basic_search_units[idx_unit][1:], len(basic_search_units_hitsets[idx_unit])))
print_warning(req, "Search stage 2: execution took %.2f seconds." % (t2 - t1))
# search stage 3: apply boolean query for each search unit:
if verbose and of.startswith("h"):
t1 = os.times()[4]
# let the initial set be the complete universe:
hitset_in_any_collection = HitSet(trailing_bits=1)
hitset_in_any_collection.discard(0)
for idx_unit in range(0, len(basic_search_units)):
this_unit_operation = basic_search_units[idx_unit][0]
this_unit_hitset = basic_search_units_hitsets[idx_unit]
if this_unit_operation == '+':
hitset_in_any_collection.intersection_update(this_unit_hitset)
elif this_unit_operation == '-':
hitset_in_any_collection.difference_update(this_unit_hitset)
elif this_unit_operation == '|':
hitset_in_any_collection.union_update(this_unit_hitset)
else:
if of.startswith("h"):
print_warning(req, "Invalid set operation %s." % this_unit_operation, "Error")
if len(hitset_in_any_collection) == 0:
# no hits found, propose alternative boolean query:
if of.startswith('h'):
nearestterms = []
for idx_unit in range(0, len(basic_search_units)):
bsu_o, bsu_p, bsu_f, bsu_m = basic_search_units[idx_unit]
if bsu_p.startswith("%") and bsu_p.endswith("%"):
bsu_p = "'" + bsu_p[1:-1] + "'"
bsu_nbhits = len(basic_search_units_hitsets[idx_unit])
# create a similar query, but with the basic search unit only
argd = {}
argd.update(req.argd)
argd['p'] = bsu_p
argd['f'] = bsu_f
nearestterms.append((bsu_p, bsu_nbhits, argd))
text = websearch_templates.tmpl_search_no_boolean_hits(
ln=ln, nearestterms=nearestterms)
print_warning(req, text)
if verbose and of.startswith("h"):
t2 = os.times()[4]
print_warning(req, "Search stage 3: boolean query gave %d hits." % len(hitset_in_any_collection))
print_warning(req, "Search stage 3: execution took %.2f seconds." % (t2 - t1))
return hitset_in_any_collection
def search_unit(p, f=None, m=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'.
This function is suitable as a low-level API.
"""
## create empty output results set:
set = HitSet()
if not p: # sanity checking
return set
if m == 'a' or m == 'r':
# we are doing either direct bibxxx search or phrase search or regexp search
set = search_unit_in_bibxxx(p, f, m)
else:
# we are doing bibwords search by default
set = search_unit_in_bibwords(p, f)
return set
def search_unit_in_bibwords(word, f, decompress=zlib.decompress):
"""Searches for 'word' inside bibwordsX table for field 'f' and returns hitset of recIDs."""
set = HitSet() # will hold output result set
set_used = 0 # not-yet-used flag, to be able to circumvent set operations
# deduce into which bibwordsX table we will search:
stemming_language = get_index_stemming_language(get_index_id_from_field("anyfield"))
bibwordsX = "idxWORD%02dF" % get_index_id_from_field("anyfield")
if f:
index_id = get_index_id_from_field(f)
if index_id:
bibwordsX = "idxWORD%02dF" % index_id
stemming_language = get_index_stemming_language(index_id)
else:
return HitSet() # word index f does not exist
# wash 'word' argument and run query:
word = string.replace(word, '*', '%') # we now use '*' as the truncation character
words = string.split(word, "->", 1) # check for span query
if len(words) == 2:
word0 = re_word.sub('', words[0])
word1 = re_word.sub('', words[1])
if stemming_language:
word0 = stem(word0, stemming_language)
word1 = stem(word1, stemming_language)
res = run_sql("SELECT term,hitlist FROM %s WHERE term BETWEEN %%s AND %%s" % bibwordsX,
(wash_index_term(word0), wash_index_term(word1)))
else:
word = re_word.sub('', word)
if stemming_language:
word = stem(word, stemming_language)
if string.find(word, '%') >= 0: # do we have wildcard in the word?
res = run_sql("SELECT term,hitlist FROM %s WHERE term LIKE %%s" % bibwordsX,
(wash_index_term(word),))
else:
res = run_sql("SELECT term,hitlist FROM %s WHERE term=%%s" % bibwordsX,
(wash_index_term(word),))
# fill the result set:
for word, hitlist in res:
hitset_bibwrd = HitSet(hitlist)
# add the results:
if set_used:
set.union_update(hitset_bibwrd)
else:
set = hitset_bibwrd
set_used = 1
# okay, return result set:
return set
def search_unit_in_bibxxx(p, f, type):
"""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)."""
p_orig = p # saving for eventual future 'no match' reporting
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,)
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:
query_addons = "BETWEEN %s AND %s"
query_params = (ps[0], ps[1])
else:
if string.find(p, '%') > -1:
query_addons = "LIKE %s"
query_params = (ps[0],)
else:
query_addons = "= %s"
query_params = (ps[0],)
# 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:
# convert old ALEPH tag names, if appropriate: (TODO: get rid of this before entering this function)
if CFG_WEBSEARCH_FIELDS_CONVERT.has_key(string.lower(f)):
f = CFG_WEBSEARCH_FIELDS_CONVERT[string.lower(f)]
# 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":
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"
res = run_sql(query, query_params + (t + '%',))
else:
# exact query for 't':
query += " AND bx.tag=%s"
res = run_sql(query, query_params + (t,))
# fill the result set:
for id_bibrec in res:
if id_bibrec[0]:
l.append(id_bibrec[0])
# check no of hits found:
nb_hits = len(l)
# okay, return result set:
set = HitSet(l)
return set
def search_unit_in_bibrec(datetext1, datetext2, type='c'):
"""
Return hitset of recIDs found that were either created or modified
(according to 'type' arg being 'c' or 'm') from datetext1 until datetext2, inclusive.
Does not pay attention to pattern, collection, anything. Useful
to intersect later on with the 'real' query.
"""
set = HitSet()
if type.startswith("m"):
type = "modification_date"
else:
type = "creation_date" # by default we are searching for creation dates
res = run_sql("SELECT id FROM bibrec WHERE %s>=%%s AND %s<=%%s" % (type, type),
(datetext1, datetext2))
for row in res:
set += row[0]
return set
def intersect_results_with_collrecs(req, hitset_in_any_collection, colls, ap=0, of="hb", verbose=0, ln=CFG_SITE_LANG):
"""Return dict of hitsets given by intersection of hitset with the collection universes."""
_ = gettext_set_language(ln)
# search stage 4: intersect with the collection universe:
if verbose and of.startswith("h"):
t1 = os.times()[4]
results = {}
results_nbhits = 0
for coll in colls:
results[coll] = hitset_in_any_collection & get_collection_reclist(coll)
results_nbhits += len(results[coll])
if results_nbhits == 0:
# no hits found, try to search in Home:
results_in_Home = hitset_in_any_collection & get_collection_reclist(CFG_SITE_NAME)
if len(results_in_Home) > 0:
# some hits found in Home, so propose this search:
if of.startswith("h"):
url = websearch_templates.build_search_url(req.argd, cc=CFG_SITE_NAME, c=[])
print_warning(req, _("No match found in collection %(x_collection)s. Other public collections gave %(x_url_open)s%(x_nb_hits)d hits%(x_url_close)s.") %\
{'x_collection': '<em>' + string.join([get_coll_i18nname(coll, ln) for coll in colls], ', ') + '</em>',
'x_url_open': '<a class="nearestterms" href="%s">' % (url),
'x_nb_hits': len(results_in_Home),
'x_url_close': '</a>'})
results = {}
else:
# no hits found in Home, recommend different search terms:
if of.startswith("h"):
print_warning(req, _("No public collection matched your query. "
"If you were looking for a non-public document, please choose "
"the desired restricted collection first."))
results = {}
if verbose and of.startswith("h"):
t2 = os.times()[4]
print_warning(req, "Search stage 4: intersecting with collection universe gave %d hits." % results_nbhits)
print_warning(req, "Search stage 4: execution took %.2f seconds." % (t2 - t1))
return results
def intersect_results_with_hitset(req, results, hitset, ap=0, aptext="", of="hb"):
"""Return intersection of search 'results' (a dict of hitsets
with collection as key) with the 'hitset', i.e. apply
'hitset' intersection to each collection within search
'results'.
If the final 'results' set is to be empty, and 'ap'
(approximate pattern) is true, and then print the `warningtext'
and return the original 'results' set unchanged. If 'ap' is
false, then return empty results set.
"""
if ap:
results_ap = copy.deepcopy(results)
else:
results_ap = {} # will return empty dict in case of no hits found
nb_total = 0
for coll in results.keys():
results[coll].intersection_update(hitset)
nb_total += len(results[coll])
if nb_total == 0:
if of.startswith("h"):
print_warning(req, aptext)
results = results_ap
return results
def create_similarly_named_authors_link_box(author_name, ln=CFG_SITE_LANG):
"""Return a box similar to ``Not satisfied...'' one by proposing
author searches for similar names. Namely, take AUTHOR_NAME
and the first initial of the firstame (after comma) and look
into author index whether authors with e.g. middle names exist.
Useful mainly for CERN Library that sometimes contains name
forms like Ellis-N, Ellis-Nick, Ellis-Nicolas all denoting the
same person. The box isn't proposed if no similarly named
authors are found to exist.
"""
# return nothing if not configured:
if CFG_WEBSEARCH_CREATE_SIMILARLY_NAMED_AUTHORS_LINK_BOX == 0:
return ""
# return empty box if there is no initial:
if re.match(r'[^ ,]+, [^ ]', author_name) is None:
return ""
# firstly find name comma initial:
author_name_to_search = re.sub(r'^([^ ,]+, +[^ ,]).*$', '\\1', author_name)
# secondly search for similar name forms:
similar_author_names = {}
for name in author_name_to_search, strip_accents(author_name_to_search):
for tag in get_field_tags("author"):
# deduce into which bibxxx table we will search:
digit1, digit2 = int(tag[0]), int(tag[1])
bx = "bib%d%dx" % (digit1, digit2)
bibx = "bibrec_bib%d%dx" % (digit1, digit2)
if len(tag) != 6 or tag[-1:]=='%':
# only the beginning of field 't' is defined, so add wildcard character:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value LIKE %%s AND bx.tag LIKE %%s""" % bx,
(name + "%", tag + "%"))
else:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value LIKE %%s AND bx.tag=%%s""" % bx,
(name + "%", tag))
for row in res:
similar_author_names[row[0]] = 1
# remove the original name and sort the list:
try:
del similar_author_names[author_name]
except KeyError:
pass
# thirdly print the box:
out = ""
if similar_author_names:
out_authors = similar_author_names.keys()
out_authors.sort()
tmp_authors = []
for out_author in out_authors:
nbhits = get_nbhits_in_bibxxx(out_author, "author")
if nbhits:
tmp_authors.append((out_author, nbhits))
out += websearch_templates.tmpl_similar_author_names(
authors=tmp_authors, ln=ln)
return out
def create_nearest_terms_box(urlargd, p, f, t='w', n=5, ln=CFG_SITE_LANG, intro_text_p=True):
"""Return text box containing list of 'n' nearest terms above/below 'p'
for the field 'f' for matching type 't' (words/phrases) in
language 'ln'.
Propose new searches according to `urlargs' with the new words.
If `intro_text_p' is true, then display the introductory message,
otherwise print only the nearest terms in the box content.
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
nearest_terms = []
if not p: # sanity check
p = "."
# look for nearest terms:
if t == 'w':
nearest_terms = get_nearest_terms_in_bibwords(p, f, n, n)
if not nearest_terms:
return "%s %s." % (_("No words index available for"), get_field_i18nname(f, ln))
else:
nearest_terms = get_nearest_terms_in_bibxxx(p, f, n, n)
if not nearest_terms:
return "%s %s." % (_("No phrase index available for"), get_field_i18nname(f, ln))
terminfo = []
for term in nearest_terms:
if t == 'w':
hits = get_nbhits_in_bibwords(term, f)
else:
hits = get_nbhits_in_bibxxx(term, f)
argd = {}
argd.update(urlargd)
# check which fields contained the requested parameter, and replace it.
for (px, fx) in ('p', 'f'), ('p1', 'f1'), ('p2', 'f2'), ('p3', 'f3'):
if px in argd:
if f == argd[fx] or f == "anyfield" or f == "":
if string.find(argd[px], p) > -1:
argd[px] = string.replace(argd[px], p, term)
break
else:
if string.find(argd[px], f+':'+p) > -1:
argd[px] = string.replace(argd[px], f+':'+p, f+':'+term)
break
elif string.find(argd[px], f+':"'+p+'"') > -1:
argd[px] = string.replace(argd[px], f+':"'+p+'"', f+':"'+term+'"')
break
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(f, ln)) + "</em>"}
else:
intro = _("Search term %s did not match any record. Nearest terms in any collection are:") % \
("<em>" + cgi.escape(p.startswith("%") and p.endswith("%") and p[1:-1] or p) + "</em>")
return websearch_templates.tmpl_nearest_term_box(p=p, ln=ln, f=f, terminfo=terminfo,
intro=intro)
def get_nearest_terms_in_bibwords(p, f, n_below, n_above):
"""Return list of +n -n nearest terms to word `p' in index for field `f'."""
nearest_words = [] # will hold the (sorted) list of nearest words to return
# deduce into which bibwordsX table we will search:
bibwordsX = "idxWORD%02dF" % get_index_id_from_field("anyfield")
if f:
index_id = get_index_id_from_field(f)
if index_id:
bibwordsX = "idxWORD%02dF" % index_id
else:
return nearest_words
# firstly try to get `n' closest words above `p':
res = run_sql("SELECT term FROM %s WHERE term<%%s ORDER BY term DESC LIMIT %%s" % bibwordsX,
(p, n_above))
for row in res:
nearest_words.append(row[0])
nearest_words.reverse()
# secondly insert given word `p':
nearest_words.append(p)
# finally try to get `n' closest words below `p':
res = run_sql("SELECT term FROM %s WHERE term>%%s ORDER BY term ASC LIMIT %%s" % bibwordsX,
(p, n_below))
for row in res:
nearest_words.append(row[0])
return nearest_words
def get_nearest_terms_in_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)
## 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.
n_fetch = 2*max(n_below, n_above)
## construct 'tl' which defines the tag list (MARC tags) to search in:
tl = []
if str(f[0]).isdigit() and str(f[1]).isdigit():
tl.append(f) # 'f' seems to be okay as it starts by two digits
else:
# deduce desired MARC tags on the basis of chosen 'f'
tl = get_field_tags(f)
## start browsing to fetch list of hits:
browsed_phrases = {} # will hold {phrase1: 1, phrase2: 1, ..., phraseN: 1} dict of browsed phrases (to make them unique)
# always add self to the results set:
browsed_phrases[p.startswith("%") and p.endswith("%") and p[1:-1] or p] = 1
for t in tl:
# deduce into which bibxxx table we will search:
digit1, digit2 = int(t[0]), int(t[1])
bx = "bib%d%dx" % (digit1, digit2)
bibx = "bibrec_bib%d%dx" % (digit1, digit2)
# firstly try to get `n' closest phrases above `p':
if len(t) != 6 or t[-1:]=='%': # only the beginning of field 't' is defined, so add wildcard character:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value<%%s AND bx.tag LIKE %%s
ORDER BY bx.value DESC LIMIT %%s""" % bx,
(p, t + "%", n_fetch))
else:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value<%%s AND bx.tag=%%s
ORDER BY bx.value DESC LIMIT %%s""" % bx,
(p, t, n_fetch))
for row in res:
browsed_phrases[row[0]] = 1
# secondly try to get `n' closest phrases equal to or below `p':
if len(t) != 6 or t[-1:]=='%': # only the beginning of field 't' is defined, so add wildcard character:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value>=%%s AND bx.tag LIKE %%s
ORDER BY bx.value ASC LIMIT %%s""" % bx,
(p, t + "%", n_fetch))
else:
res = run_sql("""SELECT bx.value FROM %s AS bx
WHERE bx.value>=%%s AND bx.tag=%%s
ORDER BY bx.value ASC LIMIT %%s""" % bx,
(p, t, n_fetch))
for row in res:
browsed_phrases[row[0]] = 1
# select first n words only: (this is needed as we were searching
# in many different tables and so aren't sure we have more than n
# words right; this of course won't be needed when we shall have
# one ACC table only for given field):
phrases_out = browsed_phrases.keys()
phrases_out.sort(lambda x, y: cmp(string.lower(strip_accents(x)),
string.lower(strip_accents(y))))
# find position of self:
try:
idx_p = phrases_out.index(p)
except:
idx_p = len(phrases_out)/2
# return n_above and n_below:
return phrases_out[max(0, idx_p-n_above):idx_p+n_below]
def get_nbhits_in_bibwords(word, f):
"""Return number of hits for word 'word' inside words index for field 'f'."""
out = 0
# deduce into which bibwordsX table we will search:
bibwordsX = "idxWORD%02dF" % get_index_id_from_field("anyfield")
if f:
index_id = get_index_id_from_field(f)
if index_id:
bibwordsX = "idxWORD%02dF" % index_id
else:
return 0
if word:
res = run_sql("SELECT hitlist FROM %s WHERE term=%%s" % bibwordsX,
(word,))
for hitlist in res:
out += len(HitSet(hitlist[0]))
return out
def get_nbhits_in_bibxxx(p, f):
"""Return number of hits for word 'word' inside words index for field 'f'."""
## determine browse field:
if not f and string.find(p, ":") > 0: # does 'p' contain ':'?
f, p = string.split(p, ":", 1)
## construct 'tl' which defines the tag list (MARC tags) to search in:
tl = []
if str(f[0]).isdigit() and str(f[1]).isdigit():
tl.append(f) # 'f' seems to be okay as it starts by two digits
else:
# deduce desired MARC tags on the basis of chosen 'f'
tl = get_field_tags(f)
# start searching:
recIDs = {} # will hold dict of {recID1: 1, recID2: 1, ..., } (unique recIDs, therefore)
for t in tl:
# deduce into which bibxxx table we will search:
digit1, digit2 = int(t[0]), int(t[1])
bx = "bib%d%dx" % (digit1, digit2)
bibx = "bibrec_bib%d%dx" % (digit1, digit2)
if len(t) != 6 or t[-1:]=='%': # only the beginning of field 't' is defined, so add wildcard character:
res = run_sql("""SELECT bibx.id_bibrec FROM %s AS bibx, %s AS bx
WHERE bx.value=%%s AND bx.tag LIKE %%s
AND bibx.id_bibxxx=bx.id""" % (bibx, bx),
(p, t + "%"))
else:
res = run_sql("""SELECT bibx.id_bibrec FROM %s AS bibx, %s AS bx
WHERE bx.value=%%s AND bx.tag=%%s
AND bibx.id_bibxxx=bx.id""" % (bibx, bx),
(p, t))
for row in res:
recIDs[row[0]] = 1
return len(recIDs)
def get_mysql_recid_from_aleph_sysno(sysno):
"""Returns DB's recID for ALEPH sysno passed in the argument (e.g. "002379334CER").
Returns None in case of failure."""
out = None
res = run_sql("""SELECT bb.id_bibrec FROM bibrec_bib97x AS bb, bib97x AS b
WHERE b.value=%s AND b.tag='970__a' AND bb.id_bibxxx=b.id""",
(sysno,))
if res:
out = res[0][0]
return out
def guess_primary_collection_of_a_record(recID):
"""Return primary collection name a record recid belongs to, by testing 980 identifier.
May lead to bad guesses when a collection is defined dynamically bia dbquery.
In that case, return 'CFG_SITE_NAME'."""
out = CFG_SITE_NAME
dbcollids = get_fieldvalues(recID, "980__a")
if dbcollids:
dbquery = "collection:" + dbcollids[0]
res = run_sql("SELECT name FROM collection WHERE dbquery=%s", (dbquery,))
if res:
out = res[0][0]
return out
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_tags(field):
"""Returns a list of MARC tags for the field code 'field'.
Returns empty list in case of error.
Example: field='author', output=['100__%','700__%']."""
out = []
query = """SELECT t.value FROM tag AS t, field_tag AS ft, field AS f
WHERE f.code=%s AND ft.id_field=f.id AND t.id=ft.id_tag
ORDER BY ft.score DESC"""
res = run_sql(query, (field, ))
for val in res:
out.append(val[0])
return out
def get_fieldvalues(recID, tag):
"""Return list of field values for field TAG inside record RECID."""
out = []
if tag == "001___":
# we have asked for recID that is not stored in bibXXx tables
out.append(str(recID))
else:
# we are going to look inside bibXXx tables
digits = tag[0:2]
try:
intdigits = int(digits)
if intdigits < 0 or intdigits > 99:
raise ValueError
except ValueError:
# invalid tag value asked for
return []
bx = "bib%sx" % digits
bibx = "bibrec_bib%sx" % digits
query = "SELECT bx.value FROM %s AS bx, %s AS bibx " \
" WHERE bibx.id_bibrec='%s' AND bx.id=bibx.id_bibxxx AND bx.tag LIKE '%s' " \
" ORDER BY bibx.field_number, bx.tag ASC" % (bx, bibx, recID, tag)
res = run_sql(query)
for row in res:
out.append(row[0])
return out
def get_fieldvalues_alephseq_like(recID, tags_in):
"""Return buffer of ALEPH sequential-like textual format with fields found in the list TAGS_IN for record RECID."""
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 false you 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, recID, tag)
res = run_sql(query)
# 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]
if ind1 == "_":
ind1 = ""
if ind2 == "_":
ind2 = ""
# print field tag
if field_number != field_number_old or field[:-1] != field_old[:-1]:
if out:
out += "\n"
out += "%09d %s " % (recID, field[:5])
field_number_old = field_number
field_old = field
# print subfield value
if field[0:2] == "00" and field[-1:] == "_":
out += value
else:
out += "$$%s%s" % (field[-1:], value)
return out
def record_exists(recID):
"""Return 1 if record RECID exists.
Return 0 if it doesn't exist.
Return -1 if it exists but is marked as deleted."""
out = 0
query = "SELECT id FROM bibrec WHERE id='%s'" % recID
res = run_sql(query, None, 1)
if res:
# record exists; now check whether it isn't marked as deleted:
dbcollids = get_fieldvalues(recID, "980__%")
if ("DELETED" in dbcollids) or (CFG_CERN_SITE and "DUMMY" in dbcollids):
out = -1 # exists, but marked as deleted
else:
out = 1 # exists fine
return out
def record_public_p(recID):
"""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)
def get_creation_date(recID, fmt="%Y-%m-%d"):
"Returns the creation date of the record 'recID'."
out = ""
res = run_sql("SELECT DATE_FORMAT(creation_date,%s) FROM bibrec WHERE id=%s", (fmt, recID), 1)
if res:
out = res[0][0]
return out
def get_modification_date(recID, fmt="%Y-%m-%d"):
"Returns the date of last modification for the record 'recID'."
out = ""
res = run_sql("SELECT DATE_FORMAT(modification_date,%s) FROM bibrec WHERE id=%s", (fmt, recID), 1)
if res:
out = res[0][0]
return out
def print_warning(req, msg, type='', prologue='<br />', epilogue='<br />'):
"Prints warning message and flushes output."
if req and msg:
req.write(websearch_templates.tmpl_print_warning(
msg = msg,
type = type,
prologue = prologue,
epilogue = epilogue,
))
return
def print_search_info(p, f, sf, so, sp, rm, of, ot, collection=CFG_SITE_NAME, nb_found=-1, jrec=1, rg=10,
as=0, ln=CFG_SITE_LANG, p1="", p2="", p3="", f1="", f2="", f3="", m1="", m2="", m3="", op1="", op2="",
sc=1, pl_in_url="",
d1y=0, d1m=0, d1d=0, d2y=0, d2m=0, d2d=0, dt="",
cpu_time=-1, middle_only=0):
"""Prints stripe with the information on 'collection' and 'nb_found' results and CPU time.
Also, prints navigation links (beg/next/prev/end) inside the results set.
If middle_only is set to 1, it will only print the middle box information (beg/netx/prev/end/etc) links.
This is suitable for displaying navigation links at the bottom of the search results page."""
out = ""
# sanity check:
if jrec < 1:
jrec = 1
if jrec > nb_found:
jrec = max(nb_found-rg+1, 1)
return websearch_templates.tmpl_print_search_info(
ln = ln,
- weburl = weburl,
collection = collection,
as = as,
collection_name = get_coll_i18nname(collection, ln),
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(req, colls, results_final_nb_total, results_final_nb, cpu_time, ln=CFG_SITE_LANG, ec=[]):
"""Prints results overview box with links to particular collections below."""
out = ""
new_colls = []
for coll in colls:
new_colls.append({
'id': get_colID(coll),
'code': coll,
'name': get_coll_i18nname(coll, ln),
})
return websearch_templates.tmpl_print_results_overview(
ln = ln,
- weburl = weburl,
results_final_nb_total = results_final_nb_total,
results_final_nb = results_final_nb,
cpu_time = cpu_time,
colls = new_colls,
ec = ec,
)
def sort_records(req, recIDs, sort_field='', sort_order='d', sort_pattern='', verbose=0, of='hb', ln=CFG_SITE_LANG):
"""Sort records in 'recIDs' list according sort field 'sort_field' in order 'sort_order'.
If more than one instance of 'sort_field' is found for a given record, try to choose that that is given by
'sort pattern', for example "sort by report number that starts by CERN-PS".
Note that 'sort_field' can be field code like 'author' or MARC tag like '100__a' directly."""
_ = gettext_set_language(ln)
## check arguments:
if not sort_field:
return recIDs
if len(recIDs) > CFG_WEBSEARCH_NB_RECORDS_TO_SORT:
if of.startswith('h'):
print_warning(req, _("Sorry, sorting is allowed on sets of up to %d records only. Using default sort order.") % CFG_WEBSEARCH_NB_RECORDS_TO_SORT, "Warning")
return recIDs
sort_fields = string.split(sort_field, ",")
recIDs_dict = {}
recIDs_out = []
## first deduce sorting MARC tag out of the 'sort_field' argument:
tags = []
for sort_field in sort_fields:
if sort_field and str(sort_field[0:2]).isdigit():
# sort_field starts by two digits, so this is probably a MARC tag already
tags.append(sort_field)
else:
# let us check the 'field' table
query = """SELECT DISTINCT(t.value) FROM tag AS t, field_tag AS ft, field AS f
WHERE f.code='%s' AND ft.id_field=f.id AND t.id=ft.id_tag
ORDER BY ft.score DESC""" % sort_field
res = run_sql(query)
if res:
for row in res:
tags.append(row[0])
else:
if of.startswith('h'):
print_warning(req, _("Sorry, %s does not seem to be a valid sort option. Choosing title sort instead.") % sort_field, "Error")
tags.append("245__a")
if verbose >= 3:
print_warning(req, "Sorting by tags %s." % tags)
if sort_pattern:
print_warning(req, "Sorting preferentially by %s." % sort_pattern)
## check if we have sorting tag defined:
if tags:
# fetch the necessary field values:
for recID in recIDs:
val = "" # will hold value for recID according to which sort
vals = [] # will hold all values found in sorting tag for recID
for tag in tags:
vals.extend(get_fieldvalues(recID, tag))
if sort_pattern:
# try to pick that tag value that corresponds to sort pattern
bingo = 0
for v in vals:
if v.lower().startswith(sort_pattern.lower()): # bingo!
bingo = 1
val = v
break
if not bingo: # sort_pattern not present, so add other vals after spaces
val = sort_pattern + " " + string.join(vals)
else:
# no sort pattern defined, so join them all together
val = string.join(vals)
val = strip_accents(val.lower()) # sort values regardless of accents and case
if recIDs_dict.has_key(val):
recIDs_dict[val].append(recID)
else:
recIDs_dict[val] = [recID]
# sort them:
recIDs_dict_keys = recIDs_dict.keys()
recIDs_dict_keys.sort()
# now that keys are sorted, create output array:
for k in recIDs_dict_keys:
for s in recIDs_dict[k]:
recIDs_out.append(s)
# ascending or descending?
if sort_order == 'a':
recIDs_out.reverse()
# okay, we are done
return recIDs_out
else:
# good, no sort needed
return recIDs
def print_records(req, recIDs, jrec=1, rg=10, format='hb', ot='', ln=CFG_SITE_LANG, relevances=[], relevances_prologue="(", relevances_epilogue="%%)", decompress=zlib.decompress, search_pattern='', print_records_prologue_p=True, print_records_epilogue_p=True, verbose=0, tab=''):
"""
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.
"""
# load the right message language
_ = gettext_set_language(ln)
# sanity checking:
if req is None:
return
# get user_info (for formatting based on user)
user_info = collect_user_info(req)
if len(recIDs):
nb_found = len(recIDs)
if rg == -9999: # print all records
rg = nb_found
else:
rg = abs(rg)
if jrec < 1: # sanity checks
jrec = 1
if jrec > nb_found:
jrec = max(nb_found-rg+1, 1)
# will print records from irec_max to irec_min excluded:
irec_max = nb_found - jrec
irec_min = nb_found - jrec - rg
if irec_min < 0:
irec_min = -1
if irec_max >= nb_found:
irec_max = nb_found - 1
#req.write("%s:%d-%d" % (recIDs, irec_min, irec_max))
if format.startswith('x'):
# print header if needed
if print_records_prologue_p:
print_records_prologue(req, format)
# print records
recIDs_to_print = [recIDs[x] for x in range(irec_max, irec_min, -1)]
format_records(recIDs_to_print,
format,
ln=ln,
search_pattern=search_pattern,
record_separator="\n",
user_info=user_info,
req=req)
# print footer if needed
if print_records_epilogue_p:
print_records_epilogue(req, format)
elif format.startswith('t') or str(format[0:3]).isdigit():
# we are doing plain text output:
for irec in range(irec_max, irec_min, -1):
x = print_record(recIDs[irec], format, ot, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
req.write(x)
if x:
req.write('\n')
elif format == 'excel':
recIDs_to_print = [recIDs[x] for x in range(irec_max, irec_min, -1)]
create_excel(recIDs=recIDs_to_print, req=req, ln=ln)
else:
# we are doing HTML output:
if format == 'hp' or format.startswith("hb_") or format.startswith("hd_"):
# portfolio and on-the-fly formats:
for irec in range(irec_max, irec_min, -1):
req.write(print_record(recIDs[irec], format, ot, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose))
elif format.startswith("hb"):
# HTML brief format:
req.write(websearch_templates.tmpl_record_format_htmlbrief_header(
ln = ln))
for irec in range(irec_max, irec_min, -1):
row_number = jrec+irec_max-irec
recid = recIDs[irec]
if relevances and relevances[irec]:
relevance = relevances[irec]
else:
relevance = ''
record = print_record(recIDs[irec], format, ot, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
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,
))
req.write(websearch_templates.tmpl_record_format_htmlbrief_footer(
ln = ln))
elif format.startswith("hd"):
# HTML detailed format:
for irec in range(irec_max, irec_min, -1):
unordered_tabs = get_detailed_page_tabs(get_colID(guess_primary_collection_of_a_record(recIDs[irec])),
recIDs[irec], ln=ln)
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x,y: cmp(x[1],y[1]))
link_ln = ''
if ln != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (weburl, recIDs[irec], tab_id, link_ln), \
+ '%s/record/%s/%s%s' % (CFG_SITE_URL, recIDs[irec], tab_id, link_ln), \
tab_id == tab,
unordered_tabs[tab_id]['enabled']) \
for (tab_id, order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
content = ''
# load content
if tab == 'usage':
r = calculate_reading_similarity_list(recIDs[irec], "downloads")
downloadsimilarity = None
downloadhistory = None
#if r:
# downloadsimilarity = r
if CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS:
downloadhistory = create_download_history_graph_and_box(recIDs[irec], ln)
r = calculate_reading_similarity_list(recIDs[irec], "pageviews")
viewsimilarity = None
if r: viewsimilarity = r
content = websearch_templates.tmpl_detailed_record_statistics(recIDs[irec],
ln,
downloadsimilarity=downloadsimilarity,
downloadhistory=downloadhistory,
viewsimilarity=viewsimilarity)
req.write(webstyle_templates.detailed_record_container(content,
recIDs[irec],
tabs,
ln))
elif tab == 'citations':
citinglist = []
citationhistory = None
recid = recIDs[irec]
selfcited = get_self_cited_by(recid)
r = calculate_cited_by_list(recid)
if r:
citinglist = r
citationhistory = create_citation_history_graph_and_box(recid, ln)
r = calculate_co_cited_with_list(recid)
cociting = None
if r:
cociting = r
content = websearch_templates.tmpl_detailed_record_citations(recid,
ln,
citinglist=citinglist,
citationhistory=citationhistory,
cociting=cociting,
selfcited=selfcited)
req.write(webstyle_templates.detailed_record_container(content,
recid,
tabs,
ln))
elif tab == 'references':
content = format_record(recIDs[irec], 'HDREF', ln=ln, user_info=user_info, verbose=verbose)
req.write(webstyle_templates.detailed_record_container(content,
recIDs[irec],
tabs,
ln))
else:
# Metadata tab
content = print_record(recIDs[irec], format, ot, ln,
search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
creationdate = None
modificationdate = None
if record_exists(recIDs[irec]) == 1:
creationdate = get_creation_date(recIDs[irec])
modificationdate = get_modification_date(recIDs[irec])
content = websearch_templates.tmpl_detailed_record_metadata(
recID = recIDs[irec],
ln = ln,
format = format,
creationdate = creationdate,
modificationdate = modificationdate,
content = content)
req.write(webstyle_templates.detailed_record_container(content,
recIDs[irec],
tabs,
ln=ln,
creationdate=creationdate,
modificationdate=modificationdate,
show_short_rec_p=False))
if len(tabs) > 0:
# Add the mini box at bottom of the page
if CFG_WEBCOMMENT_ALLOW_REVIEWS:
from invenio.webcomment import get_mini_reviews
reviews = get_mini_reviews(recid = recIDs[irec], ln=ln)
else:
reviews = ''
actions = format_record(recIDs[irec], 'HDACT', ln=ln, user_info=user_info, verbose=verbose)
files = format_record(recIDs[irec], 'HDFILE', ln=ln, user_info=user_info, verbose=verbose)
req.write(webstyle_templates.detailed_record_mini_panel(recIDs[irec],
ln,
format,
files=files,
reviews=reviews,
actions=actions))
else:
# Other formats
for irec in range(irec_max, irec_min, -1):
req.write(print_record(recIDs[irec], format, ot, ln,
search_pattern=search_pattern,
user_info=user_info, verbose=verbose))
else:
print_warning(req, _("Use different search terms."))
def print_records_prologue(req, format):
"""
Print the appropriate prologue for list of records in the given
format.
"""
prologue = "" # no prologue needed for HTML or Text formats
if format.startswith('xm'):
prologue = websearch_templates.tmpl_xml_marc_prologue()
elif format.startswith('xn'):
prologue = websearch_templates.tmpl_xml_nlm_prologue()
elif format.startswith('xr'):
prologue = websearch_templates.tmpl_xml_rss_prologue()
elif format.startswith('x'):
prologue = websearch_templates.tmpl_xml_default_prologue()
req.write(prologue)
def print_records_epilogue(req, format):
"""
Print the appropriate epilogue for list of records in the given
format.
"""
epilogue = "" # no epilogue needed for HTML or Text formats
if format.startswith('xm'):
epilogue = websearch_templates.tmpl_xml_marc_epilogue()
elif format.startswith('xn'):
epilogue = websearch_templates.tmpl_xml_nlm_epilogue()
elif format.startswith('xr'):
epilogue = websearch_templates.tmpl_xml_rss_epilogue()
elif format.startswith('x'):
epilogue = websearch_templates.tmpl_xml_default_epilogue()
req.write(epilogue)
def print_record(recID, format='hb', ot='', ln=CFG_SITE_LANG, decompress=zlib.decompress,
search_pattern=None, user_info=None, verbose=0):
"""Prints record 'recID' formatted accoding to 'format'."""
_ = gettext_set_language(ln)
out = ""
# sanity check:
record_exist_p = record_exists(recID)
if record_exist_p == 0: # doesn't exist
return out
# New Python BibFormat procedure for formatting
# Old procedure follows further below
# We must still check some special formats, but these
# should disappear when BibFormat improves.
if not (CFG_BIBFORMAT_USE_OLD_BIBFORMAT \
or format.lower().startswith('t') \
or format.lower().startswith('hm') \
or str(format[0:3]).isdigit() \
or ot):
# Unspecified format is hd
if format == '':
format = 'hd'
if record_exist_p == -1 and get_output_format_content_type(format) == 'text/html':
# HTML output displays a default value for deleted records.
# Other format have to deal with it.
out += _("The record has been deleted.")
else:
out += call_bibformat(recID, format, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
# at the end of HTML brief mode, print the "Detailed record" functionality:
if format.lower().startswith('hb') and \
format.lower() != 'hb_p':
out += websearch_templates.tmpl_print_record_brief_links(
ln = ln,
recID = recID,
- weburl = weburl
)
return out
# Old PHP BibFormat procedure for formatting
# print record opening tags, if needed:
if format == "marcxml" or format == "oai_dc":
out += " <record>\n"
out += " <header>\n"
for oai_id in get_fieldvalues(recID, CFG_OAI_ID_FIELD):
out += " <identifier>%s</identifier>\n" % oai_id
out += " <datestamp>%s</datestamp>\n" % get_modification_date(recID)
out += " </header>\n"
out += " <metadata>\n"
if format.startswith("xm") or format == "marcxml":
# look for detailed format existence:
query = "SELECT value FROM bibfmt WHERE id_bibrec='%s' AND format='%s'" % (recID, format)
res = run_sql(query, None, 1)
if res and record_exist_p == 1:
# record 'recID' is formatted in 'format', so print it
out += "%s" % decompress(res[0][0])
else:
# record 'recID' is not formatted in 'format' -- they are not in "bibfmt" table; so fetch all the data from "bibXXx" tables:
if format == "marcxml":
out += """ <record xmlns="http://www.loc.gov/MARC21/slim">\n"""
out += " <controlfield tag=\"001\">%d</controlfield>\n" % int(recID)
elif format.startswith("xm"):
out += """ <record>\n"""
out += " <controlfield tag=\"001\">%d</controlfield>\n" % int(recID)
if record_exist_p == -1:
# deleted record, so display only OAI ID and 980:
oai_ids = get_fieldvalues(recID, CFG_OAI_ID_FIELD)
if oai_ids:
out += "<datafield tag=\"%s\" ind1=\"%s\" ind2=\"%s\"><subfield code=\"%s\">%s</subfield></datafield>\n" % \
(CFG_OAI_ID_FIELD[0:3], CFG_OAI_ID_FIELD[3:4], CFG_OAI_ID_FIELD[4:5], CFG_OAI_ID_FIELD[5:6], oai_ids[0])
out += "<datafield tag=\"980\" ind1=\"\" ind2=\"\"><subfield code=\"c\">DELETED</subfield></datafield>\n"
else:
# controlfields
query = "SELECT b.tag,b.value,bb.field_number FROM bib00x AS b, bibrec_bib00x AS bb "\
"WHERE bb.id_bibrec='%s' AND b.id=bb.id_bibxxx AND b.tag LIKE '00%%' "\
"ORDER BY bb.field_number, b.tag ASC" % recID
res = run_sql(query)
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, recID, str(digit1)+str(digit2))
res = run_sql(query)
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
if field_number != field_number_old or field[:-1] != field_old[:-1]:
if field_number_old != -999:
out += """ </datafield>\n"""
out += """ <datafield tag="%s" ind1="%s" ind2="%s">\n""" % \
(encode_for_xml(field[0:3]), encode_for_xml(ind1), encode_for_xml(ind2))
field_number_old = field_number
field_old = field
# print subfield value
value = encode_for_xml(value)
out += """ <subfield code="%s">%s</subfield>\n""" % \
(encode_for_xml(field[-1:]), value)
# all fields/subfields printed in this run, so close the tag:
if field_number_old != -999:
out += """ </datafield>\n"""
i = 0 # Next loop should start looking at bib%0 and bibrec_bib00x
# we are at the end of printing the record:
out += " </record>\n"
elif format == "xd" or format == "oai_dc":
# XML Dublin Core format, possibly OAI -- select only some bibXXx fields:
out += """ <dc xmlns="http://purl.org/dc/elements/1.1/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://purl.org/dc/elements/1.1/
http://www.openarchives.org/OAI/1.1/dc.xsd">\n"""
if record_exist_p == -1:
out += ""
else:
for f in get_fieldvalues(recID, "041__a"):
out += " <language>%s</language>\n" % f
for f in get_fieldvalues(recID, "100__a"):
out += " <creator>%s</creator>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "700__a"):
out += " <creator>%s</creator>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "245__a"):
out += " <title>%s</title>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "65017a"):
out += " <subject>%s</subject>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "8564_u"):
out += " <identifier>%s</identifier>\n" % encode_for_xml(f)
for f in get_fieldvalues(recID, "520__a"):
out += " <description>%s</description>\n" % encode_for_xml(f)
out += " <date>%s</date>\n" % get_creation_date(recID)
out += " </dc>\n"
elif str(format[0:3]).isdigit():
# user has asked to print some fields only
if format == "001":
out += "<!--%s-begin-->%s<!--%s-end-->\n" % (format, recID, format)
else:
vals = get_fieldvalues(recID, format)
for val in vals:
out += "<!--%s-begin-->%s<!--%s-end-->\n" % (format, val, format)
elif format.startswith('t'):
## user directly asked for some tags to be displayed only
if record_exist_p == -1:
out += get_fieldvalues_alephseq_like(recID, ["001", CFG_OAI_ID_FIELD, "980"])
else:
out += get_fieldvalues_alephseq_like(recID, ot)
elif format == "hm":
if record_exist_p == -1:
out += "<pre>" + cgi.escape(get_fieldvalues_alephseq_like(recID, ["001", CFG_OAI_ID_FIELD, "980"])) + "</pre>"
else:
out += "<pre>" + cgi.escape(get_fieldvalues_alephseq_like(recID, ot)) + "</pre>"
elif format.startswith("h") and ot:
## user directly asked for some tags to be displayed only
if record_exist_p == -1:
out += "<pre>" + get_fieldvalues_alephseq_like(recID, ["001", CFG_OAI_ID_FIELD, "980"]) + "</pre>"
else:
out += "<pre>" + get_fieldvalues_alephseq_like(recID, ot) + "</pre>"
elif format == "hd":
# HTML detailed format
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
# look for detailed format existence:
query = "SELECT value FROM bibfmt WHERE id_bibrec='%s' AND format='%s'" % (recID, format)
res = run_sql(query, None, 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,
- weburl = weburl,
)
elif format.startswith("hb_") or format.startswith("hd_"):
# underscore means that HTML brief/detailed formats should be called on-the-fly; suitable for testing formats
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
out += call_bibformat(recID, format, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
elif format.startswith("hx"):
# BibTeX format, called on the fly:
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
out += call_bibformat(recID, format, ln, search_pattern=search_pattern,
user_info=user_info, verbose=verbose)
elif format.startswith("hs"):
# for citation/download similarity navigation links:
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
out += '<a href="%s">' % websearch_templates.build_search_url(recid=recID, ln=ln)
# firstly, title:
titles = get_fieldvalues(recID, "245__a")
if titles:
for title in titles:
out += "<strong>%s</strong>" % title
else:
# usual title not found, try conference title:
titles = get_fieldvalues(recID, "111__a")
if titles:
for title in titles:
out += "<strong>%s</strong>" % title
else:
# just print record ID:
out += "<strong>%s %d</strong>" % (get_field_i18nname("record ID", ln), recID)
out += "</a>"
# secondly, authors:
authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")
if authors:
out += " - %s" % authors[0]
if len(authors) > 1:
out += " <em>et al</em>"
# thirdly publication info:
publinfos = get_fieldvalues(recID, "773__s")
if not publinfos:
publinfos = get_fieldvalues(recID, "909C4s")
if not publinfos:
publinfos = get_fieldvalues(recID, "037__a")
if not publinfos:
publinfos = get_fieldvalues(recID, "088__a")
if publinfos:
out += " - %s" % publinfos[0]
else:
# fourthly publication year (if not publication info):
years = get_fieldvalues(recID, "773__y")
if not years:
years = get_fieldvalues(recID, "909C4y")
if not years:
years = get_fieldvalues(recID, "260__c")
if years:
out += " (%s)" % years[0]
else:
# HTML brief format by default
if record_exist_p == -1:
out += _("The record has been deleted.")
else:
query = "SELECT value FROM bibfmt WHERE id_bibrec='%s' AND format='%s'" % (recID, format)
res = run_sql(query)
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,
- weburl = weburl,
)
else:
out += websearch_templates.tmpl_print_record_brief(
ln = ln,
recID = recID,
- weburl = weburl,
)
# 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,
- weburl = weburl,
)
# print record closing tags, if needed:
if format == "marcxml" or format == "oai_dc":
out += " </metadata>\n"
out += " </record>\n"
return out
def encode_for_xml(s):
"Encode special chars in string so that it would be XML-compliant."
s = string.replace(s, '&', '&amp;')
s = string.replace(s, '<', '&lt;')
return s
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.
"""
keywords = []
if search_pattern is not None:
units = create_basic_search_units(None, str(search_pattern), None)
keywords = [unit[1] for unit in units if unit[0] != '-']
return format_record(recID,
of=format,
ln=ln,
search_pattern=keywords,
user_info=user_info,
verbose=verbose)
def log_query(hostname, query_args, uid=-1):
"""
Log query into the query and user_query tables.
Return id_query or None in case of problems.
"""
id_query = None
if uid > 0:
# log the query only if uid is reasonable
res = run_sql("SELECT id FROM query WHERE urlargs=%s", (query_args,), 1)
try:
id_query = res[0][0]
except:
id_query = run_sql("INSERT INTO query (type, urlargs) VALUES ('r', %s)", (query_args,))
if id_query:
run_sql("INSERT INTO user_query (id_user, id_query, hostname, date) VALUES (%s, %s, %s, %s)",
(uid, id_query, hostname,
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
return id_query
def log_query_info(action, p, f, colls, nb_records_found_total=-1):
"""Write some info to the log file for later analysis."""
try:
log = open(CFG_LOGDIR + "/search.log", "a")
log.write(time.strftime("%Y%m%d%H%M%S#", time.localtime()))
log.write(action+"#")
log.write(p+"#")
log.write(f+"#")
for coll in colls[:-1]:
log.write("%s," % coll)
log.write("%s#" % colls[-1])
log.write("%d" % nb_records_found_total)
log.write("\n")
log.close()
except:
pass
return
def wash_url_argument(var, new_type):
"""Wash list argument into 'new_type', that can be 'list',
'str', or 'int'. Useful for washing mod_python passed
arguments, that are all lists of strings (URL args may be
multiple), but we sometimes want only to take the first value,
and sometimes to represent it as string or numerical value."""
out = []
if new_type == 'list': # return lst
if type(var) is list:
out = var
else:
out = [var]
elif new_type == 'str': # return str
if type(var) is list:
try:
out = "%s" % var[0]
except:
out = ""
elif type(var) is str:
out = var
else:
out = "%s" % var
elif new_type == 'int': # return int
if type(var) is list:
try:
out = string.atoi(var[0])
except:
out = 0
elif type(var) is int:
out = var
elif type(var) is str:
try:
out = string.atoi(var)
except:
out = 0
else:
out = 0
return out
### CALLABLES
def perform_request_search(req=None, cc=CFG_SITE_NAME, c=None, p="", f="", rg=10, sf="", so="d", sp="", rm="", of="id", ot="", as=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=""):
"""Perform search or browse request, without checking for
authentication. Return list of recIDs found, if of=id.
Otherwise create web page.
The arguments are as follows:
req - mod_python Request class instance.
cc - current collection (e.g. "ATLAS"). The collection the
user started to search/browse from.
c - collection list (e.g. ["Theses", "Books"]). The
collections user may have selected/deselected when
starting to search from 'cc'.
p - pattern to search for (e.g. "ellis and muon or kaon").
f - field to search within (e.g. "author").
rg - records in groups of (e.g. "10"). Defines how many hits
per collection in the search results page are
displayed.
sf - sort field (e.g. "title").
so - sort order ("a"=ascending, "d"=descending).
sp - sort pattern (e.g. "CERN-") -- in case there are more
values in a sort field, this argument tells which one
to prefer
rm - ranking method (e.g. "jif"). Defines whether results
should be ranked by some known ranking method.
of - output format (e.g. "hb"). Usually starting "h" means
HTML output (and "hb" for HTML brief, "hd" for HTML
detailed), "x" means XML output, "t" means plain text
output, "id" means no output at all but to return list
of recIDs found. (Suitable for high-level API.)
ot - output only these MARC tags (e.g. "100,700,909C0b").
Useful if only some fields are to be shown in the
output, e.g. for library to control some fields.
as - advanced search ("0" means no, "1" means yes). Whether
search was called from within the advanced search
interface.
p1 - first pattern to search for in the advanced search
interface. Much like 'p'.
f1 - first field to search within in the advanced search
interface. Much like 'f'.
m1 - first matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
op1 - first operator, to join the first and the second unit
in the advanced search interface. ("a" add, "o" or,
"n" not).
p2 - second pattern to search for in the advanced search
interface. Much like 'p'.
f2 - second field to search within in the advanced search
interface. Much like 'f'.
m2 - second matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
op2 - second operator, to join the second and the third unit
in the advanced search interface. ("a" add, "o" or,
"n" not).
p3 - third pattern to search for in the advanced search
interface. Much like 'p'.
f3 - third field to search within in the advanced search
interface. Much like 'f'.
m3 - third matching type in the advanced search interface.
("a" all of the words, "o" any of the words, "e" exact
phrase, "p" partial phrase, "r" regular expression).
sc - split by collection ("0" no, "1" yes). Governs whether
we want to present the results in a single huge list,
or splitted by collection.
jrec - jump to record (e.g. "234"). Used for navigation
inside the search results.
recid - display record ID (e.g. "20000"). Do not
search/browse but go straight away to the Detailed
record page for the given recID.
recidb - display record ID bis (e.g. "20010"). If greater than
'recid', then display records from recid to recidb.
Useful for example for dumping records from the
database for reformatting.
sysno - display old system SYS number (e.g. ""). If you
migrate to CDS 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").
"""
selected_external_collections_infos = None
# wash all arguments requiring special care
try:
(cc, colls_to_display, colls_to_search) = wash_colls(cc, c, sc) # which colls to search and to display?
except InvenioWebSearchUnknownCollectionError, exc:
colname = exc.colname
if of.startswith("h"):
page_start(req, of, cc, as, ln, getUid(req),
websearch_templates.tmpl_collection_not_found_page_title(colname, ln))
req.write(websearch_templates.tmpl_collection_not_found_page_body(colname, ln))
return page_end(req, of, ln)
elif of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
return page_end(req, of, ln)
p = wash_pattern(p)
f = wash_field(f)
p1 = wash_pattern(p1)
f1 = wash_field(f1)
p2 = wash_pattern(p2)
f2 = wash_field(f2)
p3 = wash_pattern(p3)
f3 = wash_field(f3)
datetext1, datetext2 = wash_dates(d1, d1y, d1m, d1d, d2, d2y, d2m, d2d)
_ = gettext_set_language(ln)
# backwards compatibility: id, idb, sysnb -> recid, recidb, sysno (if applicable)
if sysnb != "" and sysno == "":
sysno = sysnb
if id > 0 and recid == -1:
recid = id
if idb > 0 and recidb == -1:
recidb = idb
# TODO deduce passed search limiting criterias (if applicable)
pl, pl_in_url = "", "" # no limits by default
if action != "browse" and req and req.args: # we do not want to add options while browsing or while calling via command-line
fieldargs = cgi.parse_qs(req.args)
for fieldcode in get_fieldcodes():
if fieldargs.has_key(fieldcode):
for val in fieldargs[fieldcode]:
pl += "+%s:\"%s\" " % (fieldcode, val)
pl_in_url += "&amp;%s=%s" % (urllib.quote(fieldcode), urllib.quote(val))
# deduce recid from sysno argument (if applicable):
if sysno: # ALEPH SYS number was passed, so deduce DB recID for the record:
recid = get_mysql_recid_from_aleph_sysno(sysno)
if recid is None:
recid = 0 # use recid 0 to indicate that this sysno does not exist
# deduce collection we are in (if applicable):
if recid > 0:
cc = guess_primary_collection_of_a_record(recid)
# deduce user id (if applicable):
try:
uid = getUid(req)
except:
uid = 0
## 0 - start output
if recid >= 0: # recid can be 0 if deduced from sysno and if such sysno does not exist
## 1 - detailed record display
title, description, keywords = \
websearch_templates.tmpl_record_page_header_content(req, recid, ln)
page_start(req, of, cc, as, ln, uid, title, description, keywords, recid, tab)
# Default format is hb but we are in detailed -> change 'of'
if of == "hb":
of = "hd"
if record_exists(recid):
if recidb <= recid: # sanity check
recidb = recid + 1
if of == "id":
return [recidx for recidx in range(recid, recidb) if record_exists(recidx)]
else:
print_records(req, range(recid, recidb), -1, -9999, of, ot, ln, search_pattern=p, verbose=verbose, tab=tab)
if req and of.startswith("h"): # register detailed record page view event
client_ip_address = str(req.get_remote_host(apache.REMOTE_NOLOOKUP))
register_page_view_event(recid, uid, client_ip_address)
else: # record does not exist
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
elif of.startswith("h"):
print_warning(req, _("Requested record does not seem to exist."))
elif action == "browse":
## 2 - browse needed
page_start(req, of, cc, as, ln, uid, _("Browse"))
if of.startswith("h"):
req.write(create_search_box(cc, colls_to_display, p, f, rg, sf, so, sp, rm, of, ot, as, ln, p1, f1, m1, op1,
p2, f2, m2, op2, p3, f3, m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec, action))
try:
if as == 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:
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
elif rm and p.startswith("recid:"):
## 3-ter - similarity search needed
page_start(req, of, cc, as, ln, uid, _("Search Results"))
if of.startswith("h"):
req.write(create_search_box(cc, colls_to_display, p, f, rg, sf, so, sp, rm, of, ot, as, ln, p1, f1, m1, op1,
p2, f2, m2, op2, p3, f3, m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec, action))
if record_exists(p[6:]) != 1:
# record does not exist
if of.startswith("h"):
print_warning(req, "Requested record does not seem to exist.")
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
# record well exists, so find similar ones to it
t1 = os.times()[4]
results_similar_recIDs, results_similar_relevances, results_similar_relevances_prologue, results_similar_relevances_epilogue, results_similar_comments = \
rank_records(rm, 0, get_collection_reclist(CFG_SITE_NAME), string.split(p), verbose)
if results_similar_recIDs:
t2 = os.times()[4]
cpu_time = t2 - t1
if of.startswith("h"):
req.write(print_search_info(p, f, sf, so, sp, rm, of, ot, CFG_SITE_NAME, len(results_similar_recIDs),
jrec, rg, as, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
print_warning(req, results_similar_comments)
print_records(req, results_similar_recIDs, jrec, rg, of, ot, ln,
results_similar_relevances, results_similar_relevances_prologue, results_similar_relevances_epilogue, search_pattern=p, verbose=verbose)
elif of=="id":
return results_similar_recIDs
elif of.startswith("x"):
print_records(req, results_similar_recIDs, jrec, rg, of, ot, ln,
results_similar_relevances, results_similar_relevances_prologue, results_similar_relevances_epilogue, search_pattern=p, verbose=verbose)
else:
# rank_records failed and returned some error message to display:
if of.startswith("h"):
print_warning(req, results_similar_relevances_prologue)
print_warning(req, results_similar_relevances_epilogue)
print_warning(req, results_similar_comments)
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
elif p.startswith("cocitedwith:"): #WAS EXPERIMENTAL
## 3-terter - cited by search needed
page_start(req, of, cc, as, ln, uid, _("Search Results"))
if of.startswith("h"):
req.write(create_search_box(cc, colls_to_display, p, f, rg, sf, so, sp, rm, of, ot, as, ln, p1, f1, m1, op1,
p2, f2, m2, op2, p3, f3, m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec, action))
recID = p[12:]
if record_exists(recID) != 1:
# record does not exist
if of.startswith("h"):
print_warning(req, "Requested record does not seem to exist.")
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
# record well exists, so find co-cited ones:
t1 = os.times()[4]
results_cocited_recIDs = map(lambda x: x[0], calculate_co_cited_with_list(int(recID)))
if results_cocited_recIDs:
t2 = os.times()[4]
cpu_time = t2 - t1
if of.startswith("h"):
req.write(print_search_info(p, f, sf, so, sp, rm, of, ot, CFG_SITE_NAME, len(results_cocited_recIDs),
jrec, rg, as, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
print_records(req, results_cocited_recIDs, jrec, rg, of, ot, ln, search_pattern=p, verbose=verbose)
elif of=="id":
return results_cocited_recIDs
elif of.startswith("x"):
print_records(req, results_cocited_recIDs, jrec, rg, of, ot, ln, search_pattern=p, verbose=verbose)
else:
# cited rank_records failed and returned some error message to display:
if of.startswith("h"):
print_warning(req, "nothing found")
if of == "id":
return []
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
## 3 - common search needed
page_start(req, of, cc, as, ln, uid, _("Search Results"))
if of.startswith("h"):
req.write(create_search_box(cc, colls_to_display, p, f, rg, sf, so, sp, rm, of, ot, as, ln, p1, f1, m1, op1,
p2, f2, m2, op2, p3, f3, m3, sc, pl, d1y, d1m, d1d, d2y, d2m, d2d, dt, jrec, ec, action))
t1 = os.times()[4]
results_in_any_collection = HitSet()
if as == 1 or (p1 or p2 or p3):
## 3A - advanced search
try:
results_in_any_collection = search_pattern(req, p1, f1, m1, ap=ap, of=of, verbose=verbose, ln=ln)
if len(results_in_any_collection) == 0:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
if p2:
results_tmp = search_pattern(req, p2, f2, m2, ap=ap, of=of, verbose=verbose, ln=ln)
if op1 == "a": # add
results_in_any_collection.intersection_update(results_tmp)
elif op1 == "o": # or
results_in_any_collection.union_update(results_tmp)
elif op1 == "n": # not
results_in_any_collection.difference_update(results_tmp)
else:
if of.startswith("h"):
print_warning(req, "Invalid set operation %s." % op1, "Error")
if len(results_in_any_collection) == 0:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
if p3:
results_tmp = search_pattern(req, p3, f3, m3, ap=ap, of=of, verbose=verbose, ln=ln)
if op2 == "a": # add
results_in_any_collection.intersection_update(results_tmp)
elif op2 == "o": # or
results_in_any_collection.union_update(results_tmp)
elif op2 == "n": # not
results_in_any_collection.difference_update(results_tmp)
else:
if of.startswith("h"):
print_warning(req, "Invalid set operation %s." % op2, "Error")
except:
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
else:
## 3B - simple search
try:
results_in_any_collection = search_pattern(req, p, f, ap=ap, of=of, verbose=verbose, ln=ln)
except:
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if len(results_in_any_collection) == 0:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
# search_cache_key = p+"@"+f+"@"+string.join(colls_to_search,",")
# if search_cache.has_key(search_cache_key): # is the result in search cache?
# results_final = search_cache[search_cache_key]
# else:
# results_final = search_pattern(req, p, f, colls_to_search)
# search_cache[search_cache_key] = results_final
# if len(search_cache) > CFG_WEBSEARCH_SEARCH_CACHE_SIZE: # is the cache full? (sanity cleaning)
# search_cache.clear()
# search stage 4: intersection with collection universe:
try:
results_final = intersect_results_with_collrecs(req, results_in_any_collection, colls_to_search, ap, of, verbose, ln)
except:
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if results_final == {}:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
if of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
# search stage 5: apply search option limits and restrictions:
if datetext1 != "":
if verbose and of.startswith("h"):
print_warning(req, "Search stage 5: applying time limits, from %s until %s..." % (datetext1, datetext2))
try:
results_final = intersect_results_with_hitset(req,
results_final,
search_unit_in_bibrec(datetext1, datetext2, dt),
ap,
aptext= _("No match within your time limits, "
"discarding this condition..."),
of=of)
except:
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if results_final == {}:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if pl:
pl = wash_pattern(pl)
if verbose and of.startswith("h"):
print_warning(req, "Search stage 5: applying search pattern limit %s..." % (pl,))
try:
results_final = intersect_results_with_hitset(req,
results_final,
search_pattern(req, pl, ap=0, ln=ln),
ap,
aptext=_("No match within your search limits, "
"discarding this condition..."),
of=of)
except:
if of.startswith("h"):
req.write(create_error_box(req, verbose=verbose, ln=ln))
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
if results_final == {}:
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
if of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
return page_end(req, of, ln)
t2 = os.times()[4]
cpu_time = t2 - t1
## search stage 6: display results:
results_final_nb_total = 0
results_final_nb = {} # will hold number of records found in each collection
# (in simple dict to display overview more easily)
for coll in results_final.keys():
results_final_nb[coll] = len(results_final[coll])
#results_final_nb_total += results_final_nb[coll]
# Now let us calculate results_final_nb_total more precisely,
# in order to get the total number of "distinct" hits across
# searched collections; this is useful because a record might
# have been attributed to more than one primary collection; so
# we have to avoid counting it multiple times. The price to
# pay for this accuracy of results_final_nb_total is somewhat
# increased CPU time.
if results_final.keys() == 1:
# only one collection; no need to union them
results_final_for_all_selected_colls = results_final.values()[0]
results_final_nb_total = results_final_nb.values()[0]
else:
# okay, some work ahead to union hits across collections:
results_final_for_all_selected_colls = HitSet()
for coll in results_final.keys():
results_final_for_all_selected_colls.union_update(results_final[coll])
results_final_nb_total = len(results_final_for_all_selected_colls)
if results_final_nb_total == 0:
if of.startswith('h'):
print_warning(req, "No match found, please enter different search terms.")
elif of.startswith("x"):
# Print empty, but valid XML
print_records_prologue(req, of)
print_records_epilogue(req, of)
else:
# yes, some hits found: good!
# collection list may have changed due to not-exact-match-found policy so check it out:
for coll in results_final.keys():
if coll not in colls_to_search:
colls_to_search.append(coll)
# print results overview:
if of == "id":
# we have been asked to return list of recIDs
recIDs = list(results_final_for_all_selected_colls)
if sf: # do we have to sort?
recIDs = sort_records(req, recIDs, sf, so, sp, verbose, of)
elif rm: # do we have to rank?
results_final_for_all_colls_rank_records_output = rank_records(rm, 0, results_final_for_all_selected_colls,
string.split(p) + string.split(p1) +
string.split(p2) + string.split(p3), verbose)
if results_final_for_all_colls_rank_records_output[0]:
recIDs = results_final_for_all_colls_rank_records_output[0]
return recIDs
elif of.startswith("h"):
req.write(print_results_overview(req, colls_to_search, results_final_nb_total, results_final_nb, cpu_time, ln, ec))
selected_external_collections_infos = print_external_results_overview(req, cc, [p, p1, p2, p3], f, ec, verbose, ln)
# print number of hits found for XML outputs:
if of.startswith("x"):
req.write("<!-- Search-Engine-Total-Number-Of-Results: %s -->\n" % results_final_nb_total)
# print records:
if len(colls_to_search)>1:
cpu_time = -1 # we do not want to have search time printed on each collection
print_records_prologue(req, of)
for coll in colls_to_search:
if results_final.has_key(coll) and len(results_final[coll]):
if of.startswith("h"):
req.write(print_search_info(p, f, sf, so, sp, rm, of, ot, coll, results_final_nb[coll],
jrec, rg, as, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time))
results_final_recIDs = list(results_final[coll])
results_final_relevances = []
results_final_relevances_prologue = ""
results_final_relevances_epilogue = ""
if sf: # do we have to sort?
results_final_recIDs = sort_records(req, results_final_recIDs, sf, so, sp, verbose, of)
elif rm: # do we have to rank?
results_final_recIDs_ranked, results_final_relevances, results_final_relevances_prologue, results_final_relevances_epilogue, results_final_comments = \
rank_records(rm, 0, results_final[coll],
string.split(p) + string.split(p1) +
string.split(p2) + string.split(p3), verbose)
if of.startswith("h"):
print_warning(req, results_final_comments)
if results_final_recIDs_ranked:
results_final_recIDs = results_final_recIDs_ranked
else:
# rank_records failed and returned some error message to display:
print_warning(req, results_final_relevances_prologue)
print_warning(req, results_final_relevances_epilogue)
print_records(req, results_final_recIDs, jrec, rg, of, ot, ln,
results_final_relevances,
results_final_relevances_prologue,
results_final_relevances_epilogue,
search_pattern=p,
print_records_prologue_p=False,
print_records_epilogue_p=False,
verbose=verbose)
if of.startswith("h"):
req.write(print_search_info(p, f, sf, so, sp, rm, of, ot, coll, results_final_nb[coll],
jrec, rg, as, ln, p1, p2, p3, f1, f2, f3, m1, m2, m3, op1, op2,
sc, pl_in_url,
d1y, d1m, d1d, d2y, d2m, d2d, dt, cpu_time, 1))
print_records_epilogue(req, of)
if f == "author" and of.startswith("h"):
req.write(create_similarly_named_authors_link_box(p, ln))
# log query:
try:
id_query = log_query(req.get_remote_host(), req.args, uid)
if of.startswith("h") and id_query:
# Alert/RSS teaser:
req.write(websearch_templates.tmpl_alert_rss_teaser_box_for_query(id_query, ln=ln))
except:
# do not log query if req is None (used by CLI interface)
pass
log_query_info("ss", p, f, colls_to_search, results_final_nb_total)
# External searches
if of.startswith("h"):
perform_external_collection_search(req, cc, [p, p1, p2, p3], f, ec, verbose, ln, selected_external_collections_infos)
return page_end(req, of, ln)
def perform_request_cache(req, action="show"):
"""Manipulates the search engine cache."""
global search_cache
global collection_reclist_cache
global collection_reclist_cache_timestamp
global field_i18nname_cache
global field_i18nname_cache_timestamp
global collection_i18nname_cache
global collection_i18nname_cache_timestamp
req.content_type = "text/html"
req.send_http_header()
out = ""
out += "<h1>Search Cache</h1>"
# clear cache if requested:
if action == "clear":
search_cache = {}
collection_reclist_cache = create_collection_reclist_cache()
# show collection reclist cache:
out += "<h3>Collection reclist cache</h3>"
out += "- collection table last updated: %s" % get_table_update_time('collection')
out += "<br />- reclist cache timestamp: %s" % collection_reclist_cache_timestamp
out += "<br />- reclist cache contents:"
out += "<blockquote>"
for coll in collection_reclist_cache.keys():
if collection_reclist_cache[coll]:
out += "%s (%d)<br />" % (coll, len(get_collection_reclist(coll)))
out += "</blockquote>"
# show search cache:
out += "<h3>Search Cache</h3>"
out += "<blockquote>"
if len(search_cache):
out += """<table border="=">"""
out += "<tr><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td></tr>" % \
("Pattern", "Field", "Collection", "Number of Hits")
for search_cache_key in search_cache.keys():
p, f, c = string.split(search_cache_key, "@", 2)
# find out about length of cached data:
l = 0
for coll in search_cache[search_cache_key]:
l += len(search_cache[search_cache_key][coll])
out += "<tr><td>%s</td><td>%s</td><td>%s</td><td>%d</td></tr>" % (p, f, c, l)
out += "</table>"
else:
out += "<p>Search cache is empty."
out += "</blockquote>"
- out += """<p><a href="%s/search/cache?action=clear">clear cache</a>""" % weburl
+ out += """<p><a href="%s/search/cache?action=clear">clear cache</a>""" % CFG_SITE_URL
# show field i18nname cache:
out += "<h3>Field I18N names cache</h3>"
out += "- fieldname table last updated: %s" % get_table_update_time('fieldname')
out += "<br />- i18nname cache timestamp: %s" % field_i18nname_cache_timestamp
out += "<br />- i18nname cache contents:"
out += "<blockquote>"
for field in field_i18nname_cache.keys():
for ln in field_i18nname_cache[field].keys():
out += "%s, %s = %s<br />" % (field, ln, field_i18nname_cache[field][ln])
out += "</blockquote>"
# show collection i18nname cache:
out += "<h3>Collection I18N names cache</h3>"
out += "- collectionname table last updated: %s" % get_table_update_time('collectionname')
out += "<br />- i18nname cache timestamp: %s" % collection_i18nname_cache_timestamp
out += "<br />- i18nname cache contents:"
out += "<blockquote>"
for coll in collection_i18nname_cache.keys():
for ln in collection_i18nname_cache[coll].keys():
out += "%s, %s = %s<br />" % (coll, ln, collection_i18nname_cache[coll][ln])
out += "</blockquote>"
req.write("<html>")
req.write(out)
req.write("</html>")
return "\n"
def perform_request_log(req, date=""):
"""Display search log information for given date."""
req.content_type = "text/html"
req.send_http_header()
req.write("<html>")
req.write("<h1>Search Log</h1>")
if date: # case A: display stats for a day
yyyymmdd = string.atoi(date)
req.write("<p><big><strong>Date: %d</strong></big><p>" % yyyymmdd)
req.write("""<table border="1">""")
req.write("<tr><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td><td><strong>%s</strong></td></tr>" % ("No.", "Time", "Pattern", "Field", "Collection", "Number of Hits"))
# read file:
p = os.popen("grep ^%d %s/search.log" % (yyyymmdd, CFG_LOGDIR), 'r')
lines = p.readlines()
p.close()
# process lines:
i = 0
for line in lines:
try:
datetime, as, p, f, c, nbhits = string.split(line,"#")
i += 1
req.write("<tr><td align=\"right\">#%d</td><td>%s:%s:%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" \
% (i, datetime[8:10], datetime[10:12], datetime[12:], p, f, c, nbhits))
except:
pass # ignore eventual wrong log lines
req.write("</table>")
else: # case B: display summary stats per day
yyyymm01 = int(time.strftime("%Y%m01", time.localtime()))
yyyymmdd = int(time.strftime("%Y%m%d", time.localtime()))
req.write("""<table border="1">""")
req.write("<tr><td><strong>%s</strong></td><td><strong>%s</strong></tr>" % ("Day", "Number of Queries"))
for day in range(yyyymm01, yyyymmdd + 1):
p = os.popen("grep -c ^%d %s/search.log" % (day, CFG_LOGDIR), 'r')
for line in p.readlines():
req.write("""<tr><td>%s</td><td align="right"><a href="%s/search/log?date=%d">%s</a></td></tr>""" % \
- (day, weburl, day, line))
+ (day, CFG_SITE_URL, day, line))
p.close()
req.write("</table>")
req.write("</html>")
return "\n"
def profile(p="", f="", c=CFG_SITE_NAME):
"""Profile search time."""
import profile
import pstats
profile.run("perform_request_search(p='%s',f='%s', c='%s')" % (p, f, c), "perform_request_search_profile")
p = pstats.Stats("perform_request_search_profile")
p.strip_dirs().sort_stats("cumulative").print_stats()
return 0
## test cases:
#print wash_colls(CFG_SITE_NAME,"Library Catalogue", 0)
#print wash_colls("Periodicals & Progress Reports",["Periodicals","Progress Reports"], 0)
#print wash_field("wau")
#print print_record(20,"tm","001,245")
#print create_opft_search_units(None, "PHE-87-13","reportnumber")
#print ":"+wash_pattern("* and % doo * %")+":\n"
#print ":"+wash_pattern("*")+":\n"
#print ":"+wash_pattern("ellis* ell* e*%")+":\n"
#print run_sql("SELECT name,dbquery from collection")
#print get_index_id("author")
#print get_coll_ancestors("Theses")
#print get_coll_sons("Articles & Preprints")
#print get_coll_real_descendants("Articles & Preprints")
#print get_collection_reclist("Theses")
#print log(sys.stdin)
#print search_unit_in_bibrec('2002-12-01','2002-12-12')
#print type(wash_url_argument("-1",'int'))
#print get_nearest_terms_in_bibxxx("ellis", "author", 5, 5)
#print call_bibformat(68, "HB_FLY")
#print create_collection_i18nname_cache()
#print get_fieldvalues(10, "980__a")
#print get_fieldvalues_alephseq_like(10,"001___")
#print get_fieldvalues_alephseq_like(10,"980__a")
#print get_fieldvalues_alephseq_like(10,"foo")
#print get_fieldvalues_alephseq_like(10,"-1")
#print get_fieldvalues_alephseq_like(10,"99")
#print get_fieldvalues_alephseq_like(10,["001", "980"])
## profiling:
#profile("of the this")
#print perform_request_search(p="ellis")
diff --git a/modules/websearch/lib/websearch_regression_tests.py b/modules/websearch/lib/websearch_regression_tests.py
index ddb89b05a..9dfbdc2e2 100644
--- a/modules/websearch/lib/websearch_regression_tests.py
+++ b/modules/websearch/lib/websearch_regression_tests.py
@@ -1,1130 +1,1130 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable-msg=C0301
# pylint: disable-msg=E1102
"""WebSearch module regression tests."""
__revision__ = "$Id$"
import unittest
import re
import urlparse, cgi
from sets import Set
from mechanize import Browser, LinkNotFoundError, HTTPError
-from invenio.config import weburl, CFG_SITE_NAME, CFG_SITE_LANG
+from invenio.config import CFG_SITE_URL, CFG_SITE_NAME, CFG_SITE_LANG
from invenio.testutils import make_test_suite, \
warn_user_about_tests_and_run, \
make_url, test_web_page_content, \
merge_error_messages
from invenio.urlutils import same_urls_p
from invenio.search_engine import perform_request_search
def parse_url(url):
parts = urlparse.urlparse(url)
query = cgi.parse_qs(parts[4], True)
return parts[2].split('/')[1:], query
class WebSearchWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebSearch web pages whether they are up or not."""
def test_search_interface_pages_availability(self):
"""websearch - availability of search interface pages"""
- baseurl = weburl + '/'
+ baseurl = CFG_SITE_URL + '/'
_exports = ['', 'collection/Poetry', 'collection/Poetry?as=1']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_search_results_pages_availability(self):
"""websearch - availability of search results pages"""
- baseurl = weburl + '/search'
+ baseurl = CFG_SITE_URL + '/search'
_exports = ['', '?c=Poetry', '?p=ellis', '/cache', '/log']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_search_detailed_record_pages_availability(self):
"""websearch - availability of search detailed record pages"""
- baseurl = weburl + '/record/'
+ baseurl = CFG_SITE_URL + '/record/'
_exports = ['', '1', '1/', '1/files', '1/files/']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_browse_results_pages_availability(self):
"""websearch - availability of browse results pages"""
- baseurl = weburl + '/search'
+ baseurl = CFG_SITE_URL + '/search'
_exports = ['?p=ellis&f=author&action_browse=Browse']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_help_page_availability(self):
"""websearch - availability of Help Central page"""
self.assertEqual([],
- test_web_page_content(weburl + '/help',
+ test_web_page_content(CFG_SITE_URL + '/help',
expected_text="Help Central"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/?ln=fr',
+ test_web_page_content(CFG_SITE_URL + '/help/?ln=fr',
expected_text="Centre d'aide"))
def test_search_tips_page_availability(self):
"""websearch - availability of Search Tips"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/search-tips',
+ test_web_page_content(CFG_SITE_URL + '/help/search-tips',
expected_text="Search Tips"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/search-tips?ln=fr',
+ test_web_page_content(CFG_SITE_URL + '/help/search-tips?ln=fr',
expected_text="Conseils de recherche"))
def test_search_guide_page_availability(self):
"""websearch - availability of Search Guide"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/search-guide',
+ test_web_page_content(CFG_SITE_URL + '/help/search-guide',
expected_text="Search Guide"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/search-guide?ln=fr',
+ test_web_page_content(CFG_SITE_URL + '/help/search-guide?ln=fr',
expected_text="Guide de recherche"))
class WebSearchTestLegacyURLs(unittest.TestCase):
""" Check that the application still responds to legacy URLs for
navigating, searching and browsing."""
def test_legacy_collections(self):
""" websearch - collections handle legacy urls """
browser = Browser()
def check(legacy, new, browser=browser):
browser.open(legacy)
got = browser.geturl()
self.failUnless(same_urls_p(got, new), got)
# Use the root URL unless we need more
check(make_url('/', c=CFG_SITE_NAME),
make_url('/'))
# Other collections are redirected in the /collection area
check(make_url('/', c='Poetry'),
make_url('/collection/Poetry'))
# Drop unnecessary arguments, like ln and as (when they are
# the default value)
check(make_url('/', c='Poetry', as=0, ln=CFG_SITE_LANG),
make_url('/collection/Poetry'))
# Otherwise, keep them
check(make_url('/', c='Poetry', as=1, ln=CFG_SITE_LANG),
make_url('/collection/Poetry', as=1))
# Support the /index.py addressing too
check(make_url('/index.py', c='Poetry'),
make_url('/collection/Poetry'))
def test_legacy_search(self):
""" websearch - search queries handle legacy urls """
browser = Browser()
def check(legacy, new, browser=browser):
browser.open(legacy)
got = browser.geturl()
self.failUnless(same_urls_p(got, new), got)
# /search.py is redirected on /search
# Note that `as' is a reserved word in Python 2.5
check(make_url('/search.py', p='nuclear') + 'as=1',
make_url('/search', p='nuclear') + 'as=1')
# direct recid searches are redirected to /record
check(make_url('/search.py', recid=1, ln='es'),
make_url('/record/1', ln='es'))
def test_legacy_search_help_link(self):
"""websearch - legacy Search Help page link"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/search/index.en.html',
+ test_web_page_content(CFG_SITE_URL + '/help/search/index.en.html',
expected_text="Help Central"))
def test_legacy_search_tips_link(self):
"""websearch - legacy Search Tips page link"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/search/tips.fr.html',
+ test_web_page_content(CFG_SITE_URL + '/help/search/tips.fr.html',
expected_text="Conseils de recherche"))
def test_legacy_search_guide_link(self):
"""websearch - legacy Search Guide page link"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/search/guide.en.html',
+ test_web_page_content(CFG_SITE_URL + '/help/search/guide.en.html',
expected_text="Search Guide"))
class WebSearchTestRecord(unittest.TestCase):
""" Check the interface of the /record results """
def test_format_links(self):
""" websearch - check format links for records """
browser = Browser()
# We open the record in all known HTML formats
for hformat in ('hd', 'hx', 'hm'):
browser.open(make_url('/record/1', of=hformat))
if hformat == 'hd':
# hd format should have a link to the following
# formats
for oformat in ('hx', 'hm', 'xm', 'xd'):
target = make_url('/record/1/export/%s' % oformat)
try:
browser.find_link(url=target)
except LinkNotFoundError:
self.fail('link %r should be in page' % target)
else:
# non-hd HTML formats should have a link back to
# the main detailed record
target = make_url('/record/1')
try:
browser.find_link(url=target)
except LinkNotFoundError:
self.fail('link %r should be in page' % target)
return
class WebSearchTestCollections(unittest.TestCase):
def test_traversal_links(self):
""" websearch - traverse all the publications of a collection """
browser = Browser()
try:
for as in (0, 1):
browser.open(make_url('/collection/Preprints', as=as))
for jrec in (11, 21, 11, 27):
args = {'jrec': jrec, 'cc': 'Preprints'}
if as:
args['as'] = as
url = make_url('/search', **args)
browser.follow_link(url=url)
except LinkNotFoundError:
self.fail('no link %r in %r' % (url, browser.geturl()))
def test_collections_links(self):
""" websearch - enter in collections and subcollections """
browser = Browser()
def tryfollow(url):
cur = browser.geturl()
body = browser.response().read()
try:
browser.follow_link(url=url)
except LinkNotFoundError:
print body
self.fail("in %r: could not find %r" % (
cur, url))
return
for as in (0, 1):
if as:
kargs = {'as': 1}
else:
kargs = {}
# We navigate from immediate son to immediate son...
browser.open(make_url('/', **kargs))
tryfollow(make_url('/collection/Articles%20%26%20Preprints',
**kargs))
tryfollow(make_url('/collection/Articles', **kargs))
# But we can also jump to a grandson immediately
browser.back()
browser.back()
tryfollow(make_url('/collection/ALEPH', **kargs))
return
def test_records_links(self):
""" websearch - check the links toward records in leaf collections """
browser = Browser()
browser.open(make_url('/collection/Preprints'))
def harvest():
""" Parse all the links in the page, and check that for
each link to a detailed record, we also have the
corresponding link to the similar records."""
records = Set()
similar = Set()
for link in browser.links():
path, q = parse_url(link.url)
if not path:
continue
if path[0] == 'record':
records.add(int(path[1]))
continue
if path[0] == 'search':
if not q.get('rm') == ['wrd']:
continue
recid = q['p'][0].split(':')[1]
similar.add(int(recid))
self.failUnlessEqual(records, similar)
return records
# We must have 10 links to the corresponding /records
found = harvest()
self.failUnlessEqual(len(found), 10)
# When clicking on the "Search" button, we must also have
# these 10 links on the records.
browser.select_form(name="search")
browser.submit()
found = harvest()
self.failUnlessEqual(len(found), 10)
return
class WebSearchTestBrowse(unittest.TestCase):
def test_browse_field(self):
""" websearch - check that browsing works """
browser = Browser()
browser.open(make_url('/'))
browser.select_form(name='search')
browser['f'] = ['title']
browser.submit(name='action_browse')
def collect():
# We'll get a few links to search for the actual hits, plus a
# link to the following results.
res = []
- for link in browser.links(url_regex=re.compile(weburl +
+ for link in browser.links(url_regex=re.compile(CFG_SITE_URL +
r'/search\?')):
if link.text == 'Advanced Search':
continue
dummy, q = parse_url(link.url)
res.append((link, q))
return res
# if we follow the last link, we should get another
# batch. There is an overlap of one item.
batch_1 = collect()
browser.follow_link(link=batch_1[-1][0])
batch_2 = collect()
# FIXME: we cannot compare the whole query, as the collection
# set is not equal
self.failUnlessEqual(batch_1[-2][1]['p'], batch_2[0][1]['p'])
class WebSearchTestSearch(unittest.TestCase):
def test_hits_in_other_collection(self):
""" websearch - check extension of a query to the home collection """
browser = Browser()
# We do a precise search in an isolated collection
browser.open(make_url('/collection/ISOLDE', ln='en'))
browser.select_form(name='search')
browser['f'] = ['author']
browser['p'] = 'matsubara'
browser.submit()
dummy, current_q = parse_url(browser.geturl())
link = browser.find_link(text_regex=re.compile('.*hit', re.I))
dummy, target_q = parse_url(link.url)
# the target query should be the current query without any c
# or cc specified.
for f in ('cc', 'c', 'action_search', 'ln'):
if f in current_q:
del current_q[f]
self.failUnlessEqual(current_q, target_q)
def test_nearest_terms(self):
""" websearch - provide a list of nearest terms """
browser = Browser()
browser.open(make_url(''))
# Search something weird
browser.select_form(name='search')
browser['p'] = 'gronf'
browser.submit()
dummy, original = parse_url(browser.geturl())
for to_drop in ('cc', 'action_search', 'f'):
if to_drop in original:
del original[to_drop]
# we should get a few searches back, which are identical
# except for the p field being substituted (and the cc field
# being dropped).
if 'cc' in original:
del original['cc']
- for link in browser.links(url_regex=re.compile(weburl + r'/search\?')):
+ for link in browser.links(url_regex=re.compile(CFG_SITE_URL + r'/search\?')):
if link.text == 'Advanced Search':
continue
dummy, target = parse_url(link.url)
original['p'] = [link.text]
self.failUnlessEqual(original, target)
return
def test_switch_to_simple_search(self):
""" websearch - switch to simple search """
browser = Browser()
browser.open(make_url('/collection/ISOLDE', as=1))
browser.select_form(name='search')
browser['p1'] = 'tandem'
browser['f1'] = ['title']
browser.submit()
browser.follow_link(text='Simple Search')
dummy, q = parse_url(browser.geturl())
self.failUnlessEqual(q, {'cc': ['ISOLDE'],
'p': ['tandem'],
'f': ['title']})
def test_switch_to_advanced_search(self):
""" websearch - switch to advanced search """
browser = Browser()
browser.open(make_url('/collection/ISOLDE'))
browser.select_form(name='search')
browser['p'] = 'tandem'
browser['f'] = ['title']
browser.submit()
browser.follow_link(text='Advanced Search')
dummy, q = parse_url(browser.geturl())
self.failUnlessEqual(q, {'cc': ['ISOLDE'],
'p1': ['tandem'],
'f1': ['title'],
'as': ['1']})
def test_no_boolean_hits(self):
""" websearch - check the 'no boolean hits' proposed links """
browser = Browser()
browser.open(make_url(''))
browser.select_form(name='search')
browser['p'] = 'quasinormal muon'
browser.submit()
dummy, q = parse_url(browser.geturl())
for to_drop in ('cc', 'action_search', 'f'):
if to_drop in q:
del q[to_drop]
for bsu in ('quasinormal', 'muon'):
l = browser.find_link(text=bsu)
q['p'] = bsu
if not same_urls_p(l.url, make_url('/search', **q)):
self.fail(repr((l.url, make_url('/search', **q))))
def test_similar_authors(self):
""" websearch - test similar authors box """
browser = Browser()
browser.open(make_url(''))
browser.select_form(name='search')
browser['p'] = 'Ellis, R K'
browser['f'] = ['author']
browser.submit()
l = browser.find_link(text="Ellis, R S")
self.failUnless(same_urls_p(l.url, make_url('/search',
p="Ellis, R S",
f='author')))
class WebSearchNearestTermsTest(unittest.TestCase):
"""Check various alternatives of searches leading to the nearest
terms box."""
def test_nearest_terms_box_in_okay_query(self):
""" websearch - no nearest terms box for a successful query """
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=ellis',
+ test_web_page_content(CFG_SITE_URL + '/search?p=ellis',
expected_text="jump to record"))
def test_nearest_terms_box_in_unsuccessful_simple_query(self):
""" websearch - nearest terms box for unsuccessful simple query """
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=ellisz',
+ test_web_page_content(CFG_SITE_URL + '/search?p=ellisz',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=embed",
+ expected_link_target=CFG_SITE_URL+"/search?p=embed",
expected_link_label='embed'))
def test_nearest_terms_box_in_unsuccessful_structured_query(self):
""" websearch - nearest terms box for unsuccessful structured query """
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=ellisz&f=author',
+ test_web_page_content(CFG_SITE_URL + '/search?p=ellisz&f=author',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=fabbro&f=author",
+ expected_link_target=CFG_SITE_URL+"/search?p=fabbro&f=author",
expected_link_label='fabbro'))
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=author%3Aellisz',
+ test_web_page_content(CFG_SITE_URL + '/search?p=author%3Aellisz',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=author%3Afabbro",
+ expected_link_target=CFG_SITE_URL+"/search?p=author%3Afabbro",
expected_link_label='fabbro'))
def test_nearest_terms_box_in_unsuccessful_phrase_query(self):
""" websearch - nearest terms box for unsuccessful phrase query """
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=author%3A%22Ellis%2C+Z%22',
+ test_web_page_content(CFG_SITE_URL + '/search?p=author%3A%22Ellis%2C+Z%22',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=author%3A%22Enqvist%2C+K%22",
+ expected_link_target=CFG_SITE_URL+"/search?p=author%3A%22Enqvist%2C+K%22",
expected_link_label='Enqvist, K'))
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=%22ellisz%22&f=author',
+ test_web_page_content(CFG_SITE_URL + '/search?p=%22ellisz%22&f=author',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=%22Enqvist%2C+K%22&f=author",
+ expected_link_target=CFG_SITE_URL+"/search?p=%22Enqvist%2C+K%22&f=author",
expected_link_label='Enqvist, K'))
def test_nearest_terms_box_in_unsuccessful_boolean_query(self):
""" websearch - nearest terms box for unsuccessful boolean query """
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=title%3Aellisz+author%3Aellisz',
+ test_web_page_content(CFG_SITE_URL + '/search?p=title%3Aellisz+author%3Aellisz',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=title%3Aenergie+author%3Aellisz",
+ expected_link_target=CFG_SITE_URL+"/search?p=title%3Aenergie+author%3Aellisz",
expected_link_label='energie'))
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=title%3Aenergie+author%3Aenergie',
+ test_web_page_content(CFG_SITE_URL + '/search?p=title%3Aenergie+author%3Aenergie',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=title%3Aenergie+author%3Aenqvist",
+ expected_link_target=CFG_SITE_URL+"/search?p=title%3Aenergie+author%3Aenqvist",
expected_link_label='enqvist'))
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=title%3Aellisz+author%3Aellisz&f=keyword',
+ test_web_page_content(CFG_SITE_URL + '/search?p=title%3Aellisz+author%3Aellisz&f=keyword',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=title%3Aenergie+author%3Aellisz&f=keyword",
+ expected_link_target=CFG_SITE_URL+"/search?p=title%3Aenergie+author%3Aellisz&f=keyword",
expected_link_label='energie'))
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=title%3Aenergie+author%3Aenergie&f=keyword',
+ test_web_page_content(CFG_SITE_URL + '/search?p=title%3Aenergie+author%3Aenergie&f=keyword',
expected_text="Nearest terms in any collection are",
- expected_link_target=weburl+"/search?p=title%3Aenergie+author%3Aenqvist&f=keyword",
+ expected_link_target=CFG_SITE_URL+"/search?p=title%3Aenergie+author%3Aenqvist&f=keyword",
expected_link_label='enqvist'))
class WebSearchBooleanQueryTest(unittest.TestCase):
"""Check various boolean queries."""
def test_successful_boolean_query(self):
""" websearch - successful boolean query """
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=ellis+muon',
+ test_web_page_content(CFG_SITE_URL + '/search?p=ellis+muon',
expected_text="records found",
expected_link_label="Detailed record"))
def test_unsuccessful_boolean_query_where_all_individual_terms_match(self):
""" websearch - unsuccessful boolean query where all individual terms match """
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=ellis+muon+letter',
+ test_web_page_content(CFG_SITE_URL + '/search?p=ellis+muon+letter',
expected_text="Boolean query returned no hits. Please combine your search terms differently."))
class WebSearchAuthorQueryTest(unittest.TestCase):
"""Check various author-related queries."""
def test_propose_similar_author_names_box(self):
""" websearch - propose similar author names box """
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=Ellis%2C+R&f=author',
+ test_web_page_content(CFG_SITE_URL + '/search?p=Ellis%2C+R&f=author',
expected_text="See also: similar author names",
- expected_link_target=weburl+"/search?p=Ellis%2C+R+K&f=author",
+ expected_link_target=CFG_SITE_URL+"/search?p=Ellis%2C+R+K&f=author",
expected_link_label="Ellis, R K"))
def test_do_not_propose_similar_author_names_box(self):
""" websearch - do not propose similar author names box """
- errmsgs = test_web_page_content(weburl + '/search?p=author%3A%22Ellis%2C+R%22',
- expected_link_target=weburl+"/search?p=Ellis%2C+R+K&f=author",
+ errmsgs = test_web_page_content(CFG_SITE_URL + '/search?p=author%3A%22Ellis%2C+R%22',
+ expected_link_target=CFG_SITE_URL+"/search?p=Ellis%2C+R+K&f=author",
expected_link_label="Ellis, R K")
if errmsgs[0].find("does not contain link to") > -1:
pass
else:
self.fail("Should not propose similar author names box.")
return
class WebSearchSearchEnginePythonAPITest(unittest.TestCase):
"""Check typical search engine Python API calls on the demo data."""
def test_search_engine_python_api_for_failed_query(self):
"""websearch - search engine Python API for failed query"""
self.assertEqual([],
perform_request_search(p='aoeuidhtns'))
def test_search_engine_python_api_for_successful_query(self):
"""websearch - search engine Python API for successful query"""
self.assertEqual([8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47],
perform_request_search(p='ellis'))
def test_search_engine_python_api_for_existing_record(self):
"""websearch - search engine Python API for existing record"""
self.assertEqual([8],
perform_request_search(recid=8))
def test_search_engine_python_api_for_nonexisting_record(self):
"""websearch - search engine Python API for non-existing record"""
self.assertEqual([],
perform_request_search(recid=1234567809))
def test_search_engine_python_api_for_nonexisting_collection(self):
"""websearch - search engine Python API for non-existing collection"""
self.assertEqual([],
perform_request_search(c='Foo'))
def test_search_engine_python_api_for_range_of_records(self):
"""websearch - search engine Python API for range of records"""
self.assertEqual([1, 2, 3, 4, 5, 6, 7, 8, 9],
perform_request_search(recid=1, recidb=10))
class WebSearchSearchEngineWebAPITest(unittest.TestCase):
"""Check typical search engine Web API calls on the demo data."""
def test_search_engine_web_api_for_failed_query(self):
"""websearch - search engine Web API for failed query"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=aoeuidhtns&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?p=aoeuidhtns&of=id',
expected_text="[]"))
def test_search_engine_web_api_for_successful_query(self):
"""websearch - search engine Web API for successful query"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=ellis&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?p=ellis&of=id',
expected_text="[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47]"))
def test_search_engine_web_api_for_existing_record(self):
"""websearch - search engine Web API for existing record"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?recid=8&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?recid=8&of=id',
expected_text="[8]"))
def test_search_engine_web_api_for_nonexisting_record(self):
"""websearch - search engine Web API for non-existing record"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?recid=123456789&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?recid=123456789&of=id',
expected_text="[]"))
def test_search_engine_web_api_for_nonexisting_collection(self):
"""websearch - search engine Web API for non-existing collection"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?c=Foo&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?c=Foo&of=id',
expected_text="[]"))
def test_search_engine_web_api_for_range_of_records(self):
"""websearch - search engine Web API for range of records"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?recid=1&recidb=10&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?recid=1&recidb=10&of=id',
expected_text="[1, 2, 3, 4, 5, 6, 7, 8, 9]"))
class WebSearchRestrictedCollectionTest(unittest.TestCase):
"""Test of the restricted Theses collection behaviour."""
def test_restricted_collection_interface_page(self):
"""websearch - restricted collection interface page body"""
self.assertEqual([],
- test_web_page_content(weburl + '/collection/Theses',
+ test_web_page_content(CFG_SITE_URL + '/collection/Theses',
expected_text="The contents of this collection is restricted."))
def test_restricted_search_as_anonymous_guest(self):
"""websearch - restricted collection not searchable by anonymous guest"""
browser = Browser()
- browser.open(weburl + '/search?c=Theses')
+ browser.open(CFG_SITE_URL + '/search?c=Theses')
response = browser.response().read()
if response.find("If you think you have right to access it, please authenticate yourself.") > -1:
pass
else:
self.fail("Oops, searching restricted collection without password should have redirected to login dialog.")
return
def test_restricted_search_as_authorized_person(self):
"""websearch - restricted collection searchable by authorized person"""
browser = Browser()
- browser.open(weburl + '/search?c=Theses')
+ browser.open(CFG_SITE_URL + '/search?c=Theses')
browser.select_form(nr=0)
browser['p_un'] = 'jekyll'
browser['p_pw'] = 'j123ekyll'
browser.submit()
if browser.response().read().find("records found") > -1:
pass
else:
self.fail("Oops, Dr. Jekyll should be able to search Theses collection.")
def test_restricted_search_as_unauthorized_person(self):
"""websearch - restricted collection not searchable by unauthorized person"""
browser = Browser()
- browser.open(weburl + '/search?c=Theses')
+ browser.open(CFG_SITE_URL + '/search?c=Theses')
browser.select_form(nr=0)
browser['p_un'] = 'hyde'
browser['p_pw'] = 'h123yde'
browser.submit()
# Mr. Hyde should not be able to connect:
if browser.response().read().find("You are not authorized") <= -1:
# if we got here, things are broken:
self.fail("Oops, Mr.Hyde should not be able to search Theses collection.")
def test_restricted_detailed_record_page_as_anonymous_guest(self):
"""websearch - restricted detailed record page not accessible to guests"""
browser = Browser()
- browser.open(weburl + '/record/35')
+ browser.open(CFG_SITE_URL + '/record/35')
if browser.response().read().find("You can use your nickname or your email address to login.") > -1:
pass
else:
self.fail("Oops, searching restricted collection without password should have redirected to login dialog.")
return
def test_restricted_detailed_record_page_as_authorized_person(self):
"""websearch - restricted detailed record page accessible to authorized person"""
browser = Browser()
- browser.open(weburl + '/youraccount/login')
+ browser.open(CFG_SITE_URL + '/youraccount/login')
browser.select_form(nr=0)
browser['p_un'] = 'jekyll'
browser['p_pw'] = 'j123ekyll'
browser.submit()
- browser.open(weburl + '/record/35')
+ browser.open(CFG_SITE_URL + '/record/35')
# Dr. Jekyll should be able to connect
- # (add the pw to the whole weburl because we shall be
+ # (add the pw to the whole CFG_SITE_URL because we shall be
# redirected to '/reordrestricted/'):
if browser.response().read().find("A High-performance Video Browsing System") > -1:
pass
else:
self.fail("Oops, Dr. Jekyll should be able to access restricted detailed record page.")
def test_restricted_detailed_record_page_as_unauthorized_person(self):
"""websearch - restricted detailed record page not accessible to unauthorized person"""
browser = Browser()
- browser.open(weburl + '/youraccount/login')
+ browser.open(CFG_SITE_URL + '/youraccount/login')
browser.select_form(nr=0)
browser['p_un'] = 'hyde'
browser['p_pw'] = 'h123yde'
browser.submit()
- browser.open(weburl + '/record/35')
+ browser.open(CFG_SITE_URL + '/record/35')
# Mr. Hyde should not be able to connect:
if browser.response().read().find('You are not authorized') <= -1:
# if we got here, things are broken:
self.fail("Oops, Mr.Hyde should not be able to access restricted detailed record page.")
class WebSearchRSSFeedServiceTest(unittest.TestCase):
"""Test of the RSS feed service."""
def test_rss_feed_service(self):
"""websearch - RSS feed service"""
self.assertEqual([],
- test_web_page_content(weburl + '/rss',
+ test_web_page_content(CFG_SITE_URL + '/rss',
expected_text='<rss version="2.0">'))
class WebSearchXSSVulnerabilityTest(unittest.TestCase):
"""Test possible XSS vulnerabilities of the search engine."""
def test_xss_in_collection_interface_page(self):
"""websearch - no XSS vulnerability in collection interface pages"""
self.assertEqual([],
- test_web_page_content(weburl + '/?c=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
+ test_web_page_content(CFG_SITE_URL + '/?c=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='Collection &amp;lt;SCRIPT&amp;gt;alert("XSS");&amp;lt;/SCRIPT&amp;gt; Not Found'))
def test_xss_in_collection_search_page(self):
"""websearch - no XSS vulnerability in collection search pages"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?c=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
+ test_web_page_content(CFG_SITE_URL + '/search?c=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='Collection &lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt; Not Found'))
def test_xss_in_simple_search(self):
"""websearch - no XSS vulnerability in simple search"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
+ test_web_page_content(CFG_SITE_URL + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='Search term <em>&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;</em> did not match any record.'))
def test_xss_in_structured_search(self):
"""websearch - no XSS vulnerability in structured search"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
+ test_web_page_content(CFG_SITE_URL + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='Search term <em>&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;</em> inside index <em>&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;</em> did not match any record.'))
def test_xss_in_advanced_search(self):
"""websearch - no XSS vulnerability in advanced search"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?as=1&p1=ellis&f1=author&op1=a&p2=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f2=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
+ test_web_page_content(CFG_SITE_URL + '/search?as=1&p1=ellis&f1=author&op1=a&p2=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f2=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E',
expected_text='Search term <em>&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;</em> inside index <em>&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;</em> did not match any record.'))
def test_xss_in_browse(self):
"""websearch - no XSS vulnerability in browse"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&action_browse=Browse',
+ test_web_page_content(CFG_SITE_URL + '/search?p=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&f=%3CSCRIPT%3Ealert%28%22XSS%22%29%3B%3C%2FSCRIPT%3E&action_browse=Browse',
expected_text='&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;'))
class WebSearchResultsOverview(unittest.TestCase):
"""Test of the search results page's Results overview box and links."""
def test_results_overview_split_off(self):
"""websearch - results overview box when split by collection is off"""
browser = Browser()
- browser.open(weburl + '/search?p=of&sc=0')
+ browser.open(CFG_SITE_URL + '/search?p=of&sc=0')
body = browser.response().read()
if body.find("Results overview") > -1:
self.fail("Oops, when split by collection is off, "
"results overview should not be present.")
if body.find('<a name="1"></a>') == -1:
self.fail("Oops, when split by collection is off, "
"Atlantis collection should be found.")
if body.find('<a name="15"></a>') > -1:
self.fail("Oops, when split by collection is off, "
"Multimedia & Arts should not be found.")
try:
browser.find_link(url='#15')
self.fail("Oops, when split by collection is off, "
"a link to Multimedia & Arts should not be found.")
except LinkNotFoundError:
pass
def test_results_overview_split_on(self):
"""websearch - results overview box when split by collection is on"""
browser = Browser()
- browser.open(weburl + '/search?p=of&sc=1')
+ browser.open(CFG_SITE_URL + '/search?p=of&sc=1')
body = browser.response().read()
if body.find("Results overview") == -1:
self.fail("Oops, when split by collection is on, "
"results overview should be present.")
if body.find('<a name="Atlantis%20Institute%20of%20Fictive%20Science"></a>') > -1:
self.fail("Oops, when split by collection is on, "
"Atlantis collection should not be found.")
if body.find('<a name="15"></a>') == -1:
self.fail("Oops, when split by collection is on, "
"Multimedia & Arts should be found.")
try:
browser.find_link(url='#15')
except LinkNotFoundError:
self.fail("Oops, when split by collection is on, "
"a link to Multimedia & Arts should be found.")
class WebSearchSortResultsTest(unittest.TestCase):
"""Test of the search results page's sorting capability."""
def test_sort_results_default(self):
"""websearch - search results sorting, default method"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=cern&rg=1',
+ test_web_page_content(CFG_SITE_URL + '/search?p=cern&rg=1',
expected_text="[hep-th/9809057]"))
def test_sort_results_ascending(self):
"""websearch - search results sorting, ascending field"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=cern&rg=1&sf=reportnumber&so=a',
+ test_web_page_content(CFG_SITE_URL + '/search?p=cern&rg=1&sf=reportnumber&so=a',
expected_text="ISOLTRAP"))
def test_sort_results_descending(self):
"""websearch - search results sorting, descending field"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=cern&rg=1&sf=reportnumber&so=d',
+ test_web_page_content(CFG_SITE_URL + '/search?p=cern&rg=1&sf=reportnumber&so=d',
expected_text="SCAN-9605071"))
def test_sort_results_sort_pattern(self):
"""websearch - search results sorting, preferential sort pattern"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=cern&rg=1&sf=reportnumber&so=d&sp=cern',
+ test_web_page_content(CFG_SITE_URL + '/search?p=cern&rg=1&sf=reportnumber&so=d&sp=cern',
expected_text="CERN-TH-4036"))
class WebSearchSearchResultsXML(unittest.TestCase):
"""Test search results in various output"""
def test_search_results_xm_output_split_on(self):
""" websearch - check document element of search results in xm output (split by collection on)"""
browser = Browser()
- browser.open(weburl + '/search?sc=1&of=xm')
+ browser.open(CFG_SITE_URL + '/search?sc=1&of=xm')
body = browser.response().read()
num_doc_element = body.count("<collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">")
if num_doc_element == 0:
self.fail("Oops, no document element <collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">"
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements <collection> "
"found in search results.")
num_doc_element = body.count("</collection>")
if num_doc_element == 0:
self.fail("Oops, no document element </collection> "
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements </collection> "
"found in search results.")
def test_search_results_xm_output_split_off(self):
""" websearch - check document element of search results in xm output (split by collection off)"""
browser = Browser()
- browser.open(weburl + '/search?sc=0&of=xm')
+ browser.open(CFG_SITE_URL + '/search?sc=0&of=xm')
body = browser.response().read()
num_doc_element = body.count("<collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">")
if num_doc_element == 0:
self.fail("Oops, no document element <collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">"
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements <collection> "
"found in search results.")
num_doc_element = body.count("</collection>")
if num_doc_element == 0:
self.fail("Oops, no document element </collection> "
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements </collection> "
"found in search results.")
def test_search_results_xd_output_split_on(self):
""" websearch - check document element of search results in xd output (split by collection on)"""
browser = Browser()
- browser.open(weburl + '/search?sc=1&of=xd')
+ browser.open(CFG_SITE_URL + '/search?sc=1&of=xd')
body = browser.response().read()
num_doc_element = body.count("<collection")
if num_doc_element == 0:
self.fail("Oops, no document element <collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">"
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements <collection> "
"found in search results.")
num_doc_element = body.count("</collection>")
if num_doc_element == 0:
self.fail("Oops, no document element </collection> "
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements </collection> "
"found in search results.")
def test_search_results_xd_output_split_off(self):
""" websearch - check document element of search results in xd output (split by collection off)"""
browser = Browser()
- browser.open(weburl + '/search?sc=0&of=xd')
+ browser.open(CFG_SITE_URL + '/search?sc=0&of=xd')
body = browser.response().read()
num_doc_element = body.count("<collection>")
if num_doc_element == 0:
self.fail("Oops, no document element <collection "
"xmlns=\"http://www.loc.gov/MARC21/slim\">"
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements <collection> "
"found in search results.")
num_doc_element = body.count("</collection>")
if num_doc_element == 0:
self.fail("Oops, no document element </collection> "
"found in search results.")
elif num_doc_element > 1:
self.fail("Oops, multiple document elements </collection> "
"found in search results.")
class WebSearchUnicodeQueryTest(unittest.TestCase):
"""Test of the search results for queries containing Unicode characters."""
def test_unicode_word_query(self):
"""websearch - Unicode word query"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?of=id&p=title%3A%CE%99%CE%B8%CE%AC%CE%BA%CE%B7',
+ test_web_page_content(CFG_SITE_URL + '/search?of=id&p=title%3A%CE%99%CE%B8%CE%AC%CE%BA%CE%B7',
expected_text="[76]"))
def test_unicode_word_query_not_found_term(self):
"""websearch - Unicode word query, not found term"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?p=title%3A%CE%99%CE%B8',
+ test_web_page_content(CFG_SITE_URL + '/search?p=title%3A%CE%99%CE%B8',
expected_text="ιθάκη"))
def test_unicode_exact_phrase_query(self):
"""websearch - Unicode exact phrase query"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?of=id&p=title%3A%22%CE%99%CE%B8%CE%AC%CE%BA%CE%B7%22',
+ test_web_page_content(CFG_SITE_URL + '/search?of=id&p=title%3A%22%CE%99%CE%B8%CE%AC%CE%BA%CE%B7%22',
expected_text="[76]"))
def test_unicode_partial_phrase_query(self):
"""websearch - Unicode partial phrase query"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?of=id&p=title%3A%27%CE%B7%27',
+ test_web_page_content(CFG_SITE_URL + '/search?of=id&p=title%3A%27%CE%B7%27',
expected_text="[76]"))
def test_unicode_regexp_query(self):
"""websearch - Unicode regexp query"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?of=id&p=title%3A%2F%CE%B7%2F',
+ test_web_page_content(CFG_SITE_URL + '/search?of=id&p=title%3A%2F%CE%B7%2F',
expected_text="[76]"))
class WebSearchMARCQueryTest(unittest.TestCase):
"""Test of the search results for queries containing physical MARC tags."""
def test_single_marc_tag_exact_phrase_query(self):
"""websearch - single MARC tag, exact phrase query (100__a)"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?of=id&p=100__a%3A%22Ellis%2C+J%22',
+ test_web_page_content(CFG_SITE_URL + '/search?of=id&p=100__a%3A%22Ellis%2C+J%22',
expected_text="[9, 14, 18]"))
def test_single_marc_tag_partial_phrase_query(self):
"""websearch - single MARC tag, partial phrase query (245__b)"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?of=id&p=245__b%3A%27and%27',
+ test_web_page_content(CFG_SITE_URL + '/search?of=id&p=245__b%3A%27and%27',
expected_text="[28]"))
def test_many_marc_tags_partial_phrase_query(self):
"""websearch - many MARC tags, partial phrase query (245)"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?of=id&p=245%3A%27and%27',
+ test_web_page_content(CFG_SITE_URL + '/search?of=id&p=245%3A%27and%27',
expected_text="[1, 8, 9, 14, 15, 20, 22, 24, 28, 33, 47, 48, 49, 51, 53, 64, 69, 71, 79, 82, 83, 85, 91]"))
def test_single_marc_tag_regexp_query(self):
"""websearch - single MARC tag, regexp query"""
# NOTE: regexp queries for physical MARC tags (e.g. 245:/and/)
# are not treated by the search engine by purpose. But maybe
# we should support them?!
self.assertEqual([],
- test_web_page_content(weburl + '/search?of=id&p=245%3A%2Fand%2F',
+ test_web_page_content(CFG_SITE_URL + '/search?of=id&p=245%3A%2Fand%2F',
expected_text="[]"))
class WebSearchExtSysnoQueryTest(unittest.TestCase):
"""Test of queries using external system numbers."""
def test_existing_sysno_html_output(self):
"""websearch - external sysno query, existing sysno, HTML output"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?sysno=000289446CER',
+ test_web_page_content(CFG_SITE_URL + '/search?sysno=000289446CER',
expected_text="The wall of the cave"))
def test_existing_sysno_id_output(self):
"""websearch - external sysno query, existing sysno, ID output"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?sysno=000289446CER&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?sysno=000289446CER&of=id',
expected_text="[95]"))
def test_nonexisting_sysno_html_output(self):
"""websearch - external sysno query, non-existing sysno, HTML output"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?sysno=000289446CERRRR',
+ test_web_page_content(CFG_SITE_URL + '/search?sysno=000289446CERRRR',
expected_text="Requested record does not seem to exist."))
def test_nonexisting_sysno_id_output(self):
"""websearch - external sysno query, non-existing sysno, ID output"""
self.assertEqual([],
- test_web_page_content(weburl + '/search?sysno=000289446CERRRR&of=id',
+ test_web_page_content(CFG_SITE_URL + '/search?sysno=000289446CERRRR&of=id',
expected_text="[]"))
test_suite = make_test_suite(WebSearchWebPagesAvailabilityTest,
WebSearchTestSearch,
WebSearchTestBrowse,
WebSearchTestCollections,
WebSearchTestRecord,
WebSearchTestLegacyURLs,
WebSearchNearestTermsTest,
WebSearchBooleanQueryTest,
WebSearchAuthorQueryTest,
WebSearchSearchEnginePythonAPITest,
WebSearchSearchEngineWebAPITest,
WebSearchRestrictedCollectionTest,
WebSearchRSSFeedServiceTest,
WebSearchXSSVulnerabilityTest,
WebSearchResultsOverview,
WebSearchSortResultsTest,
WebSearchSearchResultsXML,
WebSearchUnicodeQueryTest,
WebSearchMARCQueryTest,
WebSearchExtSysnoQueryTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/websearch/lib/websearch_templates.py b/modules/websearch/lib/websearch_templates.py
index e998b3ea0..fcfc7f458 100644
--- a/modules/websearch/lib/websearch_templates.py
+++ b/modules/websearch/lib/websearch_templates.py
@@ -1,2910 +1,2879 @@
# -*- coding: utf-8 -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable-msg=C0301
__revision__ = "$Id$"
import urllib
import time
import cgi
import gettext
import string
import locale
from invenio.config import \
CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH, \
CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD, \
CFG_WEBSEARCH_USE_ALEPH_SYSNOS, \
CFG_BIBRANK_SHOW_READING_STATS, \
CFG_BIBRANK_SHOW_DOWNLOAD_STATS, \
CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS, \
CFG_BIBRANK_SHOW_CITATION_LINKS, \
CFG_BIBRANK_SHOW_CITATION_STATS, \
CFG_BIBRANK_SHOW_CITATION_GRAPHS, \
CFG_WEBSEARCH_RSS_TTL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_VERSION, \
- weburl, \
+ CFG_SITE_URL, \
CFG_SITE_SUPPORT_EMAIL
from invenio.dbquery import run_sql
from invenio.messages import gettext_set_language
#from invenio.search_engine_config import CFG_EXPERIMENTAL_FEATURES
from invenio.urlutils import make_canonical_urlargd, drop_default_urlargd, create_html_link, create_url
from invenio.htmlutils import nmtoken_from_string
from invenio.webinterface_handler import wash_urlargd
from invenio.websearch_external_collections import external_collection_get_state
def get_fieldvalues(recID, tag):
"""Return list of field values for field TAG inside record RECID.
FIXME: should be imported commonly for search_engine too."""
out = []
if tag == "001___":
# we have asked for recID that is not stored in bibXXx tables
out.append(str(recID))
else:
# we are going to look inside bibXXx tables
digit = tag[0:2]
bx = "bib%sx" % digit
bibx = "bibrec_bib%sx" % digit
query = "SELECT bx.value FROM %s AS bx, %s AS bibx WHERE bibx.id_bibrec='%s' AND bx.id=bibx.id_bibxxx AND bx.tag LIKE '%s'" \
"ORDER BY bibx.field_number, bx.tag ASC" % (bx, bibx, recID, tag)
res = run_sql(query)
for row in res:
out.append(row[0])
return out
class Template:
# This dictionary maps CDS Invenio language code to locale codes (ISO 639)
tmpl_localemap = {
'bg': 'bg_BG',
'ca': 'ca_ES',
'de': 'de_DE',
'el': 'el_GR',
'en': 'en_US',
'es': 'es_ES',
'pt': 'pt_BR',
'fr': 'fr_FR',
'it': 'it_IT',
'ru': 'ru_RU',
'sk': 'sk_SK',
'cs': 'cs_CZ',
'no': 'no_NO',
'sv': 'sv_SE',
'uk': 'uk_UA',
'ja': 'ja_JA',
'pl': 'pl_PL',
'hr': 'hr_HR',
'zh_CN': 'zh_CN',
'zh_TW': 'zh_TW',
}
tmpl_default_locale = "en_US" # which locale to use by default, useful in case of failure
# Type of the allowed parameters for the web interface for search results
search_results_default_urlargd = {
'cc': (str, CFG_SITE_NAME),
'c': (list, []),
'p': (str, ""), 'f': (str, ""),
'rg': (int, 10),
'sf': (str, ""),
'so': (str, "d"),
'sp': (str, ""),
'rm': (str, ""),
'of': (str, "hb"),
'ot': (list, []),
'as': (int, 0),
'p1': (str, ""), 'f1': (str, ""), 'm1': (str, ""), 'op1':(str, ""),
'p2': (str, ""), 'f2': (str, ""), 'm2': (str, ""), 'op2':(str, ""),
'p3': (str, ""), 'f3': (str, ""), 'm3': (str, ""),
'sc': (int, 0),
'jrec': (int, 0),
'recid': (int, -1), 'recidb': (int, -1), 'sysno': (str, ""),
'id': (int, -1), 'idb': (int, -1), 'sysnb': (str, ""),
'action': (str, "search"),
'action_search': (str, ""),
'action_browse': (str, ""),
'd1': (str, ""),
'd1y': (int, 0), 'd1m': (int, 0), 'd1d': (int, 0),
'd2': (str, ""),
'd2y': (int, 0), 'd2m': (int, 0), 'd2d': (int, 0),
'dt': (str, ""),
'ap': (int, 1),
'verbose': (int, 0),
'ec': (list, []),
}
# ...and for search interfaces
search_interface_default_urlargd = {
'as': (int, 0),
'verbose': (int, 0)}
# ...and for RSS feeds
rss_default_urlargd = {'c' : (list, []),
'cc' : (str, ""),
'p' : (str, ""),
'f' : (str, ""),
'p1' : (str, ""),
'f1' : (str, ""),
'm1' : (str, ""),
'op1': (str, ""),
'p2' : (str, ""),
'f2' : (str, ""),
'm2' : (str, ""),
'op2': (str, ""),
'p3' : (str, ""),
'f3' : (str, ""),
'm3' : (str, "")}
tmpl_openurl_accepted_args = {
'genre' : (str, ''),
'aulast' : (str, ''),
'aufirst' : (str, ''),
'auinit' : (str, ''),
'auinit1' : (str, ''),
'auinitm' : (str, ''),
'issn' : (str, ''),
'eissn' : (str, ''),
'coden' : (str, ''),
'isbn' : (str, ''),
'sici' : (str, ''),
'bici' : (str, ''),
'title' : (str, ''),
'stitle' : (str, ''),
'atitle' : (str, ''),
'volume' : (str, ''),
'part' : (str, ''),
'issue' : (str, ''),
'spage' : (str, ''),
'epage' : (str, ''),
'pages' : (str, ''),
'artnum' : (str, ''),
'date' : (str, ''),
'ssn' : (str, ''),
'quarter' : (str, ''),
'url_ver' : (str, ''),
'ctx_ver' : (str, ''),
'rft_val_fmt' : (str, ''),
'rfr_id' : (str, ''),
'rft.atitle' : (str, ''),
'rft.title' : (str, ''),
'rft.jtitle' : (str, ''),
'rft.stitle' : (str, ''),
'rft.date' : (str, ''),
'rft.volume' : (str, ''),
'rft.issue' : (str, ''),
'rft.spage' : (str, ''),
'rft.epage' : (str, ''),
'rft.pages' : (str, ''),
'rft.artnumber' : (str, ''),
'rft.issn' : (str, ''),
'rft.eissn' : (str, ''),
'rft.aulast' : (str, ''),
'rft.aufirst' : (str, ''),
'rft.auinit' : (str, ''),
'rft.auinit1' : (str, ''),
'rft.auinitm' : (str, ''),
'rft.ausuffix' : (str, ''),
'rft.au' : (list, []),
'rft.aucorp' : (str, ''),
'rft.isbn' : (str, ''),
'rft.coden' : (str, ''),
'rft.sici' : (str, ''),
'rft.genre' : (str, 'unknown'),
'rft.chron' : (str, ''),
'rft.ssn' : (str, ''),
'rft.quarter' : (int, ''),
'rft.part' : (str, ''),
'rft.btitle' : (str, ''),
'rft.isbn' : (str, ''),
'rft.atitle' : (str, ''),
'rft.place' : (str, ''),
'rft.pub' : (str, ''),
'rft.edition' : (str, ''),
'rft.tpages' : (str, ''),
'rft.series' : (str, ''),
}
def tmpl_openurl2invenio(self, openurl_data):
""" Return an Invenio url corresponding to a search with the data
included in the openurl form map.
"""
from invenio.search_engine import perform_request_search
aulast = openurl_data['rft.aulast'] or openurl_data['aulast']
aufirst = openurl_data['rft.aufirst'] or openurl_data['aufirst']
auinit = openurl_data['rft.auinit'] or \
openurl_data['auinit'] or \
openurl_data['rft.auinit1'] + ' ' + openurl_data['rft.auinitm'] or \
openurl_data['auinit1'] + ' ' + openurl_data['auinitm'] or aufirst[:1]
auinit = auinit.upper()
if aulast and aufirst:
author_query = 'author:"%s, %s" or author:"%s, %s"' % (aulast, aufirst, aulast, auinit)
elif aulast and auinit:
author_query = 'author:"%s, %s"' % (aulast, auinit)
else:
author_query = ''
title = openurl_data['rft.atitle'] or \
openurl_data['atitle'] or \
openurl_data['rft.btitle'] or \
openurl_data['rft.title'] or \
openurl_data['title']
if title:
title_query = 'title:"%s"' % title
else:
title_query = ''
jtitle = openurl_data['rft.stitle'] or \
openurl_data['stitle'] or \
openurl_data['rft.jtitle'] or \
openurl_data['title']
if jtitle:
journal_query = 'journal:"%s"' % jtitle
else:
journal_query = ''
isbn = openurl_data['rft.isbn'] or \
openurl_data['isbn']
if isbn:
isbn_query = '020__a:"%s"' % isbn
else:
isbn_query = ''
issn = openurl_data['rft.eissn'] or \
openurl_data['eissn'] or \
openurl_data['rft.issn'] or \
openurl_data['issn']
if issn:
issn_query = '022__a:"%s"' % issn
else:
issn_query = ''
coden = openurl_data['rft.coden'] or openurl_data['coden']
if coden:
coden_query = '030__a:"%s"' % coden
else:
coden_query = ''
if openurl_data['rfr_id'].startswith('info:doi/'):
doi_query = '773__a:"%s"' % openurl_data['rfr_id'][len('info:doi/'):]
else:
doi_query = ''
if doi_query:
if perform_request_search(p=doi_query):
- return '%s/search%s' % (weburl, make_canonical_urlargd({
+ return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : doi_query,
'sc' : 1,
'of' : 'hd'}, {}))
if isbn_query:
if perform_request_search(p=isbn_query):
- return '%s/search%s' % (weburl, make_canonical_urlargd({
+ return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : isbn_query,
'sc' : 1,
'of' : 'hd'}, {}))
if coden_query:
if perform_request_search(p=coden_query):
- return '%s/search%s' % (weburl, make_canonical_urlargd({
+ return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : coden_query,
'sc' : 1,
'of' : 'hd'}, {}))
if author_query and title_query:
if perform_request_search(p='%s and %s' % (title_query, author_query)):
- return '%s/search%s' % (weburl, make_canonical_urlargd({
+ return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : '%s and %s' % (title_query, author_query),
'sc' : 1,
'of' : 'hd'}, {}))
if title_query:
if perform_request_search(p=title_query):
- return '%s/search%s' % (weburl, make_canonical_urlargd({
+ return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : title_query,
'sc' : 1,
'of' : 'hb'}, {}))
if title:
- return '%s/search%s' % (weburl, make_canonical_urlargd({
+ return '%s/search%s' % (CFG_SITE_URL, make_canonical_urlargd({
'p' : title,
'sc' : 1,
'of' : 'hb'}, {}))
return ''
def build_search_url(self, known_parameters={}, **kargs):
""" Helper for generating a canonical search
url. 'known_parameters' is the list of query parameters you
inherit from your current query. You can then pass keyword
arguments to modify this query.
build_search_url(known_parameters, of="xm")
The generated URL is absolute.
"""
parameters = {}
parameters.update(known_parameters)
parameters.update(kargs)
# Now, we only have the arguments which have _not_ their default value
parameters = drop_default_urlargd(parameters, self.search_results_default_urlargd)
# Asking for a recid? Return a /record/<recid> URL
if 'recid' in parameters:
- target = "%s/record/%d" % (weburl, parameters['recid'])
+ target = "%s/record/%d" % (CFG_SITE_URL, parameters['recid'])
del parameters['recid']
target += make_canonical_urlargd(parameters, self.search_results_default_urlargd)
return target
- return "%s/search%s" % (weburl, make_canonical_urlargd(parameters, self.search_results_default_urlargd))
+ return "%s/search%s" % (CFG_SITE_URL, make_canonical_urlargd(parameters, self.search_results_default_urlargd))
def build_search_interface_url(self, known_parameters={}, **kargs):
""" Helper for generating a canonical search interface URL."""
parameters = {}
parameters.update(known_parameters)
parameters.update(kargs)
c = parameters['c']
del parameters['c']
# Now, we only have the arguments which have _not_ their default value
if c and c != CFG_SITE_NAME:
- base = weburl + '/collection/' + urllib.quote(c)
+ base = CFG_SITE_URL + '/collection/' + urllib.quote(c)
else:
- base = weburl
+ base = CFG_SITE_URL
return create_url(base, drop_default_urlargd(parameters, self.search_results_default_urlargd))
def build_rss_url(self, known_parameters, **kargs):
"""Helper for generating a canonical RSS URL"""
parameters = {}
parameters.update(known_parameters)
parameters.update(kargs)
# Keep only interesting parameters
argd = wash_urlargd(parameters, self.rss_default_urlargd)
if argd:
# Handle 'c' differently since it is a list
c = argd.get('c', [])
del argd['c']
# Create query, and drop empty params
args = make_canonical_urlargd(argd, self.rss_default_urlargd)
if c != []:
# Add collections
c = [urllib.quote(coll) for coll in c]
args += '&amp;c=' + '&amp;c='.join(c)
- return weburl + '/rss' + args
+ return CFG_SITE_URL + '/rss' + args
def tmpl_record_page_header_content(self, req, recid, ln):
""" Provide extra information in the header of /record pages """
_ = gettext_set_language(ln)
title = get_fieldvalues(recid, "245__a")
if title:
title = _("Record") + '#%d: %s' %(recid, cgi.escape(title[0]))
else:
title = _("Record") + ' #%d' % recid
keywords = ', '.join(get_fieldvalues(recid, "6531_a"))
description = ' '.join(get_fieldvalues(recid, "520__a"))
description += "\n"
description += '; '.join(get_fieldvalues(recid, "100__a") + get_fieldvalues(recid, "700__a"))
return [cgi.escape(x, True) for x in (title, description, keywords)]
def tmpl_navtrail_links(self, as, ln, dads):
"""
Creates the navigation bar at top of each search page (*Home > Root collection > subcollection > ...*)
Parameters:
- 'as' *bool* - Should we display an advanced search box?
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'separator' *string* - The separator between two consecutive collections
- 'dads' *list* - A list of parent links, eachone being a dictionary of ('name', 'longname')
"""
out = []
for url, name in dads:
out.append(create_html_link(self.build_search_interface_url(c=url, as=as, ln=ln), {}, cgi.escape(name), {'class': 'navtrail'}))
return ' &gt; '.join(out)
def tmpl_webcoll_body(self, ln, collection, te_portalbox,
searchfor, np_portalbox, narrowsearch,
focuson, instantbrowse, ne_portalbox):
""" Creates the body of the main search page.
Parameters:
- 'ln' *string* - language of the page being generated
- 'collection' - collection id of the page being generated
- 'te_portalbox' *string* - The HTML code for the portalbox on top of search
- 'searchfor' *string* - The HTML code for the search options
- 'np_portalbox' *string* - The HTML code for the portalbox on bottom of search
- 'searchfor' *string* - The HTML code for the search categories (left bottom of page)
- 'focuson' *string* - The HTML code for the "focuson" categories (right bottom of page)
- 'ne_portalbox' *string* - The HTML code for the bottom of the page
"""
if not narrowsearch:
narrowsearch = instantbrowse
body = '''
- <form name="search" action="%(weburl)s/search" method="get">
+ <form name="search" action="%(siteurl)s/search" method="get">
%(searchfor)s
%(np_portalbox)s
<table cellspacing="0" cellpadding="0" border="0">
<tr>
<td valign="top">%(narrowsearch)s</td>
''' % {
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'searchfor' : searchfor,
'np_portalbox' : np_portalbox,
'narrowsearch' : narrowsearch
}
if focuson:
body += """<td valign="top">""" + focuson + """</td>"""
body += """</tr></table>
%(ne_portalbox)s
</form>""" % {'ne_portalbox' : ne_portalbox}
return body
def tmpl_portalbox(self, title, body):
"""Creates portalboxes based on the parameters
Parameters:
- 'title' *string* - The title of the box
- 'body' *string* - The HTML code for the body of the box
"""
out = """<div class="portalbox">
<div class="portalboxheader">%(title)s</div>
<div class="portalboxbody">%(body)s</div>
</div>""" % {'title' : cgi.escape(title), 'body' : body}
return out
def tmpl_searchfor_simple(self, ln, collection_id, collection_name, record_count, middle_option):
"""Produces simple *Search for* box for the current collection.
Parameters:
- 'ln' *string* - The language to display
- 'header' *string* - header of search form
- 'middle_option' *string* - HTML code for the options (any field, specific fields ...)
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''
<!--create_searchfor_simple()-->
'''
argd = drop_default_urlargd({'ln': ln, 'cc': collection_id, 'sc': 1},
self.search_results_default_urlargd)
# Only add non-default hidden values
for field, value in argd.items():
out += self.tmpl_input_hidden(field, value)
header = _("Search %s records for:") % \
self.tmpl_nbrecs_info(record_count, "","")
asearchurl = self.build_search_interface_url(c=collection_id, as=1, ln=ln)
# print commentary start:
out += '''
<table class="searchbox">
<thead>
<tr align="left">
<th colspan="3" class="searchboxheader">%(header)s</th>
</tr>
</thead>
<tbody>
<tr valign="baseline">
<td class="searchboxbody" align="left"><input type="text" name="p" size="40" value="" /></td>
<td class="searchboxbody" align="left">%(middle_option)s</td>
<td class="searchboxbody" align="left">
<input class="formbutton" type="submit" name="action_search" value="%(msg_search)s" />
<input class="formbutton" type="submit" name="action_browse" value="%(msg_browse)s" /></td>
</tr>
<tr valign="baseline">
<td class="searchboxbody" colspan="3" align="right">
<small>
- <a href="%(weburl)s/help/search-tips%(langlink)s">%(msg_search_tips)s</a> ::
+ <a href="%(siteurl)s/help/search-tips%(langlink)s">%(msg_search_tips)s</a> ::
%(asearch)s
</small>
</td>
</tr>
</tbody>
</table>
<!--/create_searchfor_simple()-->
''' % {'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'asearch' : create_html_link(asearchurl, {}, _('Advanced Search')),
'header' : header,
'middle_option' : middle_option,
'msg_search' : _('Search'),
'msg_browse' : _('Browse'),
'msg_search_tips' : _('Search Tips')}
return out
def tmpl_searchfor_advanced(self,
ln, # current language
collection_id,
collection_name,
record_count,
middle_option_1, middle_option_2, middle_option_3,
searchoptions,
sortoptions,
rankoptions,
displayoptions,
formatoptions
):
"""
Produces advanced *Search for* box for the current collection.
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- - 'ssearchurl' *string* - The URL to simple search form
-
- - 'header' *string* - header of search form
-
- 'middle_option_1' *string* - HTML code for the first row of options (any field, specific fields ...)
- 'middle_option_2' *string* - HTML code for the second row of options (any field, specific fields ...)
- 'middle_option_3' *string* - HTML code for the third row of options (any field, specific fields ...)
- 'searchoptions' *string* - HTML code for the search options
- 'sortoptions' *string* - HTML code for the sort options
- 'rankoptions' *string* - HTML code for the rank options
- 'displayoptions' *string* - HTML code for the display options
- 'formatoptions' *string* - HTML code for the format options
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''
<!--create_searchfor_advanced()-->
'''
argd = drop_default_urlargd({'ln': ln, 'as': 1, 'cc': collection_id, 'sc': 1},
self.search_results_default_urlargd)
# Only add non-default hidden values
for field, value in argd.items():
out += self.tmpl_input_hidden(field, value)
header = _("Search %s records for") % \
self.tmpl_nbrecs_info(record_count, "","")
header += ':'
ssearchurl = self.build_search_interface_url(c=collection_id, as=0, ln=ln)
out += '''
<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader" colspan="3">%(header)s</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody" style="white-space: nowrap;">
%(matchbox_m1)s<input type="text" name="p1" size="40" value="" />
</td>
<td class="searchboxbody" style="white-space: nowrap;">%(middle_option_1)s</td>
<td class="searchboxbody">%(andornot_op1)s</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody" style="white-space: nowrap;">
%(matchbox_m2)s<input type="text" name="p2" size="40" value="" />
</td>
<td class="searchboxbody">%(middle_option_2)s</td>
<td class="searchboxbody">%(andornot_op2)s</td>
</tr>
<tr valign="bottom">
<td class="searchboxbody" style="white-space: nowrap;">
%(matchbox_m3)s<input type="text" name="p3" size="40" value="" />
</td>
<td class="searchboxbody">%(middle_option_3)s</td>
<td class="searchboxbody" style="white-space: nowrap;">
<input class="formbutton" type="submit" name="action_search" value="%(msg_search)s" />
<input class="formbutton" type="submit" name="action_browse" value="%(msg_browse)s" /></td>
</tr>
<tr valign="bottom">
<td colspan="3" class="searchboxbody" align="right">
<small>
- <a href="%(weburl)s/help/search-tips%(langlink)s">%(msg_search_tips)s</a> ::
+ <a href="%(siteurl)s/help/search-tips%(langlink)s">%(msg_search_tips)s</a> ::
%(ssearch)s
</small>
</td>
</tr>
</tbody>
</table>
<!-- @todo - more imports -->
''' % {'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'ssearch' : create_html_link(ssearchurl, {}, _("Simple Search")),
'header' : header,
'matchbox_m1' : self.tmpl_matchtype_box('m1', ln=ln),
'middle_option_1' : middle_option_1,
'andornot_op1' : self.tmpl_andornot_box('op1', ln=ln),
'matchbox_m2' : self.tmpl_matchtype_box('m2', ln=ln),
'middle_option_2' : middle_option_2,
'andornot_op2' : self.tmpl_andornot_box('op2', ln=ln),
'matchbox_m3' : self.tmpl_matchtype_box('m3', ln=ln),
'middle_option_3' : middle_option_3,
'msg_search' : _("Search"),
'msg_browse' : _("Browse"),
'msg_search_tips' : _("Search Tips")}
if (searchoptions):
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(searchheader)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">%(searchoptions)s</td>
</tr>
</tbody>
</table>""" % {
'searchheader' : _("Search options:"),
'searchoptions' : searchoptions
}
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(added)s
</th>
<th class="searchboxheader">
%(until)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">%(added_or_modified)s %(date_added)s</td>
<td class="searchboxbody">%(date_until)s</td>
</tr>
</tbody>
</table>
<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(msg_sort)s
</th>
<th class="searchboxheader">
%(msg_display)s
</th>
<th class="searchboxheader">
%(msg_format)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">%(sortoptions)s %(rankoptions)s</td>
<td class="searchboxbody">%(displayoptions)s</td>
<td class="searchboxbody">%(formatoptions)s</td>
</tr>
</tbody>
</table>
<!--/create_searchfor_advanced()-->
""" % {
'added' : _("Added/modified since:"),
'until' : _("until:"),
'added_or_modified': self.tmpl_inputdatetype(ln=ln),
'date_added' : self.tmpl_inputdate("d1", ln=ln),
'date_until' : self.tmpl_inputdate("d2", ln=ln),
'msg_sort' : _("Sort by:"),
'msg_display' : _("Display results:"),
'msg_format' : _("Output format:"),
'sortoptions' : sortoptions,
'rankoptions' : rankoptions,
'displayoptions' : displayoptions,
'formatoptions' : formatoptions
}
return out
def tmpl_matchtype_box(self, name='m', value='', ln='en'):
"""Returns HTML code for the 'match type' selection box.
Parameters:
- 'name' *string* - The name of the produced select
- 'value' *string* - The selected value (if any value is already selected)
- 'ln' *string* - the language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<select name="%(name)s">
<option value="a"%(sela)s>%(opta)s</option>
<option value="o"%(selo)s>%(opto)s</option>
<option value="e"%(sele)s>%(opte)s</option>
<option value="p"%(selp)s>%(optp)s</option>
<option value="r"%(selr)s>%(optr)s</option>
</select>
""" % {'name' : name,
'sela' : self.tmpl_is_selected('a', value),
'opta' : _("All of the words:"),
'selo' : self.tmpl_is_selected('o', value),
'opto' : _("Any of the words:"),
'sele' : self.tmpl_is_selected('e', value),
'opte' : _("Exact phrase:"),
'selp' : self.tmpl_is_selected('p', value),
'optp' : _("Partial phrase:"),
'selr' : self.tmpl_is_selected('r', value),
'optr' : _("Regular expression:")
}
return out
def tmpl_is_selected(self, var, fld):
"""
Checks if *var* and *fld* are equal, and if yes, returns ' selected="selected"'. Useful for select boxes.
Parameters:
- 'var' *string* - First value to compare
- 'fld' *string* - Second value to compare
"""
if var == fld:
return ' selected="selected"'
else:
return ""
def tmpl_andornot_box(self, name='op', value='', ln='en'):
"""
Returns HTML code for the AND/OR/NOT selection box.
Parameters:
- 'name' *string* - The name of the produced select
- 'value' *string* - The selected value (if any value is already selected)
- 'ln' *string* - the language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<select name="%(name)s">
<option value="a"%(sela)s>%(opta)s</option>
<option value="o"%(selo)s>%(opto)s</option>
<option value="n"%(seln)s>%(optn)s</option>
</select>
""" % {'name' : name,
'sela' : self.tmpl_is_selected('a', value), 'opta' : _("AND"),
'selo' : self.tmpl_is_selected('o', value), 'opto' : _("OR"),
'seln' : self.tmpl_is_selected('n', value), 'optn' : _("AND NOT")
}
return out
def tmpl_inputdate(self, name, ln, sy = 0, sm = 0, sd = 0):
"""
Produces *From Date*, *Until Date* kind of selection box. Suitable for search options.
Parameters:
- 'name' *string* - The base name of the produced selects
- 'ln' *string* - the language to display
"""
# load the right message language
_ = gettext_set_language(ln)
box = """
<select name="%(name)sd">
<option value=""%(sel)s>%(any)s</option>
""" % {
'name' : name,
'any' : _("any day"),
'sel' : self.tmpl_is_selected(sd, 0)
}
for day in range(1, 32):
box += """<option value="%02d"%s>%02d</option>""" % (day, self.tmpl_is_selected(sd, day), day)
box += """</select>"""
# month
box += """
<select name="%(name)sm">
<option value=""%(sel)s>%(any)s</option>
""" % {
'name' : name,
'any' : _("any month"),
'sel' : self.tmpl_is_selected(sm, 0)
}
for mm, month in [(1, _("January")), (2, _("February")), (3, _("March")), (4, _("April")), \
(5, _("May")), (6, _("June")), (7, _("July")), (8, _("August")), \
(9, _("September")), (10, _("October")), (11, _("November")), (12, _("December"))]:
box += """<option value="%02d"%s>%s</option>""" % (mm, self.tmpl_is_selected(sm, mm), month)
box += """</select>"""
# year
box += """
<select name="%(name)sy">
<option value=""%(sel)s>%(any)s</option>
""" % {
'name' : name,
'any' : _("any year"),
'sel' : self.tmpl_is_selected(sy, 0)
}
this_year = int(time.strftime("%Y", time.localtime()))
for year in range(this_year-20, this_year+1):
box += """<option value="%d"%s>%d</option>""" % (year, self.tmpl_is_selected(sy, year), year)
box += """</select>"""
return box
def tmpl_inputdatetype(self, dt='', ln=CFG_SITE_LANG):
"""
Produces input date type selection box to choose
added-or-modified date search option.
Parameters:
- 'dt' *string - date type (c=created, m=modified)
- 'ln' *string* - the language to display
"""
# load the right message language
_ = gettext_set_language(ln)
box = """<select name="dt">
<option value="">%(added)s </option>
<option value="m"%(sel)s>%(modified)s </option>
</select>
""" % { 'added': _("Added since:"),
'modified': _("Modified since:"),
'sel': self.tmpl_is_selected(dt, 'm'),
}
return box
def tmpl_narrowsearch(self, as, ln, type, father,
has_grandchildren, sons, display_grandsons,
grandsons):
"""
Creates list of collection descendants of type *type* under title *title*.
If as==1, then links to Advanced Search interfaces; otherwise Simple Search.
Suitable for 'Narrow search' and 'Focus on' boxes.
Parameters:
- 'as' *bool* - Should we display an advanced search box?
- 'ln' *string* - The language to display
- 'type' *string* - The type of the produced box (virtual collections or normal collections)
- 'father' *collection* - The current collection
- 'has_grandchildren' *bool* - If the current collection has grand children
- 'sons' *list* - The list of the sub-collections (first level)
- 'display_grandsons' *bool* - If the grand children collections should be displayed (2 level deep display)
- 'grandsons' *list* - The list of sub-collections (second level)
"""
# load the right message language
_ = gettext_set_language(ln)
title = {'r': _("Narrow by collection:"),
'v': _("Focus on:")}[type]
if has_grandchildren:
style_prolog = "<strong>"
style_epilog = "</strong>"
else:
style_prolog = ""
style_epilog = ""
out = """<table class="%(narrowsearchbox)s">
<thead>
<tr>
<th colspan="2" align="left" class="%(narrowsearchbox)sheader">
%(title)s
</th>
</tr>
</thead>
<tbody>""" % {'title' : title,
'narrowsearchbox': {'r': 'narrowsearchbox',
'v': 'focusonsearchbox'}[type]}
# iterate through sons:
i = 0
for son in sons:
out += """<tr><td class="%(narrowsearchbox)sbody" valign="top">""" % \
{ 'narrowsearchbox': {'r': 'narrowsearchbox',
'v': 'focusonsearchbox'}[type]}
if type == 'r':
if son.restricted_p() and son.restricted_p() != father.restricted_p():
out += """<input type="checkbox" name="c" value="%(name)s" />&nbsp;</td>""" % {'name' : cgi.escape(son.name) }
else:
out += """<input type="checkbox" name="c" value="%(name)s" checked="checked" />&nbsp;</td>""" % {'name' : cgi.escape(son.name) }
else:
out += '</td>'
out += """<td valign="top">%(link)s%(recs)s """ % {
'link': create_html_link(self.build_search_interface_url(c=son.name, ln=ln, as=as),
{}, style_prolog + cgi.escape(son.get_name(ln)) + style_epilog),
'recs' : self.tmpl_nbrecs_info(son.nbrecs, ln=ln)}
if son.restricted_p():
out += """ <small class="warning">[%(msg)s]</small> """ % { 'msg' : _("restricted") }
if display_grandsons and len(grandsons[i]):
# iterate trough grandsons:
out += """<br />"""
for grandson in grandsons[i]:
out += """ <small>%(link)s%(nbrec)s</small> """ % {
'link': create_html_link(self.build_search_interface_url(c=grandson.name, ln=ln, as=as),
{},
cgi.escape(grandson.get_name(ln))),
'nbrec' : self.tmpl_nbrecs_info(grandson.nbrecs, ln=ln)}
out += """</td></tr>"""
i += 1
out += "</tbody></table>"
return out
def tmpl_searchalso(self, ln, engines_list, collection_id):
_ = gettext_set_language(ln)
box_name = _("Search also:")
html = """<table cellspacing="0" cellpadding="0" border="0">
<tr><td valign="top"><table class="searchalsosearchbox">
<thead><tr><th colspan="2" align="left" class="searchalsosearchboxheader">%(box_name)s
</th></tr></thead><tbody>
""" % locals()
for engine in engines_list:
internal_name = engine.name
name = _(internal_name)
base_url = engine.base_url
if external_collection_get_state(engine, collection_id) == 3:
checked = ' checked="checked"'
else:
checked = ''
html += """<tr><td class="searchalsosearchboxbody" valign="top">
<input type="checkbox" name="ec" id="%(id)s" value="%(internal_name)s" %(checked)s /></td>
<td valign="top" class="searchalsosearchboxbody">
<div style="white-space: nowrap"><label for="%(id)s">%(name)s</label>
<a href="%(base_url)s">
- <img src="%(weburl)s/img/external-icon-light-8x8.gif" border="0" alt="%(name)s"/></a>
+ <img src="%(siteurl)s/img/external-icon-light-8x8.gif" border="0" alt="%(name)s"/></a>
</div></td></tr>""" % \
{ 'checked': checked,
'base_url': base_url,
'internal_name': internal_name,
'name': cgi.escape(name),
'id': "extSearch" + nmtoken_from_string(name),
- 'weburl': weburl,}
+ 'siteurl': CFG_SITE_URL,}
html += """</tbody></table></td></tr></table>"""
return html
def tmpl_nbrecs_info(self, number, prolog=None, epilog=None, ln=CFG_SITE_LANG):
"""
Return information on the number of records.
Parameters:
- 'number' *string* - The number of records
- 'prolog' *string* (optional) - An HTML code to prefix the number (if **None**, will be
'<small class="nbdoccoll">(')
- 'epilog' *string* (optional) - An HTML code to append to the number (if **None**, will be
')</small>')
"""
if number is None:
number = 0
if prolog is None:
prolog = '''&nbsp;<small class="nbdoccoll">('''
if epilog is None:
epilog = ''')</small>'''
return prolog + self.tmpl_nice_number(number, ln) + epilog
def tmpl_box_restricted_content(self, ln):
"""
Displays a box containing a *restricted content* message
Parameters:
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
return _("The contents of this collection is restricted.")
def tmpl_box_no_records(self, ln):
"""
Displays a box containing a *no content* message
Parameters:
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
return _("This collection does not contain any document yet.")
def tmpl_instant_browse(self, as, ln, recids, more_link = None):
"""
Formats a list of records (given in the recids list) from the database.
Parameters:
- 'as' *int* - Advanced Search interface or not (0 or 1)
- 'ln' *string* - The language to display
- 'recids' *list* - the list of records from the database
- 'more_link' *string* - the "More..." link for the record. If not given, will not be displayed
"""
# load the right message language
_ = gettext_set_language(ln)
body = '''<table class="latestadditionsbox">'''
for recid in recids:
body += '''
<tr>
<td class="latestadditionsboxtimebody">%(date)s</td>
<td class="latestadditionsboxrecordbody">%(body)s</td>
</tr>''' % {'date': recid['date'],
'body': recid['body']
}
body += "</table>"
if more_link:
body += '<div align="right"><small>' + \
create_html_link(more_link, {}, '[&gt;&gt; %s]' % _("more")) + \
'</small></div>'
return '''
<table class="narrowsearchbox">
<thead>
<tr>
<th class="narrowsearchboxheader">%(header)s</th>
</tr>
</thead>
<tbody>
<tr>
<td class="narrowsearchboxbody">%(body)s</td>
</tr>
</tbody>
</table>''' % {'header' : _("Latest additions:"),
'body' : body,
}
def tmpl_searchwithin_select(self, ln, fieldname, selected, values):
"""
Produces 'search within' selection box for the current collection.
Parameters:
- 'ln' *string* - The language to display
- 'fieldname' *string* - the name of the select box produced
- 'selected' *string* - which of the values is selected
- 'values' *list* - the list of values in the select
"""
out = '<select name="%(fieldname)s">' % {'fieldname': fieldname}
if values:
for pair in values:
out += """<option value="%(value)s"%(selected)s>%(text)s</option>""" % {
'value' : cgi.escape(pair['value']),
'selected' : self.tmpl_is_selected(pair['value'], selected),
'text' : cgi.escape(pair['text'])
}
out += """</select>"""
return out
def tmpl_select(self, fieldname, values, selected=None, css_class=''):
"""
Produces a generic select box
Parameters:
- 'css_class' *string* - optional, a css class to display this select with
- 'fieldname' *list* - the name of the select box produced
- 'selected' *string* - which of the values is selected
- 'values' *list* - the list of values in the select
"""
if css_class != '':
class_field = ' class="%s"' % css_class
else:
class_field = ''
out = '<select name="%(fieldname)s"%(class)s>' % {
'fieldname' : fieldname,
'class' : class_field
}
for pair in values:
if pair.get('selected', False) or pair['value'] == selected:
flag = ' selected="selected"'
else:
flag = ''
out += '<option value="%(value)s"%(selected)s>%(text)s</option>' % {
'value' : cgi.escape(str(pair['value'])),
'selected' : flag,
'text' : cgi.escape(pair['text'])
}
out += """</select>"""
return out
- def tmpl_record_links(self, weburl, recid, ln):
+ def tmpl_record_links(self, recid, ln):
"""
Displays the *More info* and *Find similar* links for a record
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'recid' *string* - the id of the displayed record
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''<br /><span class="moreinfo">%(detailed)s - %(similar)s</span>''' % {
'detailed': create_html_link(self.build_search_url(recid=recid, ln=ln),
{},
_("Detailed record"), {'class': "moreinfo"}),
'similar': create_html_link(self.build_search_url(p="recid:%d" % recid, rm='wrd', ln=ln),
{},
_("Similar records"),
{'class': "moreinfo"})}
if CFG_BIBRANK_SHOW_CITATION_LINKS:
out += '''<span class="moreinfo"> - %s </span>''' % \
create_html_link(self.build_search_url(p='recid:%d' % recid, rm='citation', ln=ln),
{}, _("Cited by"), {'class': "moreinfo"})
return out
- def tmpl_record_body(self, weburl, titles, authors, dates, rns, abstracts, urls_u, urls_z, ln):
+ def tmpl_record_body(self, titles, authors, dates, rns, abstracts, urls_u, urls_z, ln):
"""
Displays the "HTML basic" format of a record
Parameters:
- - 'weburl' *string* - The base URL for the site
-
- 'authors' *list* - the authors (as strings)
- 'dates' *list* - the dates of publication
- 'rns' *list* - the quicknotes for the record
- 'abstracts' *list* - the abstracts for the record
- 'urls_u' *list* - URLs to the original versions of the record
- 'urls_z' *list* - Not used
"""
out = ""
for title in titles:
out += "<strong>%(title)s</strong> " % {
'title' : cgi.escape(title)
}
if authors:
out += " / "
for author in authors[:CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD]:
out += '%s; ' % \
create_html_link(self.build_search_url(p=author, f='author', ln=ln),
{}, cgi.escape(author))
if len(authors) > CFG_WEBSEARCH_AUTHOR_ET_AL_THRESHOLD:
out += "<em>et al</em>"
for date in dates:
out += " %s." % cgi.escape(date)
for rn in rns:
out += """ <small class="quicknote">[%(rn)s]</small>""" % {'rn' : cgi.escape(rn)}
for abstract in abstracts:
out += "<br /><small>%(abstract)s [...]</small>" % {'abstract' : cgi.escape(abstract[:1+string.find(abstract, '.')]) }
for idx in range(0, len(urls_u)):
out += """<br /><small class="note"><a class="note" href="%(url)s">%(name)s</a></small>""" % {
'url' : urls_u[idx],
'name' : urls_u[idx]
}
return out
def tmpl_search_in_bibwords(self, p, f, ln, nearest_box):
"""
Displays the *Words like current ones* links for a search
Parameters:
- 'p' *string* - Current search words
- 'f' *string* - the fields in which the search was done
- 'nearest_box' *string* - the HTML code for the "nearest_terms" box - most probably from a create_nearest_terms_box call
"""
# load the right message language
_ = gettext_set_language(ln)
out = '<p>'
if f:
out += _("Words nearest to %(x_word)s inside %(x_field)s in any collection are:") % {'x_word': '<em>' + cgi.escape(p) + '</em>',
'x_field': '<em>' + cgi.escape(f) + '</em>'}
else:
out += _("Words nearest to %(x_word)s in any collection are:") % {'x_word': '<em>' + cgi.escape(p) + '</em>'}
out += '<br />' + nearest_box + '</p>'
return out
def tmpl_nearest_term_box(self, p, ln, f, terminfo, intro):
"""
Displays the *Nearest search terms* box
Parameters:
- 'p' *string* - Current search words
- 'f' *string* - a collection description (if the search has been completed in a collection)
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'terminfo': tuple (term, hits, argd) for each near term
- 'intro' *string* - the intro HTML to prefix the box with
"""
out = '''<table class="nearesttermsbox" cellpadding="0" cellspacing="0" border="0">'''
for term, hits, argd in terminfo:
if hits:
hitsinfo = str(hits)
else:
hitsinfo = '-'
term = cgi.escape(term)
if term == p: # print search word for orientation:
nearesttermsboxbody_class = "nearesttermsboxbodyselected"
if hits > 0:
term = create_html_link(self.build_search_url(argd), {},
term, {'class': "nearesttermsselected"})
else:
nearesttermsboxbody_class = "nearesttermsboxbody"
term = create_html_link(self.build_search_url(argd), {},
term, {'class': "nearestterms"})
out += '''\
<tr>
<td class="%(nearesttermsboxbody_class)s" align="right">%(hits)s</td>
<td class="%(nearesttermsboxbody_class)s" width="15">&nbsp;</td>
<td class="%(nearesttermsboxbody_class)s" align="left">%(term)s</td>
</tr>
''' % {'hits': hitsinfo,
'nearesttermsboxbody_class': nearesttermsboxbody_class,
'term': term}
out += "</table>"
return intro + "<blockquote>" + out + "</blockquote>"
def tmpl_browse_pattern(self, f, fn, ln, browsed_phrases_in_colls, colls):
"""
Displays the *Nearest search terms* box
Parameters:
- 'f' *string* - field (*not* i18nized)
- 'fn' *string* - field name (i18nized)
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'browsed_phrases_in_colls' *array* - the phrases to display
- 'colls' *array* - the list of collection parameters of the search (c's)
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<table class="searchresultsbox">
<thead>
<tr>
<th class="searchresultsboxheader" style="text-align: right;">
%(hits)s
</th>
<th class="searchresultsboxheader" width="15">
&nbsp;
</th>
<th class="searchresultsboxheader" style="text-align: left;">
%(fn)s
</th>
</tr>
</thead>
<tbody>""" % {
'hits' : _("Hits"),
'fn' : cgi.escape(fn)
}
if len(browsed_phrases_in_colls) == 1:
# one hit only found:
phrase, nbhits = browsed_phrases_in_colls[0][0], browsed_phrases_in_colls[0][1]
query = {'c': colls,
'ln': ln,
'p': phrase,
'f': f}
out += """<tr>
<td class="searchresultsboxbody" style="text-align: right;">
%(nbhits)s
</td>
<td class="searchresultsboxbody" width="15">
&nbsp;
</td>
<td class="searchresultsboxbody" style="text-align: left;">
%(link)s
</td>
</tr>""" % {'nbhits': nbhits,
'link': create_html_link(self.build_search_url(query),
{}, cgi.escape(phrase))}
elif len(browsed_phrases_in_colls) > 1:
# first display what was found but the last one:
for phrase, nbhits in browsed_phrases_in_colls[:-1]:
query = {'c': colls,
'ln': ln,
'p': phrase,
'f': f}
out += """<tr>
<td class="searchresultsboxbody" style="text-align: right;">
%(nbhits)s
</td>
<td class="searchresultsboxbody" width="15">
&nbsp;
</td>
<td class="searchresultsboxbody" style="text-align: left;">
%(link)s
</td>
</tr>""" % {'nbhits' : nbhits,
'link': create_html_link(self.build_search_url(query),
{},
cgi.escape(phrase))}
# now display last hit as "next term":
phrase, nbhits = browsed_phrases_in_colls[-1]
query = {'c': colls,
'ln': ln,
'p': phrase,
'f': f}
out += """<tr><td colspan="2" class="normal">
&nbsp;
</td>
<td class="normal">
- <img src="%(weburl)s/img/sn.gif" alt="" border="0" />
+ <img src="%(siteurl)s/img/sn.gif" alt="" border="0" />
%(link)s
</td>
</tr>""" % {'link': create_html_link(self.build_search_url(query, action='browse'),
{}, _("next")),
- 'weburl' : weburl}
+ 'siteurl' : CFG_SITE_URL}
out += """</tbody>
</table>"""
return out
def tmpl_search_box(self, ln, as, cc, cc_intl, ot, sp,
action, fieldslist, f1, f2, f3, m1, m2, m3,
p1, p2, p3, op1, op2, rm, p, f, coll_selects,
d1y, d2y, d1m, d2m, d1d, d2d, dt, sort_fields,
sf, so, ranks, sc, rg, formats, of, pl, jrec, ec):
"""
Displays the *Nearest search terms* box
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'as' *bool* - Should we display an advanced search box?
- 'cc_intl' *string* - the i18nized current collection name
- 'cc' *string* - the internal current collection name
- 'ot', 'sp' *string* - hidden values
- 'action' *string* - the action demanded by the user
- 'fieldslist' *list* - the list of all fields available, for use in select within boxes in advanced search
- 'p, f, f1, f2, f3, m1, m2, m3, p1, p2, p3, op1, op2, op3, rm' *strings* - the search parameters
- 'coll_selects' *array* - a list of lists, each containing the collections selects to display
- 'd1y, d2y, d1m, d2m, d1d, d2d' *int* - the search between dates
- 'dt' *string* - the dates' types (creation dates, modification dates)
- 'sort_fields' *array* - the select information for the sort fields
- 'sf' *string* - the currently selected sort field
- 'so' *string* - the currently selected sort order ("a" or "d")
- 'ranks' *array* - ranking methods
- 'rm' *string* - selected ranking method
- 'sc' *string* - split by collection or not
- 'rg' *string* - selected results/page
- 'formats' *array* - available output formats
- 'of' *string* - the selected output format
- 'pl' *string* - `limit to' search pattern
"""
# load the right message language
_ = gettext_set_language(ln)
# These are hidden fields the user does not manipulate
# directly
argd = drop_default_urlargd({
'ln': ln, 'as': as,
'cc': cc, 'ot': ot, 'sp': sp, 'ec': ec,
}, self.search_results_default_urlargd)
out = '''
<h1 class="headline">%(ccname)s</h1>
- <form name="search" action="%(weburl)s/search" method="get">
+ <form name="search" action="%(siteurl)s/search" method="get">
''' % {'ccname' : cgi.escape(cc_intl),
- 'weburl' : weburl}
+ 'siteurl' : CFG_SITE_URL}
# Only add non-default hidden values
for field, value in argd.items():
out += self.tmpl_input_hidden(field, value)
leadingtext = _("Search")
if action == 'browse':
leadingtext = _("Browse")
if as == 1:
# print Advanced Search form:
# define search box elements:
out += '''
<table class="searchbox">
<thead>
<tr>
<th colspan="3" class="searchboxheader">
%(leading)s:
</th>
</tr>
</thead>
<tbody>
<tr valign="top" style="white-space:nowrap;">
<td class="searchboxbody">%(matchbox1)s
<input type="text" name="p1" size="%(sizepattern)d" value="%(p1)s" />
</td>
<td class="searchboxbody">%(searchwithin1)s</td>
<td class="searchboxbody">%(andornot1)s</td>
</tr>
<tr valign="top">
<td class="searchboxbody">%(matchbox2)s
<input type="text" name="p2" size="%(sizepattern)d" value="%(p2)s" />
</td>
<td class="searchboxbody">%(searchwithin2)s</td>
<td class="searchboxbody">%(andornot2)s</td>
</tr>
<tr valign="top">
<td class="searchboxbody">%(matchbox3)s
<input type="text" name="p3" size="%(sizepattern)d" value="%(p3)s" />
</td>
<td class="searchboxbody">%(searchwithin3)s</td>
<td class="searchboxbody" style="white-space:nowrap;">
<input class="formbutton" type="submit" name="action_search" value="%(search)s" />
<input class="formbutton" type="submit" name="action_browse" value="%(browse)s" />&nbsp;
</td>
</tr>
<tr valign="bottom">
<td colspan="3" align="right" class="searchboxbody">
<small>
- <a href="%(weburl)s/help/search-tips%(langlink)s">%(search_tips)s</a> ::
+ <a href="%(siteurl)s/help/search-tips%(langlink)s">%(search_tips)s</a> ::
%(simple_search)s
</small>
</td>
</tr>
</tbody>
</table>
''' % {
'simple_search': create_html_link(self.build_search_url(p=p1, f=f1, rm=rm, cc=cc, ln=ln, jrec=jrec, rg=rg),
{}, _("Simple Search")),
'leading' : leadingtext,
'sizepattern' : CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH,
'matchbox1' : self.tmpl_matchtype_box('m1', m1, ln=ln),
'p1' : cgi.escape(p1,1),
'searchwithin1' : self.tmpl_searchwithin_select(
ln = ln,
fieldname = 'f1',
selected = f1,
values = self._add_mark_to_field(value = f1, fields = fieldslist, ln = ln)
),
'andornot1' : self.tmpl_andornot_box(
name = 'op1',
value = op1,
ln = ln
),
'matchbox2' : self.tmpl_matchtype_box('m2', m2, ln=ln),
'p2' : cgi.escape(p2,1),
'searchwithin2' : self.tmpl_searchwithin_select(
ln = ln,
fieldname = 'f2',
selected = f2,
values = self._add_mark_to_field(value = f2, fields = fieldslist, ln = ln)
),
'andornot2' : self.tmpl_andornot_box(
name = 'op2',
value = op2,
ln = ln
),
'matchbox3' : self.tmpl_matchtype_box('m3', m3, ln=ln),
'p3' : cgi.escape(p3,1),
'searchwithin3' : self.tmpl_searchwithin_select(
ln = ln,
fieldname = 'f3',
selected = f3,
values = self._add_mark_to_field(value = f3, fields = fieldslist, ln = ln)
),
'search' : _("Search"),
'browse' : _("Browse"),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'search_tips': _("Search Tips")
}
else:
# print Simple Search form:
out += '''
<table class="searchbox">
<thead>
<tr>
<th colspan="3" class="searchboxheader">
%(leading)s:
</th>
</tr>
</thead>
<tbody>
<tr valign="top">
<td class="searchboxbody"><input type="text" name="p" size="%(sizepattern)d" value="%(p)s" /></td>
<td class="searchboxbody">%(searchwithin)s</td>
<td class="searchboxbody">
<input class="formbutton" type="submit" name="action_search" value="%(search)s" />
<input class="formbutton" type="submit" name="action_browse" value="%(browse)s" />&nbsp;
</td>
</tr>
<tr valign="bottom">
<td colspan="3" align="right" class="searchboxbody">
<small>
- <a href="%(weburl)s/help/search-tips%(langlink)s">%(search_tips)s</a> ::
+ <a href="%(siteurl)s/help/search-tips%(langlink)s">%(search_tips)s</a> ::
%(advanced_search)s
</small>
</td>
</tr>
</tbody>
</table>
''' % {
'advanced_search': create_html_link(self.build_search_url(p1=p,
f1=f,
rm=rm,
as=1,
cc=cc,
jrec=jrec,
ln=ln,
rg=rg),
{}, _("Advanced Search")),
'leading' : leadingtext,
'sizepattern' : CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH,
'p' : cgi.escape(p, 1),
'searchwithin' : self.tmpl_searchwithin_select(
ln = ln,
fieldname = 'f',
selected = f,
values = self._add_mark_to_field(value=f, fields=fieldslist, ln=ln)
),
'search' : _("Search"),
'browse' : _("Browse"),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'search_tips': _("Search Tips")
}
## secondly, print Collection(s) box:
selects = ''
for sel in coll_selects:
selects += self.tmpl_select(fieldname='c', values=sel)
out += """
<table class="searchbox">
<thead>
<tr>
<th colspan="3" class="searchboxheader">
%(leading)s %(msg_coll)s:
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td valign="top" class="searchboxbody">
%(colls)s
</td>
</tr>
</tbody>
</table>
""" % {
'leading' : leadingtext,
'msg_coll' : _("collections"),
'colls' : selects,
}
## thirdly, print search limits, if applicable:
if action != _("Browse") and pl:
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(limitto)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">
<input type="text" name="pl" size="%(sizepattern)d" value="%(pl)s" />
</td>
</tr>
</tbody>
</table>""" % {
'limitto' : _("Limit to:"),
'sizepattern' : CFG_WEBSEARCH_ADVANCEDSEARCH_PATTERN_BOX_WIDTH,
'pl' : cgi.escape(pl, 1),
}
## fourthly, print from/until date boxen, if applicable:
if action == _("Browse") or (d1y==0 and d1m==0 and d1d==0 and d2y==0 and d2m==0 and d2d==0):
pass # do not need it
else:
cell_6_a = self.tmpl_inputdatetype(dt, ln) + self.tmpl_inputdate("d1", ln, d1y, d1m, d1d)
cell_6_b = self.tmpl_inputdate("d2", ln, d2y, d2m, d2d)
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(added)s
</th>
<th class="searchboxheader">
%(until)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td class="searchboxbody">%(added_or_modified)s %(date1)s</td>
<td class="searchboxbody">%(date2)s</td>
</tr>
</tbody>
</table>""" % {
'added' : _("Added/modified since:"),
'until' : _("until:"),
'added_or_modified': self.tmpl_inputdatetype(dt, ln),
'date1' : self.tmpl_inputdate("d1", ln, d1y, d1m, d1d),
'date2' : self.tmpl_inputdate("d2", ln, d2y, d2m, d2d),
}
## fifthly, print Display results box, including sort/rank, formats, etc:
if action != _("Browse"):
rgs = []
for i in [10, 25, 50, 100, 250, 500]:
rgs.append({ 'value' : i, 'text' : "%d %s" % (i, _("results"))})
# sort by:
out += """<table class="searchbox">
<thead>
<tr>
<th class="searchboxheader">
%(sort_by)s
</th>
<th class="searchboxheader">
%(display_res)s
</th>
<th class="searchboxheader">
%(out_format)s
</th>
</tr>
</thead>
<tbody>
<tr valign="bottom">
<td valign="top" class="searchboxbody">
%(select_sf)s %(select_so)s %(select_rm)s
</td>
<td valign="top" class="searchboxbody">
%(select_rg)s %(select_sc)s
</td>
<td valign="top" class="searchboxbody">%(select_of)s</td>
</tr>
</tbody>
</table>""" % {
'sort_by' : _("Sort:"),
'display_res' : _("Display results:"),
'out_format' : _("Output format:"),
'select_sf' : self.tmpl_select(fieldname = 'sf', values = sort_fields, selected = sf, css_class = 'address'),
'select_so' : self.tmpl_select(fieldname = 'so', values = [{
'value' : 'a',
'text' : _("asc.")
}, {
'value' : 'd',
'text' : _("desc.")
}], selected = so, css_class = 'address'),
'select_rm' : self.tmpl_select(fieldname = 'rm', values = ranks, selected = rm, css_class = 'address'),
'select_rg' : self.tmpl_select(fieldname = 'rg', values = rgs, selected = rg, css_class = 'address'),
'select_sc' : self.tmpl_select(fieldname = 'sc', values = [{
'value' : 0,
'text' : _("single list")
}, {
'value' : 1,
'text' : _("split by collection")
}], selected = sc, css_class = 'address'),
'select_of' : self.tmpl_searchwithin_select(
ln = ln,
fieldname = 'of',
selected = of,
values = self._add_mark_to_field(value = of, fields = formats, chars = 3, ln = ln)
),
}
## last but not least, print end of search box:
out += """</form>"""
return out
def tmpl_input_hidden(self, name, value):
"Produces the HTML code for a hidden field "
if isinstance(value, list):
list_input = [self.tmpl_input_hidden(name, val) for val in value]
return "\n".join(list_input)
return """<input type="hidden" name="%(name)s" value="%(value)s" />""" % {
'name' : cgi.escape(str(name), 1),
'value' : cgi.escape(str(value), 1),
}
def _add_mark_to_field(self, value, fields, ln, chars = 1):
"""Adds the current value as a MARC tag in the fields array
Useful for advanced search"""
# load the right message language
_ = gettext_set_language(ln)
out = fields
if value and str(value[0:chars]).isdigit():
out.append({'value' : value,
'text' : str(value) + " " + _("MARC tag")
})
return out
def tmpl_search_pagestart(self, ln) :
"page start for search page. Will display after the page header"
return """<div class="pagebody"><div class="pagebodystripemiddle">"""
def tmpl_search_pageend(self, ln) :
"page end for search page. Will display just before the page footer"
return """</div></div>"""
def tmpl_print_warning(self, msg, type, prologue, epilogue):
"""Prints warning message and flushes output.
Parameters:
- 'msg' *string* - The message string
- 'type' *string* - the warning type
- 'prologue' *string* - HTML code to display before the warning
- 'epilogue' *string* - HTML code to display after the warning
"""
out = '\n%s<span class="quicknote">' % (prologue)
if type:
out += '%s: ' % type
out += '%s</span>%s' % (msg, epilogue)
return out
- def tmpl_print_search_info(self, ln, weburl, middle_only,
+ def tmpl_print_search_info(self, ln, middle_only,
collection, collection_name, collection_id,
as, sf, so, rm, rg, nb_found, of, ot, p, f, f1,
f2, f3, m1, m2, m3, op1, op2, p1, p2,
p3, d1y, d1m, d1d, d2y, d2m, d2d, dt,
all_fieldcodes, cpu_time, pl_in_url,
jrec, sc, sp):
"""Prints stripe with the information on 'collection' and 'nb_found' results and CPU time.
Also, prints navigation links (beg/next/prev/end) inside the results set.
If middle_only is set to 1, it will only print the middle box information (beg/netx/prev/end/etc) links.
This is suitable for displaying navigation links at the bottom of the search results page.
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'middle_only' *bool* - Only display parts of the interface
- 'collection' *string* - the collection name
- 'collection_name' *string* - the i18nized current collection name
- 'as' *bool* - if we display the advanced search interface
- 'sf' *string* - the currently selected sort format
- 'so' *string* - the currently selected sort order ("a" or "d")
- 'rm' *string* - selected ranking method
- 'rg' *int* - selected results/page
- 'nb_found' *int* - number of results found
- 'of' *string* - the selected output format
- 'ot' *string* - hidden values
- 'p' *string* - Current search words
- 'f' *string* - the fields in which the search was done
- 'f1, f2, f3, m1, m2, m3, p1, p2, p3, op1, op2' *strings* - the search parameters
- 'jrec' *int* - number of first record on this page
- 'd1y, d2y, d1m, d2m, d1d, d2d' *int* - the search between dates
- 'dt' *string* the dates' type (creation date, modification date)
- 'all_fieldcodes' *array* - all the available fields
- 'cpu_time' *float* - the time of the query in seconds
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
# left table cells: print collection name
if not middle_only:
out += '''
<a name="%(collection_id)s"></a>
- <form action="%(weburl)s/search" method="get">
+ <form action="%(siteurl)s/search" method="get">
<table class="searchresultsbox"><tr><td class="searchresultsboxheader" align="left">
<strong><big>%(collection_link)s</big></strong></td>
''' % {
'collection_id': collection_id,
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'collection_link': create_html_link(self.build_search_interface_url(c=collection, as=as, ln=ln),
{}, cgi.escape(collection_name))
}
else:
out += """
- <form action="%(weburl)s/search" method="get"><div align="center">
- """ % { 'weburl' : weburl }
+ <form action="%(siteurl)s/search" method="get"><div align="center">
+ """ % { 'siteurl' : CFG_SITE_URL }
# middle table cell: print beg/next/prev/end arrows:
if not middle_only:
out += """<td class="searchresultsboxheader" align="center">
%(recs_found)s &nbsp;""" % {
'recs_found' : _("%s records found") % ('<strong>' + self.tmpl_nice_number(nb_found, ln) + '</strong>')
}
else:
out += "<small>"
if nb_found > rg:
out += "" + cgi.escape(collection_name) + " : " + _("%s records found") % ('<strong>' + self.tmpl_nice_number(nb_found, ln) + '</strong>') + " &nbsp; "
if nb_found > rg: # navig.arrows are needed, since we have many hits
query = {'p': p, 'f': f,
'cc': collection,
'sf': sf, 'so': so,
'sp': sp, 'rm': rm,
'of': of, 'ot': ot,
'as': as, 'ln': ln,
'p1': p1, 'p2': p2, 'p3': p3,
'f1': f1, 'f2': f2, 'f3': f3,
'm1': m1, 'm2': m2, 'm3': m3,
'op1': op1, 'op2': op2,
'sc': 0,
'd1y': d1y, 'd1m': d1m, 'd1d': d1d,
'd2y': d2y, 'd2m': d2m, 'd2d': d2d,
'dt': dt,
}
# @todo here
def img(gif, txt):
- return '<img src="%(weburl)s/img/%(gif)s.gif" alt="%(txt)s" border="0" />' % {
- 'txt': txt, 'gif': gif, 'weburl': weburl}
+ return '<img src="%(siteurl)s/img/%(gif)s.gif" alt="%(txt)s" border="0" />' % {
+ 'txt': txt, 'gif': gif, 'siteurl': CFG_SITE_URL}
if jrec-rg > 1:
out += create_html_link(self.build_search_url(query, jrec=1, rg=rg),
{}, img('sb', _("begin")),
{'class': 'img'})
if jrec > 1:
out += create_html_link(self.build_search_url(query, jrec=max(jrec-rg, 1), rg=rg),
{}, img('sp', _("previous")),
{'class': 'img'})
if jrec+rg-1 < nb_found:
out += "%d - %d" % (jrec, jrec+rg-1)
else:
out += "%d - %d" % (jrec, nb_found)
if nb_found >= jrec+rg:
out += create_html_link(self.build_search_url(query,
jrec=jrec+rg,
rg=rg),
{}, img('sn', _("next")),
{'class':'img'})
if nb_found >= jrec+rg+rg:
out += create_html_link(self.build_search_url(query,
jrec=nb_found-rg+1,
rg=rg),
{}, img('se', _("end")),
{'class': 'img'})
# still in the navigation part
cc = collection
sc = 0
for var in ['p', 'cc', 'f', 'sf', 'so', 'of', 'rg', 'as', 'ln', 'p1', 'p2', 'p3', 'f1', 'f2', 'f3', 'm1', 'm2', 'm3', 'op1', 'op2', 'sc', 'd1y', 'd1m', 'd1d', 'd2y', 'd2m', 'd2d', 'dt']:
out += self.tmpl_input_hidden(name = var, value = vars()[var])
for var in ['ot', 'sp', 'rm']:
if vars()[var]:
out += self.tmpl_input_hidden(name = var, value = vars()[var])
if pl_in_url:
fieldargs = cgi.parse_qs(pl_in_url)
for fieldcode in all_fieldcodes:
# get_fieldcodes():
if fieldargs.has_key(fieldcode):
for val in fieldargs[fieldcode]:
out += self.tmpl_input_hidden(name = fieldcode, value = val)
out += """&nbsp; %(jump)s <input type="text" name="jrec" size="4" value="%(jrec)d" />""" % {
'jump' : _("jump to record:"),
'jrec' : jrec,
}
if not middle_only:
out += "</td>"
else:
out += "</small>"
# right table cell: cpu time info
if not middle_only:
if cpu_time > -1:
out += """<td class="searchresultsboxheader" align="right"><small>%(time)s</small>&nbsp;</td>""" % {
'time' : _("Search took %s seconds.") % ('%.2f' % cpu_time),
}
out += "</tr></table>"
else:
out += "</div>"
out += "</form>"
return out
def tmpl_nice_number(self, number, ln=CFG_SITE_LANG, thousands_separator=',', max_ndigits_after_dot=None):
"""
Return nicely printed number NUMBER in language LN using
given THOUSANDS_SEPARATOR character.
If max_ndigits_after_dot is specified and the number is float, the
number is rounded by taking in consideration up to max_ndigits_after_dot
digit after the dot.
This version does not pay attention to locale. See
tmpl_nice_number_via_locale().
"""
if type(number) is float:
if max_ndigits_after_dot is not None:
number = round(number, max_ndigits_after_dot)
int_part, frac_part = str(number).split('.')
return '%s.%s' % (self.tmpl_nice_number(int(int_part), ln, thousands_separator), frac_part)
else:
chars_in = list(str(number))
number = len(chars_in)
chars_out = []
for i in range(0, number):
if i % 3 == 0 and i != 0:
chars_out.append(thousands_separator)
chars_out.append(chars_in[number-i-1])
chars_out.reverse()
return ''.join(chars_out)
def tmpl_nice_number_via_locale(self, number, ln=CFG_SITE_LANG):
"""
Return nicely printed number NUM in language LN using the locale.
See also version tmpl_nice_number().
"""
if number is None:
return None
# Temporarily switch the numeric locale to the requested one, and format the number
# In case the system has no locale definition, use the vanilla form
ol = locale.getlocale(locale.LC_NUMERIC)
try:
locale.setlocale(locale.LC_NUMERIC, self.tmpl_localemap.get(ln, self.tmpl_default_locale))
except locale.Error:
return str(number)
try:
number = locale.format('%d', number, True)
except TypeError:
return str(number)
locale.setlocale(locale.LC_NUMERIC, ol)
return number
def tmpl_record_format_htmlbrief_header(self, ln):
"""Returns the header of the search results list when output
is html brief. Note that this function is called for each collection
results when 'split by collection' is enabled.
See also: tmpl_record_format_htmlbrief_footer(..),
tmpl_record_format_htmlbrief_body(..)
Parameters:
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
- <form action="%(weburl)s/yourbaskets/add" method="post">
+ <form action="%(siteurl)s/yourbaskets/add" method="post">
<table>
""" % {
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
}
return out
def tmpl_record_format_htmlbrief_footer(self, ln):
"""Returns the footer of the search results list when output
is html brief. Note that this function is called for each collection
results when 'split by collection' is enabled.
See also: tmpl_record_format_htmlbrief_header(..),
tmpl_record_format_htmlbrief_body(..)
Parameters:
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = """</table>
<br /><input class="formbutton" type="submit" name="action" value="%(basket)s" />
</form>""" % {
'basket' : _("ADD TO BASKET")
}
return out
def tmpl_record_format_htmlbrief_body(self, ln, recid,
row_number, relevance,
record, relevances_prologue,
relevances_epilogue):
"""Returns the html brief format of one record. Used in the
search results list for each record.
See also: tmpl_record_format_htmlbrief_header(..),
tmpl_record_format_htmlbrief_footer(..)
Parameters:
- 'ln' *string* - The language to display
- 'row_number' *int* - The position of this record in the list
- 'recid' *int* - The recID
- 'relevance' *string* - The relevance of the record
- 'record' *string* - The formatted record
- 'relevances_prologue' *string* - HTML code to prepend the relevance indicator
- 'relevances_epilogue' *string* - HTML code to append to the relevance indicator (used mostly for formatting)
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<tr><td valign="top" align="right" style="white-space: nowrap;">
<input name="recid" type="checkbox" value="%(recid)s" />
%(number)s.
""" % {'recid': recid,
'number': row_number}
if relevance:
out += """<br /><div class="rankscoreinfo"><a title="rank score">%(prologue)s%(relevance)s%(epilogue)s</a></div>""" % {
'prologue' : relevances_prologue,
'epilogue' : relevances_epilogue,
'relevance' : relevance
}
out += """</td><td valign="top">%s</td></tr>""" % record
return out
- def tmpl_print_results_overview(self, ln, weburl, results_final_nb_total, cpu_time, results_final_nb, colls, ec):
+ def tmpl_print_results_overview(self, ln, results_final_nb_total, cpu_time, results_final_nb, colls, ec):
"""Prints results overview box with links to particular collections below.
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'results_final_nb_total' *int* - The total number of hits for the query
- 'colls' *array* - The collections with hits, in the format:
- 'coll[code]' *string* - The code of the collection (canonical name)
- 'coll[name]' *string* - The display name of the collection
- 'results_final_nb' *array* - The number of hits, indexed by the collection codes:
- 'cpu_time' *string* - The time the query took
- 'url_args' *string* - The rest of the search query
- 'ec' *array* - selected external collections
"""
if len(colls) == 1 and not ec:
# if one collection only and no external collections, print nothing:
return ""
# load the right message language
_ = gettext_set_language(ln)
# first find total number of hits:
out = """<table class="searchresultsbox">
<thead><tr><th class="searchresultsboxheader">%(founds)s</th></tr></thead>
<tbody><tr><td class="searchresultsboxbody"> """ % {
'founds' : _("%(x_fmt_open)sResults overview:%(x_fmt_close)s Found %(x_nb_records)s records in %(x_nb_seconds)s seconds.") %\
{'x_fmt_open': '<strong>',
'x_fmt_close': '</strong>',
'x_nb_records': '<strong>' + self.tmpl_nice_number(results_final_nb_total, ln) + '</strong>',
'x_nb_seconds': '%.2f' % cpu_time}
}
# then print hits per collection:
for coll in colls:
if results_final_nb.has_key(coll['code']) and results_final_nb[coll['code']] > 0:
out += '''<strong><a href="#%(coll)s">%(coll_name)s</a></strong>,
<a href="#%(coll)s">%(number)s</a><br />''' % {
'coll' : coll['id'],
'coll_name' : cgi.escape(coll['name']),
'number' : _("%s records found") % ('<strong>' + self.tmpl_nice_number(results_final_nb[coll['code']], ln) + '</strong>')
}
out += "</td></tr></tbody></table>"
return out
def tmpl_search_no_boolean_hits(self, ln, nearestterms):
"""No hits found, proposes alternative boolean queries
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'nearestterms' *array* - Parts of the interface to display, in the format:
- 'nearestterms[nbhits]' *int* - The resulting number of hits
- 'nearestterms[url_args]' *string* - The search parameters
- 'nearestterms[p]' *string* - The search terms
"""
# load the right message language
_ = gettext_set_language(ln)
out = _("Boolean query returned no hits. Please combine your search terms differently.")
out += '''<blockquote><table class="nearesttermsbox" cellpadding="0" cellspacing="0" border="0">'''
for term, hits, argd in nearestterms:
out += '''\
<tr>
<td class="nearesttermsboxbody" align="right">%(hits)s</td>
<td class="nearesttermsboxbody" width="15">&nbsp;</td>
<td class="nearesttermsboxbody" align="left">
%(link)s
</td>
</tr>''' % {'hits' : hits,
'link': create_html_link(self.build_search_url(argd),
{}, cgi.escape(term),
{'class': "nearestterms"})}
out += """</table></blockquote>"""
return out
def tmpl_similar_author_names(self, authors, ln):
"""No hits found, proposes alternative boolean queries
Parameters:
- 'authors': a list of (name, hits) tuples
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
out = '''<a name="googlebox"></a>
<table class="googlebox"><tr><th colspan="2" class="googleboxheader">%(similar)s</th></tr>''' % {
'similar' : _("See also: similar author names")
}
for author, hits in authors:
out += '''\
<tr>
<td class="googleboxbody">%(nb)d</td>
<td class="googleboxbody">%(link)s</td>
</tr>''' % {'link': create_html_link(
self.build_search_url(p=author,
f='author',
ln=ln),
{}, cgi.escape(author), {'class':"google"}),
'nb' : hits}
out += """</table>"""
return out
- def tmpl_print_record_detailed(self, recID, ln, weburl):
+ def tmpl_print_record_detailed(self, recID, ln):
"""Displays a detailed on-the-fly record
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'recID' *int* - The record id
"""
# okay, need to construct a simple "Detailed record" format of our own:
out = "<p>&nbsp;"
# secondly, title:
titles = get_fieldvalues(recID, "245__a")
for title in titles:
out += "<p><center><big><strong>%s</strong></big></center></p>" % cgi.escape(title)
# thirdly, authors:
authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")
if authors:
out += "<p><center>"
for author in authors:
out += '%s; ' % create_html_link(self.build_search_url(
ln=ln,
p=author,
f='author'),
{}, cgi.escape(author))
out += "</center></p>"
# fourthly, date of creation:
dates = get_fieldvalues(recID, "260__c")
for date in dates:
out += "<p><center><small>%s</small></center></p>" % date
# fifthly, abstract:
abstracts = get_fieldvalues(recID, "520__a")
for abstract in abstracts:
out += """<p style="margin-left: 15%%; width: 70%%">
<small><strong>Abstract:</strong> %s</small></p>""" % abstract
# fifthly bis, keywords:
keywords = get_fieldvalues(recID, "6531_a")
if len(keywords):
out += """<p style="margin-left: 15%%; width: 70%%">
<small><strong>Keyword(s):</strong>"""
for keyword in keywords:
out += '%s; ' % create_html_link(
self.build_search_url(ln=ln,
p=keyword,
f='keyword'),
{}, cgi.escape(keyword))
out += '</small></p>'
# fifthly bis bis, published in:
prs_p = get_fieldvalues(recID, "909C4p")
prs_v = get_fieldvalues(recID, "909C4v")
prs_y = get_fieldvalues(recID, "909C4y")
prs_n = get_fieldvalues(recID, "909C4n")
prs_c = get_fieldvalues(recID, "909C4c")
for idx in range(0, len(prs_p)):
out += """<p style="margin-left: 15%%; width: 70%%">
<small><strong>Publ. in:</strong> %s""" % prs_p[idx]
if prs_v and prs_v[idx]:
out += """<strong>%s</strong>""" % prs_v[idx]
if prs_y and prs_y[idx]:
out += """(%s)""" % prs_y[idx]
if prs_n and prs_n[idx]:
out += """, no.%s""" % prs_n[idx]
if prs_c and prs_c[idx]:
out += """, p.%s""" % prs_c[idx]
out += """.</small></p>"""
# sixthly, fulltext link:
urls_z = get_fieldvalues(recID, "8564_z")
urls_u = get_fieldvalues(recID, "8564_u")
for idx in range(0, len(urls_u)):
link_text = "URL"
try:
if urls_z[idx]:
link_text = urls_z[idx]
except IndexError:
pass
out += """<p style="margin-left: 15%%; width: 70%%">
<small><strong>%s:</strong> <a href="%s">%s</a></small></p>""" % (link_text, urls_u[idx], urls_u[idx])
# print some white space at the end:
out += "<br /><br />"
return out
def tmpl_print_record_list_for_similarity_boxen(self, title, recID_score_list, ln=CFG_SITE_LANG):
"""Print list of records in the "hs" (HTML Similarity) format for similarity boxes.
RECID_SCORE_LIST is a list of (recID1, score1), (recID2, score2), etc.
"""
from invenio.search_engine import print_record, record_public_p
recID_score_list_to_be_printed = []
# firstly find 5 first public records to print:
nb_records_to_be_printed = 0
nb_records_seen = 0
while nb_records_to_be_printed < 5 and nb_records_seen < len(recID_score_list) and nb_records_seen < 50:
# looking through first 50 records only, picking first 5 public ones
(recID, score) = recID_score_list[nb_records_seen]
nb_records_seen += 1
if record_public_p(recID):
nb_records_to_be_printed += 1
recID_score_list_to_be_printed.append([recID, score])
# secondly print them:
out = '''
<table><tr>
<td>
<table><tr><td class="blocknote">%(title)s</td></tr></table>
</td>
</tr>
<tr>
<td><table>
''' % { 'title': cgi.escape(title) }
for recid, score in recID_score_list_to_be_printed:
out += '''
<tr><td><font class="rankscoreinfo"><a>(%(score)s)&nbsp;</a></font><small>&nbsp;%(info)s</small></td></tr>''' % {
'score': score,
'info' : print_record(recid, format="hs", ln=ln),
}
out += """</table></td></tr></table> """
return out
- def tmpl_print_record_brief(self, ln, recID, weburl):
+ def tmpl_print_record_brief(self, ln, recID):
"""Displays a brief record on-the-fly
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'recID' *int* - The record id
"""
out = ""
# record 'recID' does not exist in format 'format', so print some default format:
# firstly, title:
titles = get_fieldvalues(recID, "245__a")
# secondly, authors:
authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")
# thirdly, date of creation:
dates = get_fieldvalues(recID, "260__c")
# thirdly bis, report numbers:
rns = get_fieldvalues(recID, "037__a")
rns = get_fieldvalues(recID, "088__a")
# fourthly, beginning of abstract:
abstracts = get_fieldvalues(recID, "520__a")
# fifthly, fulltext link:
urls_z = get_fieldvalues(recID, "8564_z")
urls_u = get_fieldvalues(recID, "8564_u")
return self.tmpl_record_body(
- weburl = weburl,
titles = titles,
authors = authors,
dates = dates,
rns = rns,
abstracts = abstracts,
urls_u = urls_u,
urls_z = urls_z,
ln=ln)
- def tmpl_print_record_brief_links(self, ln, recID, weburl):
+ def tmpl_print_record_brief_links(self, ln, recID):
"""Displays links for brief record on-the-fly
Parameters:
- 'ln' *string* - The language to display
- - 'weburl' *string* - The base URL for the site
-
- 'recID' *int* - The record id
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if CFG_WEBSEARCH_USE_ALEPH_SYSNOS:
alephsysnos = get_fieldvalues(recID, "970__a")
if len(alephsysnos)>0:
alephsysno = alephsysnos[0]
out += '<br /><span class="moreinfo">%s</span>' % \
create_html_link(self.build_search_url(sysno=alephsysno,
ln=ln),
{}, _("Detailed record"),
{'class': "moreinfo"})
else:
out += '<br /><span class="moreinfo">%s</span>' % \
create_html_link(self.build_search_url(recid=recID, ln=ln),
{},
_("Detailed record"),
{'class': "moreinfo"})
else:
out += '<br /><span class="moreinfo">%s</span>' % \
create_html_link(self.build_search_url(recid=recID, ln=ln),
{}, _("Detailed record"),
{'class': "moreinfo"})
out += '<span class="moreinfo"> - %s</span>' % \
create_html_link(self.build_search_url(p="recid:%d" % recID,
rm="wrd",
ln=ln),
{}, _("Similar records"),
{'class': "moreinfo"})
if CFG_BIBRANK_SHOW_CITATION_LINKS:
out += '<span class="moreinfo"> - %s</span>' % \
create_html_link(self.build_search_url(p="recid:%d" % recID,
rm="citation",
ln=ln),
{}, _("Cited by"),
{'class': "moreinfo"})
return out
def tmpl_xml_rss_prologue(self):
"""Creates XML RSS 2.0 prologue."""
out = """<rss version="2.0">
<channel>
<title>%(sitename)s</title>
- <link>%(weburl)s</link>
+ <link>%(siteurl)s</link>
<description>%(sitename)s latest documents</description>
<language>%(sitelang)s</language>
<pubDate>%(timestamp)s</pubDate>
<category></category>
<generator>CDS Invenio %(version)s</generator>
<webMaster>%(sitesupportemail)s</webMaster>
<ttl>%(timetolive)s</ttl>
<image>
- <url>%(weburl)s/img/cds.png</url>
+ <url>%(siteurl)s/img/cds.png</url>
<title>%(sitename)s</title>
- <link>%(weburl)s</link>
+ <link>%(siteurl)s</link>
</image>
<textInput>
<title>Search </title>
<description>Search this site:</description>
<name>p</name>
- <link>%(weburl)s/search</link>
+ <link>%(siteurl)s/search</link>
</textInput>
""" % {'sitename': CFG_SITE_NAME,
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
'sitelang': CFG_SITE_LANG,
'timestamp': time.strftime("%a, %d %b %Y %H:%M:%S %Z", time.localtime()),
'version': CFG_VERSION,
'sitesupportemail': CFG_SITE_SUPPORT_EMAIL,
'timetolive': CFG_WEBSEARCH_RSS_TTL
}
return out
def tmpl_xml_rss_epilogue(self):
"""Creates XML RSS 2.0 epilogue."""
out = """\
</channel>
</rss>\n"""
return out
def tmpl_xml_nlm_prologue(self):
"""Creates XML NLM prologue."""
out = """<articles>\n"""
return out
def tmpl_xml_nlm_epilogue(self):
"""Creates XML NLM epilogue."""
out = """\n</articles>"""
return out
def tmpl_xml_marc_prologue(self):
"""Creates XML MARC prologue."""
out = """<collection xmlns="http://www.loc.gov/MARC21/slim">\n"""
return out
def tmpl_xml_marc_epilogue(self):
"""Creates XML MARC epilogue."""
out = """\n</collection>"""
return out
def tmpl_xml_default_prologue(self):
"""Creates XML default format prologue. (Sanity calls only.)"""
out = """<collection>\n"""
return out
def tmpl_xml_default_epilogue(self):
"""Creates XML default format epilogue. (Sanity calls only.)"""
out = """\n</collection>"""
return out
def tmpl_collection_not_found_page_title(self, colname, ln=CFG_SITE_LANG):
"""
Create page title for cases when unexisting collection was asked for.
"""
_ = gettext_set_language(ln)
out = _("Collection %s Not Found") % cgi.escape(colname)
return out
def tmpl_collection_not_found_page_body(self, colname, ln=CFG_SITE_LANG):
"""
Create page body for cases when unexisting collection was asked for.
"""
_ = gettext_set_language(ln)
out = """<h1>%(title)s</h1>
<p>%(sorry)s</p>
<p>%(you_may_want)s</p>
""" % { 'title': self.tmpl_collection_not_found_page_title(colname, ln),
'sorry': _("Sorry, collection %s does not seem to exist.") % \
('<strong>' + cgi.escape(colname) + '</strong>'),
'you_may_want': _("You may want to start browsing from %s.") % \
- ('<a href="' + weburl + '?ln=' + ln + '">' + \
+ ('<a href="' + CFG_SITE_URL + '?ln=' + ln + '">' + \
cgi.escape(CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)) + '</a>')}
return out
def tmpl_alert_rss_teaser_box_for_query(self, id_query, ln):
"""Propose teaser for setting up this query as alert or RSS feed.
Parameters:
- 'id_query' *int* - ID of the query we make teaser for
- 'ln' *string* - The language to display
"""
# load the right message language
_ = gettext_set_language(ln)
# get query arguments:
res = run_sql("SELECT urlargs FROM query WHERE id=%s", (id_query,))
argd = {}
if res:
argd = cgi.parse_qs(res[0][0])
rssurl = self.build_rss_url(argd)
- alerturl = weburl + '/youralerts/input?ln=%s&amp;idq=%s' % (ln, id_query)
+ alerturl = CFG_SITE_URL + '/youralerts/input?ln=%s&amp;idq=%s' % (ln, id_query)
out = '''<a name="googlebox"></a>
<table class="googlebox"><tr><th class="googleboxheader">%(similar)s</th></tr>
<tr><td class="googleboxbody">%(msg_alert)s</td></tr>
</table>
''' % {
'similar' : _("Interested in being notified about new results for this query?"),
'msg_alert': _("""Set up a personal %(x_url1_open)semail alert%(x_url1_close)s
or subscribe to the %(x_url2_open)sRSS feed%(x_url2_close)s.""") % \
- {'x_url1_open': '<a href="%s"><img src="%s/img/mail-icon-12x8.gif" border="0" alt="" /></a> ' % (alerturl, weburl) + ' <a class="google" href="%s">' % (alerturl),
+ {'x_url1_open': '<a href="%s"><img src="%s/img/mail-icon-12x8.gif" border="0" alt="" /></a> ' % (alerturl, CFG_SITE_URL) + ' <a class="google" href="%s">' % (alerturl),
'x_url1_close': '</a>',
- 'x_url2_open': '<a href="%s"><img src="%s/img/feed-icon-12x12.gif" border="0" alt="" /></a> ' % (rssurl, weburl) + ' <a class="google" href="%s">' % rssurl,
+ 'x_url2_open': '<a href="%s"><img src="%s/img/feed-icon-12x12.gif" border="0" alt="" /></a> ' % (rssurl, CFG_SITE_URL) + ' <a class="google" href="%s">' % rssurl,
'x_url2_close': '</a>',
}}
return out
def tmpl_detailed_record_metadata(self, recID, ln, format,
content,
creationdate=None,
modificationdate=None):
"""Returns the main detailed page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- 'format' *string* - The format in used to print the record
- 'content' *string* - The main content of the page
- 'creationdate' *string* - The creation date of the printed record
- 'modificationdate' *string* - The last modification date of the printed record
"""
_ = gettext_set_language(ln)
out = content
return out
def tmpl_detailed_record_statistics(self, recID, ln,
downloadsimilarity,
downloadhistory, viewsimilarity):
"""Returns the statistics page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- downloadsimilarity *string* - downloadsimilarity box
- downloadhistory *string* - downloadhistory box
- viewsimilarity *string* - viewsimilarity box
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if CFG_BIBRANK_SHOW_DOWNLOAD_STATS and downloadsimilarity is not None:
similar = self.tmpl_print_record_list_for_similarity_boxen (
_("People who downloaded this document also downloaded:"), downloadsimilarity, ln)
out = '<table>'
out += '''
<tr><td>%(graph)s</td></tr>
<tr><td>%(similar)s</td></tr>
- ''' % { 'weburl': weburl, 'recid': recID, 'ln': ln,
+ ''' % { 'siteurl': CFG_SITE_URL, 'recid': recID, 'ln': ln,
'similar': similar, 'more': _("more"),
'graph': downloadsimilarity
}
out += '</table>'
out += '<br />'
if CFG_BIBRANK_SHOW_READING_STATS and viewsimilarity is not None:
out += self.tmpl_print_record_list_for_similarity_boxen (
_("People who viewed this page also viewed:"), viewsimilarity, ln)
if CFG_BIBRANK_SHOW_DOWNLOAD_GRAPHS and downloadhistory is not None:
out += downloadhistory + '<br />'
return out
def tmpl_detailed_record_citations(self, recID, ln,
citinglist, citationhistory,
cociting,selfcited):
"""Returns the citations page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- citinglist *list* - a list of tuples [(x1,y1),(x2,y2),..] where x is doc id and y is number of citations
- citationhistory *string* - citationhistory box
- cociting *string* - cociting box
- selfcited list - a list of self-citations for recID
"""
# load the right message language
_ = gettext_set_language(ln)
out = '<table>'
if CFG_BIBRANK_SHOW_CITATION_STATS and citinglist is not None:
similar = self.tmpl_print_record_list_for_similarity_boxen(
_("Cited by: %s records") % len (citinglist), citinglist, ln)
out += '''
<tr><td>
%(similar)s&nbsp;%(more)s
<br /><br />
</td></tr>''' % {
'more': create_html_link(
self.build_search_url(p='recid:%d' % \
recID, #XXXX
rm='citation', ln=ln),
{}, _("more")),
'similar': similar}
if CFG_BIBRANK_SHOW_CITATION_GRAPHS and selfcited is not None:
sc_scorelist = [] #a score list for print..
for s in selfcited:
#copy weight from citations
weight = 0
for c in citinglist:
(crec,score) = c
if crec == s:
weight = score
tmp = [s,weight]
sc_scorelist.append(tmp)
scite = self.tmpl_print_record_list_for_similarity_boxen (
_(".. of which self-citations: %s records") % len (selfcited), sc_scorelist, ln)
out += '<tr><td>'+scite+'</td></tr>'
if CFG_BIBRANK_SHOW_CITATION_STATS and cociting is not None:
similar = self.tmpl_print_record_list_for_similarity_boxen (
_("Co-cited with: %s records") % len (cociting), cociting, ln)
out += '''
<tr><td>
%(similar)s&nbsp;%(more)s
<br />
</td></tr>''' % { 'more': create_html_link(self.build_search_url(p='cocitedwith:%d' % recID, ln=ln),
{}, _("more")),
'similar': similar}
if CFG_BIBRANK_SHOW_CITATION_GRAPHS and citationhistory is not None:
out += '<tr><td>%s</td></tr>' % citationhistory
out += '</table>'
return out
def tmpl_detailed_record_references(self, recID, ln, content):
"""Returns the discussion page of a record
Parameters:
- 'recID' *int* - The ID of the printed record
- 'ln' *string* - The language to display
- 'content' *string* - The main content of the page
"""
# load the right message language
_ = gettext_set_language(ln)
out = ''
if content is not None:
out += content
return out
diff --git a/modules/websearch/lib/websearch_webcoll.py b/modules/websearch/lib/websearch_webcoll.py
index 6822a1a81..e92530a1b 100644
--- a/modules/websearch/lib/websearch_webcoll.py
+++ b/modules/websearch/lib/websearch_webcoll.py
@@ -1,890 +1,889 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""Create CDS Invenio collection cache."""
__revision__ = "$Id$"
import calendar
import copy
import sys
import cgi
import re
import os
import string
import time
from invenio.config import \
CFG_CERN_SITE, \
CFG_WEBSEARCH_INSTANT_BROWSE, \
CFG_WEBSEARCH_NARROW_SEARCH_SHOW_GRANDSONS, \
CFG_WEBSEARCH_I18N_LATEST_ADDITIONS, \
CFG_CACHEDIR, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
- weburl
+ CFG_SITE_URL
from invenio.messages import gettext_set_language, language_list_long
from invenio.search_engine import HitSet, search_pattern, get_creation_date, get_field_i18nname, collection_restricted_p
from invenio.dbquery import run_sql, Error, get_table_update_time
from invenio.access_control_engine import acc_authorize_action
from invenio.bibrank_record_sorter import get_bibrank_methods
from invenio.dateutils import convert_datestruct_to_dategui
from invenio.bibformat import format_record
from invenio.websearch_external_collections import \
external_collection_load_states, \
dico_collection_external_searches, \
external_collection_sort_engine_by_name
from invenio.bibtask import task_init, task_get_option, task_set_option, \
write_message, task_has_option, task_update_progress
import invenio.template
websearch_templates = invenio.template.load('websearch')
## global vars
collection_house = {} # will hold collections we treat in this run of the program; a dict of {collname2, collobject1}, ...
# cfg_cache_last_updated_timestamp_tolerance -- cache timestamp
# tolerance (in seconds), to account for the fact that an admin might
# accidentally happen to edit the collection definitions at exactly
# the same second when some webcoll process was about to be started.
# In order to be safe, let's put an exaggerated timestamp tolerance
# value such as 20 seconds:
cfg_cache_last_updated_timestamp_tolerance = 20
# cfg_cache_last_updated_timestamp_file -- location of the cache
# timestamp file:
cfg_cache_last_updated_timestamp_file = "%s/collections/last_updated" % CFG_CACHEDIR
def get_collection(colname):
"""Return collection object from the collection house for given colname.
If does not exist, then create it."""
if not collection_house.has_key(colname):
colobject = Collection(colname)
collection_house[colname] = colobject
return collection_house[colname]
## auxiliary functions:
def mymkdir(newdir, mode=0777):
"""works the way a good mkdir should :)
- already exists, silently complete
- regular file in the way, raise an exception
- parent directory(ies) does not exist, make them as well
"""
if os.path.isdir(newdir):
pass
elif os.path.isfile(newdir):
raise OSError("a file with the same name as the desired " \
"dir, '%s', already exists." % newdir)
else:
head, tail = os.path.split(newdir)
if head and not os.path.isdir(head):
mymkdir(head, mode)
if tail:
os.umask(022)
os.mkdir(newdir, mode)
def is_selected(var, fld):
"Checks if the two are equal, and if yes, returns ' selected'. Useful for select boxes."
if var == fld:
return ' selected="selected"'
else:
return ""
def get_field(recID, tag):
"Gets list of field 'tag' for the record with 'recID' system number."
out = []
digit = tag[0:2]
bx = "bib%sx" % digit
bibx = "bibrec_bib%sx" % digit
query = "SELECT bx.value FROM %s AS bx, %s AS bibx WHERE bibx.id_bibrec='%s' AND bx.id=bibx.id_bibxxx AND bx.tag='%s'" \
% (bx, bibx, recID, tag)
res = run_sql(query)
for row in res:
out.append(row[0])
return out
class Collection:
"Holds the information on collections (id,name,dbquery)."
def __init__(self, name=""):
"Creates collection instance by querying the DB configuration database about 'name'."
self.calculate_reclist_run_already = 0 # to speed things up without much refactoring
self.update_reclist_run_already = 0 # to speed things up without much refactoring
self.reclist_with_nonpublic_subcolls = HitSet()
if not name:
self.name = CFG_SITE_NAME # by default we are working on the home page
self.id = 1
self.dbquery = None
self.nbrecs = None
self.reclist = HitSet()
else:
self.name = name
try:
res = run_sql("""SELECT id,name,dbquery,nbrecs,reclist FROM collection
WHERE name=%s""", (name,))
if res:
self.id = res[0][0]
self.name = res[0][1]
self.dbquery = res[0][2]
self.nbrecs = res[0][3]
try:
self.reclist = HitSet(res[0][4])
except:
self.reclist = HitSet()
else: # collection does not exist!
self.id = None
self.dbquery = None
self.nbrecs = None
self.reclist = HitSet()
except Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
sys.exit(1)
def get_name(self, ln=CFG_SITE_LANG, name_type="ln", prolog="", epilog="", prolog_suffix=" ", epilog_suffix=""):
"""Return nicely formatted collection name for language LN.
The NAME_TYPE may be 'ln' (=long name), 'sn' (=short name), etc."""
out = prolog
i18name = ""
res = run_sql("SELECT value FROM collectionname WHERE id_collection=%s AND ln=%s AND type=%s", (self.id, ln, name_type))
try:
i18name += res[0][0]
except IndexError:
pass
if i18name:
out += i18name
else:
out += self.name
out += epilog
return out
def get_ancestors(self):
"Returns list of ancestors of the current collection."
ancestors = []
id_son = self.id
while 1:
query = "SELECT cc.id_dad,c.name FROM collection_collection AS cc, collection AS c "\
"WHERE cc.id_son=%d AND c.id=cc.id_dad" % int(id_son)
res = run_sql(query, None, 1)
if res:
col_ancestor = get_collection(res[0][1])
ancestors.append(col_ancestor)
id_son = res[0][0]
else:
break
ancestors.reverse()
return ancestors
def restricted_p(self):
"""Predicate to test if the collection is restricted or not. Return the contect of the
`restrited' column of the collection table (typically Apache group). Otherwise return
None if the collection is public."""
if collection_restricted_p(self.name):
return 1
return None
def get_sons(self, type='r'):
"Returns list of direct sons of type 'type' for the current collection."
sons = []
id_dad = self.id
query = "SELECT cc.id_son,c.name FROM collection_collection AS cc, collection AS c "\
"WHERE cc.id_dad=%d AND cc.type='%s' AND c.id=cc.id_son ORDER BY score DESC, c.name ASC" % (int(id_dad), type)
res = run_sql(query)
for row in res:
sons.append(get_collection(row[1]))
return sons
def get_descendants(self, type='r'):
"Returns list of all descendants of type 'type' for the current collection."
descendants = []
id_dad = self.id
query = "SELECT cc.id_son,c.name FROM collection_collection AS cc, collection AS c "\
"WHERE cc.id_dad=%d AND cc.type='%s' AND c.id=cc.id_son ORDER BY score DESC" % (int(id_dad), type)
res = run_sql(query)
for row in res:
col_desc = get_collection(row[1])
descendants.append(col_desc)
descendants += col_desc.get_descendants()
return descendants
def write_cache_file(self, filename='', filebody=''):
"Write a file inside collection cache."
# open file:
dirname = "%s/collections/%d" % (CFG_CACHEDIR, self.id)
mymkdir(dirname)
fullfilename = dirname + "/%s.html" % filename
try:
os.umask(022)
f = open(fullfilename, "w")
except IOError, v:
try:
(code, message) = v
except:
code = 0
message = v
print "I/O Error: " + str(message) + " (" + str(code) + ")"
sys.exit(1)
# print user info:
write_message("... creating %s" % fullfilename, verbose=6)
sys.stdout.flush()
# print page body:
f.write(filebody)
# close file:
f.close()
def update_webpage_cache(self):
"""Create collection page header, navtrail, body (including left and right stripes) and footer, and
call write_cache_file() afterwards to update the collection webpage cache."""
## precalculate latest additions for non-aggregate
## collections (the info is ln and as independent)
if self.dbquery and not CFG_WEBSEARCH_I18N_LATEST_ADDITIONS:
self.create_latest_additions_info()
## do this for each language:
for lang, lang_fullname in language_list_long():
# but only if some concrete language was not chosen only:
if task_get_option("language", lang) == lang:
if self.dbquery and CFG_WEBSEARCH_I18N_LATEST_ADDITIONS:
self.create_latest_additions_info(ln=lang)
# load the right message language
_ = gettext_set_language(lang)
## first, update navtrail:
for as in range(0, 2):
self.write_cache_file("navtrail-as=%s-ln=%s" % (as, lang),
self.create_navtrail_links(as, lang))
## second, update page body:
for as in range(0, 2): # do both simple search and advanced search pages:
body = websearch_templates.tmpl_webcoll_body(
ln=lang, collection=self.name,
te_portalbox = self.create_portalbox(lang, 'te'),
searchfor = self.create_searchfor(as, lang),
np_portalbox = self.create_portalbox(lang, 'np'),
narrowsearch = self.create_narrowsearch(as, lang, 'r'),
focuson = self.create_narrowsearch(as, lang, "v") + \
self.create_external_collections_box(),
instantbrowse = self.create_instant_browse(as=as, ln=lang),
ne_portalbox = self.create_portalbox(lang, 'ne')
)
self.write_cache_file("body-as=%s-ln=%s" % (as, lang), body)
## third, write portalboxes:
self.write_cache_file("portalbox-tp-ln=%s" % lang, self.create_portalbox(lang, "tp"))
self.write_cache_file("portalbox-te-ln=%s" % lang, self.create_portalbox(lang, "te"))
self.write_cache_file("portalbox-lt-ln=%s" % lang, self.create_portalbox(lang, "lt"))
self.write_cache_file("portalbox-rt-ln=%s" % lang, self.create_portalbox(lang, "rt"))
## fourth, write 'last updated' information:
self.write_cache_file("last-updated-ln=%s" % lang,
convert_datestruct_to_dategui(time.localtime(),
ln=lang))
return
def create_navtrail_links(self, as=0, ln=CFG_SITE_LANG):
"""Creates navigation trail links, i.e. links to collection
ancestors (except Home collection). If as==1, then links to
Advanced Search interfaces; otherwise Simple Search.
"""
dads = []
for dad in self.get_ancestors():
if dad.name != CFG_SITE_NAME: # exclude Home collection
dads.append((dad.name, dad.get_name(ln)))
return websearch_templates.tmpl_navtrail_links(
as=as, ln=ln, dads=dads)
def create_portalbox(self, lang=CFG_SITE_LANG, position="rt"):
"""Creates portalboxes of language CFG_SITE_LANG of the position POSITION by consulting DB configuration database.
The position may be: 'lt'='left top', 'rt'='right top', etc."""
out = ""
query = "SELECT p.title,p.body FROM portalbox AS p, collection_portalbox AS cp "\
" WHERE cp.id_collection=%d AND p.id=cp.id_portalbox AND cp.ln='%s' AND cp.position='%s' "\
" ORDER BY cp.score DESC" % (self.id, lang, position)
res = run_sql(query)
for row in res:
title, body = row[0], row[1]
if title:
out += websearch_templates.tmpl_portalbox(title = title,
body = body)
else:
# no title specified, so print body ``as is'' only:
out += body
return out
def create_narrowsearch(self, as=0, ln=CFG_SITE_LANG, type="r"):
"""Creates list of collection descendants of type 'type' under title 'title'.
If as==1, then links to Advanced Search interfaces; otherwise Simple Search.
Suitable for 'Narrow search' and 'Focus on' boxes."""
# get list of sons and analyse it
sons = self.get_sons(type)
if not sons:
return ''
# get descendents
descendants = self.get_descendants(type)
grandsons = []
if CFG_WEBSEARCH_NARROW_SEARCH_SHOW_GRANDSONS:
# load grandsons for each son
for son in sons:
grandsons.append(son.get_sons())
# return ""
return websearch_templates.tmpl_narrowsearch(
as = as,
ln = ln,
type = type,
father = self,
has_grandchildren = len(descendants)>len(sons),
sons = sons,
display_grandsons = CFG_WEBSEARCH_NARROW_SEARCH_SHOW_GRANDSONS,
grandsons = grandsons
)
def create_external_collections_box(self, ln=CFG_SITE_LANG):
external_collection_load_states()
if not dico_collection_external_searches.has_key(self.id):
return ""
engines_list = external_collection_sort_engine_by_name(dico_collection_external_searches[self.id])
return websearch_templates.tmpl_searchalso(ln, engines_list, self.id)
def create_latest_additions_info(self, rg=CFG_WEBSEARCH_INSTANT_BROWSE, ln=CFG_SITE_LANG):
"""
Create info about latest additions that will be used for
create_instant_browse() later.
"""
self.latest_additions_info = []
if self.nbrecs and self.reclist:
# firstly, get last 'rg' records:
recIDs = list(self.reclist)
# FIXME: temporary hack in order to tweak latest additions
# list for some CERN collections:
if CFG_CERN_SITE and self.name in ['CERN Yellow Reports']:
# detect recIDs only from the current year:
recIDs = list(self.reclist & \
search_pattern(p='year:' + time.strftime("%Y", time.localtime())))
total = len(recIDs)
to_display = min(rg, total)
for idx in range(total-1, total-to_display-1, -1):
recid = recIDs[idx]
self.latest_additions_info.append({'id': recid,
'format': format_record(recid, "hb", ln=ln),
'date': get_creation_date(recid, fmt="%Y-%m-%d<br />%H:%i")})
return
def create_instant_browse(self, rg=CFG_WEBSEARCH_INSTANT_BROWSE, as=0, ln=CFG_SITE_LANG):
"Searches database and produces list of last 'rg' records."
if self.restricted_p():
return websearch_templates.tmpl_box_restricted_content(ln = ln)
# FIXME: temporary hack in order not to display latest
# additions box for some CERN collections:
if CFG_CERN_SITE and self.name in ['Periodicals', 'Electronic Journals']:
return ""
try:
self.latest_additions_info
latest_additions_info_p = True
except:
latest_additions_info_p = False
if latest_additions_info_p:
passIDs = []
for idx in range(0, min(len(self.latest_additions_info), rg)):
passIDs.append({'id': self.latest_additions_info[idx]['id'],
'body': self.latest_additions_info[idx]['format'] + \
- websearch_templates.tmpl_record_links(weburl=weburl,
- recid=self.latest_additions_info[idx]['id'],
+ websearch_templates.tmpl_record_links(recid=self.latest_additions_info[idx]['id'],
ln=ln),
'date': self.latest_additions_info[idx]['date']})
if self.nbrecs > rg:
url = websearch_templates.build_search_url(
cc=self.name, jrec=rg+1, ln=ln, as=as)
else:
url = ""
return websearch_templates.tmpl_instant_browse(
as=as, ln=ln, recids=passIDs, more_link=url)
return websearch_templates.tmpl_box_no_records(ln=ln)
def create_searchoptions(self):
"Produces 'Search options' portal box."
box = ""
query = """SELECT DISTINCT(cff.id_field),f.code,f.name FROM collection_field_fieldvalue AS cff, field AS f
WHERE cff.id_collection=%d AND cff.id_fieldvalue IS NOT NULL AND cff.id_field=f.id
ORDER BY cff.score DESC""" % self.id
res = run_sql(query)
if res:
for row in res:
field_id = row[0]
field_code = row[1]
field_name = row[2]
query_bis = """SELECT fv.value,fv.name FROM fieldvalue AS fv, collection_field_fieldvalue AS cff
WHERE cff.id_collection=%d AND cff.type='seo' AND cff.id_field=%d AND fv.id=cff.id_fieldvalue
ORDER BY cff.score_fieldvalue DESC, cff.score DESC, fv.name ASC""" % (self.id, field_id)
res_bis = run_sql(query_bis)
if res_bis:
values = [{'value' : '', 'text' : 'any' + field_name}] # FIXME: internationalisation of "any"
for row_bis in res_bis:
values.append({'value' : cgi.escape(row_bis[0], 1), 'text' : row_bis[1]})
box += websearch_templates.tmpl_select(
fieldname = field_code,
values = values
)
return box
def create_sortoptions(self, ln=CFG_SITE_LANG):
"""Produces 'Sort options' portal box."""
# load the right message language
_ = gettext_set_language(ln)
box = ""
query = """SELECT f.code,f.name FROM field AS f, collection_field_fieldvalue AS cff
WHERE id_collection=%d AND cff.type='soo' AND cff.id_field=f.id
ORDER BY cff.score DESC, f.name ASC""" % self.id
values = [{'value' : '', 'text': "- %s -" % _("latest first")}]
res = run_sql(query)
if res:
for row in res:
values.append({'value' : row[0], 'text': row[1]})
else:
for tmp in ('title', 'author', 'report number', 'year'):
values.append({'value' : tmp.replace(' ', ''), 'text' : get_field_i18nname(tmp, ln)})
box = websearch_templates.tmpl_select(
fieldname = 'sf',
css_class = 'address',
values = values
)
box += websearch_templates.tmpl_select(
fieldname = 'so',
css_class = 'address',
values = [
{'value' : 'a' , 'text' : _("asc.")},
{'value' : 'd' , 'text' : _("desc.")}
]
)
return box
def create_rankoptions(self, ln=CFG_SITE_LANG):
"Produces 'Rank options' portal box."
# load the right message language
_ = gettext_set_language(ln)
values = [{'value' : '', 'text': "- %s %s -" % (string.lower(_("OR")), _("rank by"))}]
for (code, name) in get_bibrank_methods(self.id, ln):
values.append({'value' : code, 'text': name})
box = websearch_templates.tmpl_select(
fieldname = 'sf',
css_class = 'address',
values = values
)
return box
def create_displayoptions(self, ln=CFG_SITE_LANG):
"Produces 'Display options' portal box."
# load the right message language
_ = gettext_set_language(ln)
values = []
for i in ['10', '25', '50', '100', '250', '500']:
values.append({'value' : i, 'text' : i + ' ' + _("results")})
box = websearch_templates.tmpl_select(
fieldname = 'rg',
css_class = 'address',
values = values
)
if self.get_sons():
box += websearch_templates.tmpl_select(
fieldname = 'sc',
css_class = 'address',
values = [
{'value' : '1' , 'text' : _("split by collection")},
{'value' : '0' , 'text' : _("single list")}
]
)
return box
def create_formatoptions(self, ln=CFG_SITE_LANG):
"Produces 'Output format options' portal box."
# load the right message language
_ = gettext_set_language(ln)
box = ""
values = []
query = """SELECT f.code,f.name FROM format AS f, collection_format AS cf
WHERE cf.id_collection=%d AND cf.id_format=f.id AND f.visibility='1'
ORDER BY cf.score DESC, f.name ASC""" % self.id
res = run_sql(query)
if res:
for row in res:
values.append({'value' : row[0], 'text': row[1]})
else:
values.append({'value' : 'hb', 'text' : "HTML %s" % _("brief")})
box = websearch_templates.tmpl_select(
fieldname = 'of',
css_class = 'address',
values = values
)
return box
def create_searchwithin_selection_box(self, fieldname='f', value='', ln='en'):
"""Produces 'search within' selection box for the current collection."""
# get values
query = """SELECT f.code,f.name FROM field AS f, collection_field_fieldvalue AS cff
WHERE cff.type='sew' AND cff.id_collection=%d AND cff.id_field=f.id
ORDER BY cff.score DESC, f.name ASC""" % self.id
res = run_sql(query)
values = [{'value' : '', 'text' : get_field_i18nname("any field", ln)}]
if res:
for row in res:
values.append({'value' : row[0], 'text' : row[1]})
else:
if CFG_CERN_SITE:
for tmp in ['title', 'author', 'abstract', 'report number', 'year']:
values.append({'value' : tmp.replace(' ', ''), 'text' : get_field_i18nname(tmp, ln)})
else:
for tmp in ['title', 'author', 'abstract', 'keyword', 'report number', 'year', 'fulltext', 'reference']:
values.append({'value' : tmp.replace(' ', ''), 'text' : get_field_i18nname(tmp, ln)})
return websearch_templates.tmpl_searchwithin_select(
fieldname = fieldname,
ln = ln,
selected = value,
values = values
)
def create_searchexample(self):
"Produces search example(s) for the current collection."
out = "$collSearchExamples = getSearchExample(%d, $se);" % self.id
return out
def create_searchfor(self, as=0, ln=CFG_SITE_LANG):
"Produces either Simple or Advanced 'Search for' box for the current collection."
if as == 1:
return self.create_searchfor_advanced(ln)
else:
return self.create_searchfor_simple(ln)
def create_searchfor_simple(self, ln=CFG_SITE_LANG):
"Produces simple 'Search for' box for the current collection."
return websearch_templates.tmpl_searchfor_simple(
ln=ln,
collection_id = self.name,
collection_name=self.get_name(ln=ln),
record_count=self.nbrecs,
middle_option = self.create_searchwithin_selection_box(ln=ln),
)
def create_searchfor_advanced(self, ln=CFG_SITE_LANG):
"Produces advanced 'Search for' box for the current collection."
return websearch_templates.tmpl_searchfor_advanced(
ln = ln,
collection_id = self.name,
collection_name=self.get_name(ln=ln),
record_count=self.nbrecs,
middle_option_1 = self.create_searchwithin_selection_box('f1', ln=ln),
middle_option_2 = self.create_searchwithin_selection_box('f2', ln=ln),
middle_option_3 = self.create_searchwithin_selection_box('f3', ln=ln),
searchoptions = self.create_searchoptions(),
sortoptions = self.create_sortoptions(ln),
rankoptions = self.create_rankoptions(ln),
displayoptions = self.create_displayoptions(ln),
formatoptions = self.create_formatoptions(ln)
)
def calculate_reclist(self):
"""Calculate, set and return the (reclist, reclist_with_nonpublic_subcolls) tuple for given collection."""
if self.calculate_reclist_run_already:
# do we have to recalculate?
return (self.reclist, self.reclist_with_nonpublic_subcolls)
write_message("... calculating reclist of %s" % self.name, verbose=6)
reclist = HitSet() # will hold results for public sons only; good for storing into DB
reclist_with_nonpublic_subcolls = HitSet() # will hold results for both public and nonpublic sons; good for deducing total
# number of documents
if not self.dbquery:
# A - collection does not have dbquery, so query recursively all its sons
# that are either non-restricted or that have the same restriction rules
for coll in self.get_sons():
coll_reclist, coll_reclist_with_nonpublic_subcolls = coll.calculate_reclist()
if ((coll.restricted_p() is None) or
(coll.restricted_p() == self.restricted_p())):
# add this reclist ``for real'' only if it is public
reclist.union_update(coll_reclist)
reclist_with_nonpublic_subcolls.union_update(coll_reclist_with_nonpublic_subcolls)
else:
# B - collection does have dbquery, so compute it:
# (note: explicitly remove DELETED records)
if CFG_CERN_SITE:
reclist = search_pattern(None, self.dbquery + \
' -collection:"DELETED" -collection:"DUMMY"')
else:
reclist = search_pattern(None, self.dbquery + ' -collection:"DELETED"')
reclist_with_nonpublic_subcolls = copy.deepcopy(reclist)
# store the results:
self.nbrecs = len(reclist_with_nonpublic_subcolls)
self.reclist = reclist
self.reclist_with_nonpublic_subcolls = reclist_with_nonpublic_subcolls
# last but not least, update the speed-up flag:
self.calculate_reclist_run_already = 1
# return the two sets:
return (self.reclist, self.reclist_with_nonpublic_subcolls)
def update_reclist(self):
"Update the record universe for given collection; nbrecs, reclist of the collection table."
if self.update_reclist_run_already:
# do we have to reupdate?
return 0
write_message("... updating reclist of %s (%s recs)" % (self.name, self.nbrecs), verbose=6)
sys.stdout.flush()
try:
run_sql("UPDATE collection SET nbrecs=%s, reclist=%s WHERE id=%s",
(self.nbrecs, self.reclist.fastdump(), self.id))
self.reclist_updated_since_start = 1
except Error, e:
print "Database Query Error %d: %s." % (e.args[0], e.args[1])
sys.exit(1)
# last but not least, update the speed-up flag:
self.update_reclist_run_already = 1
return 0
def get_datetime(var, format_string="%Y-%m-%d %H:%M:%S"):
"""Returns a date string according to the format string.
It can handle normal date strings and shifts with respect
to now."""
date = time.time()
shift_re = re.compile("([-\+]{0,1})([\d]+)([dhms])")
factors = {"d":24*3600, "h":3600, "m":60, "s":1}
m = shift_re.match(var)
if m:
sign = m.groups()[0] == "-" and -1 or 1
factor = factors[m.groups()[2]]
value = float(m.groups()[1])
date = time.localtime(date + sign * factor * value)
date = time.strftime(format_string, date)
else:
date = time.strptime(var, format_string)
date = time.strftime(format_string, date)
return date
def get_current_time_timestamp():
"""Return timestamp corresponding to the current time."""
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
def compare_timestamps_with_tolerance(timestamp1,
timestamp2,
tolerance=0):
"""Compare two timestamps TIMESTAMP1 and TIMESTAMP2, of the form
'2005-03-31 17:37:26'. Optionally receives a TOLERANCE argument
(in seconds). Return -1 if TIMESTAMP1 is less than TIMESTAMP2
minus TOLERANCE, 0 if they are equal within TOLERANCE limit,
and 1 if TIMESTAMP1 is greater than TIMESTAMP2 plus TOLERANCE.
"""
# remove any trailing .00 in timestamps:
timestamp1 = re.sub(r'\.[0-9]+$', '', timestamp1)
timestamp2 = re.sub(r'\.[0-9]+$', '', timestamp2)
# first convert timestamps to Unix epoch seconds:
timestamp1_seconds = calendar.timegm(time.strptime(timestamp1, "%Y-%m-%d %H:%M:%S"))
timestamp2_seconds = calendar.timegm(time.strptime(timestamp2, "%Y-%m-%d %H:%M:%S"))
# now compare them:
if timestamp1_seconds < timestamp2_seconds - tolerance:
return -1
elif timestamp1_seconds > timestamp2_seconds + tolerance:
return 1
else:
return 0
def get_database_last_updated_timestamp():
"""Return last updated timestamp for collection-related and
record-related database tables.
"""
database_tables_timestamps = []
database_tables_timestamps.append(get_table_update_time('bibrec'))
database_tables_timestamps.append(get_table_update_time('bibfmt'))
database_tables_timestamps.append(get_table_update_time('idxWORD%'))
database_tables_timestamps.append(get_table_update_time('collection%'))
database_tables_timestamps.append(get_table_update_time('portalbox'))
database_tables_timestamps.append(get_table_update_time('field%'))
database_tables_timestamps.append(get_table_update_time('format%'))
database_tables_timestamps.append(get_table_update_time('rnkMETHODNAME'))
return max(database_tables_timestamps)
def get_cache_last_updated_timestamp():
"""Return last updated cache timestamp."""
try:
f = open(cfg_cache_last_updated_timestamp_file, "r")
except:
return "1970-01-01 00:00:00"
timestamp = f.read()
f.close()
return timestamp
def set_cache_last_updated_timestamp(timestamp):
"""Set last updated cache timestamp to TIMESTAMP."""
try:
f = open(cfg_cache_last_updated_timestamp_file, "w")
except:
pass
f.write(timestamp)
f.close()
return timestamp
def main():
"""Main that construct all the bibtask."""
task_init(authorization_action="runwebcoll",
authorization_msg="WebColl Task Submission",
description="""Description: webcoll updates the collection cache
(record universe for a given collection plus web page elements)
based on invenio.conf and DB configuration parameters.
If the collection name is passed as the second argument, it'll update
this collection only. If the collection name is immediately followed
by a plus sign, it will also update all its desdendants. The
top-level collection name may be entered as the void string.\n""",
help_specific_usage=" -c, --collection\t Update cache for the given"
"collection only. [all]\n"
" -f, --force\t Force update even if cache is up to date. [no]\n"
" -p, --part\t Update only certain cache parts (1=reclist,"
" 2=webpage). [both]\n"
" -l, --language\t Update pages in only certain language"
" (e.g. fr). [all]\n",
version=__revision__,
specific_params=("c:fp:l:", [
"collection=",
"force",
"part=",
"language="
]),
task_submit_elaborate_specific_parameter_fnc=task_submit_elaborate_specific_parameter,
task_submit_check_options_fnc=task_submit_check_options,
task_run_fnc=task_run_core)
def task_submit_elaborate_specific_parameter(key, value, opts, args):
""" Given the string key it checks it's meaning, eventually using the value.
Usually it fills some key in the options dict.
It must return True if it has elaborated the key, False, if it doesn't
know that key.
eg:
if key in ['-n', '--number']:
self.options['number'] = value
return True
return False
"""
if key in ("-c", "--collection"):
task_set_option("collection", value)
elif key in ("-f", "--force"):
task_set_option("force", 1)
elif key in ("-p", "--part"):
task_set_option("part", int(value))
elif key in ("-l", "--language"):
task_set_option("language", value)
else:
return False
return True
def task_submit_check_options():
if task_has_option('collection'):
coll = get_collection(task_get_option("collection"))
if coll.id is None:
raise StandardError, 'Collection %s does not exist' % coll.name
return True
def task_run_core():
""" Reimplement to add the body of the task."""
task_run_start_timestamp = get_current_time_timestamp()
colls = []
# decide whether we need to run or not, by comparing last updated timestamps:
write_message("Database timestamp is %s." % get_database_last_updated_timestamp(), verbose=3)
write_message("Collection cache timestamp is %s." % get_cache_last_updated_timestamp(), verbose=3)
if task_has_option("part"):
write_message("Running cache update part %s only." % task_get_option("part"), verbose=3)
if task_has_option("force") or \
compare_timestamps_with_tolerance(get_database_last_updated_timestamp(),
get_cache_last_updated_timestamp(),
cfg_cache_last_updated_timestamp_tolerance) >= 0:
## either forced update was requested or cache is not up to date, so recreate it:
# firstly, decide which collections to do:
if task_has_option("collection"):
coll = get_collection(task_get_option("collection"))
colls.append(coll)
else:
res = run_sql("SELECT name FROM collection ORDER BY id")
for row in res:
colls.append(get_collection(row[0]))
# secondly, update collection reclist cache:
if task_get_option('part', 1) == 1:
i = 0
for coll in colls:
i += 1
write_message("%s / reclist cache update" % coll.name)
coll.calculate_reclist()
coll.update_reclist()
task_update_progress("Part 1/2: done %d/%d" % (i, len(colls)))
# thirdly, update collection webpage cache:
if task_get_option("part", 2) == 2:
i = 0
for coll in colls:
i += 1
write_message("%s / webpage cache update" % coll.name)
coll.update_webpage_cache()
task_update_progress("Part 2/2: done %d/%d" % (i, len(colls)))
# finally update the cache last updated timestamp:
# (but only when all collections were updated, not when only
# some of them were forced-updated as per admin's demand)
if not task_has_option("collection"):
set_cache_last_updated_timestamp(task_run_start_timestamp)
write_message("Collection cache timestamp is set to %s." % get_cache_last_updated_timestamp(), verbose=3)
else:
## cache up to date, we don't have to run
write_message("Collection cache is up to date, no need to run.")
## we are done:
return True
### okay, here we go:
if __name__ == '__main__':
main()
diff --git a/modules/websearch/lib/websearch_webinterface.py b/modules/websearch/lib/websearch_webinterface.py
index c3c089f81..78ed04a91 100644
--- a/modules/websearch/lib/websearch_webinterface.py
+++ b/modules/websearch/lib/websearch_webinterface.py
@@ -1,826 +1,826 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSearch URL handler."""
__revision__ = "$Id$"
import cgi
import os
import datetime
from urllib import quote
from mod_python import apache
try:
Set = set
except NameError:
from sets import Set
from invenio.config import \
- weburl, \
+ CFG_SITE_URL, \
CFG_SITE_NAME, \
CFG_CACHEDIR, \
CFG_SITE_LANG, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_WEBSEARCH_INSTANT_BROWSE_RSS, \
CFG_WEBSEARCH_RSS_TTL, \
CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS
from invenio.dbquery import Error
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import redirect_to_url, make_canonical_urlargd, drop_default_urlargd
from invenio.webuser import getUid, page_not_authorized, get_user_preferences, \
collect_user_info, http_check_credentials
from invenio import search_engine
from invenio.websubmit_webinterface import WebInterfaceFilesPages
from invenio.webcomment_webinterface import WebInterfaceCommentsPages
from invenio.webpage import page, create_error_box
from invenio.messages import gettext_set_language
from invenio.search_engine import get_colID, get_coll_i18nname, \
check_user_can_view_record, collection_restricted_p, restricted_collection_cache
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
from invenio.bibformat import format_records
from invenio.websearch_webcoll import mymkdir, get_collection
from invenio.intbitset import intbitset
from invenio.bibupload import find_record_from_sysno
import invenio.template
websearch_templates = invenio.template.load('websearch')
search_results_default_urlargd = websearch_templates.search_results_default_urlargd
search_interface_default_urlargd = websearch_templates.search_interface_default_urlargd
output_formats = ['xm', 'xd', 'hm', 'hx', 'hd', 'hb', 'xe', 'xn']
def wash_search_urlargd(form):
"""
Create canonical search arguments from those passed via web form.
"""
argd = wash_urlargd(form, search_results_default_urlargd)
# Sometimes, users pass ot=245,700 instead of
# ot=245&ot=700. Normalize that.
ots = []
for ot in argd['ot']:
ots += ot.split(',')
argd['ot'] = ots
# We can either get the mode of function as
# action=<browse|search>, or by setting action_browse or
# action_search.
if argd['action_browse']:
argd['action'] = 'browse'
elif argd['action_search']:
argd['action'] = 'search'
else:
if argd['action'] not in ('browse', 'search'):
argd['action'] = 'search'
del argd['action_browse']
del argd['action_search']
return argd
class WebInterfaceAuthorPage(WebInterfaceDirectory):
""" Handle /author/Doe%2C+John etc """
_exports = ['author']
def __init__(self, authorname=''):
"""Constructor."""
self.authorname = authorname
def _lookup(self, component, path):
"""This handler parses dynamic URLs (/author/John+Doe)."""
return WebInterfaceAuthorPage(component), path
def __call__(self, req, form):
"""Serve the page in the given language."""
argd = wash_urlargd(form, {'ln': (str, CFG_SITE_LANG)})
req.argd = argd #needed since perform_req_search
#wants to check it in case of no results
self.authorname = self.authorname.replace("+"," ")
search_engine.perform_request_search(req=req, p=self.authorname, f="author", of="hb")
index = __call__
class WebInterfaceRecordPages(WebInterfaceDirectory):
""" Handling of a /record/<recid> URL fragment """
_exports = ['', 'files', 'reviews', 'comments', 'usage',
'references', 'export', 'citations']
#_exports.extend(output_formats)
def __init__(self, recid, tab, format=None):
self.recid = recid
self.tab = tab
self.format = format
self.export = self
self.files = WebInterfaceFilesPages(self.recid)
self.reviews = WebInterfaceCommentsPages(self.recid, reviews=1)
self.comments = WebInterfaceCommentsPages(self.recid)
self.usage = self
self.references = self
self.citations = self
self.export = WebInterfaceRecordExport(self.recid, self.format)
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
argd['tab'] = self.tab
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : search_engine.guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
- weburl + '/record/' + str(self.recid) + make_canonical_urlargd(argd, \
+ CFG_SITE_URL + '/record/' + str(self.recid) + make_canonical_urlargd(argd, \
search_results_default_urlargd)}, {'ln' : CFG_SITE_LANG})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
# mod_python does not like to return [] in case when of=id:
out = search_engine.perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
# Return the same page wether we ask for /record/123 or /record/123/
index = __call__
class WebInterfaceRecordRestrictedPages(WebInterfaceDirectory):
""" Handling of a /record-restricted/<recid> URL fragment """
_exports = ['', 'files', 'reviews', 'comments', 'usage',
'references', 'export', 'citations']
#_exports.extend(output_formats)
def __init__(self, recid, tab, format=None):
self.recid = recid
self.tab = tab
self.format = format
self.files = WebInterfaceFilesPages(self.recid)
self.reviews = WebInterfaceCommentsPages(self.recid, reviews=1)
self.comments = WebInterfaceCommentsPages(self.recid)
self.usage = self
self.references = self
self.citations = self
self.export = WebInterfaceRecordExport(self.recid, self.format)
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
user_info = collect_user_info(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
record_primary_collection = search_engine.guess_primary_collection_of_a_record(self.recid)
if collection_restricted_p(record_primary_collection):
(auth_code, dummy) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=record_primary_collection)
if auth_code:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
# Keep all the arguments, they might be reused in the
# record page itself to derivate other queries
req.argd = argd
# mod_python does not like to return [] in case when of=id:
out = search_engine.perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
# Return the same page wether we ask for /record/123 or /record/123/
index = __call__
class WebInterfaceSearchResultsPages(WebInterfaceDirectory):
""" Handling of the /search URL and its sub-pages. """
_exports = ['', 'authenticate', 'cache', 'log']
def __call__(self, req, form):
""" Perform a search. """
argd = wash_search_urlargd(form)
_ = gettext_set_language(argd['ln'])
if req.method == 'POST':
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
uid = getUid(req)
user_info = collect_user_info(req)
if uid == -1:
return page_not_authorized(req, "../",
text = _("You are not authorized to view this area."),
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
involved_collections = Set()
involved_collections.update(argd['c'])
involved_collections.add(argd['cc'])
if argd['id'] > 0:
argd['recid'] = argd['id']
if argd['idb'] > 0:
argd['recidb'] = argd['idb']
if argd['sysno']:
tmp_recid = find_record_from_sysno(argd['sysno'])
if tmp_recid:
argd['recid'] = tmp_recid
if argd['sysnb']:
tmp_recid = find_record_from_sysno(argd['sysnb'])
if tmp_recid:
argd['recidb'] = tmp_recid
if argd['recid'] > 0:
if argd['recidb'] > argd['recid']:
# Hack to check if among the restricted collections
# at least a record of the range is there and
# then if the user is not authorized for that
# collection.
recids = intbitset(xrange(argd['recid'], argd['recidb']))
restricted_colls = restricted_collection_cache.get_cache()
for collname in restricted_colls:
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=collname)
if auth_code:
coll_recids = get_collection(collname).reclist
if coll_recids & recids:
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : collname})
target = '/youraccount/login' + \
make_canonical_urlargd({'action' : cookie, 'ln' : argd['ln'], 'referer' : \
- weburl + '/search' + make_canonical_urlargd(argd, \
+ CFG_SITE_URL + '/search' + make_canonical_urlargd(argd, \
search_results_default_urlargd)}, {'ln' : CFG_SITE_LANG})
return redirect_to_url(req, target)
else:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
else:
involved_collections.add(search_engine.guess_primary_collection_of_a_record(argd['recid']))
# If any of the collection requires authentication, redirect
# to the authentication form.
for coll in involved_collections:
if collection_restricted_p(coll):
(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : coll})
target = '/youraccount/login' + \
make_canonical_urlargd({'action' : cookie, 'ln' : argd['ln'], 'referer' : \
- weburl + '/search' + make_canonical_urlargd(argd, \
+ CFG_SITE_URL + '/search' + make_canonical_urlargd(argd, \
search_results_default_urlargd)}, {'ln' : CFG_SITE_LANG})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
# Keep all the arguments, they might be reused in the
# search_engine itself to derivate other queries
req.argd = argd
# mod_python does not like to return [] in case when of=id:
out = search_engine.perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
def cache(self, req, form):
"""Search cache page."""
argd = wash_urlargd(form, {'action': (str, 'show')})
return search_engine.perform_request_cache(req, action=argd['action'])
def log(self, req, form):
"""Search log page."""
argd = wash_urlargd(form, {'date': (str, '')})
return search_engine.perform_request_log(req, date=argd['date'])
def authenticate(self, req, form):
"""Restricted search results pages."""
argd = wash_search_urlargd(form)
user_info = collect_user_info(req)
for coll in argd['c'] + [argd['cc']]:
if collection_restricted_p(coll):
(auth_code, dummy) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
if auth_code:
return page_not_authorized(req, "../",
text="You are not authorized to view this collection.",
navmenuid='search')
# Keep all the arguments, they might be reused in the
# search_engine itself to derivate other queries
req.argd = argd
uid = getUid(req)
if uid > 0:
pref = get_user_preferences(uid)
try:
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
# mod_python does not like to return [] in case when of=id:
out = search_engine.perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
# Parameters for the legacy URLs, of the form /?c=ALEPH
legacy_collection_default_urlargd = {
'as': (int, 0),
'verbose': (int, 0),
'c': (str, CFG_SITE_NAME)}
class WebInterfaceSearchInterfacePages(WebInterfaceDirectory):
""" Handling of collection navigation."""
_exports = [('index.py', 'legacy_collection'),
('', 'legacy_collection'),
('search.py', 'legacy_search'),
'search', 'openurl', 'testsso']
search = WebInterfaceSearchResultsPages()
def testsso(self, req, form):
""" For testing single sign-on """
req.add_common_vars()
sso_env = {}
for var, value in req.subprocess_env.iteritems():
if var.startswith('HTTP_ADFS_'):
sso_env[var] = value
out = "<HTML><HEAD><TITLE>SSO test</TITLE</HEAD>"
out += "<BODY><TABLE>"
for var, value in sso_env.iteritems():
out += "<TR><TD><STRONG>%s</STRONG></TD><TD>%s</TD></TR>" % (var, value)
out += "</TABLE></BODY></HTML>"
return out
def _lookup(self, component, path):
""" This handler is invoked for the dynamic URLs (for
collections and records)"""
if component == 'collection':
c = '/'.join(path)
def answer(req, form):
"""Accessing collections cached pages."""
# Accessing collections: this is for accessing the
# cached page on top of each collection.
argd = wash_urlargd(form, search_interface_default_urlargd)
# We simply return the cached page of the collection
argd['c'] = c
if not argd['c']:
# collection argument not present; display
# home collection by default
argd['c'] = CFG_SITE_NAME
return display_collection(req, **argd)
return answer, []
elif component == 'record' or component == 'record-restricted':
try:
recid = int(path[0])
except IndexError:
# display record #1 for URL /record without a number
recid = 1
except ValueError:
if path[0] == '':
# display record #1 for URL /record/ without a number
recid = 1
else:
# display page not found for URLs like /record/foo
return None, []
if recid <= 0:
# display page not found for URLs like /record/-5 or /record/0
return None, []
format = None
tab = ''
try:
if path[1] in ['', 'files', 'reviews', 'comments',
'usage', 'references', 'citations']:
tab = path[1]
elif path[1] == 'export':
tab = ''
format = path[2]
# format = None
# elif path[1] in output_formats:
# tab = ''
# format = path[1]
else:
# display page not found for URLs like /record/references
# for a collection where 'references' tabs is not visible
return None, []
except IndexError:
# Keep normal url if tabs is not specified
pass
#if component == 'record-restricted':
#return WebInterfaceRecordRestrictedPages(recid, tab, format), path[1:]
#else:
return WebInterfaceRecordPages(recid, tab, format), path[1:]
return None, []
def openurl(self, req, form):
""" OpenURL Handler."""
argd = wash_urlargd(form, websearch_templates.tmpl_openurl_accepted_args)
ret_url = websearch_templates.tmpl_openurl2invenio(argd)
if ret_url:
return redirect_to_url(req, ret_url)
else:
- return redirect_to_url(req, weburl)
+ return redirect_to_url(req, CFG_SITE_URL)
def legacy_collection(self, req, form):
"""Collection URL backward compatibility handling."""
accepted_args = dict(legacy_collection_default_urlargd)
accepted_args.update({'referer' : (str, '%s/youraccount/your'),
'realm' : (str, '')})
argd = wash_urlargd(form, accepted_args)
# Apache authentication stuff
if argd['realm']:
http_check_credentials(req, argd['realm'])
return redirect_to_url(req, argd['referer'] or '%s/youraccount/youradminactivities' % CFG_SITE_SECURE_URL)
del argd['referer']
del argd['realm']
# If we specify no collection, then we don't need to redirect
# the user, so that accessing <http://yoursite/> returns the
# default collection.
if not form.has_key('c'):
return display_collection(req, **argd)
# make the collection an element of the path, and keep the
# other query elements as is. If the collection is CFG_SITE_NAME,
# however, redirect to the main URL.
c = argd['c']
del argd['c']
if c == CFG_SITE_NAME:
target = '/'
else:
target = '/collection/' + quote(c)
target += make_canonical_urlargd(argd, legacy_collection_default_urlargd)
return redirect_to_url(req, target)
def legacy_search(self, req, form):
"""Search URL backward compatibility handling."""
argd = wash_search_urlargd(form)
# We either jump into the generic search form, or the specific
# /record/... display if a recid is requested
if argd['recid'] != -1:
target = '/record/%d' % argd['recid']
del argd['recid']
else:
target = '/search'
target += make_canonical_urlargd(argd, search_results_default_urlargd)
return redirect_to_url(req, target)
def display_collection(req, c, as, verbose, ln):
"""Display search interface page for collection c by looking
in the collection cache."""
_ = gettext_set_language(ln)
req.argd = drop_default_urlargd({'as': as, 'verbose': verbose, 'ln': ln},
search_interface_default_urlargd)
# get user ID:
try:
uid = getUid(req)
user_preferences = {}
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this collection",
navmenuid='search')
elif uid > 0:
user_preferences = get_user_preferences(uid)
except Error:
return page(title=_("Internal Error"),
body = create_error_box(req, verbose=verbose, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req,
navmenuid='search')
# start display:
req.content_type = "text/html"
req.send_http_header()
# deduce collection id:
colID = get_colID(c)
if type(colID) is not int:
page_body = '<p>' + (_("Sorry, collection %s does not seem to exist.") % ('<strong>' + str(c) + '</strong>')) + '</p>'
- page_body = '<p>' + (_("You may want to start browsing from %s.") % ('<a href="' + weburl + '?ln=' + ln + '">' + get_coll_i18nname(CFG_SITE_NAME, ln) + '</a>')) + '</p>'
+ page_body = '<p>' + (_("You may want to start browsing from %s.") % ('<a href="' + CFG_SITE_URL + '?ln=' + ln + '">' + get_coll_i18nname(CFG_SITE_NAME, ln) + '</a>')) + '</p>'
return page(title=_("Collection %s Not Found") % cgi.escape(c),
body=page_body,
description=(CFG_SITE_NAME + ' - ' + _("Not found") + ': ' + cgi.escape(str(c))),
keywords="%s" % CFG_SITE_NAME,
uid=uid,
language=ln,
req=req,
navmenuid='search')
# display collection interface page:
try:
filedesc = open("%s/collections/%d/navtrail-as=%d-ln=%s.html" % (CFG_CACHEDIR, colID, as, ln), "r")
c_navtrail = filedesc.read()
filedesc.close()
filedesc = open("%s/collections/%d/body-as=%d-ln=%s.html" % (CFG_CACHEDIR, colID, as, ln), "r")
c_body = filedesc.read()
filedesc.close()
filedesc = open("%s/collections/%d/portalbox-tp-ln=%s.html" % (CFG_CACHEDIR, colID, ln), "r")
c_portalbox_tp = filedesc.read()
filedesc.close()
filedesc = open("%s/collections/%d/portalbox-te-ln=%s.html" % (CFG_CACHEDIR, colID, ln), "r")
c_portalbox_te = filedesc.read()
filedesc.close()
filedesc = open("%s/collections/%d/portalbox-lt-ln=%s.html" % (CFG_CACHEDIR, colID, ln), "r")
c_portalbox_lt = filedesc.read()
filedesc.close()
# show help boxes (usually located in "tr", "top right")
# if users have not banned them in their preferences:
c_portalbox_rt = ""
if user_preferences.get('websearch_helpbox', 1) > 0:
filedesc = open("%s/collections/%d/portalbox-rt-ln=%s.html" % (CFG_CACHEDIR, colID, ln), "r")
c_portalbox_rt = filedesc.read()
filedesc.close()
filedesc = open("%s/collections/%d/last-updated-ln=%s.html" % (CFG_CACHEDIR, colID, ln), "r")
c_last_updated = filedesc.read()
filedesc.close()
title = get_coll_i18nname(c, ln)
- rssurl = weburl + '/rss'
+ rssurl = CFG_SITE_URL + '/rss'
if c != CFG_SITE_NAME:
rssurl += '?cc=' + quote(c)
return page(title=title,
body=c_body,
navtrail=c_navtrail,
description="%s - %s" % (CFG_SITE_NAME, c),
keywords="%s, %s" % (CFG_SITE_NAME, c),
uid=uid,
language=ln,
req=req,
cdspageboxlefttopadd=c_portalbox_lt,
cdspageboxrighttopadd=c_portalbox_rt,
titleprologue=c_portalbox_tp,
titleepilogue=c_portalbox_te,
lastupdated=c_last_updated,
navmenuid='search',
rssurl=rssurl)
except:
if verbose >= 9:
req.write("<br />c=%s" % c)
req.write("<br />as=%s" % as)
req.write("<br />ln=%s" % ln)
req.write("<br />colID=%s" % colID)
req.write("<br />uid=%s" % uid)
return page(title=_("Internal Error"),
body = create_error_box(req, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
uid=uid,
language=ln,
req=req,
navmenuid='search')
return "\n"
class WebInterfaceRSSFeedServicePages(WebInterfaceDirectory):
"""RSS 2.0 feed service pages."""
def __call__(self, req, form):
"""RSS 2.0 feed service."""
# Keep only interesting parameters for the search
argd = wash_urlargd(form, websearch_templates.rss_default_urlargd)
for coll in argd['c'] + [argd['cc']]:
if collection_restricted_p(coll):
#user_info = collect_user_info(req)
#(auth_code, auth_msg) = acc_authorize_action(user_info, VIEWRESTRCOLL, collection=coll)
#if auth_code and user_info['email'] == 'guest':
#cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : coll})
#target = '/youraccount/login' + \
#make_canonical_urlargd({'action' : cookie, 'ln' : argd['ln'], 'referer' : \
- #weburl + user_info['uri']}, {})
+ #CFG_SITE_URL + user_info['uri']}, {})
#return redirect_to_url(req, target)
#elif auth_code:
#raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED
raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED
# Create a standard filename with these parameters
args = websearch_templates.build_rss_url(argd).split('/')[-1]
req.content_type = "application/rss+xml"
req.send_http_header()
try:
# Try to read from cache
path = "%s/rss/%s.xml" % (CFG_CACHEDIR, args)
# Check if cache needs refresh
filedesc = open(path, "r")
last_update_time = datetime.datetime.fromtimestamp(os.stat(os.path.abspath(path)).st_mtime)
assert(datetime.datetime.now() < last_update_time + datetime.timedelta(minutes=CFG_WEBSEARCH_RSS_TTL))
c_rss = filedesc.read()
filedesc.close()
req.write(c_rss)
return
except Exception, e:
# do it live and cache
rss_prologue = '<?xml version="1.0" encoding="UTF-8"?>\n' + \
websearch_templates.tmpl_xml_rss_prologue() + '\n'
req.write(rss_prologue)
recIDs = search_engine.perform_request_search(req, of="id",
c=argd['c'], cc=argd['cc'],
p=argd['p'], f=argd['f'],
p1=argd['p1'], f1=argd['f1'],
m1=argd['m1'], op1=argd['op1'],
p2=argd['p2'], f2=argd['f2'],
m2=argd['m2'], op2=argd['op2'],
p3=argd['p3'], f3=argd['f3'],
m3=argd['m3'])[:-(CFG_WEBSEARCH_INSTANT_BROWSE_RSS+1):-1]
rss_body = format_records(recIDs,
of='xr',
record_separator="\n",
req=req, epilogue="\n")
rss_epilogue = websearch_templates.tmpl_xml_rss_epilogue() + '\n'
req.write(rss_epilogue)
# update cache
dirname = "%s/rss" % (CFG_CACHEDIR)
mymkdir(dirname)
fullfilename = "%s/rss/%s.xml" % (CFG_CACHEDIR, args)
try:
# Remove the file just in case it already existed
# so that a bit of space is created
os.remove(fullfilename)
except OSError:
pass
# Check if there's enough space to cache the request.
if len(os.listdir(dirname)) < CFG_WEBSEARCH_RSS_MAX_CACHED_REQUESTS:
try:
os.umask(022)
f = open(fullfilename, "w")
except IOError, v:
raise v
f.write(rss_prologue + rss_body + rss_epilogue)
f.close()
index = __call__
class WebInterfaceRecordExport(WebInterfaceDirectory):
""" Handling of a /record/<recid>/export/<format> URL fragment """
_exports = output_formats
def __init__(self, recid, format=None):
self.recid = recid
self.format = format
for output_format in output_formats:
self.__dict__[output_format] = self
return
def __call__(self, req, form):
argd = wash_search_urlargd(form)
argd['recid'] = self.recid
if self.format is not None:
argd['of'] = self.format
req.argd = argd
uid = getUid(req)
if uid == -1:
return page_not_authorized(req, "../",
text="You are not authorized to view this record.",
navmenuid='search')
elif uid > 0:
pref = get_user_preferences(uid)
try:
argd['rg'] = int(pref['websearch_group_records'])
except (KeyError, ValueError):
pass
# Check if the record belongs to a restricted primary
# collection. If yes, redirect to the authenticated URL.
user_info = collect_user_info(req)
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : search_engine.guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : argd['ln'], 'referer' : \
- weburl + '/record/' + str(self.recid) + make_canonical_urlargd(argd, \
+ CFG_SITE_URL + '/record/' + str(self.recid) + make_canonical_urlargd(argd, \
search_results_default_urlargd)}, {'ln' : CFG_SITE_LANG})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg,\
navmenuid='search')
# mod_python does not like to return [] in case when of=id:
out = search_engine.perform_request_search(req, **argd)
if out == []:
return str(out)
else:
return out
# Return the same page wether we ask for /record/123/export/xm or /record/123/export/xm/
index = __call__
diff --git a/modules/websearch/lib/websearchadmin_regression_tests.py b/modules/websearch/lib/websearchadmin_regression_tests.py
index 4ef42c5db..ec295a93e 100644
--- a/modules/websearch/lib/websearchadmin_regression_tests.py
+++ b/modules/websearch/lib/websearchadmin_regression_tests.py
@@ -1,75 +1,75 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSearch Admin Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class WebSearchAdminWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebSearch Admin web pages whether they are up or not."""
def test_websearch_admin_interface_pages_availability(self):
"""websearchadmin - availability of WebSearch Admin interface pages"""
- baseurl = weburl + '/admin/websearch/websearchadmin.py'
+ baseurl = CFG_SITE_URL + '/admin/websearch/websearchadmin.py'
_exports = ['',
'?mtype=perform_showall',
'?mtype=perform_addcollection',
'?mtype=perform_addcollectiontotree',
'?mtype=perform_modifycollectiontree',
'?mtype=perform_checkwebcollstatus',
'?mtype=perform_checkcollectionstatus',]
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_websearch_admin_guide_availability(self):
"""websearchadmin - availability of WebSearch Admin guide pages"""
- url = weburl + '/help/admin/websearch-admin-guide'
+ url = CFG_SITE_URL + '/help/admin/websearch-admin-guide'
error_messages = test_web_page_content(url,
expected_text="WebSearch Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(WebSearchAdminWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/websearch/lib/websearchadminlib.py b/modules/websearch/lib/websearchadminlib.py
index efdda8d5a..1791b7e82 100644
--- a/modules/websearch/lib/websearchadminlib.py
+++ b/modules/websearch/lib/websearchadminlib.py
@@ -1,3247 +1,3247 @@
## $Id$
## Administrator interface for WebSearch
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
# pylint: disable-msg=C0301
"""CDS Invenio WebSearch Administrator Interface."""
__revision__ = "$Id$"
import cgi
import re
import random
import time
from invenio.config import \
CFG_CACHEDIR, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
- weburl,\
+ CFG_SITE_URL,\
CFG_WEBCOMMENT_ALLOW_COMMENTS,\
CFG_WEBCOMMENT_ALLOW_REVIEWS
from invenio.bibrankadminlib import \
write_outcome, \
modify_translations, \
get_def_name, \
get_name, \
get_languages, \
addadminbox, \
tupletotable, \
createhiddenform
from invenio.dbquery import \
run_sql, \
get_table_update_time
from invenio.websearch_external_collections import \
external_collections_dictionary, \
external_collection_sort_engine_by_name, \
external_collection_get_state, \
external_collection_get_update_state_list, \
external_collection_apply_changes
from invenio.websearch_external_collections_utils import \
get_collection_descendants
from invenio.websearch_external_collections_config import CFG_EXTERNAL_COLLECTION_STATES_NAME
from invenio.bibformat_elements import bfe_references
from invenio.bibformat_engine import BibFormatObject
from invenio.bibdocfile import BibRecDocs
from invenio.messages import gettext_set_language
from invenio.bibrank_citation_searcher import get_cited_by
from invenio.access_control_admin import acc_get_action_id
from invenio.access_control_config import VIEWRESTRCOLL
def getnavtrail(previous = ''):
"""Get the navtrail"""
- navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (weburl,)
+ navtrail = """<a class="navtrail" href="%s/help/admin">Admin Area</a> """ % (CFG_SITE_URL,)
navtrail = navtrail + previous
return navtrail
def perform_modifytranslations(colID, ln, sel_type='', trans=[], confirm=-1, callback='yes'):
"""Modify the translations of a collection
sel_type - the nametype to modify
trans - the translations in the same order as the languages from get_languages()"""
output = ''
subtitle = ''
sitelangs = get_languages()
if confirm in ["2", 2] and colID:
finresult = modify_translations(colID, sitelangs, sel_type, trans, "collection")
col_dict = dict(get_def_name('', "collection"))
if colID and col_dict.has_key(int(colID)):
colID = int(colID)
- subtitle = """<a name="3">3. Modify translations for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a href="%s/help/admin/websearch-admin-guide#3.3">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="3">3. Modify translations for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a href="%s/help/admin/websearch-admin-guide#3.3">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
if type(trans) is str:
trans = [trans]
if sel_type == '':
sel_type = get_col_nametypes()[0][0]
header = ['Language', 'Translation']
actions = []
types = get_col_nametypes()
if len(types) > 1:
text = """
<span class="adminlabel">Name type</span>
<select name="sel_type" class="admin_w200">
"""
for (key, value) in types:
text += """<option value="%s" %s>%s""" % (key, key == sel_type and 'selected="selected"' or '', value)
trans_names = get_name(colID, ln, key, "collection")
if trans_names and trans_names[0][0]:
text += ": %s" % trans_names[0][0]
text += "</option>"
text += """</select>"""
output += createhiddenform(action="modifytranslations#3",
text=text,
button="Select",
colID=colID,
ln=ln,
confirm=0)
if confirm in [-1, "-1", 0, "0"]:
trans = []
for (key, value) in sitelangs:
try:
trans_names = get_name(colID, key, sel_type, "collection")
trans.append(trans_names[0][0])
except StandardError, e:
trans.append('')
for nr in range(0, len(sitelangs)):
actions.append(["%s %s" % (sitelangs[nr][1], (sitelangs[nr][0]==CFG_SITE_LANG and '<small>(def)</small>' or ''))])
actions[-1].append('<input type="text" name="trans" size="30" value="%s"/>' % trans[nr])
text = tupletotable(header=header, tuple=actions)
output += createhiddenform(action="modifytranslations#3",
text=text,
button="Modify",
colID=colID,
sel_type=sel_type,
ln=ln,
confirm=2)
if sel_type and len(trans) and confirm in ["2", 2]:
output += write_outcome(finresult)
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_modifytranslations", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyrankmethods(colID, ln, func='', rnkID='', confirm=0, callback='yes'):
"""Modify which rank methods is visible to the collection
func - remove or add rank method
rnkID - the id of the rank method."""
output = ""
subtitle = ""
col_dict = dict(get_def_name('', "collection"))
rnk_dict = dict(get_def_name('', "rnkMETHOD"))
if colID and col_dict.has_key(int(colID)):
colID = int(colID)
if func in ["0", 0] and confirm in ["1", 1]:
finresult = attach_rnk_col(colID, rnkID)
elif func in ["1", 1] and confirm in ["1", 1]:
finresult = detach_rnk_col(colID, rnkID)
- subtitle = """<a name="9">9. Modify rank options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.9">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="9">9. Modify rank options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.9">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """
<dl>
<dt>The rank methods enabled for the collection '%s' is:</dt>
""" % col_dict[colID]
rnkmethods = get_col_rnk(colID, ln)
output += """<dd>"""
if not rnkmethods:
output += """No rank methods"""
else:
for id, name in rnkmethods:
output += """%s, """ % name
output += """</dd>
</dl>
"""
rnk_list = get_def_name('', "rnkMETHOD")
rnk_dict_in_col = dict(get_col_rnk(colID, ln))
rnk_list = filter(lambda x: not rnk_dict_in_col.has_key(x[0]), rnk_list)
if rnk_list:
text = """
<span class="adminlabel">Enable:</span>
<select name="rnkID" class="admin_w200">
<option value="-1">- select rank method -</option>
"""
for (id, name) in rnk_list:
text += """<option value="%s" %s>%s</option>""" % (id, (func in ["0", 0] and confirm in ["0", 0] and int(rnkID) == int(id)) and 'selected="selected"' or '' , name)
text += """</select>"""
output += createhiddenform(action="modifyrankmethods#9",
text=text,
button="Enable",
colID=colID,
ln=ln,
func=0,
confirm=1)
if confirm in ["1", 1] and func in ["0", 0] and int(rnkID) != -1:
output += write_outcome(finresult)
elif confirm not in ["0", 0] and func in ["0", 0]:
output += """<b><span class="info">Please select a rank method.</span></b>"""
coll_list = get_col_rnk(colID, ln)
if coll_list:
text = """
<span class="adminlabel">Disable:</span>
<select name="rnkID" class="admin_w200">
<option value="-1">- select rank method-</option>
"""
for (id, name) in coll_list:
text += """<option value="%s" %s>%s</option>""" % (id, (func in ["1", 1] and confirm in ["0", 0] and int(rnkID) == int(id)) and 'selected="selected"' or '' , name)
text += """</select>"""
output += createhiddenform(action="modifyrankmethods#9",
text=text,
button="Disable",
colID=colID,
ln=ln,
func=1,
confirm=1)
if confirm in ["1", 1] and func in ["1", 1] and int(rnkID) != -1:
output += write_outcome(finresult)
elif confirm not in ["0", 0] and func in ["1", 1]:
output += """<b><span class="info">Please select a rank method.</span></b>"""
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_modifyrankmethods", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_addcollectiontotree(colID, ln, add_dad='', add_son='', rtype='', mtype='', callback='yes', confirm=-1):
"""Form to add a collection to the tree.
add_dad - the dad to add the collection to
add_son - the collection to add
rtype - add it as a regular or virtual
mtype - add it to the regular or virtual tree."""
output = ""
output2 = ""
- subtitle = """Attach collection to tree&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.2">?</a>]</small>""" % (weburl)
+ subtitle = """Attach collection to tree&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.2">?</a>]</small>""" % (CFG_SITE_URL)
col_dict = dict(get_def_name('', "collection"))
if confirm not in [-1, "-1"] and not (add_son and add_dad and rtype):
output2 += """<b><span class="info">All fields must be filled.</span></b><br /><br />
"""
elif add_son and add_dad and rtype:
add_son = int(add_son)
add_dad = int(add_dad)
if confirm not in [-1, "-1"]:
if add_son == add_dad:
output2 += """<b><span class="info">Cannot add a collection as a pointer to itself.</span></b><br /><br />
"""
elif check_col(add_dad, add_son):
res = add_col_dad_son(add_dad, add_son, rtype)
output2 += write_outcome(res)
if res[0] == 1:
output2 += """<b><span class="info"><br /> The collection will appear on your website after the next webcoll run. You can either run it manually or wait until bibsched does it for you.</span></b><br /><br />
"""
else:
output2 += """<b><span class="info">Cannot add the collection '%s' as a %s subcollection of '%s' since it will either create a loop, or the association already exists.</span></b><br /><br />
""" % (col_dict[add_son], (rtype=="r" and 'regular' or 'virtual'), col_dict[add_dad])
add_son = ''
add_dad = ''
rtype = ''
tree = get_col_tree(colID)
col_list = col_dict.items()
col_list.sort(compare_on_val)
output = show_coll_not_in_tree(colID, ln, col_dict)
text = """
<span class="adminlabel">Attach collection:</span>
<select name="add_son" class="admin_w200">
<option value="">- select collection -</option>
"""
for (id, name) in col_list:
if id != colID:
text += """<option value="%s" %s>%s</option>""" % (id, str(id)==str(add_son) and 'selected="selected"' or '', name)
text += """
</select><br />
<span class="adminlabel">to parent collection:</span>
<select name="add_dad" class="admin_w200">
<option value="">- select parent collection -</option>
"""
for (id, name) in col_list:
text += """<option value="%s" %s>%s</option>
""" % (id, str(id)==add_dad and 'selected="selected"' or '', name)
text += """</select><br />
"""
text += """
<span class="adminlabel">with relationship:</span>
<select name="rtype" class="admin_w200">
<option value="">- select relationship -</option>
<option value="r" %s>Regular (Narrow by...)</option>
<option value="v" %s>Virtual (Focus on...)</option>
</select>
""" % ((rtype=="r" and 'selected="selected"' or ''), (rtype=="v" and 'selected="selected"' or ''))
- output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/addcollectiontotree" % weburl,
+ output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/addcollectiontotree" % CFG_SITE_URL,
text=text,
button="Add",
colID=colID,
ln=ln,
confirm=1)
output += output2
#output += perform_showtree(colID, ln)
body = [output]
if callback:
return perform_index(colID, ln, mtype="perform_addcollectiontotree", content=addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_addcollection(colID, ln, colNAME='', dbquery='', callback="yes", confirm=-1):
"""form to add a new collection.
colNAME - the name of the new collection
dbquery - the dbquery of the new collection"""
output = ""
- subtitle = """Create new collection&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.1">?</a>]</small>""" % (weburl)
+ subtitle = """Create new collection&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.1">?</a>]</small>""" % (CFG_SITE_URL)
text = """
<span class="adminlabel">Default name</span>
<input class="admin_w200" type="text" name="colNAME" value="%s" /><br />
""" % colNAME
- output = createhiddenform(action="%s/admin/websearch/websearchadmin.py/addcollection" % weburl,
+ output = createhiddenform(action="%s/admin/websearch/websearchadmin.py/addcollection" % CFG_SITE_URL,
text=text,
colID=colID,
ln=ln,
button="Add collection",
confirm=1)
if colNAME and confirm in ["1", 1]:
res = add_col(colNAME, '')
output += write_outcome(res)
if res[0] == 1:
output += perform_addcollectiontotree(colID=colID, ln=ln, add_son=res[1], callback='')
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please give the collection a name.</span></b>"""
body = [output]
if callback:
return perform_index(colID, ln=ln, mtype="perform_addcollection", content=addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifydbquery(colID, ln, dbquery='', callback='yes', confirm=-1):
"""form to modify the dbquery of the collection.
dbquery - the dbquery of the collection."""
subtitle = ''
output = ""
col_dict = dict(get_def_name('', "collection"))
if colID and col_dict.has_key(int(colID)):
colID = int(colID)
- subtitle = """<a name="1">1. Modify collection query for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.1">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="1">1. Modify collection query for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.1">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
if confirm == -1:
res = run_sql("SELECT dbquery FROM collection WHERE id=%s" % colID)
dbquery = res[0][0]
if not dbquery:
dbquery = ''
reg_sons = len(get_col_tree(colID, 'r'))
vir_sons = len(get_col_tree(colID, 'v'))
if reg_sons > 1:
if dbquery:
output += "Warning: This collection got subcollections, and should because of this not have a collection query, for further explanation, check the WebSearch Guide<br />"
elif reg_sons <= 1:
if not dbquery:
output += "Warning: This collection does not have any subcollections, and should because of this have a collection query, for further explanation, check the WebSearch Guide<br />"
text = """
<span class="adminlabel">Query</span>
<input class="admin_w200" type="text" name="dbquery" value="%s" /><br />
""" % cgi.escape(dbquery, 1)
output += createhiddenform(action="modifydbquery",
text=text,
button="Modify",
colID=colID,
ln=ln,
confirm=1)
if confirm in ["1", 1]:
res = modify_dbquery(colID, dbquery)
if res:
if dbquery == "":
text = """<b><span class="info">Query removed for this collection.</span></b>"""
else:
text = """<b><span class="info">Query set for this collection.</span></b>"""
else:
text = """<b><span class="info">Sorry, could not change query.</span></b>"""
output += text
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_modifydbquery", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifycollectiontree(colID, ln, move_up='', move_down='', move_from='', move_to='', delete='', rtype='', callback='yes', confirm=0):
"""to modify the collection tree: move a collection up and down, delete a collection, or change the father of the collection.
colID - the main collection of the tree, the root
move_up - move this collection up (is not the collection id, but the place in the tree)
move_up - move this collection down (is not the collection id, but the place in the tree)
move_from - move this collection from the current positon (is not the collection id, but the place in the tree)
move_to - move the move_from collection and set this as it's father. (is not the collection id, but the place in the tree)
delete - delete this collection from the tree (is not the collection id, but the place in the tree)
rtype - the type of the collection in the tree, regular or virtual"""
colID = int(colID)
tree = get_col_tree(colID, rtype)
col_dict = dict(get_def_name('', "collection"))
- subtitle = """Modify collection tree: %s&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.3">?</a>]&nbsp;&nbsp;&nbsp;<a href="%s/admin/websearch/websearchadmin.py/showtree?colID=%s&amp;ln=%s">Printer friendly version</a></small>""" % (col_dict[colID], weburl, weburl, colID, ln)
+ subtitle = """Modify collection tree: %s&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#2.3">?</a>]&nbsp;&nbsp;&nbsp;<a href="%s/admin/websearch/websearchadmin.py/showtree?colID=%s&amp;ln=%s">Printer friendly version</a></small>""" % (col_dict[colID], CFG_SITE_URL, CFG_SITE_URL, colID, ln)
fin_output = ""
output = ""
try:
if move_up:
move_up = int(move_up)
switch = find_last(tree, move_up)
if switch and switch_col_treescore(tree[move_up], tree[switch]):
output += """<b><span class="info">Moved the %s collection '%s' up and '%s' down.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]], col_dict[tree[switch][0]])
else:
output += """<b><span class="info">Could not move the %s collection '%s' up and '%s' down.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]], col_dict[tree[switch][0]])
elif move_down:
move_down = int(move_down)
switch = find_next(tree, move_down)
if switch and switch_col_treescore(tree[move_down], tree[switch]):
output += """<b><span class="info">Moved the %s collection '%s' down and '%s' up.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_down][0]], col_dict[tree[switch][0]])
else:
output += """<b><span class="info">Could not move the %s collection '%s' up and '%s' down.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_up][0]],col_dict[tree[switch][0]])
elif delete:
delete = int(delete)
if confirm in [0, "0"]:
if col_dict[tree[delete][0]] != col_dict[tree[delete][3]]:
text = """<b>Do you want to remove the %s collection '%s' and its subcollections in the %s collection '%s'.</b>
""" % ((tree[delete][4]=="r" and 'regular' or 'virtual'), col_dict[tree[delete][0]], (rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]])
else:
text = """<b>Do you want to remove all subcollections of the %s collection '%s'.</b>
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]])
- output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifycollectiontree#tree" % weburl,
+ output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifycollectiontree#tree" % CFG_SITE_URL,
text=text,
button="Confirm",
colID=colID,
delete=delete,
rtype=rtype,
ln=ln,
confirm=1)
- output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % weburl,
+ output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL,
text="<b>To cancel</b>",
button="Cancel",
colID=colID,
ln=ln)
else:
if remove_col_subcol(tree[delete][0], tree[delete][3], rtype):
if col_dict[tree[delete][0]] != col_dict[tree[delete][3]]:
output += """<b><span class="info">Removed the %s collection '%s' and its subcollections in subdirectory '%s'.</span></b><br /><br />
""" % ((tree[delete][4]=="r" and 'regular' or 'virtual'), col_dict[tree[delete][0]], col_dict[tree[delete][3]])
else:
output += """<b><span class="info">Removed the subcollections of the %s collection '%s'.</span></b><br /><br />
""" % ((rtype=="r" and 'regular' or 'virtual'), col_dict[tree[delete][3]])
else:
output += """<b><span class="info">Could not remove the collection from the tree.</span></b><br /><br />
"""
delete = ''
elif move_from and not move_to:
move_from_rtype = move_from[0]
move_from_id = int(move_from[1:len(move_from)])
text = """<b>Select collection to place the %s collection '%s' under.</b><br /><br />
""" % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree[move_from_id][0]])
- output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % weburl,
+ output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL,
text=text,
button="Cancel",
colID=colID,
ln=ln)
elif move_from and move_to:
move_from_rtype = move_from[0]
move_from_id = int(move_from[1:len(move_from)])
move_to_rtype = move_to[0]
move_to_id = int(move_to[1:len(move_to)])
tree_from = get_col_tree(colID, move_from_rtype)
tree_to = get_col_tree(colID, move_to_rtype)
if confirm in [0, '0']:
if move_from_id == move_to_id and move_from_rtype == move_to_rtype:
output += """<b><span class="info">Cannot move to itself.</span></b><br /><br />
"""
elif tree_from[move_from_id][3] == tree_to[move_to_id][0] and move_from_rtype==move_to_rtype:
output += """<b><span class="info">The collection is already there.</span></b><br /><br />
"""
elif check_col(tree_to[move_to_id][0], tree_from[move_from_id][0]) or (tree_to[move_to_id][0] == 1 and tree_from[move_from_id][3] == tree_to[move_to_id][0] and move_from_rtype != move_to_rtype):
text = """<b>Move %s collection '%s' to the %s collection '%s'.</b>
""" % ((tree_from[move_from_id][4]=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (tree_to[move_to_id][4]=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]])
- output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifycollectiontree#tree" % weburl,
+ output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifycollectiontree#tree" % CFG_SITE_URL,
text=text,
button="Confirm",
colID=colID,
move_from=move_from,
move_to=move_to,
ln=ln,
rtype=rtype,
confirm=1)
- output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % weburl,
+ output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/index?mtype=perform_modifycollectiontree#tree" % CFG_SITE_URL,
text="""<b>To cancel</b>""",
button="Cancel",
colID=colID,
ln=ln)
else:
output += """<b><span class="info">Cannot move the collection '%s' and set it as a subcollection of '%s' since it will create a loop.</span></b><br /><br />
""" % (col_dict[tree_from[move_from_id][0]], col_dict[tree_to[move_to_id][0]])
else:
if (move_to_id != 0 and move_col_tree(tree_from[move_from_id], tree_to[move_to_id])) or (move_to_id == 0 and move_col_tree(tree_from[move_from_id], tree_to[move_to_id], move_to_rtype)):
output += """<b><span class="info">Moved %s collection '%s' to the %s collection '%s'.</span></b><br /><br />
""" % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (move_to_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]])
else:
output += """<b><span class="info">Could not move %s collection '%s' to the %s collection '%s'.</span></b><br /><br />
""" % ((move_from_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_from[move_from_id][0]], (move_to_rtype=="r" and 'regular' or 'virtual'), col_dict[tree_to[move_to_id][0]])
move_from = ''
move_to = ''
else:
output += """
"""
except StandardError, e:
return """<b><span class="info">An error occured.</span></b>
"""
output += """<table border ="0" width="100%">
<tr><td width="50%">
<b>Narrow by collection:</b>
</td><td width="50%">
<b>Focus on...:</b>
</td></tr><tr><td valign="top">
"""
tree = get_col_tree(colID, 'r')
output += create_colltree(tree, col_dict, colID, ln, move_from, move_to, 'r', "yes")
output += """</td><td valign="top">
"""
tree = get_col_tree(colID, 'v')
output += create_colltree(tree, col_dict, colID, ln, move_from, move_to, 'v', "yes")
output += """</td>
</tr>
</table>
"""
body = [output]
if callback:
return perform_index(colID, ln, mtype="perform_modifycollectiontree", content=addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showtree(colID, ln):
"""create collection tree/hiarchy"""
col_dict = dict(get_def_name('', "collection"))
subtitle = "Collection tree: %s" % col_dict[int(colID)]
output = """<table border ="0" width="100%">
<tr><td width="50%">
<b>Narrow by collection:</b>
</td><td width="50%">
<b>Focus on...:</b>
</td></tr><tr><td valign="top">
"""
tree = get_col_tree(colID, 'r')
output += create_colltree(tree, col_dict, colID, ln, '', '', 'r', '')
output += """</td><td valign="top">
"""
tree = get_col_tree(colID, 'v')
output += create_colltree(tree, col_dict, colID, ln, '', '', 'v', '')
output += """</td>
</tr>
</table>
"""
body = [output]
return addadminbox(subtitle, body)
def perform_addportalbox(colID, ln, title='', body='', callback='yes', confirm=-1):
"""form to add a new portalbox
title - the title of the portalbox
body - the body of the portalbox"""
col_dict = dict(get_def_name('', "collection"))
colID = int(colID)
subtitle = """<a name="5.1"></a>Create new portalbox"""
text = """
<span class="adminlabel">Title</span>
<textarea cols="50" rows="1" class="admin_wvar" type="text" name="title">%s</textarea><br />
<span class="adminlabel">Body</span>
<textarea cols="50" rows="10" class="admin_wvar" type="text" name="body">%s</textarea><br />
""" % (cgi.escape(title), cgi.escape(body))
output = createhiddenform(action="addportalbox#5.1",
text=text,
button="Add",
colID=colID,
ln=ln,
confirm=1)
if body and confirm in [1, "1"]:
res = add_pbx(title, body)
output += write_outcome(res)
if res[1] == 1:
output += """<b><span class="info"><a href="addexistingportalbox?colID=%s&amp;ln=%s&amp;pbxID=%s#5">Add portalbox to collection</a></span></b>""" % (colID, ln, res[1])
elif confirm not in [-1, "-1"]:
output += """<b><span class="info">Body field must be filled.</span></b>
"""
body = [output]
return perform_showportalboxes(colID, ln, content=addadminbox(subtitle, body))
def perform_addexistingportalbox(colID, ln, pbxID=-1, score=0, position='', sel_ln='', callback='yes', confirm=-1):
"""form to add an existing portalbox to a collection.
colID - the collection to add the portalbox to
pbxID - the portalbox to add
score - the importance of the portalbox.
position - the position of the portalbox on the page
sel_ln - the language of the portalbox"""
subtitle = """<a name="5.2"></a>Add existing portalbox to collection"""
output = ""
colID = int(colID)
res = get_pbx()
pos = get_pbx_pos()
lang = dict(get_languages())
col_dict = dict(get_def_name('', "collection"))
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
col_pbx = get_col_pbx(colID)
col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx))
if len(res) > 0:
text = """
<span class="adminlabel">Portalbox</span>
<select name="pbxID" class="admin_w200">
<option value="-1">- Select portalbox -</option>
"""
for (id, t_title, t_body) in res:
text += """<option value="%s" %s>%s - %s...</option>""" % \
(id, id == int(pbxID) and 'selected="selected"' or '',
t_title, cgi.escape(t_body[0:25 - len(t_title)]))
text += """</select><br />
<span class="adminlabel">Language</span>
<select name="sel_ln" class="admin_w200">
<option value="">- Select language -</option>
"""
listlang = lang.items()
listlang.sort()
for (key, name) in listlang:
text += """<option value="%s" %s>%s</option>
""" % (key, key == sel_ln and 'selected="selected"' or '', name)
text += """</select><br />
<span class="adminlabel">Position</span>
<select name="position" class="admin_w200">
<option value="">- Select position -</option>
"""
listpos = pos.items()
listpos.sort()
for (key, name) in listpos:
text += """<option value="%s" %s>%s</option>""" % (key, key==position and 'selected="selected"' or '', name)
text += "</select>"
output += createhiddenform(action="addexistingportalbox#5.2",
text=text,
button="Add",
colID=colID,
ln=ln,
confirm=1)
else:
output = """No existing portalboxes to add, please create a new one.
"""
if pbxID > -1 and position and sel_ln and confirm in [1, "1"]:
pbxID = int(pbxID)
res = add_col_pbx(colID, pbxID, sel_ln, position, '')
output += write_outcome(res)
elif pbxID > -1 and confirm not in [-1, "-1"]:
output += """<b><span class="info">All fields must be filled.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showportalboxes(colID, ln, content=output)
def perform_deleteportalbox(colID, ln, pbxID=-1, callback='yes', confirm=-1):
"""form to delete a portalbox which is not in use.
colID - the current collection.
pbxID - the id of the portalbox"""
subtitle = """<a name="5.3"></a>Delete an unused portalbox"""
output = ""
colID = int(colID)
if pbxID not in [-1, "-1"] and confirm in [1, "1"]:
ares = get_pbx()
pbx_dict = dict(map(lambda x: (x[0], x[1]), ares))
if pbx_dict.has_key(int(pbxID)):
pname = pbx_dict[int(pbxID)]
ares = delete_pbx(int(pbxID))
else:
return """<b><span class="info">This portalbox does not exist</span></b>"""
res = get_pbx()
col_dict = dict(get_def_name('', "collection"))
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
col_pbx = get_col_pbx()
col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx))
if len(res) > 0:
text = """
<span class="adminlabel">Portalbox</span>
<select name="pbxID" class="admin_w200">
"""
text += """<option value="-1">- Select portalbox -"""
for (id, t_title, t_body) in res:
if not col_pbx.has_key(id):
text += """<option value="%s" %s>%s - %s...""" % (id, id == int(pbxID) and 'selected="selected"' or '', t_title, cgi.escape(t_body[0:10]))
text += "</option>"
text += """</select><br />"""
output += createhiddenform(action="deleteportalbox#5.3",
text=text,
button="Delete",
colID=colID,
ln=ln,
confirm=1)
if pbxID not in [-1, "-1"]:
pbxID = int(pbxID)
if confirm in [1, "1"]:
output += write_outcome(ares)
elif confirm not in [-1, "-1"]:
output += """<b><span class="info">Choose a portalbox to delete.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showportalboxes(colID, ln, content=output)
def perform_modifyportalbox(colID, ln, pbxID=-1, score='', position='', sel_ln='', title='', body='', callback='yes', confirm=-1):
"""form to modify a portalbox in a collection, or change the portalbox itself.
colID - the id of the collection.
pbxID - the portalbox to change
score - the score of the portalbox connected to colID which should be changed.
position - the position of the portalbox in collection colID to change."""
subtitle = ""
output = ""
colID = int(colID)
res = get_pbx()
pos = get_pbx_pos()
lang = dict(get_languages())
col_dict = dict(get_def_name('', "collection"))
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
col_pbx = get_col_pbx(colID)
col_pbx = dict(map(lambda x: (x[0], x[5]), col_pbx))
if pbxID not in [-1, "-1"]:
pbxID = int(pbxID)
subtitle = """<a name="5.4"></a>Modify portalbox '%s' for this collection""" % pbx_dict[pbxID]
col_pbx = get_col_pbx(colID)
if not (score and position) and not (body and title):
for (id_pbx, id_collection, tln, score, position, title, body) in col_pbx:
if id_pbx == pbxID:
break
output += """Collection (presentation) specific values (Changes implies only to this collection.)<br />"""
text = """
<span class="adminlabel">Position</span>
<select name="position" class="admin_w200">
"""
listpos = pos.items()
listpos.sort()
for (key, name) in listpos:
text += """<option value="%s" %s>%s""" % (key, key==position and 'selected="selected"' or '', name)
text += "</option>"
text += """</select><br />"""
output += createhiddenform(action="modifyportalbox#5.4",
text=text,
button="Modify",
colID=colID,
pbxID=pbxID,
score=score,
title=title,
body=cgi.escape(body, 1),
sel_ln=sel_ln,
ln=ln,
confirm=3)
if pbxID > -1 and score and position and confirm in [3, "3"]:
pbxID = int(pbxID)
res = modify_pbx(colID, pbxID, sel_ln, score, position, '', '')
res2 = get_pbx()
pbx_dict = dict(map(lambda x: (x[0], x[1]), res2))
output += write_outcome(res)
output += """<br />Portalbox (content) specific values (any changes appears everywhere the portalbox is used.)"""
text = """
<span class="adminlabel">Title</span>
<textarea cols="50" rows="1" class="admin_wvar" type="text" name="title">%s</textarea><br />
""" % cgi.escape(title)
text += """
<span class="adminlabel">Body</span>
<textarea cols="50" rows="10" class="admin_wvar" type="text" name="body">%s</textarea><br />
""" % cgi.escape(body)
output += createhiddenform(action="modifyportalbox#5.4",
text=text,
button="Modify",
colID=colID,
pbxID=pbxID,
sel_ln=sel_ln,
score=score,
position=position,
ln=ln,
confirm=4)
if pbxID > -1 and confirm in [4, "4"]:
pbxID = int(pbxID)
res = modify_pbx(colID, pbxID, sel_ln, '', '', title, body)
output += write_outcome(res)
else:
output = """No portalbox to modify."""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showportalboxes(colID, ln, content=output)
def perform_switchpbxscore(colID, id_1, id_2, sel_ln, ln):
"""Switch the score of id_1 and id_2 in collection_portalbox.
colID - the current collection
id_1/id_2 - the id's to change the score for.
sel_ln - the language of the portalbox"""
output = ""
res = get_pbx()
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
res = switch_pbx_score(colID, id_1, id_2, sel_ln)
output += write_outcome(res)
return perform_showportalboxes(colID, ln, content=output)
def perform_showportalboxes(colID, ln, callback='yes', content='', confirm=-1):
"""show the portalboxes of this collection.
colID - the portalboxes to show the collection for."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
- subtitle = """<a name="5">5. Modify portalboxes for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.5">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="5">5. Modify portalboxes for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.5">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = ""
pos = get_pbx_pos()
output = """<dl>
<dt>Portalbox actions (not related to this collection)
<dd><a href="addportalbox?colID=%s&amp;ln=%s#5.1">Create new portalbox</a></dd>
<dd><a href="deleteportalbox?colID=%s&amp;ln=%s#5.3">Delete an unused portalbox</a></dd>
<dt>Collection specific actions
<dd><a href="addexistingportalbox?colID=%s&amp;ln=%s#5.2">Add existing portalbox to collection</a></dd>
</dl>
""" % (colID, ln, colID, ln, colID, ln)
header = ['Position', 'Language', '', 'Title', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
pos_list = pos.items()
pos_list.sort()
if len(get_col_pbx(colID)) > 0:
for (key, value) in sitelangs:
for (pos_key, pos_value) in pos_list:
res = get_col_pbx(colID, key, pos_key)
i = 0
for (pbxID, colID_pbx, tln, score, position, title, body) in res:
move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>"""
if i != 0:
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchpbxscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;sel_ln=%s&amp;rand=%s#5"><img border="0" src="%s/img/smallup.gif" title="Move portalbox up"></a>""" % (weburl, colID, ln, pbxID, res[i - 1][0], tln, random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchpbxscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;sel_ln=%s&amp;rand=%s#5"><img border="0" src="%s/img/smallup.gif" title="Move portalbox up"></a>""" % (CFG_SITE_URL, colID, ln, pbxID, res[i - 1][0], tln, random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
move += "</td><td>"
i += 1
if i != len(res):
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchpbxscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;sel_ln=%s&amp;rand=%s#5"><img border="0" src="%s/img/smalldown.gif" title="Move portalbox down"></a>""" % (weburl, colID, ln, pbxID, res[i][0], tln, random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchpbxscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;sel_ln=%s&amp;rand=%s#5"><img border="0" src="%s/img/smalldown.gif" title="Move portalbox down"></a>""" % (CFG_SITE_URL, colID, ln, pbxID, res[i][0], tln, random.randint(0, 1000), CFG_SITE_URL)
move += """</td></tr></table>"""
actions.append(["%s" % (i==1 and pos[position] or ''), "%s" % (i==1 and lang[tln] or ''), move, "%s" % title])
for col in [(('Modify', 'modifyportalbox'), ('Remove', 'removeportalbox'),)]:
- actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;pbxID=%s&amp;sel_ln=%s#5.4">%s</a>' % (weburl, col[0][1], colID, ln, pbxID, tln, col[0][0]))
+ actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;pbxID=%s&amp;sel_ln=%s#5.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, pbxID, tln, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;pbxID=%s&amp;sel_ln=%s#5.5">%s</a>' % (weburl, function, colID, ln, pbxID, tln, str)
+ actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;pbxID=%s&amp;sel_ln=%s#5.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, pbxID, tln, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No portalboxes exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showportalboxes", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_removeportalbox(colID, ln, pbxID='', sel_ln='', callback='yes', confirm=0):
"""form to remove a portalbox from a collection.
colID - the current collection, remove the portalbox from this collection.
sel_ln - remove the portalbox with this language
pbxID - remove the portalbox with this id"""
subtitle = """<a name="5.5"></a>Remove portalbox"""
output = ""
col_dict = dict(get_def_name('', "collection"))
res = get_pbx()
pbx_dict = dict(map(lambda x: (x[0], x[1]), res))
if colID and pbxID and sel_ln:
colID = int(colID)
pbxID = int(pbxID)
if confirm in ["0", 0]:
text = """Do you want to remove the portalbox '%s' from the collection '%s'.""" % (pbx_dict[pbxID], col_dict[colID])
output += createhiddenform(action="removeportalbox#5.5",
text=text,
button="Confirm",
colID=colID,
pbxID=pbxID,
sel_ln=sel_ln,
confirm=1)
elif confirm in ["1", 1]:
res = remove_pbx(colID, pbxID, sel_ln)
output += write_outcome(res)
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showportalboxes(colID, ln, content=output)
def perform_switchfmtscore(colID, type, id_1, id_2, ln):
"""Switch the score of id_1 and id_2 in the table type.
colID - the current collection
id_1/id_2 - the id's to change the score for.
type - like "format" """
fmt_dict = dict(get_def_name('', "format"))
res = switch_score(colID, id_1, id_2, type)
output = write_outcome(res)
return perform_showoutputformats(colID, ln, content=output)
def perform_switchfldscore(colID, id_1, id_2, fmeth, ln):
"""Switch the score of id_1 and id_2 in collection_field_fieldvalue.
colID - the current collection
id_1/id_2 - the id's to change the score for."""
fld_dict = dict(get_def_name('', "field"))
res = switch_fld_score(colID, id_1, id_2)
output = write_outcome(res)
if fmeth == "soo":
return perform_showsortoptions(colID, ln, content=output)
elif fmeth == "sew":
return perform_showsearchfields(colID, ln, content=output)
elif fmeth == "seo":
return perform_showsearchoptions(colID, ln, content=output)
def perform_switchfldvaluescore(colID, id_1, id_fldvalue_1, id_fldvalue_2, ln):
"""Switch the score of id_1 and id_2 in collection_field_fieldvalue.
colID - the current collection
id_1/id_2 - the id's to change the score for."""
name_1 = run_sql("SELECT name from fieldvalue where id=%s" % id_fldvalue_1)[0][0]
name_2 = run_sql("SELECT name from fieldvalue where id=%s" % id_fldvalue_2)[0][0]
res = switch_fld_value_score(colID, id_1, id_fldvalue_1, id_fldvalue_2)
output = write_outcome(res)
return perform_modifyfield(colID, fldID=id_1, ln=ln, content=output)
def perform_addnewfieldvalue(colID, fldID, ln, name='', value='', callback="yes", confirm=-1):
"""form to add a new fieldvalue.
name - the name of the new fieldvalue
value - the value of the new fieldvalue
"""
output = ""
subtitle = """<a name="7.4"></a>Add new value"""
text = """
<span class="adminlabel">Display name</span>
<input class="admin_w200" type="text" name="name" value="%s" /><br />
<span class="adminlabel">Search value</span>
<input class="admin_w200" type="text" name="value" value="%s" /><br />
""" % (name, value)
- output = createhiddenform(action="%s/admin/websearch/websearchadmin.py/addnewfieldvalue" % weburl,
+ output = createhiddenform(action="%s/admin/websearch/websearchadmin.py/addnewfieldvalue" % CFG_SITE_URL,
text=text,
colID=colID,
fldID=fldID,
ln=ln,
button="Add",
confirm=1)
if name and value and confirm in ["1", 1]:
res = add_fldv(name, value)
output += write_outcome(res)
if res[0] == 1:
res = add_col_fld(colID, fldID, 'seo', res[1])
if res[0] == 0:
output += "<br />" + write_outcome(res)
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please fill in name and value.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output)
def perform_modifyfieldvalue(colID, fldID, fldvID, ln, name='', value='', callback="yes", confirm=-1):
"""form to modify a fieldvalue.
name - the name of the fieldvalue
value - the value of the fieldvalue
"""
if confirm in [-1, "-1"]:
res = get_fld_value(fldvID)
(id, name, value) = res[0]
output = ""
subtitle = """<a name="7.4"></a>Modify existing value"""
output = """<dl>
<dt><b><span class="info">Warning: Modifications done below will also inflict on all places the modified data is used.</span></b></dt>
</dl>"""
text = """
<span class="adminlabel">Display name</span>
<input class="admin_w200" type="text" name="name" value="%s" /><br />
<span class="adminlabel">Search value</span>
<input class="admin_w200" type="text" name="value" value="%s" /><br />
""" % (name, value)
- output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifyfieldvalue" % weburl,
+ output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifyfieldvalue" % CFG_SITE_URL,
text=text,
colID=colID,
fldID=fldID,
fldvID=fldvID,
ln=ln,
button="Update",
confirm=1)
- output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifyfieldvalue" % weburl,
+ output += createhiddenform(action="%s/admin/websearch/websearchadmin.py/modifyfieldvalue" % CFG_SITE_URL,
text="Delete value and all associations",
colID=colID,
fldID=fldID,
fldvID=fldvID,
ln=ln,
button="Delete",
confirm=2)
if name and value and confirm in ["1", 1]:
res = update_fldv(fldvID, name, value)
output += write_outcome(res)
#if res:
# output += """<b><span class="info">Operation successfully completed.</span></b>"""
#else:
# output += """<b><span class="info">Operation failed.</span></b>"""
elif confirm in ["2", 2]:
res = delete_fldv(fldvID)
output += write_outcome(res)
elif confirm not in ["-1", -1]:
output += """<b><span class="info">Please fill in name and value.</span></b>"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output)
def perform_removefield(colID, ln, fldID='', fldvID='', fmeth='', callback='yes', confirm=0):
"""form to remove a field from a collection.
colID - the current collection, remove the field from this collection.
sel_ln - remove the field with this language
fldID - remove the field with this id"""
if fmeth == "soo":
field = "sort option"
elif fmeth == "sew":
field = "search field"
elif fmeth == "seo":
field = "search option"
else:
field = "field"
subtitle = """<a name="6.4"><a name="7.4"><a name="8.4"></a>Remove %s""" % field
output = ""
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
res = get_fld_value()
fldv_dict = dict(map(lambda x: (x[0], x[1]), res))
if colID and fldID:
colID = int(colID)
fldID = int(fldID)
if fldvID and fldvID != "None":
fldvID = int(fldvID)
if confirm in ["0", 0]:
text = """Do you want to remove the %s '%s' %s from the collection '%s'.""" % (field, fld_dict[fldID], (fldvID not in["", "None"] and "with value '%s'" % fldv_dict[fldvID] or ''), col_dict[colID])
output += createhiddenform(action="removefield#6.5",
text=text,
button="Confirm",
colID=colID,
fldID=fldID,
fldvID=fldvID,
fmeth=fmeth,
confirm=1)
elif confirm in ["1", 1]:
res = remove_fld(colID, fldID, fldvID)
output += write_outcome(res)
body = [output]
output = "<br />" + addadminbox(subtitle, body)
if fmeth == "soo":
return perform_showsortoptions(colID, ln, content=output)
elif fmeth == "sew":
return perform_showsearchfields(colID, ln, content=output)
elif fmeth == "seo":
return perform_showsearchoptions(colID, ln, content=output)
def perform_removefieldvalue(colID, ln, fldID='', fldvID='', fmeth='', callback='yes', confirm=0):
"""form to remove a field from a collection.
colID - the current collection, remove the field from this collection.
sel_ln - remove the field with this language
fldID - remove the field with this id"""
subtitle = """<a name="7.4"></a>Remove value"""
output = ""
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
res = get_fld_value()
fldv_dict = dict(map(lambda x: (x[0], x[1]), res))
if colID and fldID:
colID = int(colID)
fldID = int(fldID)
if fldvID and fldvID != "None":
fldvID = int(fldvID)
if confirm in ["0", 0]:
text = """Do you want to remove the value '%s' from the search option '%s'.""" % (fldv_dict[fldvID], fld_dict[fldID])
output += createhiddenform(action="removefieldvalue#7.4",
text=text,
button="Confirm",
colID=colID,
fldID=fldID,
fldvID=fldvID,
fmeth=fmeth,
confirm=1)
elif confirm in ["1", 1]:
res = remove_fld(colID, fldID, fldvID)
output += write_outcome(res)
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID=fldID, ln=ln, content=output)
def perform_rearrangefieldvalue(colID, fldID, ln, callback='yes', confirm=-1):
"""rearrang the fieldvalues alphabetically
colID - the collection
fldID - the field to rearrange the fieldvalue for
"""
subtitle = "Order values alphabetically"
output = ""
col_fldv = get_col_fld(colID, 'seo', fldID)
col_fldv = dict(map(lambda x: (x[1], x[0]), col_fldv))
fldv_names = get_fld_value()
fldv_names = map(lambda x: (x[0], x[1]), fldv_names)
if not col_fldv.has_key(None):
vscore = len(col_fldv)
for (fldvID, name) in fldv_names:
if col_fldv.has_key(fldvID):
run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s" % (vscore, colID, fldID, fldvID))
vscore -= 1
output += write_outcome((1, ""))
else:
output += write_outcome((0, (0, "No values to order")))
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID, ln, content=output)
def perform_rearrangefield(colID, ln, fmeth, callback='yes', confirm=-1):
"""rearrang the fields alphabetically
colID - the collection
"""
subtitle = "Order fields alphabetically"
output = ""
col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, fmeth)))
fld_names = get_def_name('', "field")
if len(col_fld) > 0:
score = len(col_fld)
for (fldID, name) in fld_names:
if col_fld.has_key(fldID):
run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s" % (score, colID, fldID))
score -= 1
output += write_outcome((1, ""))
else:
output += write_outcome((0, (0, "No fields to order")))
body = [output]
output = "<br />" + addadminbox(subtitle, body)
if fmeth == "soo":
return perform_showsortoptions(colID, ln, content=output)
elif fmeth == "sew":
return perform_showsearchfields(colID, ln, content=output)
elif fmeth == "seo":
return perform_showsearchoptions(colID, ln, content=output)
def perform_addexistingfieldvalue(colID, fldID, fldvID=-1, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
"""form to add an existing fieldvalue to a field.
colID - the collection
fldID - the field to add the fieldvalue to
fldvID - the fieldvalue to add"""
subtitle = """</a><a name="7.4"></a>Add existing value to search option"""
output = ""
if fldvID not in [-1, "-1"] and confirm in [1, "1"]:
fldvID = int(fldvID)
ares = add_col_fld(colID, fldID, 'seo', fldvID)
colID = int(colID)
fldID = int(fldID)
lang = dict(get_languages())
res = get_def_name('', "field")
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(res)
col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, 'seo')))
fld_value = get_fld_value()
fldv_dict = dict(map(lambda x: (x[0], x[1]), fld_value))
text = """
<span class="adminlabel">Value</span>
<select name="fldvID" class="admin_w200">
<option value="-1">- Select value -</option>
"""
res = run_sql("SELECT id,name,value FROM fieldvalue ORDER BY name")
for (id, name, value) in res:
text += """<option value="%s" %s>%s - %s</option>
""" % (id, id == int(fldvID) and 'selected="selected"' or '', name, value)
text += """</select><br />"""
output += createhiddenform(action="addexistingfieldvalue#7.4",
text=text,
button="Add",
colID=colID,
fldID=fldID,
ln=ln,
confirm=1)
if fldvID not in [-1, "-1"] and confirm in [1, "1"]:
output += write_outcome(ares)
elif confirm in [1, "1"]:
output += """<b><span class="info">Select a value to add and try again.</span></b>"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_modifyfield(colID, fldID, ln, content=output)
def perform_addexistingfield(colID, ln, fldID=-1, fldvID=-1, fmeth='', callback='yes', confirm=-1):
"""form to add an existing field to a collection.
colID - the collection to add the field to
fldID - the field to add
sel_ln - the language of the field"""
subtitle = """<a name="6.2"></a><a name="7.2"></a><a name="8.2"></a>Add existing field to collection"""
output = ""
if fldID not in [-1, "-1"] and confirm in [1, "1"]:
fldID = int(fldID)
ares = add_col_fld(colID, fldID, fmeth, fldvID)
colID = int(colID)
lang = dict(get_languages())
res = get_def_name('', "field")
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(res)
col_fld = dict(map(lambda x: (x[0], x[1]), get_col_fld(colID, fmeth)))
fld_value = get_fld_value()
fldv_dict = dict(map(lambda x: (x[0], x[1]), fld_value))
if fldvID:
fldvID = int(fldvID)
text = """
<span class="adminlabel">Field</span>
<select name="fldID" class="admin_w200">
<option value="-1">- Select field -</option>
"""
for (id, var) in res:
if fmeth == 'seo' or (fmeth != 'seo' and not col_fld.has_key(id)):
text += """<option value="%s" %s>%s</option>
""" % (id, '', fld_dict[id])
text += """</select><br />"""
output += createhiddenform(action="addexistingfield#6.2",
text=text,
button="Add",
colID=colID,
fmeth=fmeth,
ln=ln,
confirm=1)
if fldID not in [-1, "-1"] and confirm in [1, "1"]:
output += write_outcome(ares)
elif fldID in [-1, "-1"] and confirm not in [-1, "-1"]:
output += """<b><span class="info">Select a field.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
if fmeth == "soo":
return perform_showsortoptions(colID, ln, content=output)
elif fmeth == "sew":
return perform_showsearchfields(colID, ln, content=output)
elif fmeth == "seo":
return perform_showsearchoptions(colID, ln, content=output)
def perform_showsortoptions(colID, ln, callback='yes', content='', confirm=-1):
"""show the sort fields of this collection.."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
fld_type = get_sort_nametypes()
- subtitle = """<a name="8">8. Modify sort options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.8">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="8">8. Modify sort options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.8">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """<dl>
<dt>Field actions (not related to this collection)</dt>
<dd>Go to the BibIndex interface to modify the available sort options</dd>
<dt>Collection specific actions
<dd><a href="addexistingfield?colID=%s&amp;ln=%s&amp;fmeth=soo#8.2">Add sort option to collection</a></dd>
<dd><a href="rearrangefield?colID=%s&amp;ln=%s&amp;fmeth=soo#8.2">Order sort options alphabetically</a></dd>
</dl>
""" % (colID, ln, colID, ln)
header = ['', 'Sort option', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
fld_type_list = fld_type.items()
if len(get_col_fld(colID, 'soo')) > 0:
res = get_col_fld(colID, 'soo')
i = 0
for (fldID, fldvID, stype, score, score_fieldvalue) in res:
move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>"""
if i != 0:
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=soo&amp;rand=%s#8"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (weburl, colID, ln, fldID, res[i - 1][0], random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=soo&amp;rand=%s#8"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i - 1][0], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;&nbsp;"
move += "</td><td>"
i += 1
if i != len(res):
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=soo&amp;rand=%s#8"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>""" % (weburl, colID, ln, fldID, res[i][0], random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=soo&amp;rand=%s#8"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i][0], random.randint(0, 1000), CFG_SITE_URL)
move += """</td></tr></table>"""
actions.append([move, fld_dict[int(fldID)]])
for col in [(('Remove sort option', 'removefield'),)]:
- actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=soo#8.4">%s</a>' % (weburl, col[0][1], colID, ln, fldID, col[0][0]))
+ actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=soo#8.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=soo#8.5">%s</a>' % (weburl, function, colID, ln, fldID, str)
+ actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=soo#8.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No sort options exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showsortoptions", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showsearchfields(colID, ln, callback='yes', content='', confirm=-1):
"""show the search fields of this collection.."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
fld_type = get_sort_nametypes()
- subtitle = """<a name="6">6. Modify search fields for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.6">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="6">6. Modify search fields for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.6">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """<dl>
<dt>Field actions (not related to this collection)</dt>
<dd>Go to the BibIndex interface to modify the available search fields</dd>
<dt>Collection specific actions
<dd><a href="addexistingfield?colID=%s&amp;ln=%s&amp;fmeth=sew#6.2">Add search field to collection</a></dd>
<dd><a href="rearrangefield?colID=%s&amp;ln=%s&amp;fmeth=sew#6.2">Order search fields alphabetically</a></dd>
</dl>
""" % (colID, ln, colID, ln)
header = ['', 'Search field', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
fld_type_list = fld_type.items()
if len(get_col_fld(colID, 'sew')) > 0:
res = get_col_fld(colID, 'sew')
i = 0
for (fldID, fldvID, stype, score, score_fieldvalue) in res:
move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>"""
if i != 0:
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=sew&amp;rand=%s#6"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (weburl, colID, ln, fldID, res[i - 1][0], random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=sew&amp;rand=%s#6"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, res[i - 1][0], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
move += "</td><td>"
i += 1
if i != len(res):
- move += '<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=sew&amp;rand=%s#6"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>' % (weburl, colID, ln, fldID, res[i][0], random.randint(0, 1000), weburl)
+ move += '<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=sew&amp;rand=%s#6"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>' % (CFG_SITE_URL, colID, ln, fldID, res[i][0], random.randint(0, 1000), CFG_SITE_URL)
move += """</td></tr></table>"""
actions.append([move, fld_dict[int(fldID)]])
for col in [(('Remove search field', 'removefield'),)]:
- actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=sew#6.4">%s</a>' % (weburl, col[0][1], colID, ln, fldID, col[0][0]))
+ actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=sew#6.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s#6.5">%s</a>' % (weburl, function, colID, ln, fldID, str)
+ actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s#6.5">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No search fields exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showsearchfields", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_showsearchoptions(colID, ln, callback='yes', content='', confirm=-1):
"""show the sort and search options of this collection.."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
fld_type = get_sort_nametypes()
- subtitle = """<a name="7">7. Modify search options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.7">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="7">7. Modify search options for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.7">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """<dl>
<dt>Field actions (not related to this collection)</dt>
<dd>Go to the BibIndex interface to modify the available search options</dd>
<dt>Collection specific actions
<dd><a href="addexistingfield?colID=%s&amp;ln=%s&amp;fmeth=seo#7.2">Add search option to collection</a></dd>
<dd><a href="rearrangefield?colID=%s&amp;ln=%s&amp;fmeth=seo#7.2">Order search options alphabetically</a></dd>
</dl>
""" % (colID, ln, colID, ln)
header = ['', 'Search option', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
fld_type_list = fld_type.items()
fld_distinct = run_sql("SELECT distinct(id_field) FROM collection_field_fieldvalue WHERE type='seo' AND id_collection=%s ORDER by score desc" % colID)
if len(fld_distinct) > 0:
i = 0
for (id) in fld_distinct:
fldID = id[0]
col_fld = get_col_fld(colID, 'seo', fldID)
move = ""
if i != 0:
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=seo&amp;rand=%s#7"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (weburl, colID, ln, fldID, fld_distinct[i - 1][0], random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=seo&amp;rand=%s#7"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fld_distinct[i - 1][0], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
i += 1
if i != len(fld_distinct):
- move += '<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=seo&amp;rand=%s#7"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>' % (weburl, colID, ln, fldID, fld_distinct[i][0], random.randint(0, 1000), weburl)
+ move += '<a href="%s/admin/websearch/websearchadmin.py/switchfldscore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_2=%s&amp;fmeth=seo&amp;rand=%s#7"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>' % (CFG_SITE_URL, colID, ln, fldID, fld_distinct[i][0], random.randint(0, 1000), CFG_SITE_URL)
actions.append([move, "%s" % fld_dict[fldID]])
for col in [(('Modify values', 'modifyfield'), ('Remove search option', 'removefield'),)]:
- actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s#7.3">%s</a>' % (weburl, col[0][1], colID, ln, fldID, col[0][0]))
+ actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s#7.3">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=seo#7.3">%s</a>' % (weburl, function, colID, ln, fldID, str)
+ actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fmeth=seo#7.3">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No search options exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showsearchoptions", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyfield(colID, fldID, fldvID='', ln=CFG_SITE_LANG, content='', callback='yes', confirm=0):
"""Modify the fieldvalues for a field"""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
fld_dict = dict(get_def_name('', "field"))
fld_type = get_sort_nametypes()
fldID = int(fldID)
subtitle = """<a name="7.3">Modify values for field '%s'</a>""" % (fld_dict[fldID])
output = """<dl>
<dt>Value specific actions
<dd><a href="addexistingfieldvalue?colID=%s&amp;ln=%s&amp;fldID=%s#7.4">Add existing value to search option</a></dd>
<dd><a href="addnewfieldvalue?colID=%s&amp;ln=%s&amp;fldID=%s#7.4">Add new value to search option</a></dd>
<dd><a href="rearrangefieldvalue?colID=%s&amp;ln=%s&amp;fldID=%s#7.4">Order values alphabetically</a></dd>
</dl>
""" % (colID, ln, fldID, colID, ln, fldID, colID, ln, fldID)
header = ['', 'Value name', 'Actions']
actions = []
sitelangs = get_languages()
lang = dict(sitelangs)
fld_type_list = fld_type.items()
col_fld = list(get_col_fld(colID, 'seo', fldID))
if len(col_fld) == 1 and col_fld[0][1] is None:
output += """<b><span class="info">No values added for this search option yet</span></b>"""
else:
j = 0
for (fldID, fldvID, stype, score, score_fieldvalue) in col_fld:
fieldvalue = get_fld_value(fldvID)
move = ""
if j != 0:
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldvaluescore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_fldvalue_1=%s&amp;id_fldvalue_2=%s&amp;rand=%s#7.3"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (weburl, colID, ln, fldID, fldvID, col_fld[j - 1][1], random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldvaluescore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_fldvalue_1=%s&amp;id_fldvalue_2=%s&amp;rand=%s#7.3"><img border="0" src="%s/img/smallup.gif" title="Move up"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fldvID, col_fld[j - 1][1], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
j += 1
if j != len(col_fld):
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldvaluescore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_fldvalue_1=%s&amp;id_fldvalue_2=%s&amp;rand=%s#7.3"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>""" % (weburl, colID, ln, fldID, fldvID, col_fld[j][1], random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchfldvaluescore?colID=%s&amp;ln=%s&amp;id_1=%s&amp;id_fldvalue_1=%s&amp;id_fldvalue_2=%s&amp;rand=%s#7.3"><img border="0" src="%s/img/smalldown.gif" title="Move down"></a>""" % (CFG_SITE_URL, colID, ln, fldID, fldvID, col_fld[j][1], random.randint(0, 1000), CFG_SITE_URL)
if fieldvalue[0][1] != fieldvalue[0][2] and fldvID is not None:
actions.append([move, "%s - %s" % (fieldvalue[0][1], fieldvalue[0][2])])
elif fldvID is not None:
actions.append([move, "%s" % fieldvalue[0][1]])
move = ''
for col in [(('Modify value', 'modifyfieldvalue'), ('Remove value', 'removefieldvalue'),)]:
- actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fldvID=%s&amp;fmeth=seo#7.4">%s</a>' % (weburl, col[0][1], colID, ln, fldID, fldvID, col[0][0]))
+ actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fldvID=%s&amp;fmeth=seo#7.4">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, fldID, fldvID, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fldvID=%s#7.4">%s</a>' % (weburl, function, colID, ln, fldID, fldvID, str)
+ actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fldID=%s&amp;fldvID=%s#7.4">%s</a>' % (CFG_SITE_URL, function, colID, ln, fldID, fldvID, str)
output += tupletotable(header=header, tuple=actions)
output += content
body = [output]
output = "<br />" + addadminbox(subtitle, body)
if len(col_fld) == 0:
output = content
return perform_showsearchoptions(colID, ln, content=output)
def perform_showoutputformats(colID, ln, callback='yes', content='', confirm=-1):
"""shows the outputformats of the current collection
colID - the collection id."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
- subtitle = """<a name="10">10. Modify output formats for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.10">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="10">10. Modify output formats for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.10">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """
<dl>
<dt>Output format actions (not specific to the chosen collection)
<dd>Go to the BibFormat interface to modify</dd>
<dt>Collection specific actions
<dd><a href="addexistingoutputformat?colID=%s&amp;ln=%s#10.2">Add existing output format to collection</a></dd>
</dl>
""" % (colID, ln)
header = ['', 'Code', 'Output format', 'Actions']
actions = []
col_fmt = get_col_fmt(colID)
fmt_dict = dict(get_def_name('', "format"))
i = 0
if len(col_fmt) > 0:
for (id_format, colID_fld, code, score) in col_fmt:
move = """<table cellspacing="1" cellpadding="0" border="0"><tr><td>"""
if i != 0:
- move += """<a href="%s/admin/websearch/websearchadmin.py/switchfmtscore?colID=%s&amp;ln=%s&amp;type=format&amp;id_1=%s&amp;id_2=%s&amp;rand=%s#10"><img border="0" src="%s/img/smallup.gif" title="Move format up"></a>""" % (weburl, colID, ln, id_format, col_fmt[i - 1][0], random.randint(0, 1000), weburl)
+ move += """<a href="%s/admin/websearch/websearchadmin.py/switchfmtscore?colID=%s&amp;ln=%s&amp;type=format&amp;id_1=%s&amp;id_2=%s&amp;rand=%s#10"><img border="0" src="%s/img/smallup.gif" title="Move format up"></a>""" % (CFG_SITE_URL, colID, ln, id_format, col_fmt[i - 1][0], random.randint(0, 1000), CFG_SITE_URL)
else:
move += "&nbsp;&nbsp;&nbsp;"
move += "</td><td>"
i += 1
if i != len(col_fmt):
- move += '<a href="%s/admin/websearch/websearchadmin.py/switchfmtscore?colID=%s&amp;ln=%s&amp;type=format&amp;id_1=%s&amp;id_2=%s&amp;rand=%s#10"><img border="0" src="%s/img/smalldown.gif" title="Move format down"></a>' % (weburl, colID, ln, id_format, col_fmt[i][0], random.randint(0, 1000), weburl)
+ move += '<a href="%s/admin/websearch/websearchadmin.py/switchfmtscore?colID=%s&amp;ln=%s&amp;type=format&amp;id_1=%s&amp;id_2=%s&amp;rand=%s#10"><img border="0" src="%s/img/smalldown.gif" title="Move format down"></a>' % (CFG_SITE_URL, colID, ln, id_format, col_fmt[i][0], random.randint(0, 1000), CFG_SITE_URL)
move += """</td></tr></table>"""
actions.append([move, code, fmt_dict[int(id_format)]])
for col in [(('Remove', 'removeoutputformat'),)]:
- actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fmtID=%s#10">%s</a>' % (weburl, col[0][1], colID, ln, id_format, col[0][0]))
+ actions[-1].append('<a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fmtID=%s#10">%s</a>' % (CFG_SITE_URL, col[0][1], colID, ln, id_format, col[0][0]))
for (str, function) in col[1:]:
- actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fmtID=%s#10">%s</a>' % (weburl, function, colID, ln, id_format, str)
+ actions[-1][-1] += ' / <a href="%s/admin/websearch/websearchadmin.py/%s?colID=%s&amp;ln=%s&amp;fmtID=%s#10">%s</a>' % (CFG_SITE_URL, function, colID, ln, id_format, str)
output += tupletotable(header=header, tuple=actions)
else:
output += """No output formats exists for this collection"""
output += content
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_showoutputformats", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def external_collections_build_select(colID, external_collection):
output = '<select name="state" class="admin_w200">'
if external_collection.parser:
max_state = 4
else:
max_state = 2
num_selected = external_collection_get_state(external_collection, colID)
for num in range(max_state):
state_name = CFG_EXTERNAL_COLLECTION_STATES_NAME[num]
if num == num_selected:
selected = ' selected'
else:
selected = ''
output += '<option value="%(num)d"%(selected)s>%(state_name)s</option>' % {'num': num, 'selected': selected, 'state_name': state_name}
output += '</select>\n'
return output
def perform_manage_external_collections(colID, ln, callback='yes', content='', confirm=-1):
"""Show the interface to configure external collections to the user."""
colID = int(colID)
subtitle = """<a name="11">11. Configuration of related external collections</a>
- &nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.11">?</a>]</small>""" % weburl
+ &nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.11">?</a>]</small>""" % CFG_SITE_URL
output = '<form action="update_external_collections" method="POST"><input type="hidden" name="colID" value="%(colID)d">' % {'colID': colID}
table_header = ['External collection', 'Mode', 'Apply also to daughter collections?']
table_content = []
external_collections = external_collection_sort_engine_by_name(external_collections_dictionary.values())
for external_collection in external_collections:
collection_name = external_collection.name
select = external_collections_build_select(colID, external_collection)
recurse = '<input type=checkbox name="recurse" value="%(collection_name)s">' % {'collection_name': collection_name}
table_content.append([collection_name, select, recurse])
output += tupletotable(header=table_header, tuple=table_content)
output += '<input class="adminbutton" type="submit" value="Modify"/>'
output += '</form>'
return addadminbox(subtitle, [output])
def perform_update_external_collections(colID, ln, state_list, recurse_list):
colID = int(colID)
changes = []
output = ""
if not state_list:
return 'Warning : No state found.<br />' + perform_manage_external_collections(colID, ln)
external_collections = external_collection_sort_engine_by_name(external_collections_dictionary.values())
if len(external_collections) != len(state_list):
return 'Warning : Size of state_list different from external_collections!<br />' + perform_manage_external_collections(colID, ln)
for (external_collection, state) in zip(external_collections, state_list):
state = int(state)
collection_name = external_collection.name
recurse = recurse_list and collection_name in recurse_list
oldstate = external_collection_get_state(external_collection, colID)
if oldstate != state or recurse:
changes += external_collection_get_update_state_list(external_collection, colID, state, recurse)
external_collection_apply_changes(changes)
return output + '<br /><br />' + perform_manage_external_collections(colID, ln)
def perform_showdetailedrecordoptions(colID, ln, callback='yes', content='', confirm=-1):
"""Show the interface to configure detailed record page to the user."""
colID = int(colID)
subtitle = """<a name="12">12. Configuration of detailed record page</a>
- &nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.12">?</a>]</small>""" % weburl
+ &nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.12">?</a>]</small>""" % CFG_SITE_URL
output = '''<form action="update_detailed_record_options" method="post">
<table><tr><td>
<input type="hidden" name="colID" value="%(colID)d">
<dl>
<dt><b>Show tabs:</b></dt>
<dd>
''' % {'colID': colID}
for (tab_id, tab_info) in get_detailed_page_tabs(colID).iteritems():
if tab_id == 'comments' and \
not CFG_WEBCOMMENT_ALLOW_REVIEWS and \
not CFG_WEBCOMMENT_ALLOW_COMMENTS:
continue
check = ''
output += '''<input type="checkbox" id="id%(tabid)s" name="tabs" value="%(tabid)s" %(check)s />
<label for="id%(tabid)s">&nbsp;%(label)s</label><br />
''' % {'tabid':tab_id,
'check':((tab_info['visible'] and 'checked="checked"') or ''),
'label':tab_info['label']}
output += '</dd></dl></td><td>'
output += '</td></tr></table><input class="adminbutton" type="submit" value="Modify"/>'
output += '''<input type="checkbox" id="recurse" name="recurse" value="1" />
<label for="recurse">&nbsp;Also apply to subcollections</label>'''
output += '</form>'
return addadminbox(subtitle, [output])
def perform_update_detailed_record_options(colID, ln, tabs, recurse):
"""Update the preferences for the tab to show/hide in the detailed record page."""
colID = int(colID)
changes = []
output = '<b><span class="info">Operation successfully completed.</span></b>'
if '' in tabs:
tabs.remove('')
tabs.append('metadata')
def update_settings(colID, tabs, recurse):
run_sql("DELETE FROM collectiondetailedrecordpagetabs WHERE id_collection='%s'" % colID)
run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \
" SET id_collection='%s', tabs='%s'" % (colID, ';'.join(tabs)))
## for enabled_tab in tabs:
## run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \
## " SET id_collection='%s', tabs='%s'" % (colID, ';'.join(tabs)))
if recurse:
for descendant_id in get_collection_descendants(colID):
update_settings(descendant_id, tabs, recurse)
update_settings(colID, tabs, recurse)
## for colID in colIDs:
## run_sql("DELETE FROM collectiondetailedrecordpagetabs WHERE id_collection='%s'" % colID)
## for enabled_tab in tabs:
## run_sql("REPLACE INTO collectiondetailedrecordpagetabs" + \
## " SET id_collection='%s', tabs='%s'" % (colID, ';'.join(tabs)))
#if callback:
return perform_editcollection(colID, ln, "perform_modifytranslations",
'<br /><br />' + output + '<br /><br />' + \
perform_showdetailedrecordoptions(colID, ln))
#else:
# return addadminbox(subtitle, body)
#return output + '<br /><br />' + perform_showdetailedrecordoptions(colID, ln)
def perform_addexistingoutputformat(colID, ln, fmtID=-1, callback='yes', confirm=-1):
"""form to add an existing output format to a collection.
colID - the collection the format should be added to
fmtID - the format to add."""
subtitle = """<a name="10.2"></a>Add existing output format to collection"""
output = ""
if fmtID not in [-1, "-1"] and confirm in [1, "1"]:
ares = add_col_fmt(colID, fmtID)
colID = int(colID)
res = get_def_name('', "format")
fmt_dict = dict(res)
col_dict = dict(get_def_name('', "collection"))
col_fmt = get_col_fmt(colID)
col_fmt = dict(map(lambda x: (x[0], x[2]), col_fmt))
if len(res) > 0:
text = """
<span class="adminlabel">Output format</span>
<select name="fmtID" class="admin_w200">
<option value="-1">- Select output format -</option>
"""
for (id, name) in res:
if not col_fmt.has_key(id):
text += """<option value="%s" %s>%s</option>
""" % (id, id == int(fmtID) and 'selected="selected"' or '', name)
text += """</select><br />
"""
output += createhiddenform(action="addexistingoutputformat#10.2",
text=text,
button="Add",
colID=colID,
ln=ln,
confirm=1)
else:
output = """No existing output formats to add, please create a new one."""
if fmtID not in [-1, "-1"] and confirm in [1, "1"]:
output += write_outcome(ares)
elif fmtID in [-1, "-1"] and confirm not in [-1, "-1"]:
output += """<b><span class="info">Please select output format.</span></b>"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showoutputformats(colID, ln, content=output)
def perform_deleteoutputformat(colID, ln, fmtID=-1, callback='yes', confirm=-1):
"""form to delete an output format not in use.
colID - the collection id of the current collection.
fmtID - the format id to delete."""
subtitle = """<a name="10.3"></a>Delete an unused output format"""
output = """
<dl>
<dd>Deleting an output format will also delete the translations associated.</dd>
</dl>
"""
colID = int(colID)
if fmtID not in [-1, "-1"] and confirm in [1, "1"]:
fmt_dict = dict(get_def_name('', "format"))
old_colNAME = fmt_dict[int(fmtID)]
ares = delete_fmt(int(fmtID))
res = get_def_name('', "format")
fmt_dict = dict(res)
col_dict = dict(get_def_name('', "collection"))
col_fmt = get_col_fmt()
col_fmt = dict(map(lambda x: (x[0], x[2]), col_fmt))
if len(res) > 0:
text = """
<span class="adminlabel">Output format</span>
<select name="fmtID" class="admin_w200">
"""
text += """<option value="-1">- Select output format -"""
for (id, name) in res:
if not col_fmt.has_key(id):
text += """<option value="%s" %s>%s""" % (id, id == int(fmtID) and 'selected="selected"' or '', name)
text += "</option>"
text += """</select><br />"""
output += createhiddenform(action="deleteoutputformat#10.3",
text=text,
button="Delete",
colID=colID,
ln=ln,
confirm=0)
if fmtID not in [-1, "-1"]:
fmtID = int(fmtID)
if confirm in [0, "0"]:
text = """<b>Do you want to delete the output format '%s'.</b>
""" % fmt_dict[fmtID]
output += createhiddenform(action="deleteoutputformat#10.3",
text=text,
button="Confirm",
colID=colID,
fmtID=fmtID,
ln=ln,
confirm=1)
elif confirm in [1, "1"]:
output += write_outcome(ares)
elif confirm not in [-1, "-1"]:
output += """<b><span class="info">Choose a output format to delete.</span></b>
"""
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showoutputformats(colID, ln, content=output)
def perform_removeoutputformat(colID, ln, fmtID='', callback='yes', confirm=0):
"""form to remove an output format from a collection.
colID - the collection id of the current collection.
fmtID - the format id.
"""
subtitle = """<a name="10.5"></a>Remove output format"""
output = ""
col_dict = dict(get_def_name('', "collection"))
fmt_dict = dict(get_def_name('', "format"))
if colID and fmtID:
colID = int(colID)
fmtID = int(fmtID)
if confirm in ["0", 0]:
text = """Do you want to remove the output format '%s' from the collection '%s'.""" % (fmt_dict[fmtID], col_dict[colID])
output += createhiddenform(action="removeoutputformat#10.5",
text=text,
button="Confirm",
colID=colID,
fmtID=fmtID,
confirm=1)
elif confirm in ["1", 1]:
res = remove_fmt(colID, fmtID)
output += write_outcome(res)
body = [output]
output = "<br />" + addadminbox(subtitle, body)
return perform_showoutputformats(colID, ln, content=output)
def perform_index(colID=1, ln=CFG_SITE_LANG, mtype='', content='', confirm=0):
"""The index method, calling methods to show the collection tree, create new collections and add collections to tree.
"""
subtitle = "Overview"
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
output = ""
fin_output = ""
if not col_dict.has_key(1):
res = add_col(CFG_SITE_NAME, '')
if res:
fin_output += """<b><span class="info">Created root collection.</span></b><br />"""
else:
return "Cannot create root collection, please check database."
if CFG_SITE_NAME != run_sql("SELECT name from collection WHERE id=1")[0][0]:
res = run_sql("update collection set name='%s' where id=1" % CFG_SITE_NAME)
if res:
fin_output += """<b><span class="info">The name of the root collection has been modified to be the same as the %(sitename)s installation name given prior to installing %(sitename)s.</span><b><br />""" % {'sitename' : CFG_SITE_NAME}
else:
return "Error renaming root collection."
fin_output += """
<table>
<tr>
<td>0.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_showall">Show all</a></small></td>
<td>1.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_addcollection">Create new collection</a></small></td>
<td>2.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_addcollectiontotree">Attach collection to tree</a></small></td>
<td>3.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_modifycollectiontree">Modify collection tree</a></small></td>
<td>4.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_checkwebcollstatus">Webcoll Status</a></small></td>
</tr><tr>
<td>5.&nbsp;<small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&amp;ln=%s&amp;mtype=perform_checkcollectionstatus">Collection Status</a></small></td>
<td>6.&nbsp;<small><a href="%s/help/admin/websearch-admin-guide?ln=%s">Guide</a></small></td>
</tr>
</table>
- """ % (weburl, colID, ln, weburl, colID, ln, weburl, colID, ln, weburl, colID, ln, weburl, colID, ln, weburl, colID, ln, weburl, ln)
+ """ % (CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, colID, ln, CFG_SITE_URL, ln)
if mtype == "":
fin_output += """<br /><br /><b><span class="info">For managing the collections, select an item from the menu.</span><b><br />"""
if mtype == "perform_addcollection" and content:
fin_output += content
elif mtype == "perform_addcollection" or mtype == "perform_showall":
fin_output += perform_addcollection(colID=colID, ln=ln, callback='')
fin_output += "<br />"
if mtype == "perform_addcollectiontotree" and content:
fin_output += content
elif mtype == "perform_addcollectiontotree" or mtype == "perform_showall":
fin_output += perform_addcollectiontotree(colID=colID, ln=ln, callback='')
fin_output += "<br />"
if mtype == "perform_modifycollectiontree" and content:
fin_output += content
elif mtype == "perform_modifycollectiontree" or mtype == "perform_showall":
fin_output += perform_modifycollectiontree(colID=colID, ln=ln, callback='')
fin_output += "<br />"
if mtype == "perform_checkwebcollstatus" and content:
fin_output += content
elif mtype == "perform_checkwebcollstatus" or mtype == "perform_showall":
fin_output += perform_checkwebcollstatus(colID, ln, callback='')
if mtype == "perform_checkcollectionstatus" and content:
fin_output += content
elif mtype == "perform_checkcollectionstatus" or mtype == "perform_showall":
fin_output += perform_checkcollectionstatus(colID, ln, callback='')
body = [fin_output]
return addadminbox('<b>Menu</b>', body)
def show_coll_not_in_tree(colID, ln, col_dict):
"""Returns collections not in tree"""
tree = get_col_tree(colID)
in_tree = {}
output = "These collections are not in the tree, and should be added:<br />"
for (id, up, down, dad, reltype) in tree:
in_tree[id] = 1
in_tree[dad] = 1
res = run_sql("SELECT id from collection")
if len(res) != len(in_tree):
for id in res:
if not in_tree.has_key(id[0]):
output += """<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&amp;ln=%s" title="Edit collection">%s</a> ,
- """ % (weburl, id[0], ln, col_dict[id[0]])
+ """ % (CFG_SITE_URL, id[0], ln, col_dict[id[0]])
output += "<br /><br />"
else:
output = ""
return output
def create_colltree(tree, col_dict, colID, ln, move_from='', move_to='', rtype='', edit=''):
"""Creates the presentation of the collection tree, with the buttons for modifying it.
tree - the tree to present, from get_tree()
col_dict - the name of the collections in a dictionary
colID - the collection id to start with
move_from - if a collection to be moved has been chosen
move_to - the collection which should be set as father of move_from
rtype - the type of the tree, regular or virtual
edit - if the method should output the edit buttons."""
if move_from:
move_from_rtype = move_from[0]
move_from_id = int(move_from[1:len(move_from)])
tree_from = get_col_tree(colID, move_from_rtype)
tree_to = get_col_tree(colID, rtype)
tables = 0
tstack = []
i = 0
text = """
<table border ="0" cellspacing="0" cellpadding="0">"""
for i in range(0, len(tree)):
id_son = tree[i][0]
up = tree[i][1]
down = tree[i][2]
dad = tree[i][3]
reltype = tree[i][4]
tmove_from = ""
j = i
while j > 0:
j = j - 1
try:
if tstack[j][1] == dad:
table = tstack[j][2]
for k in range(0, tables - table):
tables = tables - 1
text += """</table></td></tr>
"""
break
except StandardError, e:
pass
text += """<tr><td>
"""
if i > 0 and tree[i][1] == 0:
tables = tables + 1
text += """</td><td></td><td></td><td></td><td><table border="0" cellspacing="0" cellpadding="0"><tr><td>
"""
if i == 0:
tstack.append((id_son, dad, 1))
else:
tstack.append((id_son, dad, tables))
if up == 1 and edit:
- text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_up=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/smallup.gif" title="Move collection up"></a>""" % (weburl, colID, ln, i, rtype, tree[i][0], weburl)
+ text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_up=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/smallup.gif" title="Move collection up"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL)
else:
text += """&nbsp;"""
text += "</td><td>"
if down == 1 and edit:
- text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_down=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/smalldown.gif" title="Move collection down"></a>""" % (weburl, colID, ln, i, rtype, tree[i][0], weburl)
+ text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_down=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/smalldown.gif" title="Move collection down"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL)
else:
text += """&nbsp;"""
text += "</td><td>"
if edit:
if move_from and move_to:
tmove_from = move_from
move_from = ''
if not (move_from == "" and i == 0) and not (move_from != "" and int(move_from[1:len(move_from)]) == i and rtype == move_from[0]):
check = "true"
if move_from:
#if tree_from[move_from_id][0] == tree_to[i][0] or not check_col(tree_to[i][0], tree_from[move_from_id][0]):
# check = ''
#elif not check_col(tree_to[i][0], tree_from[move_from_id][0]):
# check = ''
#if not check and (tree_to[i][0] == 1 and tree_from[move_from_id][3] == tree_to[i][0] and move_from_rtype != rtype):
# check = "true"
if check:
text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_from=%s&amp;move_to=%s%s&amp;rtype=%s#tree"><img border="0" src="%s/img/move_to.gif" title="Move '%s' to '%s'"></a>
- """ % (weburl, colID, ln, move_from, rtype, i, rtype, weburl, col_dict[tree_from[int(move_from[1:len(move_from)])][0]], col_dict[tree_to[i][0]])
+ """ % (CFG_SITE_URL, colID, ln, move_from, rtype, i, rtype, CFG_SITE_URL, col_dict[tree_from[int(move_from[1:len(move_from)])][0]], col_dict[tree_to[i][0]])
else:
try:
- text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_from=%s%s&amp;rtype=%s#%s"><img border="0" src="%s/img/move_from.gif" title="Move '%s' from this location."></a>""" % (weburl, colID, ln, rtype, i, rtype, tree[i][0], weburl, col_dict[tree[i][0]])
+ text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;move_from=%s%s&amp;rtype=%s#%s"><img border="0" src="%s/img/move_from.gif" title="Move '%s' from this location."></a>""" % (CFG_SITE_URL, colID, ln, rtype, i, rtype, tree[i][0], CFG_SITE_URL, col_dict[tree[i][0]])
except KeyError:
pass
else:
text += """<img border="0" src="%s/img/white_field.gif">
- """ % weburl
+ """ % CFG_SITE_URL
else:
text += """<img border="0" src="%s/img/white_field.gif">
- """ % weburl
+ """ % CFG_SITE_URL
text += """
</td>
<td>"""
if edit:
try:
- text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;delete=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/iconcross.gif" title="Remove colletion from tree"></a>""" % (weburl, colID, ln, i, rtype, tree[i][0], weburl)
+ text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&amp;ln=%s&amp;delete=%s&amp;rtype=%s#%s"><img border="0" src="%s/img/iconcross.gif" title="Remove colletion from tree"></a>""" % (CFG_SITE_URL, colID, ln, i, rtype, tree[i][0], CFG_SITE_URL)
except KeyError:
pass
elif i != 0:
text += """<img border="0" src="%s/img/white_field.gif">
- """ % weburl
+ """ % CFG_SITE_URL
text += """</td><td>
"""
if tmove_from:
move_from = tmove_from
try:
- text += """<a name="%s"></a>%s<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&amp;ln=%s" title="Edit collection">%s</a>%s%s%s""" % (tree[i][0], (reltype=="v" and '<i>' or ''), weburl, tree[i][0], ln, col_dict[id_son], (move_to=="%s%s" %(rtype, i) and '&nbsp;<img border="0" src="%s/img/move_to.gif">' % weburl or ''), (move_from=="%s%s" % (rtype, i) and '&nbsp;<img border="0" src="%s/img/move_from.gif">' % weburl or ''), (reltype=="v" and '</i>' or ''))
+ text += """<a name="%s"></a>%s<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&amp;ln=%s" title="Edit collection">%s</a>%s%s%s""" % (tree[i][0], (reltype=="v" and '<i>' or ''), CFG_SITE_URL, tree[i][0], ln, col_dict[id_son], (move_to=="%s%s" %(rtype, i) and '&nbsp;<img border="0" src="%s/img/move_to.gif">' % CFG_SITE_URL or ''), (move_from=="%s%s" % (rtype, i) and '&nbsp;<img border="0" src="%s/img/move_from.gif">' % CFG_SITE_URL or ''), (reltype=="v" and '</i>' or ''))
except KeyError:
pass
text += """</td></tr>
"""
while tables > 0:
text += """</table></td></tr>
"""
tables = tables - 1
text += """</table>
"""
return text
def perform_deletecollection(colID, ln, confirm=-1, callback='yes'):
"""form to delete a collection
colID - id of collection
"""
subtitle =''
output = """
<span class="warning">
<strong>
<dl>
<dt>WARNING:</dt>
<dd>When deleting a collection, you also deletes all data related to the collection like translations, relations to other collections and information about which rank methods to use.
<br />For more information, please go to the <a title="See guide" href="%s/help/admin/websearch-admin-guide">WebSearch guide</a> and read the section regarding deleting a collection.</dd>
</dl>
</strong>
</span>
- """ % weburl
+ """ % CFG_SITE_URL
col_dict = dict(get_def_name('', "collection"))
if colID != 1 and colID and col_dict.has_key(int(colID)):
colID = int(colID)
- subtitle = """<a name="4">4. Delete collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.4">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="4">4. Delete collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.4">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
res = run_sql("SELECT * from collection_collection WHERE id_dad=%s" % colID)
res2 = run_sql("SELECT * from collection_collection WHERE id_son=%s" % colID)
if not res and not res2:
if confirm in ["-1", -1]:
text = """Do you want to delete this collection."""
output += createhiddenform(action="deletecollection#4",
text=text,
colID=colID,
button="Delete",
confirm=0)
elif confirm in ["0", 0]:
text = """Are you sure you want to delete this collection."""
output += createhiddenform(action="deletecollection#4",
text=text,
colID=colID,
button="Confirm",
confirm=1)
elif confirm in ["1", 1]:
result = delete_col(colID)
if not result:
raise Exception
else:
output = """<b><span class="info">Can not delete a collection that is a part of the collection tree, remove collection from the tree and try again.</span></b>"""
else:
subtitle = """4. Delete collection"""
output = """<b><span class="info">Not possible to delete the root collection</span></b>"""
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_deletecollection", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_editcollection(colID=1, ln=CFG_SITE_LANG, mtype='', content=''):
"""interface to modify a collection. this method is calling other methods which again is calling this and sending back the output of the method.
if callback, the method will call perform_editcollection, if not, it will just return its output.
colID - id of the collection
mtype - the method that called this method.
content - the output from that method."""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
if not col_dict.has_key(colID):
return """<b><span class="info">Collection deleted.</span></b>
"""
fin_output = """
<table>
<tr>
<td><b>Menu</b></td>
</tr>
<tr>
<td>0.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s">Show all</a></small></td>
<td>1.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_modifydbquery">Modify collection query</a></small></td>
<td>2.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_modifyrestricted">Modify access restrictions</a></small></td>
<td>3.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_modifytranslations">Modify translations</a></small></td>
<td>4.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_deletecollection">Delete collection</a></small></td>
</tr><tr>
<td>5.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showportalboxes">Modify portalboxes</a></small></td>
<td>6.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showsearchfields#6">Modify search fields</a></small></td>
<td>7.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showsearchoptions#7">Modify search options</a></small></td>
<td>8.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showsortoptions#8">Modify sort options</a></small></td>
<td>9.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_modifyrankmethods#9">Modify rank options</a></small></td>
</tr><tr>
<td>10.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showoutputformats#10">Modify output formats</a></small></td>
<td>11.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_manage_external_collections#11">Configuration of related external collections</a></small></td>
<td>12.&nbsp;<small><a href="editcollection?colID=%s&amp;ln=%s&amp;mtype=perform_showdetailedrecordoptions#12">Detailed record page options</a></small></td>
</tr>
</table>
""" % (colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln, colID, ln)
if mtype == "perform_modifydbquery" and content:
fin_output += content
elif mtype == "perform_modifydbquery" or not mtype:
fin_output += perform_modifydbquery(colID, ln, callback='')
if mtype == "perform_modifyrestricted" and content:
fin_output += content
elif mtype == "perform_modifyrestricted" or not mtype:
fin_output += perform_modifyrestricted(colID, ln, callback='')
if mtype == "perform_modifytranslations" and content:
fin_output += content
elif mtype == "perform_modifytranslations" or not mtype:
fin_output += perform_modifytranslations(colID, ln, callback='')
if mtype == "perform_deletecollection" and content:
fin_output += content
elif mtype == "perform_deletecollection" or not mtype:
fin_output += perform_deletecollection(colID, ln, callback='')
if mtype == "perform_showportalboxes" and content:
fin_output += content
elif mtype == "perform_showportalboxes" or not mtype:
fin_output += perform_showportalboxes(colID, ln, callback='')
if mtype == "perform_showsearchfields" and content:
fin_output += content
elif mtype == "perform_showsearchfields" or not mtype:
fin_output += perform_showsearchfields(colID, ln, callback='')
if mtype == "perform_showsearchoptions" and content:
fin_output += content
elif mtype == "perform_showsearchoptions" or not mtype:
fin_output += perform_showsearchoptions(colID, ln, callback='')
if mtype == "perform_showsortoptions" and content:
fin_output += content
elif mtype == "perform_showsortoptions" or not mtype:
fin_output += perform_showsortoptions(colID, ln, callback='')
if mtype == "perform_modifyrankmethods" and content:
fin_output += content
elif mtype == "perform_modifyrankmethods" or not mtype:
fin_output += perform_modifyrankmethods(colID, ln, callback='')
if mtype == "perform_showoutputformats" and content:
fin_output += content
elif mtype == "perform_showoutputformats" or not mtype:
fin_output += perform_showoutputformats(colID, ln, callback='')
if mtype == "perform_manage_external_collections" and content:
fin_output += content
elif mtype == "perform_manage_external_collections" or not mtype:
fin_output += perform_manage_external_collections(colID, ln, callback='')
if mtype == "perform_showdetailedrecordoptions" and content:
fin_output += content
elif mtype == "perform_showdetailedrecordoptions" or not mtype:
fin_output += perform_showdetailedrecordoptions(colID, ln, callback='')
return addadminbox("Overview of edit options for collection '%s'" % col_dict[colID], [fin_output])
def perform_checkwebcollstatus(colID, ln, confirm=0, callback='yes'):
"""Check status of the collection tables with respect to the webcoll cache."""
- subtitle = """<a name="11"></a>Webcoll Status&nbsp;&nbsp;&nbsp;[<a href="%s/help/admin/websearch-admin-guide#4">?</a>]""" % weburl
+ subtitle = """<a name="11"></a>Webcoll Status&nbsp;&nbsp;&nbsp;[<a href="%s/help/admin/websearch-admin-guide#4">?</a>]""" % CFG_SITE_URL
output = ""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
output += """<br /><b>Last updates:</b><br />"""
collection_table_update_time = ""
collection_web_update_time = ""
collection_table_update_time = get_table_update_time('collection')
output += "Collection table last updated: %s<br />" % collection_table_update_time
try:
file = open("%s/collections/last_updated" % CFG_CACHEDIR)
collection_web_update_time = file.readline().strip()
output += "Collection cache last updated: %s<br />" % collection_web_update_time
file.close()
except:
pass
# reformat collection_web_update_time to the format suitable for comparisons
try:
collection_web_update_time = time.strftime("%Y-%m-%d %H:%M:%S",
time.strptime(collection_web_update_time, "%d %b %Y %H:%M:%S"))
except ValueError, e:
pass
if collection_table_update_time > collection_web_update_time:
output += """<br /><b><span class="info">Warning: The collections has been modified since last time Webcoll was executed, to process the changes, Webcoll must be executed.</span></b><br />"""
header = ['ID', 'Name', 'Time', 'Status', 'Progress']
actions = []
output += """<br /><b>Last BibSched tasks:</b><br />"""
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='webcoll' and runtime< now() ORDER by runtime")
if len(res) > 0:
(id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[len(res) - 1]
webcoll__update_time = runtime
actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')])
else:
actions.append(['', 'webcoll', '', '', 'Not executed yet'])
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='bibindex' and runtime< now() ORDER by runtime")
if len(res) > 0:
(id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[len(res) - 1]
actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')])
else:
actions.append(['', 'bibindex', '', '', 'Not executed yet'])
output += tupletotable(header=header, tuple=actions)
output += """<br /><b>Next scheduled BibSched run:</b><br />"""
actions = []
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='webcoll' and runtime > now() ORDER by runtime")
webcoll_future = ""
if len(res) > 0:
(id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[0]
webcoll__update_time = runtime
actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')])
webcoll_future = "yes"
else:
actions.append(['', 'webcoll', '', '', 'Not scheduled'])
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='bibindex' and runtime > now() ORDER by runtime")
bibindex_future = ""
if len(res) > 0:
(id, proc, host, user, runtime, sleeptime, arguments, status, progress) = res[0]
actions.append([id, proc, runtime, (status !="" and status or ''), (progress !="" and progress or '')])
bibindex_future = "yes"
else:
actions.append(['', 'bibindex', '', '', 'Not scheduled'])
output += tupletotable(header=header, tuple=actions)
if webcoll_future == "":
output += """<br /><b><span class="info">Warning: Webcoll is not scheduled for a future run by bibsched, any updates to the collection will not be processed.</span></b><br />"""
if bibindex_future == "":
output += """<br /><b><span class="info">Warning: Bibindex is not scheduled for a future run by bibsched, any updates to the records will not be processed.</span></b><br />"""
body = [output]
if callback:
return perform_index(colID, ln, "perform_checkwebcollstatus", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_modifyrestricted(colID, ln, rest='', callback='yes', confirm=-1):
"""modify which apache group is allowed to access the collection.
rest - the groupname"""
subtitle = ''
output = ""
col_dict = dict(get_def_name('', "collection"))
action_id = acc_get_action_id(VIEWRESTRCOLL)
if colID and col_dict.has_key(int(colID)):
colID = int(colID)
- subtitle = """<a name="2">2. Modify access restrictions for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.2">?</a>]</small>""" % (col_dict[colID], weburl)
+ subtitle = """<a name="2">2. Modify access restrictions for collection '%s'</a>&nbsp;&nbsp;&nbsp;<small>[<a title="See guide" href="%s/help/admin/websearch-admin-guide#3.2">?</a>]</small>""" % (col_dict[colID], CFG_SITE_URL)
output = """<p>Please note that CDS Invenio versions greater than <em>0.92.1</em> manage collection restriction via the standard
<strong><a href="/admin/webaccess/webaccessadmin.py/modifyauthorizations?id_action=%i&reverse=1">WebAccess Admin Interface</a></strong> (action '%s').</p>
""" % (action_id, VIEWRESTRCOLL)
body = [output]
if callback:
return perform_editcollection(colID, ln, "perform_modifyrestricted", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def perform_checkcollectionstatus(colID, ln, confirm=0, callback='yes'):
"""Check the configuration of the collections."""
from invenio.search_engine import collection_restricted_p
- subtitle = """<a name="11"></a>Collection Status&nbsp;&nbsp;&nbsp;[<a href="%s/help/admin/websearch-admin-guide#5">?</a>]""" % weburl
+ subtitle = """<a name="11"></a>Collection Status&nbsp;&nbsp;&nbsp;[<a href="%s/help/admin/websearch-admin-guide#5">?</a>]""" % CFG_SITE_URL
output = ""
colID = int(colID)
col_dict = dict(get_def_name('', "collection"))
collections = run_sql("SELECT id, name, dbquery FROM collection ORDER BY id")
header = ['ID', 'Name', 'Query', 'Subcollections', 'Restricted', 'I18N', 'Status']
rnk_list = get_def_name('', "rnkMETHOD")
actions = []
for (id, name, dbquery) in collections:
reg_sons = len(get_col_tree(id, 'r'))
vir_sons = len(get_col_tree(id, 'v'))
status = ""
langs = run_sql("SELECT ln from collectionname where id_collection=%s" % id)
i8n = ""
if len(langs) > 0:
for lang in langs:
i8n += "%s, " % lang
else:
i8n = """<b><span class="info">None</span></b>"""
if (reg_sons > 1 and dbquery) or dbquery=="":
status = """<b><span class="warning">1:Query</span></b>"""
elif dbquery is None and reg_sons == 1:
status = """<b><span class="warning">2:Query</span></b>"""
elif dbquery == "" and reg_sons == 1:
status = """<b><span class="warning">3:Query</span></b>"""
if (reg_sons > 1 or vir_sons > 1):
subs = """<b><span class="info">Yes</span></b>"""
else:
subs = """<b><span class="info">No</span></b>"""
if dbquery is None:
dbquery = """<b><span class="info">No</span></b>"""
restricted = collection_restricted_p(name)
if restricted:
restricted = """<b><span class="warning">Yes</span></b>"""
if status:
status += """<b><span class="warning">,4:Restricted</span></b>"""
else:
status += """<b><span class="warning">4:Restricted</span></b>"""
else:
restricted = """<b><span class="info">No</span></b>"""
if status == "":
status = """<b><span class="info">OK</span></b>"""
- actions.append([id, """<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&amp;ln=%s">%s</a>""" % (weburl, id, ln, name), dbquery, subs, restricted, i8n, status])
+ actions.append([id, """<a href="%s/admin/websearch/websearchadmin.py/editcollection?colID=%s&amp;ln=%s">%s</a>""" % (CFG_SITE_URL, id, ln, name), dbquery, subs, restricted, i8n, status])
output += tupletotable(header=header, tuple=actions)
body = [output]
return addadminbox(subtitle, body)
if callback:
return perform_index(colID, ln, "perform_checkcollectionstatus", addadminbox(subtitle, body))
else:
return addadminbox(subtitle, body)
def get_col_tree(colID, rtype=''):
"""Returns a presentation of the tree as a list. TODO: Add loop detection
colID - startpoint for the tree
rtype - get regular or virtual part of the tree"""
try:
colID = int(colID)
stack = [colID]
ssize = 0
tree = [(colID, 0, 0, colID, 'r')]
while len(stack) > 0:
ccolID = stack.pop()
if ccolID == colID and rtype:
res = run_sql("SELECT id_son, score, type FROM collection_collection WHERE id_dad=%s AND type='%s' ORDER BY score ASC,id_son" % (ccolID, rtype))
else:
res = run_sql("SELECT id_son, score, type FROM collection_collection WHERE id_dad=%s ORDER BY score ASC,id_son" % ccolID)
ssize += 1
ntree = []
for i in range(0, len(res)):
id_son = res[i][0]
score = res[i][1]
rtype = res[i][2]
stack.append(id_son)
if i == (len(res) - 1):
up = 0
else:
up = 1
if i == 0:
down = 0
else:
down = 1
ntree.insert(0, (id_son, up, down, ccolID, rtype))
tree = tree[0:ssize] + ntree + tree[ssize:len(tree)]
return tree
except StandardError, e:
return ()
def add_col_dad_son(add_dad, add_son, rtype):
"""Add a son to a collection (dad)
add_dad - add to this collection id
add_son - add this collection id
rtype - either regular or virtual"""
try:
res = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s ORDER BY score ASC" % add_dad)
highscore = 0
for score in res:
if int(score[0]) > highscore:
highscore = int(score[0])
highscore += 1
res = run_sql("INSERT INTO collection_collection(id_dad,id_son,score,type) values(%s,%s,%s,'%s')" % (add_dad, add_son, highscore, rtype))
return (1, highscore)
except StandardError, e:
return (0, e)
def compare_on_val(first, second):
"""Compare the two values"""
return cmp(first[1], second[1])
def get_col_fld(colID=-1, type = '', id_field=''):
"""Returns either all portalboxes associated with a collection, or based on either colID or language or both.
colID - collection id
ln - language id"""
sql = "SELECT id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue, field WHERE id_field=field.id"
try:
if colID > -1:
sql += " AND id_collection=%s" % colID
if id_field:
sql += " AND id_field=%s" % id_field
if type:
sql += " AND type='%s'" % type
sql += " ORDER BY type, score desc, score_fieldvalue desc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_col_pbx(colID=-1, ln='', position = ''):
"""Returns either all portalboxes associated with a collection, or based on either colID or language or both.
colID - collection id
ln - language id"""
sql = "SELECT id_portalbox, id_collection, ln, score, position, title, body FROM collection_portalbox, portalbox WHERE id_portalbox = portalbox.id"
try:
if colID > -1:
sql += " AND id_collection=%s" % colID
if ln:
sql += " AND ln='%s'" % ln
if position:
sql += " AND position='%s'" % position
sql += " ORDER BY position, ln, score desc"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_col_fmt(colID=-1):
"""Returns all formats currently associated with a collection, or for one specific collection
colID - the id of the collection"""
try:
if colID not in [-1, "-1"]:
res = run_sql("SELECT id_format, id_collection, code, score FROM collection_format, format WHERE id_format = format.id AND id_collection=%s ORDER BY score desc" % colID)
else:
res = run_sql("SELECT id_format, id_collection, code, score FROM collection_format, format WHERE id_format = format.id ORDER BY score desc")
return res
except StandardError, e:
return ""
def get_col_rnk(colID, ln):
""" Returns a list of the rank methods the given collection is attached to
colID - id from collection"""
try:
res1 = dict(run_sql("SELECT id_rnkMETHOD, '' FROM collection_rnkMETHOD WHERE id_collection=%s" % colID))
res2 = get_def_name('', "rnkMETHOD")
result = filter(lambda x: res1.has_key(x[0]), res2)
return result
except StandardError, e:
return ()
def get_pbx():
"""Returns all portalboxes"""
try:
res = run_sql("SELECT id, title, body FROM portalbox ORDER by title,body")
return res
except StandardError, e:
return ""
def get_fld_value(fldvID = ''):
"""Returns fieldvalue"""
try:
sql = "SELECT id, name, value FROM fieldvalue"
if fldvID:
sql += " WHERE id=%s" % fldvID
sql += " ORDER BY name"
res = run_sql(sql)
return res
except StandardError, e:
return ""
def get_pbx_pos():
"""Returns a list of all the positions for a portalbox"""
position = {}
position["rt"] = "Right Top"
position["lt"] = "Left Top"
position["te"] = "Title Epilog"
position["tp"] = "Title Prolog"
position["ne"] = "Narrow by coll epilog"
position["np"] = "Narrow by coll prolog"
return position
def get_sort_nametypes():
"""Return a list of the various translationnames for the fields"""
type = {}
type['soo'] = 'Sort options'
type['seo'] = 'Search options'
type['sew'] = 'Search within'
return type
def get_fmt_nametypes():
"""Return a list of the various translationnames for the output formats"""
type = []
type.append(('ln', 'Long name'))
return type
def get_fld_nametypes():
"""Return a list of the various translationnames for the fields"""
type = []
type.append(('ln', 'Long name'))
return type
def get_col_nametypes():
"""Return a list of the various translationnames for the collections"""
type = []
type.append(('ln', 'Long name'))
return type
def find_last(tree, start_son):
"""Find the previous collection in the tree with the same father as start_son"""
id_dad = tree[start_son][3]
while start_son > 0:
start_son -= 1
if tree[start_son][3] == id_dad:
return start_son
def find_next(tree, start_son):
"""Find the next collection in the tree with the same father as start_son"""
id_dad = tree[start_son][3]
while start_son < len(tree):
start_son += 1
if tree[start_son][3] == id_dad:
return start_son
def remove_col_subcol(id_son, id_dad, type):
"""Remove a collection as a son of another collection in the tree, if collection isn't used elsewhere in the tree, remove all registered sons of the id_son.
id_son - collection id of son to remove
id_dad - the id of the dad"""
try:
if id_son != id_dad:
tree = get_col_tree(id_son)
res = run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s" % (id_son, id_dad))
else:
tree = get_col_tree(id_son, type)
res = run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s and type='%s'" % (id_son, id_dad, type))
if not run_sql("SELECT * from collection_collection WHERE id_son=%s and type='%s'" % (id_son, type)):
for (id, up, down, dad, rtype) in tree:
res = run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s" % (id, dad))
return (1, "")
except StandardError, e:
return (0, e)
def check_col(add_dad, add_son):
"""Check if the collection can be placed as a son of the dad without causing loops.
add_dad - collection id
add_son - collection id"""
try:
stack = [add_dad]
res = run_sql("SELECT id_dad FROM collection_collection WHERE id_dad=%s AND id_son=%s" % (add_dad, add_son))
if res:
raise StandardError
while len(stack) > 0:
colID = stack.pop()
res = run_sql("SELECT id_dad FROM collection_collection WHERE id_son=%s" % colID)
for id in res:
if int(id[0]) == int(add_son):
raise StandardError
else:
stack.append(id[0])
return (1, "")
except StandardError, e:
return (0, e)
def attach_rnk_col(colID, rnkID):
"""attach rank method to collection
rnkID - id from rnkMETHOD table
colID - id of collection, as in collection table """
try:
res = run_sql("INSERT INTO collection_rnkMETHOD(id_collection, id_rnkMETHOD) values (%s,%s)" % (colID, rnkID))
return (1, "")
except StandardError, e:
return (0, e)
def detach_rnk_col(colID, rnkID):
"""detach rank method from collection
rnkID - id from rnkMETHOD table
colID - id of collection, as in collection table """
try:
res = run_sql("DELETE FROM collection_rnkMETHOD WHERE id_collection=%s AND id_rnkMETHOD=%s" % (colID, rnkID))
return (1, "")
except StandardError, e:
return (0, e)
def switch_col_treescore(col_1, col_2):
try:
res1 = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s and id_son=%s" % (col_1[3], col_1[0]))
res2 = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s and id_son=%s" % (col_2[3], col_2[0]))
res = run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s and id_son=%s" % (res2[0][0], col_1[3], col_1[0]))
res = run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s and id_son=%s" % (res1[0][0], col_2[3], col_2[0]))
return (1, "")
except StandardError, e:
return (0, e)
def move_col_tree(col_from, col_to, move_to_rtype=''):
"""Move a collection from one point in the tree to another. becomes a son of the endpoint.
col_from - move this collection from current point
col_to - and set it as a son of this collection.
move_to_rtype - either virtual or regular collection"""
try:
res = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s ORDER BY score asc" % col_to[0])
highscore = 0
for score in res:
if int(score[0]) > highscore:
highscore = int(score[0])
highscore += 1
if not move_to_rtype:
move_to_rtype = col_from[4]
res = run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s" % (col_from[0], col_from[3]))
res = run_sql("INSERT INTO collection_collection(id_dad,id_son,score,type) values(%s,%s,%s,'%s')" % (col_to[0], col_from[0], highscore, move_to_rtype))
return (1, "")
except StandardError, e:
return (0, e)
def remove_pbx(colID, pbxID, ln):
"""Removes a portalbox from the collection given.
colID - the collection the format is connected to
pbxID - the portalbox which should be removed from the collection.
ln - the language of the portalbox to be removed"""
try:
res = run_sql("DELETE FROM collection_portalbox WHERE id_collection=%s AND id_portalbox=%s AND ln='%s'" % (colID, pbxID, ln))
return (1, "")
except StandardError, e:
return (0, e)
def remove_fmt(colID, fmtID):
"""Removes a format from the collection given.
colID - the collection the format is connected to
fmtID - the format which should be removed from the collection."""
try:
res = run_sql("DELETE FROM collection_format WHERE id_collection=%s AND id_format=%s" % (colID, fmtID))
return (1, "")
except StandardError, e:
return (0, e)
def remove_fld(colID, fldID, fldvID=''):
"""Removes a field from the collection given.
colID - the collection the format is connected to
fldID - the field which should be removed from the collection."""
try:
sql = "DELETE FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s" % (colID, fldID)
if fldvID:
if fldvID != "None":
sql += " AND id_fieldvalue=%s" % fldvID
else:
sql += " AND id_fieldvalue is NULL"
res = run_sql(sql)
return (1, "")
except StandardError, e:
return (0, e)
def delete_fldv(fldvID):
"""Deletes all data for the given fieldvalue
fldvID - delete all data in the tables associated with fieldvalue and this id"""
try:
res = run_sql("DELETE FROM collection_field_fieldvalue WHERE id_fieldvalue=%s" % fldvID)
res = run_sql("DELETE FROM fieldvalue WHERE id=%s" % fldvID)
return (1, "")
except StandardError, e:
return (0, e)
def delete_pbx(pbxID):
"""Deletes all data for the given portalbox
pbxID - delete all data in the tables associated with portalbox and this id """
try:
res = run_sql("DELETE FROM collection_portalbox WHERE id_portalbox=%s" % pbxID)
res = run_sql("DELETE FROM portalbox WHERE id=%s" % pbxID)
return (1, "")
except StandardError, e:
return (0, e)
def delete_fmt(fmtID):
"""Deletes all data for the given format
fmtID - delete all data in the tables associated with format and this id """
try:
res = run_sql("DELETE FROM format WHERE id=%s" % fmtID)
res = run_sql("DELETE FROM collection_format WHERE id_format=%s" % fmtID)
res = run_sql("DELETE FROM formatname WHERE id_format=%s" % fmtID)
return (1, "")
except StandardError, e:
return (0, e)
def delete_col(colID):
"""Deletes all data for the given collection
colID - delete all data in the tables associated with collection and this id """
try:
res = run_sql("DELETE FROM collection WHERE id=%s" % colID)
res = run_sql("DELETE FROM collectionname WHERE id_collection=%s" % colID)
res = run_sql("DELETE FROM collection_rnkMETHOD WHERE id_collection=%s" % colID)
res = run_sql("DELETE FROM collection_collection WHERE id_dad=%s" % colID)
res = run_sql("DELETE FROM collection_collection WHERE id_son=%s" % colID)
res = run_sql("DELETE FROM collection_portalbox WHERE id_collection=%s" % colID)
res = run_sql("DELETE FROM collection_format WHERE id_collection=%s" % colID)
res = run_sql("DELETE FROM collection_field_fieldvalue WHERE id_collection=%s" % colID)
return (1, "")
except StandardError, e:
return (0, e)
def add_fmt(code, name, rtype):
"""Add a new output format. Returns the id of the format.
code - the code for the format, max 6 chars.
name - the default name for the default language of the format.
rtype - the default nametype"""
try:
res = run_sql("INSERT INTO format (code, name) values (%s,%s)", (code, name))
fmtID = run_sql("SELECT id FROM format WHERE code=%s", (code,))
res = run_sql("INSERT INTO formatname(id_format, type, ln, value) VALUES (%s,%s,%s,%s)",
(fmtID[0][0], rtype, CFG_SITE_LANG, name))
return (1, fmtID)
except StandardError, e:
return (0, e)
def update_fldv(fldvID, name, value):
"""Modify existing fieldvalue
fldvID - id of fieldvalue to modify
value - the value of the fieldvalue
name - the name of the fieldvalue."""
try:
res = run_sql("UPDATE fieldvalue set name=%s where id=%s", (name, fldvID))
res = run_sql("UPDATE fieldvalue set value=%s where id=%s", (value, fldvID))
return (1, "")
except StandardError, e:
return (0, e)
def add_fldv(name, value):
"""Add a new fieldvalue, returns id of fieldvalue
value - the value of the fieldvalue
name - the name of the fieldvalue."""
try:
res = run_sql("SELECT id FROM fieldvalue WHERE name=%s and value=%s", (name, value))
if not res:
res = run_sql("INSERT INTO fieldvalue (name, value) values (%s,%s)", (name, value))
res = run_sql("SELECT id FROM fieldvalue WHERE name=%s and value=%s", (name, value))
if res:
return (1, res[0][0])
else:
raise StandardError
except StandardError, e:
return (0, e)
def add_pbx(title, body):
try:
res = run_sql("INSERT INTO portalbox (title, body) values (%s,%s)", (title, body))
res = run_sql("SELECT id FROM portalbox WHERE title=%s AND body=%s", (title, body))
if res:
return (1, res[0][0])
else:
raise StandardError
except StandardError, e:
return (0, e)
def add_col(colNAME, dbquery=None):
"""Adds a new collection to collection table
colNAME - the default name for the collection, saved to collection and collectionname
dbquery - query related to the collection"""
# BTW, sometimes '' are passed instead of None, so change them to None
if not dbquery:
dbquery = None
try:
rtype = get_col_nametypes()[0][0]
colID = run_sql("SELECT id FROM collection WHERE id=1")
if colID:
res = run_sql("INSERT INTO collection (name,dbquery) VALUES (%s,%s)",
(colNAME,dbquery))
else:
res = run_sql("INSERT INTO collection (id,name,dbquery) VALUES (1,%s,%s)",
(colNAME,dbquery))
colID = run_sql("SELECT id FROM collection WHERE name=%s", (colNAME,))
res = run_sql("INSERT INTO collectionname(id_collection, type, ln, value) VALUES (%s,%s,%s,%s)" % (colID[0][0], rtype, CFG_SITE_LANG, colNAME))
if colID:
return (1, colID[0][0])
else:
raise StandardError
except StandardError, e:
return (0, e)
def add_col_pbx(colID, pbxID, ln, position, score=''):
"""add a portalbox to the collection.
colID - the id of the collection involved
pbxID - the portalbox to add
ln - which language the portalbox is for
score - decides which portalbox is the most important
position - position on page the portalbox should appear."""
try:
if score:
res = run_sql("INSERT INTO collection_portalbox(id_portalbox, id_collection, ln, score, position) values (%s,%s,'%s',%s,'%s')" % (pbxID, colID, ln, score, position))
else:
res = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and ln='%s' and position='%s' ORDER BY score desc, ln, position" % (colID, ln, position))
if res:
score = int(res[0][0])
else:
score = 0
res = run_sql("INSERT INTO collection_portalbox(id_portalbox, id_collection, ln, score, position) values (%s,%s,'%s',%s,'%s')" % (pbxID, colID, ln, (score + 1), position))
return (1, "")
except StandardError, e:
return (0, e)
def add_col_fmt(colID, fmtID, score=''):
"""Add a output format to the collection.
colID - the id of the collection involved
fmtID - the id of the format.
score - the score of the format, decides sorting, if not given, place the format on top"""
try:
if score:
res = run_sql("INSERT INTO collection_format(id_format, id_collection, score) values (%s,%s,%s)" % (fmtID, colID, score))
else:
res = run_sql("SELECT score FROM collection_format WHERE id_collection=%s ORDER BY score desc" % colID)
if res:
score = int(res[0][0])
else:
score = 0
res = run_sql("INSERT INTO collection_format(id_format, id_collection, score) values (%s,%s,%s)" % (fmtID, colID, (score + 1)))
return (1, "")
except StandardError, e:
return (0, e)
def add_col_fld(colID, fldID, type, fldvID=''):
"""Add a sort/search/field to the collection.
colID - the id of the collection involved
fldID - the id of the field.
fldvID - the id of the fieldvalue.
type - which type, seo, sew...
score - the score of the format, decides sorting, if not given, place the format on top"""
try:
if fldvID and fldvID not in [-1, "-1"]:
run_sql("DELETE FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type='%s' and id_fieldvalue is NULL" % (colID, fldID, type))
res = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type='%s' ORDER BY score desc" % (colID, fldID, type))
if res:
score = int(res[0][0])
res = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type='%s' ORDER BY score_fieldvalue desc" % (colID, fldID, type))
else:
res = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and type='%s' ORDER BY score desc" % (colID, type))
if res:
score = int(res[0][0]) + 1
else:
score = 1
res = run_sql("SELECT * FROM collection_field_fieldvalue where id_field=%s and id_collection=%s and type='%s' and id_fieldvalue=%s" % (fldID, colID, type, fldvID))
if not res:
run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=score_fieldvalue+1 WHERE id_field=%s AND id_collection=%s and type='%s'" % (fldID, colID, type))
res = run_sql("INSERT INTO collection_field_fieldvalue(id_field, id_fieldvalue, id_collection, type, score, score_fieldvalue) values (%s,%s,%s,'%s',%s,%s)" % (fldID, fldvID, colID, type, score, 1))
else:
return (0, (1, "Already exists"))
else:
res = run_sql("SELECT * FROM collection_field_fieldvalue WHERE id_collection=%s AND type='%s' and id_field=%s and id_fieldvalue is NULL" % (colID, type, fldID))
if res:
return (0, (1, "Already exists"))
else:
run_sql("UPDATE collection_field_fieldvalue SET score=score+1")
res = run_sql("INSERT INTO collection_field_fieldvalue(id_field, id_collection, type, score,score_fieldvalue) values (%s,%s,'%s',%s, 0)" % (fldID, colID, type, 1))
return (1, "")
except StandardError, e:
return (0, e)
def modify_dbquery(colID, dbquery=None):
"""Modify the dbquery of an collection.
colID - the id of the collection involved
dbquery - the new dbquery"""
# BTW, sometimes '' is passed instead of None, so change it to None
if not dbquery:
dbquery = None
try:
res = run_sql("UPDATE collection SET dbquery=%s WHERE id=%s", (dbquery, colID))
return (1, "")
except StandardError, e:
return (0, e)
def modify_pbx(colID, pbxID, sel_ln, score='', position='', title='', body=''):
"""Modify a portalbox
colID - the id of the collection involved
pbxID - the id of the portalbox that should be modified
sel_ln - the language of the portalbox that should be modified
title - the title
body - the content
score - if several portalboxes in one position, who should appear on top.
position - position on page"""
try:
if title:
res = run_sql("UPDATE portalbox SET title=%s WHERE id=%s", (title, pbxID))
if body:
res = run_sql("UPDATE portalbox SET body=%s WHERE id=%s", (body, pbxID))
if score:
res = run_sql("UPDATE collection_portalbox SET score='%s' WHERE id_collection=%s and id_portalbox=%s and ln='%s'" % (score, colID, pbxID, sel_ln))
if position:
res = run_sql("UPDATE collection_portalbox SET position='%s' WHERE id_collection=%s and id_portalbox=%s and ln='%s'" % (position, colID, pbxID, sel_ln))
return (1, "")
except Exception, e:
return (0, e)
def switch_fld_score(colID, id_1, id_2):
"""Switch the scores of id_1 and id_2 in collection_field_fieldvalue
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s" % (colID, id_1))
res2 = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s" % (colID, id_2))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
else:
res = run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s" % (res2[0][0], colID, id_1))
res = run_sql("UPDATE collection_field_fieldvalue SET score=%s WHERE id_collection=%s and id_field=%s" % (res1[0][0], colID, id_2))
return (1, "")
except StandardError, e:
return (0, e)
def switch_fld_value_score(colID, id_1, fldvID_1, fldvID_2):
"""Switch the scores of two field_value
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s" % (colID, id_1, fldvID_1))
res2 = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s" % (colID, id_1, fldvID_2))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
else:
res = run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s" % (res2[0][0], colID, id_1, fldvID_1))
res = run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s" % (res1[0][0], colID, id_1, fldvID_2))
return (1, "")
except Exception, e:
return (0, e)
def switch_pbx_score(colID, id_1, id_2, sel_ln):
"""Switch the scores of id_1 and id_2 in the table given by the argument.
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and id_portalbox=%s and ln='%s'" % (colID, id_1, sel_ln))
res2 = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and id_portalbox=%s and ln='%s'" % (colID, id_2, sel_ln))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln='%s'" % (res2[0][0], colID, id_1, sel_ln))
res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln='%s'" % (res1[0][0], colID, id_2, sel_ln))
return (1, "")
except Exception, e:
return (0, e)
def switch_score(colID, id_1, id_2, table):
"""Switch the scores of id_1 and id_2 in the table given by the argument.
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM collection_%s WHERE id_collection=%s and id_%s=%s" % (table, colID, table, id_1))
res2 = run_sql("SELECT score FROM collection_%s WHERE id_collection=%s and id_%s=%s" % (table, colID, table, id_2))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
res = run_sql("UPDATE collection_%s SET score=%s WHERE id_collection=%s and id_%s=%s" % (table, res2[0][0], colID, table, id_1))
res = run_sql("UPDATE collection_%s SET score=%s WHERE id_collection=%s and id_%s=%s" % (table, res1[0][0], colID, table, id_2))
return (1, "")
except Exception, e:
return (0, e)
def get_detailed_page_tabs(colID=None, recID=None, ln=CFG_SITE_LANG):
"""
Returns the complete list of tabs to be displayed in the
detailed record pages.
Returned structured is a dict with
- key : last component of the url that leads to detailed record tab: http:www.../record/74/key
- values: a dictionary with the following keys:
- label: *string* label to be printed as tab (Not localized here)
- visible: *boolean* if False, tab should not be shown
- enabled: *boolean* if True, tab should be disabled
- order: *int* position of the tab in the list of tabs
- ln: language of the tab labels
returns dict
"""
_ = gettext_set_language(ln)
tabs = {'metadata' : {'label': _('Information'), 'visible': False, 'enabled': True, 'order': 1},
'references': {'label': _('References'), 'visible': False, 'enabled': True, 'order': 2},
'citations' : {'label': _('Citations'), 'visible': False, 'enabled': True, 'order': 3},
'comments' : {'label': _('Discussion'), 'visible': False, 'enabled': True, 'order': 4},
'usage' : {'label': _('Usage statistics'), 'visible': False, 'enabled': True, 'order': 5},
'files' : {'label': _('Fulltext'), 'visible': False, 'enabled': True, 'order': 6}
}
res = run_sql("SELECT tabs FROM collectiondetailedrecordpagetabs " + \
"WHERE id_collection='%s'" % colID)
if len(res) > 0:
tabs_state = res[0][0].split(';')
for tab_state in tabs_state:
if tabs.has_key(tab_state):
tabs[tab_state]['visible'] = True;
else:
# no preference set for this collection.
# assume all tabs are displayed
for key in tabs.keys():
tabs[key]['visible'] = True
if not CFG_WEBCOMMENT_ALLOW_COMMENTS and \
not CFG_WEBCOMMENT_ALLOW_REVIEWS:
tabs['comments']['visible'] = False
tabs['comments']['enabled'] = False
if recID is not None:
# Disable references if no references found
bfo = BibFormatObject(recID)
if bfe_references.format(bfo, '', '') == '':
tabs['references']['enabled'] = False
# Disable citations if not citations found
if len(get_cited_by(recID)) == 0:
# TODO: Also check for cocitations
tabs['citations']['enabled'] = False
# Disable fulltext if no file found
brd = BibRecDocs(recID)
if len(brd.list_bibdocs()) == 0:
tabs['files']['enabled'] = False
tabs[''] = tabs['metadata']
del tabs['metadata']
return tabs
diff --git a/modules/websearch/web/admin/websearchadmin.py b/modules/websearch/web/admin/websearchadmin.py
index f078bcec1..e4dc8731d 100644
--- a/modules/websearch/web/admin/websearchadmin.py
+++ b/modules/websearch/web/admin/websearchadmin.py
@@ -1,1075 +1,1075 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio WebSearch Administrator Interface."""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
import sys
import invenio.websearchadminlib as wsc
from invenio.bibrankadminlib import check_user
from invenio.webpage import page, create_error_box, adderrorbox
-from invenio.config import weburl, CFG_SITE_SECURE_URL, CFG_SITE_LANG, CFG_SITE_NAME
+from invenio.config import CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_SITE_LANG, CFG_SITE_NAME
from invenio.dbquery import Error
from invenio.webuser import getUid, page_not_authorized
from invenio.messages import gettext_set_language
from invenio.urlutils import wash_url_argument
def switchfmtscore(req, colID, type, id_1, id_2, ln=CFG_SITE_LANG):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_switchfmtscore(colID=colID,
ln=ln,
type=type,
id_1=id_1,
id_2=id_2),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def switchfldscore(req, colID, id_1, id_2, fmeth, ln=CFG_SITE_LANG):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_switchfldscore(colID=colID,
ln=ln,
id_1=id_1,
id_2=id_2,
fmeth=fmeth),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def switchfldvaluescore(req, colID, id_1, id_fldvalue_1, id_fldvalue_2, ln=CFG_SITE_LANG):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_switchfldvaluescore(colID=colID,
ln=ln,
id_1=id_1,
id_fldvalue_1=id_fldvalue_1,
id_fldvalue_2=id_fldvalue_2),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def runwebcoll(req, colID, ln=CFG_SITE_LANG, confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="WebSearch Admin",
body=wsc.perform_checkwebcollstatus(colID=colID,
ln=ln,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def switchpbxscore(req, colID, id_1, id_2, sel_ln,ln=CFG_SITE_LANG):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_switchpbxscore(colID=colID,
ln=ln,
id_1=id_1,
id_2=id_2,
sel_ln=sel_ln),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifydbquery(req, colID, ln=CFG_SITE_LANG, dbquery='', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_modifydbquery(colID=colID,
ln=ln,
dbquery=dbquery,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showtree(req, colID, ln=CFG_SITE_LANG):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Collection tree",
body=wsc.perform_showtree(colID=colID,
ln=ln),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifytranslations(req, colID, ln=CFG_SITE_LANG, sel_type='', trans = [], confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_modifytranslations(colID=colID,
ln=ln,
sel_type=sel_type,
trans=trans,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addcollectiontotree(req, colID, ln=CFG_SITE_LANG, add_dad='', add_son='', rtype='', mtype='', callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="WebSearch Admin",
body=wsc.perform_addcollectiontotree(colID=colID,
ln=CFG_SITE_LANG,
add_dad=add_dad,
add_son=add_son,
rtype=rtype,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addcollection(req, colID, ln=CFG_SITE_LANG, colNAME='', dbquery='', callback="yes", confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="WebSearch Admin",
body=wsc.perform_addcollection(colID=colID,
ln=CFG_SITE_LANG,
colNAME=colNAME,
dbquery=dbquery,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyrankmethods(req, colID, ln=CFG_SITE_LANG, func='', rnkID='', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_modifyrankmethods(colID=colID,
ln=ln,
func=func,
rnkID=rnkID,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def deletecollection(req, colID, ln=CFG_SITE_LANG, confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_deletecollection(colID=colID,
ln=ln,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def editcollection(req, colID=1, ln=CFG_SITE_LANG, mtype=''):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_editcollection(colID=colID,
ln=ln,
mtype=mtype),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addoutputformat(req, colID, ln=CFG_SITE_LANG, code='', name='', callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_addoutputformat(colID=colID,
ln=ln,
code=code,
name=name,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showoutputformats(req, colID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_showoutputformats(colID=colID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addexistingoutputformat(req, colID, ln=CFG_SITE_LANG, fmtID=-1, callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_addexistingoutputformat(colID=colID,
ln=ln,
fmtID=fmtID,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def deleteoutputformat(req, colID, ln=CFG_SITE_LANG, fmtID=-1, callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_deleteoutputformat(colID=colID,
ln=ln,
fmtID=fmtID,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def removeoutputformat(req, colID, ln=CFG_SITE_LANG, fmtID='', callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_removeoutputformat(colID=colID,
ln=ln,
fmtID=fmtID,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def update_external_collections(req, colID, ln=CFG_SITE_LANG, state=None, recurse=None):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body = wsc.perform_update_external_collections(colID, ln, state, recurse),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def update_detailed_record_options(req, colID, ln=CFG_SITE_LANG, tabs=[], recurse=0):
"""Update the preferences for the tab to show/hide in the detailed record page. """
_tabs = wash_url_argument(tabs, 'list')
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body = wsc.perform_update_detailed_record_options(colID, ln, _tabs, recurse),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def removefieldvalue(req, colID, ln=CFG_SITE_LANG, fldID='', fldvID='', fmeth='', callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_removefieldvalue(colID=colID,
ln=ln,
fldID=fldID,
fldvID=fldvID,
fmeth=fmeth,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def removefield(req, colID, ln=CFG_SITE_LANG, fldID='', fldvID='', fmeth='', callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_removefield(colID=colID,
ln=ln,
fldID=fldID,
fldvID=fldvID,
fmeth=fmeth,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyfield(req, colID, fldID, fldvID='', ln=CFG_SITE_LANG, callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_modifyfield(colID=colID,
fldID=fldID,
fldvID=fldvID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyoutputformat(req, colID, ln=CFG_SITE_LANG, fmtID=-1, sel_type='', trans=[], confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_modifyoutputformat(colID=colID,
ln=ln,
fmtID=fmtID,
sel_type=sel_type,
trans=trans,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showsearchoptions(req, colID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_showsearchoptions(colID=colID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addexistingfield(req, colID, ln=CFG_SITE_LANG, fldID=-1, fldvID=-1, fmeth='', callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_addexistingfield(colID=colID,
ln=ln,
fldID=fldID,
fldvID=fldvID,
fmeth=fmeth,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page(title='Authorization failure',
uid=uid,
body=adderrorbox('try to login first',
datalist=["""You are not a user authorized to perform admin tasks, try to
- <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, weburl)]),
+ <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, CFG_SITE_URL)]),
navtrail= navtrail_previous_links,
lastupdated=__lastupdated__)
def rearrangefield(req, colID, ln=CFG_SITE_LANG, fmeth='', callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_rearrangefield(colID=colID,
ln=ln,
fmeth=fmeth,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page(title='Authorization failure',
uid=uid,
body=adderrorbox('try to login first',
datalist=["""You are not a user authorized to perform admin tasks, try to
- <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, weburl)]),
+ <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, CFG_SITE_URL)]),
navtrail= navtrail_previous_links,
lastupdated=__lastupdated__)
def addexistingfieldvalue(req, colID, fldID, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_addexistingfieldvalue(colID=colID,
ln=ln,
fldID=fldID,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page(title='Authorization failure',
uid=uid,
body=adderrorbox('try to login first',
datalist=["""You are not a user authorized to perform admin tasks, try to
- <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, weburl)]),
+ <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, CFG_SITE_URL)]),
navtrail= navtrail_previous_links,
lastupdated=__lastupdated__)
def rearrangefieldvalue(req, colID, fldID, ln=CFG_SITE_LANG, callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_rearrangefieldvalue(colID=colID,
ln=ln,
fldID=fldID,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page(title='Authorization failure',
uid=uid,
body=adderrorbox('try to login first',
datalist=["""You are not a user authorized to perform admin tasks, try to
- <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, weburl)]),
+ <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, CFG_SITE_URL)]),
navtrail= navtrail_previous_links,
lastupdated=__lastupdated__)
def addnewfieldvalue(req, colID, fldID, ln=CFG_SITE_LANG, name='', value='', callback="yes", confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_addnewfieldvalue(colID=colID,
fldID=fldID,
ln=ln,
name=name,
value=value,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyfieldvalue(req, colID, fldID, fldvID, ln=CFG_SITE_LANG, name='', value='', callback="yes", confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_modifyfieldvalue(colID=colID,
fldID=fldID,
fldvID=fldvID,
ln=ln,
name=name,
value=value,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
navtrail = navtrail_previous_links,
req=req,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showsearchfields(req, colID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_showsearchfields(colID=colID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showsortoptions(req, colID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_showsortoptions(colID=colID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifyportalbox(req, colID, ln=CFG_SITE_LANG, pbxID=-1, score='', position='', sel_ln='', title='', body='', callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_modifyportalbox(colID=colID,
ln=ln,
pbxID=pbxID,
score=score,
position=position,
sel_ln=sel_ln,
title=title,
body=body,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def removeportalbox(req, colID, ln=CFG_SITE_LANG, pbxID='', sel_ln='', callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_removeportalbox(colID=colID,
ln=ln,
pbxID=pbxID,
sel_ln=sel_ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addexistingportalbox(req, colID, ln=CFG_SITE_LANG, pbxID=-1, score=0, position='', sel_ln='', callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_addexistingportalbox(colID=colID,
ln=ln,
pbxID=pbxID,
score=score,
position=position,
sel_ln=sel_ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page(title='Authorization failure',
uid=uid,
body=adderrorbox('try to login first',
datalist=["""You are not a user authorized to perform admin tasks, try to
- <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, weburl)]),
+ <a href="%s/youraccount/login?referer=%s/admin/websearch/websearchadmin.py/">login</a> with another account.""" % (CFG_SITE_SECURE_URL, CFG_SITE_URL)]),
navtrail= navtrail_previous_links,
lastupdated=__lastupdated__)
def deleteportalbox(req, colID, ln=CFG_SITE_LANG, pbxID=-1, callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_deleteportalbox(colID=colID,
ln=ln,
pbxID=pbxID,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def showportalboxes(req, colID, ln=CFG_SITE_LANG, callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_showportalboxes(colID=colID,
ln=ln,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def addportalbox(req, colID, ln=CFG_SITE_LANG, title='', body='', callback='yes', confirm=-1):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="Edit Collection",
body=wsc.perform_addportalbox(colID=colID,
ln=ln,
title=title,
body=body,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def modifycollectiontree(req, colID, ln=CFG_SITE_LANG, move_up='', move_down='', move_from='', move_to='', delete='', rtype='', callback='yes', confirm=0):
- navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (weburl)
+ navtrail_previous_links = wsc.getnavtrail() + """&gt; <a class="navtrail" href="%s/admin/websearch/websearchadmin.py/">WebSearch Admin</a> """ % (CFG_SITE_URL)
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="WebSearch Admin",
body=wsc.perform_modifycollectiontree(colID=colID,
ln=ln,
move_up=move_up,
move_down=move_down,
move_from=move_from,
move_to=move_to,
delete=delete,
rtype=rtype,
callback=callback,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def index(req, colID=1, ln=CFG_SITE_LANG, mtype='', content='', confirm=0):
navtrail_previous_links = wsc.getnavtrail()
try:
uid = getUid(req)
except Error, e:
return error_page(req)
auth = check_user(req,'cfgwebsearch')
if not auth[0]:
return page(title="WebSearch Admin",
body=wsc.perform_index(colID=colID,
ln=ln,
mtype=mtype,
content=content,
confirm=confirm),
uid=uid,
language=ln,
req=req,
navtrail = navtrail_previous_links,
lastupdated=__lastupdated__)
else:
return page_not_authorized(req=req, text=auth[1], navtrail=navtrail_previous_links)
def error_page(req, ln=CFG_SITE_LANG, verbose=1):
_ = gettext_set_language(ln)
return page(title=_("Internal Error"),
body = create_error_box(req, verbose=verbose, ln=ln),
description="%s - Internal Error" % CFG_SITE_NAME,
keywords="%s, Internal Error" % CFG_SITE_NAME,
language=ln,
req=req)
diff --git a/modules/websession/doc/admin/websession-admin-guide.webdoc b/modules/websession/doc/admin/websession-admin-guide.webdoc
index b3e0c3685..d6c271ad3 100644
--- a/modules/websession/doc/admin/websession-admin-guide.webdoc
+++ b/modules/websession/doc/admin/websession-admin-guide.webdoc
@@ -1,86 +1,86 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebSession Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p><table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
WARNING: THIS ADMIN GUIDE IS NOT FULLY COMPLETED
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
This Admin Guide is not yet completed. Moreover, some
admin-level functionality for this module exists only in the form of
manual recipes. We are in the process of developing both the
guide as well as the web admin interface. If you are interested
in seeing some specific things implemented with high priority,
please contact us at <CFG_SITE_SUPPORT_EMAIL>. Thanks for your interest!
</td>
</tr>
</tbody>
</table></p>
<h2>Guest User Sessions</h2>
<p>Guest users create a lot of entries in <CFG_SITE_NAME_INTL> tables that are
related to their web sessions, their search history, personal baskets,
etc. This data has to be garbage-collected periodically. This is done
via a bibsched program, the <em>Invenio Gargabe Collector</em> (<strong>InvenioGC</strong>):
<blockquote>
<pre>
$ inveniogc -s 1d
</pre>
</blockquote>
</p>
<p>The Invenio Gargabe Collector can be used for keeping clean and slim other
areas of CDS Invenio, too:
<blockquote>
<pre>
Usage: /opt/cds-dev/bin/inveniogc [options]
Command options:
-l, --logs Clean up/compress old logs and temporary files.
-g, --guests Clean up expired guest user related information. (default if nothing is specified)
-d, --documents Clean up delete documents and revisions older than 3650 days
-a, --all Calls every cleaning action.
Scheduling options:
-u, --user=USER User name to submit the task as, password needed.
-t, --runtime=TIME Time to execute the task (now), e.g.: +15s, 5m, 3h, 2002-10-27 13:57:26
-s, --sleeptime=SLEEP Sleeping frequency after which to repeat task (no), e.g.: 30m, 2h, 1d
General options:
-h, --help Print this help.
-V, --version Print version information.
-v, --verbose=LEVEL Verbose level (0=min, 1=default, 9=max).
</pre>
</blockquote>
You can use InvenioGC for cleaning old logs (e.g. BibSched task logs) and temporary
files, by specfying the <tt>--logs</tt> option on the command line,
or very old revisions of documents, by specfying <tt>--documents</tt>.</p>
-<p>Please see also the <a href="<WEBURL>/help/admin/howto-run">HOWTO Run</a> guide.</p>
+<p>Please see also the <a href="<CFG_SITE_URL>/help/admin/howto-run">HOWTO Run</a> guide.</p>
diff --git a/modules/websession/lib/webaccount.py b/modules/websession/lib/webaccount.py
index 19ae6b4d5..096f73704 100644
--- a/modules/websession/lib/webaccount.py
+++ b/modules/websession/lib/webaccount.py
@@ -1,337 +1,335 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import sys
import string
import cgi
import re
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_CERN_SITE, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_VERSION, \
- weburl
+ CFG_SITE_URL
from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION, SUPERADMINROLE
from invenio.webpage import page
from invenio.dbquery import run_sql
from invenio.webuser import getUid,isGuestUser, get_user_preferences, \
set_user_preferences, collect_user_info
from invenio.access_control_admin import acc_find_user_role_actions
from invenio.messages import gettext_set_language
from invenio.external_authentication import InvenioWebAccessExternalAuthError
import invenio.template
websession_templates = invenio.template.load('websession')
def perform_info(req, ln):
"""Display the main features of CDS personalize"""
out = ""
uid = getUid(req)
return websession_templates.tmpl_account_info(
ln = ln,
uid = uid,
guest = isGuestUser(uid),
CFG_CERN_SITE = CFG_CERN_SITE,
);
def perform_display_external_user_settings(settings, ln):
"""show external user settings which is a dictionary."""
_ = gettext_set_language(ln)
html_settings = ""
print_settings = False
settings_keys = settings.keys()
settings_keys.sort()
for key in settings_keys:
value = settings[key]
if key.startswith("EXTERNAL_") and not "HIDDEN_" in key:
print_settings = True
key = key[9:].capitalize()
html_settings += websession_templates.tmpl_external_setting(ln, key, value)
return print_settings and websession_templates.tmpl_external_user_settings(ln, html_settings) or ""
def perform_youradminactivities(user_info, ln):
"""Return text for the `Your Admin Activities' box. Analyze
whether user UID has some admin roles, and if yes, then print
suitable links for the actions he can do. If he's not admin,
print a simple non-authorized message."""
your_role_actions = acc_find_user_role_actions(user_info)
your_roles = []
your_admin_activities = []
guest = isGuestUser(user_info['uid'])
for (role, action) in your_role_actions:
if role not in your_roles:
your_roles.append(role)
if action not in your_admin_activities:
your_admin_activities.append(action)
if SUPERADMINROLE in your_roles:
for action in ["runbibedit", "cfgbibformat", "cfgbibharvest", "cfgoairepository", "cfgbibrank", "cfgbibindex", "cfgwebaccess", "cfgwebcomment", "cfgwebsearch", "cfgwebsubmit"]:
if action not in your_admin_activities:
your_admin_activities.append(action)
return websession_templates.tmpl_account_adminactivities(
ln = ln,
uid = user_info['uid'],
guest = guest,
roles = your_roles,
activities = your_admin_activities,
- weburl = weburl,
)
def perform_display_account(req,username,bask,aler,sear,msgs,grps,ln):
"""Display a dynamic page that shows the user's account."""
# load the right message language
_ = gettext_set_language(ln)
uid = getUid(req)
user_info = collect_user_info(req)
#your account
if isGuestUser(uid):
user = "guest"
login = "%s/youraccount/login?ln=%s" % (CFG_SITE_SECURE_URL, ln)
accBody = _("You are logged in as guest. You may want to %(x_url_open)slogin%(x_url_close)s as a regular user.") %\
{'x_url_open': '<a href="' + login + '">',
'x_url_close': '</a>'}
accBody += "<br /><br />"
bask=aler=msgs= _("The %(x_fmt_open)sguest%(x_fmt_close)s users need to %(x_url_open)sregister%(x_url_close)s first") %\
{'x_fmt_open': '<strong class="headline">',
'x_fmt_close': '</strong>',
'x_url_open': '<a href="' + login + '">',
'x_url_close': '</a>'}
sear= _("No queries found")
else:
user = username
accBody = websession_templates.tmpl_account_body(
ln = ln,
user = user,
)
return websession_templates.tmpl_account_page(
ln = ln,
- weburl = weburl,
accBody = accBody,
baskets = bask,
alerts = aler,
searches = sear,
messages = msgs,
groups = grps,
administrative = perform_youradminactivities(user_info, ln)
)
def template_account(title, body, ln):
"""It is a template for print each of the options from the user's account."""
return websession_templates.tmpl_account_template(
ln = ln,
title = title,
body = body
)
def warning_guest_user(type, ln=CFG_SITE_LANG):
"""It returns an alert message,showing that the user is a guest user and should log into the system."""
# load the right message language
_ = gettext_set_language(ln)
return websession_templates.tmpl_warning_guest_user(
ln = ln,
type = type,
)
def perform_delete(ln):
"""Delete the account of the user, not implement yet."""
# TODO
return websession_templates.tmpl_account_delete(ln = ln)
def perform_set(email, ln, verbose=0):
"""Perform_set(email,password): edit your account parameters, email and
password.
"""
try:
res = run_sql("SELECT id, nickname FROM user WHERE email=%s", (email,))
uid = res[0][0]
nickname = res[0][1]
except:
uid = 0
nickname = ""
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS
prefs = get_user_preferences(uid)
if CFG_EXTERNAL_AUTHENTICATION.has_key(prefs['login_method']) and CFG_EXTERNAL_AUTHENTICATION[prefs['login_method']][0]:
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL = 3
out = websession_templates.tmpl_user_preferences(
ln = ln,
email = email,
email_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL >= 2),
password_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS_LOCAL >= 3),
nickname = nickname,
)
if len(CFG_EXTERNAL_AUTHENTICATION) > 1:
try:
uid = run_sql("SELECT id FROM user where email=%s", (email,))
uid = uid[0][0]
except:
uid = 0
current_login_method = prefs['login_method']
methods = CFG_EXTERNAL_AUTHENTICATION.keys()
# Filtering out methods that don't provide user_exists to check if
# a user exists in the external auth method before letting him/her
# to switch.
for method in methods:
if CFG_EXTERNAL_AUTHENTICATION[method][0]:
try:
if not CFG_EXTERNAL_AUTHENTICATION[method][0].user_exists(email):
methods.remove(method)
except (AttributeError, InvenioWebAccessExternalAuthError):
methods.remove(method)
methods.sort()
if len(methods) > 1:
out += websession_templates.tmpl_user_external_auth(
ln = ln,
methods = methods,
current = current_login_method,
method_disabled = (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4)
)
try:
current_group_records = prefs['websearch_group_records']
except KeyError:
current_group_records = 10
try:
show_latestbox = prefs['websearch_latestbox']
except KeyError:
show_latestbox = True
try:
show_helpbox = prefs['websearch_helpbox']
except KeyError:
show_helpbox = True
out += websession_templates.tmpl_user_websearch_edit(
ln = ln,
current = current_group_records,
show_latestbox = show_latestbox,
show_helpbox = show_helpbox,
)
if verbose >= 9:
for key, value in prefs.items():
out += "<b>%s</b>:%s<br />" % (key, value)
out += perform_display_external_user_settings(prefs, ln)
return out
def create_register_page_box(referer='', ln=CFG_SITE_LANG):
"""Register a new account."""
return websession_templates.tmpl_register_page(
referer = referer,
ln = ln,
level = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS,
)
## create_login_page_box(): ask for the user's email and password, for login into the system
def create_login_page_box(referer='', apache_msg="", ln=CFG_SITE_LANG):
# List of referer regexep and message to print
_ = gettext_set_language(ln)
login_referrer2msg = (
(re.compile(r"/search"), "<p>" + _("This collection is restricted. If you think you have right to access it, please authenticate yourself.") + "</p>"),
)
msg = ""
for regexp, txt in login_referrer2msg:
if regexp.search(referer):
msg = txt
break
# FIXME: Temporary Hack to help CDS current migration
if CFG_CERN_SITE and apache_msg:
return msg + apache_msg
if apache_msg:
msg += apache_msg + "<p>Otherwise please, provide the correct authorization" \
" data in the following form.</p>"
internal = None
for system in CFG_EXTERNAL_AUTHENTICATION.keys():
if not CFG_EXTERNAL_AUTHENTICATION[system][0]:
internal = system
break
register_available = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS <= 1 and internal
methods = CFG_EXTERNAL_AUTHENTICATION.keys()
methods.sort()
selected = ''
for method in methods:
if CFG_EXTERNAL_AUTHENTICATION[method][1]:
selected = method
break
return websession_templates.tmpl_login_form(
ln = ln,
referer = referer,
internal = internal,
register_available = register_available,
methods = methods,
selected_method = selected,
msg = msg,
)
# perform_logout: display the message of not longer authorized,
def perform_logout(req, ln):
return websession_templates.tmpl_account_logout(ln = ln)
#def perform_lost: ask the user for his email, in order to send him the lost password
def perform_lost(ln):
return websession_templates.tmpl_lost_password_form(ln)
#def perform_reset_password: ask the user for a new password to reset the lost one
def perform_reset_password(ln, email, reset_key, msg=''):
return websession_templates.tmpl_reset_password_form(ln, email, reset_key, msg)
# perform_emailSent(email): confirm that the password has been emailed to 'email' address
def perform_emailSent(email, ln):
return websession_templates.tmpl_account_emailSent(ln = ln, email = email)
# peform_emailMessage : display a error message when the email introduced is not correct, and sugest to try again
def perform_emailMessage(eMsg, ln):
return websession_templates.tmpl_account_emailMessage( ln = ln,
msg = eMsg
)
# perform_back(): template for return to a previous page, used for login,register and setting
def perform_back(mess,act,linkname='', ln='en'):
if not linkname:
linkname = act
return websession_templates.tmpl_back_form(
ln = ln,
message = mess,
act = act,
link = linkname,
)
diff --git a/modules/websession/lib/websession_templates.py b/modules/websession/lib/websession_templates.py
index e34408b4c..d27db4292 100644
--- a/modules/websession/lib/websession_templates.py
+++ b/modules/websession/lib/websession_templates.py
@@ -1,2145 +1,2141 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import urllib
import time
import cgi
import gettext
import string
import locale
from invenio.config import \
CFG_CERN_SITE, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_VERSION, \
- weburl
+ CFG_SITE_URL
from invenio.access_control_config import CFG_EXTERNAL_AUTH_USING_SSO, \
CFG_EXTERNAL_AUTH_LOGOUT_SSO
from invenio.websession_config import \
CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS, \
CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS
from invenio.urlutils import make_canonical_urlargd
from invenio.messages import gettext_set_language
from invenio.websession_config import CFG_WEBSESSION_GROUP_JOIN_POLICY
class Template:
def tmpl_back_form(self, ln, message, act, link):
"""
A standard one-message-go-back-link page.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'message' *string* - The message to display
- 'act' *string* - The action to accomplish when going back
- 'link' *string* - The link text
"""
out = """
<table>
<tr>
<td align="left">%(message)s
<a href="./%(act)s?ln=%(ln)s">%(link)s</a></td>
</tr>
</table>
"""% {
'message' : message,
'act' : act,
'link' : link,
'ln' : ln
}
return out
def tmpl_external_setting(self, ln, key, value):
_ = gettext_set_language(ln)
out = """
<tr>
<td align="right"><strong>%s:</strong></td>
<td><i>%s</i></td>
</tr>""" % (key, value)
return out
def tmpl_external_user_settings(self, ln, html_settings):
_ = gettext_set_language(ln)
out = """
<p><big><strong class="headline">%(external_user_settings)s</strong></big></p>
<table>
%(html_settings)s
</table>
<p><big><strong class="headline">%(external_user_groups)s</strong></big></p>
<p>%(consult_external_groups)s</p>
""" % {
'external_user_settings' : _('External account settings'),
'html_settings' : html_settings,
'consult_external_groups' : _('You can consult the list of your external groups directly in the %(x_url_open)sgroups page%(x_url_close)s.') % {
'x_url_open' : '<a href="../yourgroups/display?ln=%s#external_groups">' % ln,
'x_url_close' : '</a>'
},
'external_user_groups' : _('External user groups'),
}
return out
def tmpl_user_preferences(self, ln, email, email_disabled, password_disabled, nickname):
"""
Displays a form for the user to change his email/password.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'email' *string* - The email of the user
- 'email_disabled' *boolean* - If the user has the right to edit his email
- 'password_disabled' *boolean* - If the user has the right to edit his password
- 'nickname' *string* - The nickname of the user (empty string if user does not have it)
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<p><big><strong class="headline">%(edit_params)s</strong></big></p>
<form method="post" action="%(sitesecureurl)s/youraccount/change" name="edit_logins_settings">
<p>%(change_user)s</p>
<table>
<tr><td align="right" valign="top"><strong>
%(nickname_label)s:</strong><br />
<small class="important">(%(mandatory)s)</small>
</td><td valign="top">
%(nickname_prefix)s%(nickname)s%(nickname_suffix)s<br />
<small><span class="quicknote">%(note)s:</span>
%(fixed_nickname_note)s
</small>
</td>
</tr>
<tr><td align="right"><strong>
%(new_email)s:</strong><br />
<small class="important">(%(mandatory)s)</small>
</td><td>
<input type="text" size="25" name="email" %(email_disabled)s value="%(email)s" /><br />
<small><span class="quicknote">%(example)s:</span>
<span class="example">john.doe@example.com</span>
</small>
</td>
</tr>
<tr><td></td><td align="left">
<code class="blocknote"><input class="formbutton" type="submit" value="%(set_values)s" /></code>&nbsp;&nbsp;&nbsp;
</td></tr>
</table>
<input type="hidden" name="action" value="edit" />
</form>
""" % {
'change_user' : _("If you want to change your email or set for the first time your nickname, please set new values in the form below."),
'edit_params' : _("Edit login credentials"),
'nickname_label' : _("Nickname"),
'nickname' : nickname,
'nickname_prefix' : nickname=='' and '<input type="text" size="25" name="nickname" value=""' or '',
'nickname_suffix' : nickname=='' and '" /><br /><small><span class="quicknote">'+_("Example")+':</span><span class="example">johnd</span></small>' or '',
'new_email' : _("New email address"),
'mandatory' : _("mandatory"),
'example' : _("Example"),
'note' : _("Note"),
'set_values' : _("Set new values"),
'email' : email,
'email_disabled' : email_disabled and "readonly" or "",
'sitesecureurl': CFG_SITE_SECURE_URL,
'fixed_nickname_note' : _('Since this is considered as a signature for comments and reviews, once set it can not be changed.')
}
if not password_disabled and not CFG_EXTERNAL_AUTH_USING_SSO:
out += """
<form method="post" action="%(sitesecureurl)s/youraccount/change" name="edit_password">
<p>%(change_pass)s</p>
<table>
<tr>
<td align="right"><strong>%(old_password)s:</strong><br />
<small class="important">(%(mandatory)s)</small>
</td><td align="left">
<input type="password" size="25" name="old_password" %(password_disabled)s /><br />
<small><span class="quicknote">%(note)s:</span>
%(old_password_note)s
</small>
</td>
</tr>
<tr>
<td align="right"><strong>%(new_password)s:</strong><br />
<small class="quicknote">(%(optional)s)</small>
</td><td align="left">
<input type="password" size="25" name="password" %(password_disabled)s /><br />
<small><span class="quicknote">%(note)s:</span>
%(password_note)s
</small>
</td>
</tr>
<tr>
<td align="right"><strong>%(retype_password)s:</strong></td>
<td align="left">
<input type="password" size="25" name="password2" %(password_disabled)s value="" />
</td>
</tr>
<tr><td></td><td align="left">
<code class="blocknote"><input class="formbutton" type="submit" value="%(set_values)s" /></code>&nbsp;&nbsp;&nbsp;
</td></tr>
</table>
<input type="hidden" name="action" value="edit" />
</form>
""" % {
'change_pass' : _("If you want to change your password, please enter the old one and set the new value in the form below."),
'mandatory' : _("mandatory"),
'old_password' : _("Old password"),
'new_password' : _("New password"),
'optional' : _("optional"),
'note' : _("Note"),
'password_note' : _("The password phrase may contain punctuation, spaces, etc."),
'old_password_note' : _("You must fill the old password in order to set a new one."),
'retype_password' : _("Retype password"),
'set_values' : _("Set new password"),
'password_disabled' : password_disabled and "disabled" or "",
'sitesecureurl': CFG_SITE_SECURE_URL,
}
elif not CFG_EXTERNAL_AUTH_USING_SSO and CFG_CERN_SITE:
out += "<p>" + _("""If you are using a lightweight CERN account you can
%(x_url_open)sreset the password%(x_url_close)s.""") % \
{'x_url_open' : \
'<a href="http://cern.ch/LightweightRegistration/ResetPassword.aspx%s">' \
% (make_canonical_urlargd({'email': email, 'returnurl' : CFG_SITE_SECURE_URL + '/youraccount/edit' + make_canonical_urlargd({'lang' : ln}, {})}, {})), 'x_url_close' : '</a>'} + "</p>"
elif CFG_EXTERNAL_AUTH_USING_SSO and CFG_CERN_SITE:
out += "<p>" + _("""You can change or reset your CERN account password by means of the %(x_url_open)sCERN account system%(x_url_close)s.""") % \
{'x_url_open' : '<a href="https://cern.ch/login/password.aspx">', 'x_url_close' : '</a>'} + "</p>"
return out
def tmpl_user_websearch_edit(self, ln, current = 10, show_latestbox = True, show_helpbox = True):
_ = gettext_set_language(ln)
out = """
<form method="post" action="%(sitesecureurl)s/youraccount/change" name="edit_websearch_settings">
<p><big><strong class="headline">%(edit_websearch_settings)s</strong></big></p>
<table>
<tr><td align="right"><input type="checkbox" %(checked_latestbox)s value="1" name="latestbox" /></td>
<td valign="top"><b>%(show_latestbox)s</b></td></tr>
<tr><td align="right"><input type="checkbox" %(checked_helpbox)s value="1" name="helpbox" /></td>
<td valign="top"><b>%(show_helpbox)s</b></td></tr>
<tr><td align="right"><select name="group_records">
""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'edit_websearch_settings' : _("Edit search-related settings"),
'show_latestbox' : _("Show the latest additions box"),
'checked_latestbox' : show_latestbox and 'checked="checked"' or '',
'show_helpbox' : _("Show collection help boxes"),
'checked_helpbox' : show_helpbox and 'checked="checked"' or '',
}
for i in 10, 20, 50, 100, 200:
out += """<option %(selected)s>%(i)s</option>
""" % {
'selected' : current == i and 'selected="selected"' or '',
'i' : i
}
out += """</select></td><td valign="top"><b>%(select_group_records)s</b></td></tr>
<tr><td></td><td><input class="formbutton" type="submit" value="%(update_settings)s" /></td></tr>
</table>
</form>""" % {
'update_settings' : _("Update settings"),
'select_group_records' : _("Number of search results per page"),
}
return out
def tmpl_user_external_auth(self, ln, methods, current, method_disabled):
"""
Displays a form for the user to change his authentication method.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'methods' *array* - The methods of authentication
- 'method_disabled' *boolean* - If the user has the right to change this
- 'current' *string* - The currently selected method
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<form method="post" action="%(sitesecureurl)s/youraccount/change">
<big><strong class="headline">%(edit_method)s</strong></big>
<p>%(explain_method)s:</p>
<table>
<tr><td valign="top"><b>%(select_method)s:</b></td><td>
""" % {
'edit_method' : _("Edit login method"),
'explain_method' : _("Please select which login method you would like to use to authenticate yourself"),
'select_method' : _("Select method"),
'sitesecureurl': CFG_SITE_SECURE_URL,
}
for system in methods:
out += """<input type="radio" name="login_method" value="%(system)s" %(disabled)s %(selected)s />%(system)s<br />""" % {
'system' : system,
'disabled' : method_disabled and 'disabled="disabled"' or "",
'selected' : current == system and 'checked="checked"' or "",
}
out += """ </td></tr>
<tr><td>&nbsp;</td>
<td><input class="formbutton" type="submit" value="%(select_method)s" /></td></tr></table>
</form>""" % {
'select_method' : _("Select method"),
}
return out
def tmpl_lost_password_form(self, ln):
"""
Displays a form for the user to ask for his password sent by email.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'msg' *string* - Explicative message on top of the form.
"""
# load the right message language
_ = gettext_set_language(ln)
out = "<p>" + _("If you have lost the password for your %(sitename)s %(x_fmt_open)sinternal account%(x_fmt_close)s, then please enter your email address in the following form in order to have a password reset link emailed to you.") % {'x_fmt_open' : '<em>', 'x_fmt_close' : '</em>', 'sitename' : CFG_SITE_NAME_INTL[ln]} + "</p>"
out += """
<blockquote>
<form method="post" action="../youraccount/send_email">
<table>
<tr>
<td align="right"><strong>%(email)s:</strong></td>
<td><input type="text" size="25" name="p_email" value="" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="action" value="lost" />
</td>
</tr>
<tr><td>&nbsp;</td>
<td><code class="blocknote"><input class="formbutton" type="submit" value="%(send)s" /></code></td>
</tr>
</table>
</form>
</blockquote>
""" % {
'ln': ln,
'email' : _("Email address"),
'send' : _("Send password reset link"),
}
if CFG_CERN_SITE:
out += "<p>" + _("If you have been using the %(x_fmt_open)sCERN login system%(x_fmt_close)s, then you can recover your password through the %(x_url_open)sCERN authentication system%(x_url_close)s.") % {'x_fmt_open' : '<em>', 'x_fmt_close' : '</em>', 'x_url_open' : '<a href="https://cern.ch/lightweightregistration/ResetPassword.aspx%s">' \
% make_canonical_urlargd({'lf': 'auth', 'returnURL' : CFG_SITE_SECURE_URL + '/youraccount/login?ln='+ln}, {}), 'x_url_close' : '</a>'} + " "
else:
out += "<p>" + _("Note that if you have been using an external login system, then we cannot do anything and you have to ask there.") + " "
out += _("Alternatively, you can ask %s to change your login system from external to internal.") % ("""<a href="mailto:%(email)s">%(email)s</a>""" % { 'email' : CFG_SITE_SUPPORT_EMAIL }) + "</p>"
return out
def tmpl_account_info(self, ln, uid, guest, CFG_CERN_SITE):
"""
Displays the account information
Parameters:
- 'ln' *string* - The language to display the interface in
- 'uid' *string* - The user id
- 'guest' *boolean* - If the user is guest
- 'CFG_CERN_SITE' *boolean* - If the site is a CERN site
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<p>%(account_offer)s</p>
<blockquote>
<dl>
""" % {
'account_offer' : _("%s offers you the possibility to personalize the interface, to set up your own personal library of documents, or to set up an automatic alert query that would run periodically and would notify you of search results by email.") % CFG_SITE_NAME_INTL[ln],
}
if not guest:
out += """
<dt>
<a href="./edit?ln=%(ln)s">%(your_settings)s</a>
</dt>
<dd>%(change_account)s</dd>""" % {
'ln' : ln,
'your_settings' : _("Your Settings"),
'change_account' : _("Set or change your account email address or password. Specify your preferences about the look and feel of the interface.")
}
out += """
<dt><a href="../youralerts/display?ln=%(ln)s">%(your_searches)s</a></dt>
<dd>%(search_explain)s</dd>
<dt><a href="../yourbaskets/display?ln=%(ln)s">%(your_baskets)s</a></dt>
<dd>%(basket_explain)s""" % {
'ln' : ln,
'your_searches' : _("Your Searches"),
'search_explain' : _("View all the searches you performed during the last 30 days."),
'your_baskets' : _("Your Baskets"),
'basket_explain' : _("With baskets you can define specific collections of items, store interesting records you want to access later or share with others."),
}
if guest:
out += self.tmpl_warning_guest_user(ln = ln, type = "baskets")
out += """</dd>
<dt><a href="../youralerts/list?ln=%(ln)s">%(your_alerts)s</a></dt>
<dd>%(explain_alerts)s""" % {
'ln' : ln,
'your_alerts' : _("Your Alerts"),
'explain_alerts' : _("Subscribe to a search which will be run periodically by our service. The result can be sent to you via Email or stored in one of your baskets."),
}
if guest:
out += self.tmpl_warning_guest_user(type="alerts", ln = ln)
out += "</dd>"
if CFG_CERN_SITE:
out += """</dd>
<dt><a href="http://weblib.cern.ch/cgi-bin/checkloan?uid=&amp;version=2">%(your_loans)s</a></dt>
<dd>%(explain_loans)s</dd>""" % {
'your_loans' : _("Your Loans"),
'explain_loans' : _("Check out book you have on loan, submit borrowing requests, etc. Requires CERN ID."),
}
out += """
</dl>
</blockquote>"""
return out
def tmpl_warning_guest_user(self, ln, type):
"""
Displays a warning message about the specified type
Parameters:
- 'ln' *string* - The language to display the interface in
- 'type' *string* - The type of data that will get lost in case of guest account (for the moment: 'alerts' or 'baskets')
"""
# load the right message language
_ = gettext_set_language(ln)
if (type=='baskets'):
msg = _("You are logged in as a guest user, so your baskets will disappear at the end of the current session.") + ' '
elif (type=='alerts'):
msg = _("You are logged in as a guest user, so your alerts will disappear at the end of the current session.") + ' '
msg += _("If you wish you can %(x_url_open)slogin or register here%(x_url_close)s.") % {'x_url_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/login?ln=' + ln + '">',
'x_url_close': '</a>'}
return """<table class="errorbox" summary="">
<tr>
<th class="errorboxheader">%s</th>
</tr>
</table>""" % msg
def tmpl_account_body(self, ln, user):
"""
Displays the body of the actions of the user
Parameters:
- 'ln' *string* - The language to display the interface in
- 'user' *string* - The username (nickname or email)
"""
# load the right message language
_ = gettext_set_language(ln)
out = _("You are logged in as %(x_user)s. You may want to a) %(x_url1_open)slogout%(x_url1_close)s; b) edit your %(x_url2_open)saccount settings%(x_url2_close)s.") %\
{'x_user': user,
'x_url1_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/logout?ln=' + ln + '">',
'x_url1_close': '</a>',
'x_url2_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/edit?ln=' + ln + '">',
'x_url2_close': '</a>',
}
return out + "<br /><br />"
def tmpl_account_template(self, title, body, ln, url):
"""
Displays a block of the your account page
Parameters:
- 'ln' *string* - The language to display the interface in
- 'title' *string* - The title of the block
- 'body' *string* - The body of the block
- 'url' *string* - The URL to go to the proper section
"""
out ="""
<table class="searchbox" width="90%%" summary="" >
<tr>
<th class="searchboxheader"><a href="%s">%s</a></th>
</tr>
<tr>
<td class="searchboxbody">%s</td>
</tr>
</table>""" % (url, title, body)
return out
- def tmpl_account_page(self, ln, weburl, accBody, baskets, alerts, searches, messages, groups, administrative):
+ def tmpl_account_page(self, ln, accBody, baskets, alerts, searches, messages, groups, administrative):
"""
Displays the your account page
Parameters:
- 'ln' *string* - The language to display the interface in
- - 'weburl' *string* - The URL of CDS Invenio
-
- 'accBody' *string* - The body of the heading block
- 'baskets' *string* - The body of the baskets block
- 'alerts' *string* - The body of the alerts block
- 'searches' *string* - The body of the searches block
- 'messages' *string* - The body of the messages block
- 'groups' *string* - The body of the groups block
- 'administrative' *string* - The body of the administrative block
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
out += self.tmpl_account_template(_("Your Account"), accBody, ln, '/youraccount/edit?ln=%s' % ln)
out += self.tmpl_account_template(_("Your Messages"), messages, ln, '/yourmessages/display?ln=%s' % ln)
out += self.tmpl_account_template(_("Your Baskets"), baskets, ln, '/yourbaskets/display?ln=%s' % ln)
out += self.tmpl_account_template(_("Your Alert Searches"), alerts, ln, '/youralerts/list?ln=%s' % ln)
out += self.tmpl_account_template(_("Your Searches"), searches, ln, '/youralerts/display?ln=%s' % ln)
groups_description = _("You can consult the list of %(x_url_open)syour groups%(x_url_close)s you are administering or are a member of.")
- groups_description %= {'x_url_open': '<a href="' + weburl + '/yourgroups/display?ln=' + ln + '">',
+ groups_description %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yourgroups/display?ln=' + ln + '">',
'x_url_close': '</a>'}
out += self.tmpl_account_template(_("Your Groups"), groups_description, ln, '/yourgroups/display?ln=%s' % ln)
submission_description = _("You can consult the list of %(x_url_open)syour submissions%(x_url_close)s and inquire about their status.")
- submission_description %= {'x_url_open': '<a href="' + weburl + '/yoursubmissions.py?ln=' + ln + '">',
+ submission_description %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yoursubmissions.py?ln=' + ln + '">',
'x_url_close': '</a>'}
out += self.tmpl_account_template(_("Your Submissions"), submission_description, ln, '/yoursubmissions.py?ln=%s' % ln)
approval_description = _("You can consult the list of %(x_url_open)syour approvals%(x_url_close)s with the documents you approved or refereed.")
- approval_description %= {'x_url_open': '<a href="' + weburl + '/yourapprovals.py?ln=' + ln + '">',
+ approval_description %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yourapprovals.py?ln=' + ln + '">',
'x_url_close': '</a>'}
out += self.tmpl_account_template(_("Your Approvals"), approval_description, ln, '/yourapprovals.py?ln=%s' % ln)
out += self.tmpl_account_template(_("Your Administrative Activities"), administrative, ln, '/admin')
return out
def tmpl_account_emailMessage(self, ln, msg):
"""
Displays a link to retrieve the lost password
Parameters:
- 'ln' *string* - The language to display the interface in
- 'msg' *string* - Explicative message on top of the form.
"""
# load the right message language
_ = gettext_set_language(ln)
out =""
out +="""
<body>
%(msg)s <a href="../youraccount/lost?ln=%(ln)s">%(try_again)s</a>
</body>
""" % {
'ln' : ln,
'msg' : msg,
'try_again' : _("Try again")
}
return out
def tmpl_account_reset_password_email_body(self, email, reset_key, ip_address, ln=CFG_SITE_LANG):
"""
The body of the email that sends lost internal account
passwords to users.
"""
_ = gettext_set_language(ln)
out = """
%(intro)s
%(intro2)s
<%(link)s>
%(outro)s
%(outro2)s""" % {
'intro': _("Somebody (possibly you) coming from %(ip_address)s "
"has asked\nfor a password reset at %(sitename)s\nfor "
"the account \"%(email)s\"." % {
'sitename' :CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
'email' : email,
'ip_address' : ip_address,
}
),
'intro2' : _("If you want to reset the password for this account, please go to:"),
'link' : "%s/youraccount/access%s" %
(CFG_SITE_SECURE_URL, make_canonical_urlargd({
'ln' : ln,
'mailcookie' : reset_key
}, {})),
'outro' : _("in order to confirm the validity of this request."),
'outro2' : _("Please note that this URL will remain valid for about %(days)s days only.") % {'days' : CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS},
}
return out
def tmpl_account_address_activation_email_body(self, email, address_activation_key, ip_address, ln=CFG_SITE_LANG):
"""
The body of the email that sends email address activation cookie
passwords to users.
"""
_ = gettext_set_language(ln)
out = """
%(intro)s
%(intro2)s
<%(link)s>
%(outro)s
%(outro2)s""" % {
'intro': _("Somebody (possibly you) coming from %(ip_address)s "
"has asked\nto register a new account at %(sitename)s\nfor the "
"email address \"%(email)s\"." % {
'sitename' :CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
'email' : email,
'ip_address' : ip_address,
}
),
'intro2' : _("If you want to complete this account registration, please go to:"),
'link' : "%s/youraccount/access%s" %
(CFG_SITE_SECURE_URL, make_canonical_urlargd({
'ln' : ln,
'mailcookie' : address_activation_key
}, {})),
'outro' : _("in order to confirm the validity of this request."),
'outro2' : _("Please note that this URL will remain valid for about %(days)s days only.") % {'days' : CFG_WEBSESSION_ADDRESS_ACTIVATION_EXPIRE_IN_DAYS},
}
return out
def tmpl_account_emailSent(self, ln, email):
"""
Displays a confirmation message for an email sent
Parameters:
- 'ln' *string* - The language to display the interface in
- 'email' *string* - The email to which the message has been sent
"""
# load the right message language
_ = gettext_set_language(ln)
out =""
out += _("Okay, a password reset link has been emailed to %s.") % email
return out
def tmpl_account_delete(self, ln):
"""
Displays a confirmation message about deleting the account
Parameters:
- 'ln' *string* - The language to display the interface in
"""
# load the right message language
_ = gettext_set_language(ln)
out = "<p>" + _("""Deleting your account""") + '</p>'
return out
def tmpl_account_logout(self, ln):
"""
Displays a confirmation message about logging out
Parameters:
- 'ln' *string* - The language to display the interface in
"""
# load the right message language
_ = gettext_set_language(ln)
out = _("You are no longer recognized by our system.") + ' '
if CFG_EXTERNAL_AUTH_USING_SSO and CFG_EXTERNAL_AUTH_LOGOUT_SSO:
out += _("""You are still recognized by the centralized
%(x_fmt_open)sSSO%(x_fmt_close)s system. You can
%(x_url_open)slogout from SSO%(x_url_close)s, too.""") % \
{'x_fmt_open' : '<strong>', 'x_fmt_close' : '</strong>',
'x_url_open' : '<a href="%s">' % CFG_EXTERNAL_AUTH_LOGOUT_SSO,
'x_url_close' : '</a>'}
out += '<br />'
out += _("If you wish you can %(x_url_open)slogin here%(x_url_close)s.") % \
{'x_url_open': '<a href="./login?ln=' + ln + '">',
'x_url_close': '</a>'}
return out
def tmpl_login_form(self, ln, referer, internal, register_available, methods, selected_method, msg=None):
"""
Displays a login form
Parameters:
- 'ln' *string* - The language to display the interface in
- 'referer' *string* - The referer URL - will be redirected upon after login
- 'internal' *boolean* - If we are producing an internal authentication
- 'register_available' *boolean* - If users can register freely in the system
- 'methods' *array* - The available authentication methods
- 'selected_method' *string* - The default authentication method
- 'msg' *string* - The message to print before the form, if needed
"""
# load the right message language
_ = gettext_set_language(ln)
if msg is "":
out = "<p>%(please_login)s" % {
'please_login' : _("If you already have an account, please login using the form below.")
}
if CFG_CERN_SITE:
out += "<p>" + _("If you don't own a CERN account yet, you can register a %(x_url_open)snew CERN lightweight account%(x_url_close)s.") % {'x_url_open' : '<a href="https://www.cern.ch/lightweightregistration/RegisterAccount.aspx">', 'x_url_close' : '</a>'} + "</p>"
else:
if register_available:
out += "<p>"+_("If you don't own an account yet, please %(x_url_open)sregister%(x_url_close)s an internal account.") %\
{'x_url_open': '<a href="../youraccount/register?ln=' + ln + '">',
'x_url_close': '</a>'} + "</p>"
else:
out += "<p>" + _("It is not possible to create an account yourself. Contact %s if you want an account.") % ('<a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL)) + "</p>"
else:
out = "<p>%s</p>" % msg
out += """<form method="post" action="../youraccount/login">
<table>
"""
if len(methods) > 1:
# more than one method, must make a select
login_select = """<select name="login_method">"""
for method in methods:
login_select += """<option value="%(method)s" %(selected)s>%(method)s</option>""" % {
'method' : method,
'selected' : (method == selected_method and 'selected="selected"' or "")
}
login_select += "</select>"
out += """
<tr>
<td align="right"><strong>%(login_title)s</strong></td>
<td>%(login_select)s</td>
</tr>""" % {
'login_title' : _("Login method:"),
'login_select' : login_select,
}
else:
# only one login method available
out += """<input type="hidden" name="login_method" value="%s">""" % (methods[0])
out += """<tr>
<td align="right">
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="referer" value="%(referer)s" />
<strong>%(username)s:</strong>
</td>
<td><input type="text" size="25" name="p_un" value="" /></td>
</tr>
<tr>
<td align="right"><strong>%(password)s:</strong></td>
<td align="left"><input type="password" size="25" name="p_pw" value="" /></td>
</tr>
<tr>
<td></td>
<td align="center" colspan="3"><code class="blocknote"><input class="formbutton" type="submit" name="action" value="%(login)s" /></code>""" % {
'ln': ln,
'referer' : cgi.escape(referer),
'username' : _("Username"),
'password' : _("Password"),
'login' : _("login"),
}
if internal:
out += """&nbsp;&nbsp;&nbsp;(<a href="./lost?ln=%(ln)s">%(lost_pass)s</a>)""" % {
'ln' : ln,
'lost_pass' : _("Lost your password?")
}
out += """</td>
</tr>
</table></form>"""
out += """<p><strong>%(note)s:</strong> %(note_text)s</p>""" % {
'note' : _("Note"),
'note_text': _("You can use your nickname or your email address to login.")}
return out
def tmpl_lost_your_password_teaser(self, ln=CFG_SITE_LANG):
"""Displays a short sentence to attract user to the fact that
maybe he lost his password. Used by the registration page.
"""
_ = gettext_set_language(ln)
out = ""
out += """<a href="./lost?ln=%(ln)s">%(maybe_lost_pass)s</a>""" % {
'ln' : ln,
'maybe_lost_pass': ("Maybe you have lost your password?")
}
return out
def tmpl_reset_password_form(self, ln, email, reset_key, msg=''):
"""Display a form to reset the password."""
_ = gettext_set_language(ln)
out = ""
out = "<p>%s</p>" % _("Your request is valid. Please set the new "
"desired password in the following form.")
if msg:
out += """<p class='warning'>%s</p>""" % msg
out += """
<form method='post' action='../youraccount/resetpassword?ln=%(ln)s'>
<input type='hidden' name='k' value='%(reset_key)s' />
<input type='hidden' name='e' value='%(email)s' />
<input type='hidden' name='reset' value='1' />
<table>
<tr><td align='right'><strong>%(set_password_for)s</strong>:</td><td><em>%(email)s</em></td></tr>
<tr><td align='right'><strong>%(type_new_password)s</strong>:</td>
<td><input type='password' name='password' value='123' /></td></tr>
<tr><td align='right'><strong>%(type_it_again)s</strong>:</td>
<td><input type='password' name='password2' value='' /></td></tr>
<tr><td align="center" colspan="2">
<input class="formbutton" type="submit" name="action" value="%(set_new_password)s" />
</td></tr>
</table>
</form>""" % {
'ln' : ln,
'reset_key' : reset_key,
'email' : email,
'set_password_for' : _('Set a new password for'),
'type_new_password' : _('Type the new password'),
'type_it_again' : _('Type again the new password'),
'set_new_password' : _('Set the new password')
}
return out
def tmpl_register_page(self, ln, referer, level):
"""
Displays a login form
Parameters:
- 'ln' *string* - The language to display the interface in
- 'referer' *string* - The referer URL - will be redirected upon after login
- 'level' *int* - Login level (0 - all access, 1 - accounts activated, 2+ - no self-registration)
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if level <= 1:
out += _("Please enter your email address and desired nickname and password:")
if level == 1:
out += _("It will not be possible to use the account before it has been verified and activated.")
out += """
<form method="post" action="../youraccount/register">
<input type="hidden" name="referer" value="%(referer)s" />
<table>
<tr>
<td align="right"><strong>%(email_address)s:</strong><br /><small class="important">(%(mandatory)s)</small></td>
<td><input type="text" size="25" name="p_email" value="" /><br />
<small><span class="quicknote">%(example)s:</span>
<span class="example">john.doe@example.com</span></small>
</td>
<td></td>
</tr>
<tr>
<td align="right"><strong>%(nickname)s:</strong><br /><small class="important">(%(mandatory)s)</small></td>
<td><input type="text" size="25" name="p_nickname" value="" /><br />
<small><span class="quicknote">%(example)s:</span>
<span class="example">johnd</span></small>
</td>
<td></td>
</tr>
<tr>
<td align="right"><strong>%(password)s:</strong><br /><small class="quicknote">(%(optional)s)</small></td>
<td align="left"><input type="password" size="25" name="p_pw" value="" /><br />
<small><span class="quicknote">%(note)s:</span> %(password_contain)s</small>
</td>
<td></td>
</tr>
<tr>
<td align="right"><strong>%(retype)s:</strong></td>
<td align="left"><input type="password" size="25" name="p_pw2" value="" /></td>
<td></td>
</tr>
<tr>
<td></td>
<td align="left" colspan="3"><code class="blocknote"><input class="formbutton" type="submit" name="action" value="%(register)s" /></code></td>
</tr>
</table>
</form>
<p><strong>%(note)s:</strong> %(explain_acc)s""" % {
'referer' : cgi.escape(referer),
'email_address' : _("Email address"),
'nickname' : _("Nickname"),
'password' : _("Password"),
'mandatory' : _("mandatory"),
'optional' : _("optional"),
'example' : _("Example"),
'note' : _("Note"),
'password_contain' : _("The password phrase may contain punctuation, spaces, etc."),
'retype' : _("Retype Password"),
'register' : _("register"),
'explain_acc' : _("Please do not use valuable passwords such as your Unix, AFS or NICE passwords with this service. Your email address will stay strictly confidential and will not be disclosed to any third party. It will be used to identify you for personal services of %s. For example, you may set up an automatic alert search that will look for new preprints and will notify you daily of new arrivals by email.") % CFG_SITE_NAME,
}
return out
- def tmpl_account_adminactivities(self, ln, weburl, uid, guest, roles, activities):
+ def tmpl_account_adminactivities(self, ln, uid, guest, roles, activities):
"""
Displays the admin activities block for this user
Parameters:
- 'ln' *string* - The language to display the interface in
- - 'weburl' *string* - The address of the site
-
- 'uid' *string* - The used id
- 'guest' *boolean* - If the user is guest
- 'roles' *array* - The current user roles
- 'activities' *array* - The user allowed activities
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
# guest condition
if guest:
return _("You seem to be a guest user. You have to %(x_url_open)slogin%(x_url_close)s first.") % \
{'x_url_open': '<a href="../youraccount/login?ln=' + ln + '">',
'x_url_close': '<a/>'}
# no rights condition
if not roles:
return "<p>" + _("You are not authorized to access administrative functions.") + "</p>"
# displaying form
out += "<p>" + _("You are enabled to the following roles: %(x_role)s.") % {'x_role': ('<em>' + string.join(roles, ", ") + "</em> ")} + '</p>'
if activities:
out += _("Here are some interesting web admin links for you:")
# print proposed links:
activities.sort(lambda x, y: cmp(string.lower(x), string.lower(y)))
for action in activities:
if action == "runbibedit":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibedit/bibeditadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Run BibEdit"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibedit/bibeditadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Run BibEdit"))
if action == "cfgbibformat":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibformat/bibformatadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure BibFormat"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibformat/bibformatadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure BibFormat"))
if action == "cfgbibharvest":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibharvest/bibharvestadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure BibHarvest"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibharvest/bibharvestadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure BibHarvest"))
if action == "cfgoairepository":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibharvest/oaiarchiveadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure OAI Repository"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibharvest/oaiarchiveadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure OAI Repository"))
if action == "cfgbibindex":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibindex/bibindexadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure BibIndex"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibindex/bibindexadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure BibIndex"))
if action == "cfgbibrank":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibrank/bibrankadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure BibRank"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/bibrank/bibrankadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure BibRank"))
if action == "cfgwebaccess":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/webaccess/webaccessadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure WebAccess"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/webaccess/webaccessadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebAccess"))
if action == "cfgwebcomment":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/webcomment/webcommentadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure WebComment"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/webcomment/webcommentadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebComment"))
if action == "cfgwebsearch":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/websearch/websearchadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure WebSearch"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/websearch/websearchadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebSearch"))
if action == "cfgwebsubmit":
- out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/websubmit/websubmitadmin.py?ln=%s">%s</a>""" % (weburl, ln, _("Configure WebSubmit"))
+ out += """<br />&nbsp;&nbsp;&nbsp; <a href="%s/admin/websubmit/websubmitadmin.py?ln=%s">%s</a>""" % (CFG_SITE_URL, ln, _("Configure WebSubmit"))
out += "<br />" + _("For more admin-level activities, see the complete %(x_url_open)sAdmin Area%(x_url_close)s.") %\
- {'x_url_open': '<a href="' + weburl + '/help/admin?ln=' + ln + '">',
+ {'x_url_open': '<a href="' + CFG_SITE_URL + '/help/admin?ln=' + ln + '">',
'x_url_close': '</a>'}
return out
def tmpl_create_userinfobox(self, ln, url_referer, guest, username, submitter, referee, admin):
"""
Displays the user block
Parameters:
- 'ln' *string* - The language to display the interface in
- 'url_referer' *string* - URL of the page being displayed
- 'guest' *boolean* - If the user is guest
- 'username' *string* - The username (nickname or email)
- 'submitter' *boolean* - If the user is submitter
- 'referee' *boolean* - If the user is referee
- 'admin' *boolean* - If the user is admin
"""
# load the right message language
_ = gettext_set_language(ln)
- out = """<img src="%s/img/user-icon-1-20x20.gif" border="0" alt=""/> """ % weburl
+ out = """<img src="%s/img/user-icon-1-20x20.gif" border="0" alt=""/> """ % CFG_SITE_URL
if guest:
out += """%(guest_msg)s ::
<a class="userinfo" href="%(sitesecureurl)s/youraccount/login?ln=%(ln)s%(referer)s">%(login)s</a>""" % {
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'sitesecureurl': CFG_SITE_SECURE_URL,
'ln' : ln,
'guest_msg' : _("guest"),
'session' : _("session"),
'alerts' : _("alerts"),
'baskets' : _("baskets"),
'login' : _("login"),
'referer' : url_referer and ('&amp;referer=%s' % urllib.quote(url_referer)) or '',
}
else:
out += """%(username)s ::
<a class="userinfo" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(account)s</a> ::
- <a class="userinfo" href="%(weburl)s/yourmessages/display?ln=%(ln)s">%(messages)s</a> ::
- <a class="userinfo" href="%(weburl)s/yourbaskets/display?ln=%(ln)s">%(baskets)s</a> ::
- <a class="userinfo" href="%(weburl)s/youralerts/list?ln=%(ln)s">%(alerts)s</a> ::
- <a class="userinfo" href="%(weburl)s/yourgroups/display?ln=%(ln)s">%(groups)s</a> ::
- <a class="userinfo" href="%(weburl)s/stats/?ln=%(ln)s">%(stats)s</a> :: """ % {
+ <a class="userinfo" href="%(siteurl)s/yourmessages/display?ln=%(ln)s">%(messages)s</a> ::
+ <a class="userinfo" href="%(siteurl)s/yourbaskets/display?ln=%(ln)s">%(baskets)s</a> ::
+ <a class="userinfo" href="%(siteurl)s/youralerts/list?ln=%(ln)s">%(alerts)s</a> ::
+ <a class="userinfo" href="%(siteurl)s/yourgroups/display?ln=%(ln)s">%(groups)s</a> ::
+ <a class="userinfo" href="%(siteurl)s/stats/?ln=%(ln)s">%(stats)s</a> :: """ % {
'username' : username,
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln' : ln,
'account' : _("account"),
'alerts' : _("alerts"),
'messages': _("messages"),
'baskets' : _("baskets"),
'groups' : _("groups"),
'stats' : _("statistics"),
}
if submitter:
- out += """<a class="userinfo" href="%(weburl)s/yoursubmissions.py?ln=%(ln)s">%(submission)s</a> :: """ % {
- 'weburl' : weburl,
+ out += """<a class="userinfo" href="%(siteurl)s/yoursubmissions.py?ln=%(ln)s">%(submission)s</a> :: """ % {
+ 'siteurl' : CFG_SITE_URL,
'ln' : ln,
'submission' : _("submissions"),
}
if referee:
- out += """<a class="userinfo" href="%(weburl)s/yourapprovals.py?ln=%(ln)s">%(approvals)s</a> :: """ % {
- 'weburl' : weburl,
+ out += """<a class="userinfo" href="%(siteurl)s/yourapprovals.py?ln=%(ln)s">%(approvals)s</a> :: """ % {
+ 'siteurl' : CFG_SITE_URL,
'ln' : ln,
'approvals' : _("approvals"),
}
if admin:
out += """<a class="userinfo" href="%(sitesecureurl)s/youraccount/youradminactivities?ln=%(ln)s">%(administration)s</a> :: """ % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln' : ln,
'administration' : _("administration"),
}
out += """<a class="userinfo" href="%(sitesecureurl)s/youraccount/logout?ln=%(ln)s">%(logout)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln' : ln,
'logout' : _("logout"),
}
return out
def tmpl_warning(self, warnings, ln=CFG_SITE_LANG):
"""
Prepare the warnings list
@param warnings: list of warning tuples (warning_msg, arg1, arg2, etc)
@return html string of warnings
"""
from invenio.errorlib import get_msgs_for_code_list
span_class = 'important'
out = ""
if type(warnings) is not list:
warnings = [warnings]
if len(warnings) > 0:
warnings_parsed = get_msgs_for_code_list(warnings, 'warning', ln)
for (warning_code, warning_text) in warnings_parsed:
if not warning_code.startswith('WRN'):
#display only warnings that begin with WRN to user
continue
span_class = 'important'
out += '''
<span class="%(span_class)s">%(warning)s</span><br />''' % \
{ 'span_class' : span_class,
'warning' : warning_text }
return out
else:
return ""
def tmpl_warnings(self, warnings, ln=CFG_SITE_LANG):
"""
Display len(warnings) warning fields
@param infos: list of strings
@param ln=language
@return html output
"""
if not((type(warnings) is list) or (type(warnings) is tuple)):
warnings = [warnings]
warningbox = ""
if warnings != []:
warningbox = "<div class=\"warningbox\">\n <b>Warning:</b>\n"
for warning in warnings:
lines = warning.split("\n")
warningbox += " <p>"
for line in lines[0:-1]:
warningbox += line + " <br />\n"
warningbox += lines[-1] + " </p>"
warningbox += "</div><br />\n"
return warningbox
def tmpl_display_all_groups(self,
infos,
admin_group_html,
member_group_html,
external_group_html = None,
warnings=[],
ln=CFG_SITE_LANG):
"""
Displays the 3 tables of groups: admin, member and external
Parameters:
- 'ln' *string* - The language to display the interface in
- 'admin_group_html' *string* - HTML code for displaying all the groups
the user is the administrator of
- 'member_group_html' *string* - HTML code for displaying all the groups
the user is member of
- 'external_group_html' *string* - HTML code for displaying all the
external groups the user is member of
"""
_ = gettext_set_language(ln)
group_text = self.tmpl_infobox(infos)
group_text += self.tmpl_warning(warnings)
if external_group_html:
group_text += """
<table>
<tr>
<td>%s</td>
</tr>
<tr>
<td><br />%s</td>
</tr>
<tr>
<td><br /><a name='external_groups'></a>%s</td>
</tr>
</table>""" %(admin_group_html, member_group_html, external_group_html)
else:
group_text += """
<table>
<tr>
<td>%s</td>
</tr>
<tr>
<td><br />%s</td>
</tr>
</table>""" %(admin_group_html, member_group_html)
return group_text
def tmpl_display_admin_groups(self, groups, ln=CFG_SITE_LANG):
"""
Display the groups the user is admin of.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'groups' *list* - All the group the user is admin of
- 'infos' *list* - Display infos on top of admin group table
"""
_ = gettext_set_language(ln)
img_link = """
- <a href="%(weburl)s/yourgroups/%(action)s?grpID=%(grpID)s&amp;ln=%(ln)s">
- <img src="%(weburl)s/img/%(img)s" alt="%(text)s" style="border:0" width="25"
+ <a href="%(siteurl)s/yourgroups/%(action)s?grpID=%(grpID)s&amp;ln=%(ln)s">
+ <img src="%(siteurl)s/img/%(img)s" alt="%(text)s" style="border:0" width="25"
height="25" /><br /><small>%(text)s</small>
</a>"""
out = self.tmpl_group_table_title(img="/img/group_admin.png",
text=_("You are an administrator of the following groups:") )
out += """
<table class="mailbox">
<thead class="mailboxheader">
<tr class="inboxheader">
<td>%s</td>
<td>%s</td>
<td style="width: 20px;" >&nbsp;</td>
<td style="width: 20px;">&nbsp;</td>
</tr>
</thead>
<tfoot>
<tr style="height:0px;">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tfoot>
<tbody class="mailboxbody">""" %(_("Group"), _("Description"))
if len(groups) == 0:
out += """
<tr class="mailboxrecord" style="height: 100px;">
<td colspan="4" style="text-align: center;">
<small>%s</small>
</td>
</tr>""" %(_("You are not an administrator of any groups."),)
for group_data in groups:
(grpID, name, description) = group_data
- edit_link = img_link % {'weburl' : weburl,
+ edit_link = img_link % {'siteurl' : CFG_SITE_URL,
'grpID' : grpID,
'ln': ln,
'img':"webbasket_create_small.png",
'text':_("Edit group"),
'action':"edit"
}
- members_link = img_link % {'weburl' : weburl,
+ members_link = img_link % {'siteurl' : CFG_SITE_URL,
'grpID' : grpID,
'ln': ln,
'img':"webbasket_usergroup.png",
'text':_("Edit %s members") % '',
'action':"members"
}
out += """
<tr class="mailboxrecord">
<td>%s</td>
<td>%s</td>
<td style="text-align: center;" >%s</td>
<td style="text-align: center;" >%s</td>
</tr>""" % (cgi.escape(name), cgi.escape(description), edit_link, members_link)
out += """
<tr class="mailboxfooter">
<td colspan="2">
<form name="newGroup" action="create?ln=%(ln)s" method="post">
<input type="submit" name="create_group" value="%(write_label)s" class="formbutton" />
</form>
</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
</tbody>
</table>""" % {'ln': ln,
'write_label': _("Create new group"),
}
return out
def tmpl_display_member_groups(self, groups, ln=CFG_SITE_LANG):
"""
Display the groups the user is member of.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'groups' *list* - All the group the user is member of
"""
_ = gettext_set_language(ln)
group_text = self.tmpl_group_table_title(img="/img/webbasket_us.png", text=_("You are a member of the following groups:"))
group_text += """
<table class="mailbox">
<thead class="mailboxheader">
<tr class="inboxheader">
<td>%s</td>
<td>%s</td>
</tr>
</thead>
<tfoot>
<tr style="height:0px;">
<td></td>
<td></td>
</tr>
</tfoot>
<tbody class="mailboxbody">""" % (_("Group"), _("Description"))
if len(groups) == 0:
group_text += """
<tr class="mailboxrecord" style="height: 100px;">
<td colspan="2" style="text-align: center;">
<small>%s</small>
</td>
</tr>""" %(_("You are not a member of any groups."),)
for group_data in groups:
(id, name, description) = group_data
group_text += """
<tr class="mailboxrecord">
<td>%s</td>
<td>%s</td>
</tr>""" % (cgi.escape(name), cgi.escape(description))
group_text += """
<tr class="mailboxfooter">
<td>
<form name="newGroup" action="join?ln=%(ln)s" method="post">
<input type="submit" name="join_group" value="%(join_label)s" class="formbutton" />
</form>
</td>
<td>
<form name="newGroup" action="leave?ln=%(ln)s" method="post">
<input type="submit" name="leave" value="%(leave_label)s" class="formbutton" />
</form>
</td>
</tr>
</tbody>
</table>
""" % {'ln': ln,
'join_label': _("Join new group"),
'leave_label':_("Leave group")
}
return group_text
def tmpl_display_external_groups(self, groups, ln=CFG_SITE_LANG):
"""
Display the external groups the user is member of.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'groups' *list* - All the group the user is member of
"""
_ = gettext_set_language(ln)
group_text = self.tmpl_group_table_title(img="/img/webbasket_us.png", text=_("You are a member of the following external groups:"))
group_text += """
<table class="mailbox">
<thead class="mailboxheader">
<tr class="inboxheader">
<td>%s</td>
<td>%s</td>
</tr>
</thead>
<tfoot>
<tr style="height:0px;">
<td></td>
<td></td>
</tr>
</tfoot>
<tbody class="mailboxbody">""" % (_("Group"), _("Description"))
if len(groups) == 0:
group_text += """
<tr class="mailboxrecord" style="height: 100px;">
<td colspan="2" style="text-align: center;">
<small>%s</small>
</td>
</tr>""" %(_("You are not a member of any external groups."),)
for group_data in groups:
(id, name, description) = group_data
group_text += """
<tr class="mailboxrecord">
<td>%s</td>
<td>%s</td>
</tr>""" % (cgi.escape(name), cgi.escape(description))
group_text += """
</tbody>
</table>
"""
return group_text
def tmpl_display_input_group_info(self,
group_name,
group_description,
join_policy,
act_type="create",
grpID="",
warnings=[],
ln=CFG_SITE_LANG):
"""
Display group data when creating or updating a group:
Name, description, join_policy.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'group_name' *string* - name of the group
- 'group_description' *string* - description of the group
- 'join_policy' *string* - join policy
- 'act_type' *string* - info about action : create or edit(update)
- 'grpID' *string* - ID of the group(not null in case of group editing)
- 'warnings' *list* - Display warning if values are not correct
"""
_ = gettext_set_language(ln)
#default
hidden_id =""
form_name = "create_group"
- action = weburl + '/yourgroups/create'
+ action = CFG_SITE_URL + '/yourgroups/create'
button_label = _("Create new group")
button_name = "create_button"
label = _("Create new group")
delete_text = ""
if act_type == "update":
form_name = "update_group"
- action = weburl + '/yourgroups/edit'
+ action = CFG_SITE_URL + '/yourgroups/edit'
button_label = _("Update group")
button_name = "update"
label = _('Edit group %s') % cgi.escape(group_name)
delete_text = """<input type="submit" value="%s" class="formbutton" name="%s" />"""
delete_text %= (_("Delete group"),"delete")
if grpID != "":
hidden_id = """<input type="hidden" name="grpID" value="%s" />"""
hidden_id %= grpID
out = self.tmpl_warning(warnings)
out += """
<form name="%(form_name)s" action="%(action)s" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<div style="padding:10px;">
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>%(label)s</b><br />
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
<td>%(name_label)s</td>
<td>
<input type="text" name="group_name" value="%(group_name)s" />
</td>
</tr>
<tr>
<td>%(description_label)s</td>
<td>
<input type="text" name="group_description" value="%(group_description)s" />
</td>
</tr>
<tr>
<td>%(join_policy_label)s</td>
<td>
%(join_policy)s
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
%(hidden_id)s
<table>
<tr>
<td>
<input type="submit" value="%(button_label)s" class="formbutton" name="%(button_name)s" />
</td>
<td>
%(delete_text)s
</td>
<td>
<input type="submit" value="%(cancel_label)s" class="formbutton" name="cancel" />
</td>
</tr>
</table>
</div>
</form>
"""
out %= {'action' : action,
- 'logo': weburl + '/img/webbasket_create.png',
+ 'logo': CFG_SITE_URL + '/img/webbasket_create.png',
'label': label,
'form_name' : form_name,
'name_label': _("Group name:"),
'delete_text': delete_text,
'description_label': _("Group description:"),
'join_policy_label': _("Group join policy:"),
'group_name': cgi.escape(group_name, 1),
'group_description': cgi.escape(group_description, 1),
'button_label': button_label,
'button_name':button_name,
'cancel_label':_("Cancel"),
'hidden_id':hidden_id,
'ln': ln,
'join_policy' :self.__create_join_policy_selection_menu("join_policy",
join_policy,
ln)
}
return out
def tmpl_display_input_join_group(self,
group_list,
group_name,
group_from_search,
search,
warnings=[],
ln=CFG_SITE_LANG):
"""
Display the groups the user can join.
He can use default select list or the search box
Parameters:
- 'ln' *string* - The language to display the interface in
- 'group_list' *list* - All the group the user can join
- 'group_name' *string* - Name of the group the user is looking for
- 'group_from search' *list* - List of the group the user can join matching group_name
- 'search' *int* - User is looking for group using group_name
- 'warnings' *list* - Display warning if two group are selected
"""
_ = gettext_set_language(ln)
out = self.tmpl_warning(warnings)
search_content = ""
if search:
search_content = """<tr><td>&nbsp;</td><td>"""
if group_from_search != []:
search_content += self.__create_select_menu('grpID', group_from_search, _("Please select:"))
else:
search_content += _("No matching group")
search_content += """</td><td>&nbsp;</td></tr>"""
out += """
<form name="join_group" action="%(action)s" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<div style="padding:10px;">
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>%(label)s</b><br />
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
<td>%(list_label)s</td>
<td>
%(group_list)s
</td>
<td>
&nbsp;
</td>
</tr>
<tr>
<td><br />%(label2)s</td>
<td><br /><input type="text" name="group_name" value="%(group_name)s" /></td>
<td><br />
<input type="submit" name="find_button" value="%(find_label)s" class="nonsubmitbutton" />
</td>
</tr>
%(search_content)s
</table>
</td>
</tr>
</tbody>
</table>
<table>
<tr>
<td>
<input type="submit" name="join_button" value="%(label)s" class="formbutton" />
</td>
<td>
<input type="submit" value="%(cancel_label)s" class="formbutton" name="cancel" />
</td>
</tr>
</table>
</div>
</form>
"""
- out %= {'action' : weburl + '/yourgroups/join',
- 'logo': weburl + '/img/webbasket_create.png',
+ out %= {'action' : CFG_SITE_URL + '/yourgroups/join',
+ 'logo': CFG_SITE_URL + '/img/webbasket_create.png',
'label': _("Join group"),
'group_name': cgi.escape(group_name, 1),
'label2':_("or find it") + ': ',
'list_label':_("Choose group:"),
'ln': ln,
'find_label': _("Find group"),
'cancel_label':_("Cancel"),
'group_list' :self.__create_select_menu("grpID",group_list, _("Please select:")),
'search_content' : search_content
}
return out
def tmpl_display_manage_member(self,
grpID,
group_name,
members,
pending_members,
infos=[],
warnings=[],
ln=CFG_SITE_LANG):
"""Display current members and waiting members of a group.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'grpID *string* - ID of the group
- 'group_name' *string* - Name of the group
- 'members' *list* - List of the current members
- 'pending_members' *list* - List of the waiting members
- 'infos' *tuple of 2 lists* - Message to inform user about his last action
- 'warnings' *list* - Display warning if two group are selected
"""
_ = gettext_set_language(ln)
out = self.tmpl_warning(warnings)
out += self.tmpl_infobox(infos)
out += """
<form name="member" action="%(action)s" method="post">
<p>%(title)s</p>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s"/>
<table>
<tr>
<td>
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(imgurl)s/webbasket_usergroup.png" alt="%(img_alt_header1)s" />
</td>
<td class="bsktitle">
%(header1)s<br />
&nbsp;
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
%(member_text)s
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(imgurl)s/webbasket_usergroup_gray.png" alt="%(img_alt_header2)s" />
</td>
<td class="bsktitle">
%(header2)s<br />
&nbsp;
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
%(pending_text)s
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table class="bskbasket" style="width: 400px">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(imgurl)s/iconpen.gif" alt="%(img_alt_header3)s" />
</td>
<td class="bsktitle">
<b>%(header3)s</b><br />
&nbsp;
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
<td colspan="2" style="padding: 0 5 10 5;">%(invite_text)s</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<input type="submit" value="%(cancel_label)s" class="formbutton" name="cancel" />
</td>
</tr>
</table>
</form>
"""
if members :
member_list = self.__create_select_menu("member_id", members, _("Please select:"))
member_text = """
<td style="padding: 0 5 10 5;">%s</td>
<td style="padding: 0 5 10 5;">
<input type="submit" name="remove_member" value="%s" class="nonsubmitbutton"/>
</td>""" % (member_list,_("Remove member"))
else :
member_text = """<td style="padding: 0 5 10 5;" colspan="2">%s</td>""" % _("No members.")
if pending_members :
pending_list = self.__create_select_menu("pending_member_id", pending_members, _("Please select:"))
pending_text = """
<td style="padding: 0 5 10 5;">%s</td>
<td style="padding: 0 5 10 5;">
<input type="submit" name="add_member" value="%s" class="nonsubmitbutton"/>
</td>
<td style="padding: 0 5 10 5;">
<input type="submit" name="reject_member" value="%s" class="nonsubmitbutton"/>
</td>""" % (pending_list,_("Accept member"), _("Reject member"))
else :
pending_text = """<td style="padding: 0 5 10 5;" colspan="2">%s</td>""" % _("No members awaiting approval.")
header1 = self.tmpl_group_table_title(text=_("Current members"))
header2 = self.tmpl_group_table_title(text=_("Members awaiting approval"))
header3 = _("Invite new members")
link_open = '<a href="%s/yourmessages/write?ln=%s">'
- link_open %= (weburl, ln)
+ link_open %= (CFG_SITE_URL, ln)
invite_text = _("If you want to invite new members to join your group, please use the %(x_url_open)sweb message%(x_url_close)s system.") % \
{'x_url_open': link_open,
'x_url_close': '</a>'}
- action = weburl + '/yourgroups/members?ln=' + ln
+ action = CFG_SITE_URL + '/yourgroups/members?ln=' + ln
out %= {'title':_('Group: %s') % group_name,
'member_text' : member_text,
'pending_text' :pending_text,
'action':action,
'grpID':grpID,
'header1': header1,
'header2': header2,
'header3': header3,
'img_alt_header1': _("Current members"),
'img_alt_header2': _("Members awaiting approval"),
'img_alt_header3': _("Invite new members"),
'invite_text': invite_text,
- 'imgurl': weburl + '/img',
+ 'imgurl': CFG_SITE_URL + '/img',
'cancel_label':_("Cancel"),
'ln':ln
}
return out
def tmpl_display_input_leave_group(self,
groups,
warnings=[],
ln=CFG_SITE_LANG):
"""Display groups the user can leave.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'groups' *list* - List of groups the user is currently member of
- 'warnings' *list* - Display warning if no group is selected
"""
_ = gettext_set_language(ln)
out = self.tmpl_warning(warnings)
out += """
<form name="leave" action="%(action)s" method="post">
<input type="hidden" name="ln" value="%(ln)s" />
<div style="padding:10px;">
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bskactions">
<img src="%(logo)s" alt="%(label)s" />
</td>
<td class="bsktitle">
<b>%(label)s</b><br />
</td>
</tr>
</thead>
<tfoot>
<tr><td colspan="2"></td></tr>
</tfoot>
<tbody>
<tr>
<td colspan="2">
<table>
<tr>
<td>%(list_label)s</td>
<td>
%(groups)s
</td>
<td>
&nbsp;
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<table>
<tr>
<td>
%(submit)s
</td>
<td>
<input type="submit" value="%(cancel_label)s" class="formbutton" name="cancel" />
</td>
</tr>
</table>
</div>
</form>
"""
if groups:
groups = self.__create_select_menu("grpID", groups, _("Please select:"))
list_label = _("Group list")
submit = """<input type="submit" name="leave_button" value="%s" class="formbutton"/>""" % _("Leave group")
else :
groups = _("You are not member of any group.")
list_label = ""
submit = ""
- action = weburl + '/yourgroups/leave?ln=%s'
+ action = CFG_SITE_URL + '/yourgroups/leave?ln=%s'
action %= (ln)
out %= {'groups' : groups,
'list_label' : list_label,
'action':action,
- 'logo': weburl + '/img/webbasket_create.png',
+ 'logo': CFG_SITE_URL + '/img/webbasket_create.png',
'label' : _("Leave group"),
'cancel_label':_("Cancel"),
'ln' :ln,
'submit' : submit
}
return out
def tmpl_confirm_delete(self, grpID, ln=CFG_SITE_LANG):
"""
display a confirm message when deleting a group
@param ln: language
@return html output
"""
_ = gettext_set_language(ln)
- action = weburl + '/yourgroups/edit'
+ action = CFG_SITE_URL + '/yourgroups/edit'
out = """
<form name="delete_group" action="%(action)s" method="post">
<table class="confirmoperation">
<tr>
<td colspan="2" class="confirmmessage">
%(message)s
</td>
</tr>
<tr>
<td>
<input type="hidden" name="confirmed" value="1" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s" />
<input type="submit" name="delete" value="%(yes_label)s" class="formbutton" />
</td>
<td>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s" />
<input type="submit" value="%(no_label)s" class="formbutton" />
</td>
</tr>
</table>
</form>"""% {'message': _("Are you sure you want to delete this group?"),
'ln':ln,
'yes_label': _("Yes"),
'no_label': _("No"),
'grpID':grpID,
'action': action
}
return out
def tmpl_confirm_leave(self, uid, grpID, ln=CFG_SITE_LANG):
"""
display a confirm message
@param ln: language
@return html output
"""
_ = gettext_set_language(ln)
- action = weburl + '/yourgroups/leave'
+ action = CFG_SITE_URL + '/yourgroups/leave'
out = """
<form name="leave_group" action="%(action)s" method="post">
<table class="confirmoperation">
<tr>
<td colspan="2" class="confirmmessage">
%(message)s
</td>
</tr>
<tr>
<td>
<input type="hidden" name="confirmed" value="1" />
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s" />
<input type="submit" name="leave_button" value="%(yes_label)s" class="formbutton" />
</td>
<td>
<input type="hidden" name="ln" value="%(ln)s" />
<input type="hidden" name="grpID" value="%(grpID)s" />
<input type="submit" value="%(no_label)s" class="formbutton" />
</td>
</tr>
</table>
</form>"""% {'message': _("Are you sure you want to leave this group?"),
'ln':ln,
'yes_label': _("Yes"),
'no_label': _("No"),
'grpID':grpID,
'action': action
}
return out
def __create_join_policy_selection_menu(self, name, current_join_policy, ln=CFG_SITE_LANG):
"""Private function. create a drop down menu for selection of join policy
@param current_join_policy: join policy as defined in CFG_WEBSESSION_GROUP_JOIN_POLICY
@param ln: language
"""
_ = gettext_set_language(ln)
elements = [(CFG_WEBSESSION_GROUP_JOIN_POLICY['VISIBLEOPEN'],
_("Visible and open for new members")),
(CFG_WEBSESSION_GROUP_JOIN_POLICY['VISIBLEMAIL'],
_("Visible but new members need approval"))
]
select_text = _("Please select:")
return self.__create_select_menu(name, elements, select_text, selected_key=current_join_policy)
def __create_select_menu(self, name, elements, select_text, multiple=0, selected_key=None):
""" private function, returns a popup menu
@param name: name of HTML control
@param elements: list of (key, value)
"""
if multiple :
out = """
<select name="%s" multiple="multiple" style="width:100%%">"""% (name)
else :
out = """<select name="%s" style="width:100%%">""" % name
out += '<option value="-1">%s</option>' % (select_text)
for (key, label) in elements:
selected = ''
if key == selected_key:
selected = ' selected="selected"'
out += '<option value="%s"%s>%s</option>'% (key, selected, label)
out += '</select>'
return out
def tmpl_infobox(self, infos, ln=CFG_SITE_LANG):
"""Display len(infos) information fields
@param infos: list of strings
@param ln=language
@return html output
"""
_ = gettext_set_language(ln)
if not((type(infos) is list) or (type(infos) is tuple)):
infos = [infos]
infobox = ""
for info in infos:
infobox += '<div><span class="info">'
lines = info.split("\n")
for line in lines[0:-1]:
infobox += line + "<br />\n"
infobox += lines[-1] + "</span></div>\n"
return infobox
def tmpl_navtrail(self, ln=CFG_SITE_LANG, title=""):
"""
display the navtrail, e.g.:
Your account > Your group > title
@param title: the last part of the navtrail. Is not a link
@param ln: language
return html formatted navtrail
"""
_ = gettext_set_language(ln)
nav_h1 = '<a class="navtrail" href="%s/youraccount/display">%s</a>'
nav_h2 = ""
if (title != ""):
nav_h2 = ' &gt; <a class="navtrail" href="%s/yourgroups/display">%s</a>'
- nav_h2 = nav_h2 % (weburl, _("Your Groups"))
+ nav_h2 = nav_h2 % (CFG_SITE_URL, _("Your Groups"))
- return nav_h1 % (weburl, _("Your Account")) + nav_h2
+ return nav_h1 % (CFG_SITE_URL, _("Your Account")) + nav_h2
def tmpl_group_table_title(self, img="", text="", ln=CFG_SITE_LANG):
"""
display the title of a table:
- 'img' *string* - img path
- 'text' *string* - title
- 'ln' *string* - The language to display the interface in
"""
out = "<div>"
if img:
out += """
<img src="%s" alt="" />
- """ % (weburl + img)
+ """ % (CFG_SITE_URL + img)
out += """
<b>%s</b>
</div>""" % text
return out
def tmpl_admin_msg(self, group_name, grpID, ln=CFG_SITE_LANG):
"""
return message content for joining group
- 'group_name' *string* - name of the group
- 'grpID' *string* - ID of the group
- 'ln' *string* - The language to display the interface in
"""
_ = gettext_set_language(ln)
subject = _("Group %s: New membership request") % group_name
- url = weburl + "/yourgroups/members?grpID=%i&ln=%s"
+ url = CFG_SITE_URL + "/yourgroups/members?grpID=%i&ln=%s"
url %= (int(grpID), ln)
# FIXME: which user? We should show his nickname.
body = (_("A user wants to join the group %s.") % group_name) + '<br />'
body += _("Please %(x_url_open)saccept or reject%(x_url_close)s this user's request.") % {'x_url_open': '<a href="' + url + '">',
'x_url_close': '</a>'}
body += '<br />'
return subject, body
def tmpl_member_msg(self,
group_name,
accepted=0,
ln=CFG_SITE_LANG):
"""
return message content when new member is accepted/rejected
- 'group_name' *string* - name of the group
- 'accepted' *int* - 1 if new membership has been accepted, 0 if it has been rejected
- 'ln' *string* - The language to display the interface in
"""
_ = gettext_set_language(ln)
if accepted:
subject = _("Group %s: Join request has been accepted") % (group_name)
body = _("Your request for joining group %s has been accepted.") % (group_name)
else:
subject = _("Group %s: Join request has been rejected") % (group_name)
body = _("Your request for joining group %s has been rejected.") % (group_name)
- url = weburl + "/yourgroups/display?ln=" + ln
+ url = CFG_SITE_URL + "/yourgroups/display?ln=" + ln
body += '<br />'
body += _("You can consult the list of %(x_url_open)syour groups%(x_url_close)s.") % {'x_url_open': '<a href="' + url + '">',
'x_url_close': '</a>'}
body += '<br />'
return subject, body
def tmpl_delete_msg(self,
group_name,
ln=CFG_SITE_LANG):
"""
return message content when new member is accepted/rejected
- 'group_name' *string* - name of the group
- 'ln' *string* - The language to display the interface in
"""
_ = gettext_set_language(ln)
subject = _("Group %s has been deleted") % group_name
- url = weburl + "/yourgroups/display?ln=" + ln
+ url = CFG_SITE_URL + "/yourgroups/display?ln=" + ln
body = _("Group %s has been deleted by its administrator.") % group_name
body += '<br />'
body += _("You can consult the list of %(x_url_open)syour groups%(x_url_close)s.") % {'x_url_open': '<a href="' + url + '">',
'x_url_close': '</a>'}
body += '<br />'
return subject, body
def tmpl_group_info(self, nb_admin_groups=0, nb_member_groups=0, nb_total_groups=0, ln=CFG_SITE_LANG):
"""
display infos about groups (used by myaccount.py)
@param nb_admin_group: number of groups the user is admin of
@param nb_member_group: number of groups the user is member of
@param total_group: number of groups the user belongs to
@param ln: language
return: html output.
"""
_ = gettext_set_language(ln)
out = _("You can consult the list of %(x_url_open)s%(x_nb_total)i groups%(x_url_close)s you are subscribed to (%(x_nb_member)i) or administering (%(x_nb_admin)i).")
- out %= {'x_url_open': '<a href="' + weburl + '/yourgroups/display?ln=' + ln + '">',
+ out %= {'x_url_open': '<a href="' + CFG_SITE_URL + '/yourgroups/display?ln=' + ln + '">',
'x_nb_total': nb_total_groups,
'x_url_close': '</a>',
'x_nb_admin': nb_admin_groups,
'x_nb_member': nb_member_groups}
return out
diff --git a/modules/websession/lib/websession_webinterface.py b/modules/websession/lib/websession_webinterface.py
index 781d5f044..7674e9622 100644
--- a/modules/websession/lib/websession_webinterface.py
+++ b/modules/websession/lib/websession_webinterface.py
@@ -1,1144 +1,1144 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio ACCOUNT HANDLING"""
__revision__ = "$Id$"
__lastupdated__ = """$Date$"""
try:
from mod_python import apache
except ImportError:
pass
from datetime import timedelta
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
- weburl, \
+ CFG_SITE_URL, \
CFG_CERN_SITE
from invenio.websession_config import CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS
from invenio import webuser
from invenio.webpage import page
from invenio import webaccount
from invenio import webbasket
from invenio import webalert
from invenio.dbquery import run_sql
from invenio.webmessage import account_new_mail
from invenio.access_control_engine import make_apache_message, make_list_apache_firerole
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import redirect_to_url, make_canonical_urlargd
from invenio import webgroup
from invenio import webgroup_dblayer
from invenio.messages import gettext_set_language
from invenio.mailutils import send_email
from invenio.access_control_mailcookie import mail_cookie_retrieve_kind, \
mail_cookie_check_pw_reset, mail_cookie_delete_cookie, \
mail_cookie_create_pw_reset, mail_cookie_check_role, \
mail_cookie_check_mail_activation, InvenioWebAccessMailCookieError, \
InvenioWebAccessMailCookieDeletedError, mail_cookie_check_authorize_action
from invenio.access_control_config import CFG_WEBACCESS_WARNING_MSGS, \
CFG_EXTERNAL_AUTH_USING_SSO, CFG_EXTERNAL_AUTH_LOGOUT_SSO, \
CFG_EXTERNAL_AUTHENTICATION
import invenio.template
websession_templates = invenio.template.load('websession')
class WebInterfaceYourAccountPages(WebInterfaceDirectory):
_exports = ['', 'edit', 'change', 'lost', 'display',
'send_email', 'youradminactivities', 'access',
'delete', 'logout', 'login', 'register', 'resetpassword']
_force_https = True
def index(self, req, form):
redirect_to_url(req, '%s/youraccount/display' % CFG_SITE_SECURE_URL)
def access(self, req, form):
args = wash_urlargd(form, {'mailcookie' : (str, '')})
_ = gettext_set_language(args['ln'])
title = _("Mail Cookie Service")
try:
kind = mail_cookie_retrieve_kind(args['mailcookie'])
if kind == 'pw_reset':
redirect_to_url(req, '%s/youraccount/resetpassword?k=%s&ln=%s' % (CFG_SITE_SECURE_URL, args['mailcookie'], args['ln']))
elif kind == 'role':
uid = webuser.getUid(req)
try:
(role_name, expiration) = mail_cookie_check_role(args['mailcookie'], uid)
except InvenioWebAccessMailCookieDeletedError:
return page(title=_("Role authorization request"), req=req, body=_("This request for an authorization has already been authorized."), uid=webuser.getUid(req), navmenuid='youraccount', language=args['ln'])
return page(title=title,
body=webaccount.perform_back(
_("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.") %
{'x_role' : '<strong>%s</strong>' % role_name,
'x_expiration' : '<em>%s</em>' % expiration.strftime("%Y-%m-%d %H:%M:%S")},
'login', _('login'), args['ln']),
req=req,
uid=webuser.getUid(req),
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
elif kind == 'mail_activation':
try:
email = mail_cookie_check_mail_activation(args['mailcookie'])
if not email:
raise StandardError
webuser.confirm_email(email)
body = "<p>" + _("You have confirmed the validity of your email"
" address!") + "</p>"
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
body += "<p>" + _("Please, wait for the administrator to "
"enable your account.") + "</p>"
else:
uid = webuser.update_Uid(req, email)
body += "<p>" + _("You can now go to %(x_url_open)syour account page%(x_url_close)s.") % {'x_url_open' : '<a href="/youraccount/display?ln=%s">' % args['ln'], 'x_url_close' : '</a>'} + "</p>"
return page(title=_("Email address successfully activated"),
body=body, req=req, language=args['ln'], uid=webuser.getUid(req), lastupdated=__lastupdated__, navmenuid='youraccount')
except InvenioWebAccessMailCookieDeletedError, e:
body = "<p>" + _("You have already confirmed the validity of your email address!") + "</p>"
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
body += "<p>" + _("Please, wait for the administrator to "
"enable your account.") + "</p>"
else:
body += "<p>" + _("You can now go to %(x_url_open)syour account page%(x_url_close)s.") % {'x_url_open' : '<a href="/youraccount/display?ln=%s">' % args['ln'], 'x_url_close' : '</a>'} + "</p>"
return page(title=_("Email address successfully activated"),
body=body, req=req, language=args['ln'], uid=webuser.getUid(req), lastupdated=__lastupdated__, navmenuid='youraccount')
return webuser.page_not_authorized(req, "../youraccount/access",
text=_("This request for confirmation of an email "
"address is not valid or"
" is expired."), navmenuid='youraccount')
except InvenioWebAccessMailCookieError:
return webuser.page_not_authorized(req, "../youraccount/access",
text=_("This request for an authorization is not valid or"
" is expired."), navmenuid='youraccount')
def resetpassword(self, req, form):
args = wash_urlargd(form, {
'k' : (str, ''),
'reset' : (int, 0),
'password' : (str, ''),
'password2' : (str, '')
})
_ = gettext_set_language(args['ln'])
email = mail_cookie_check_pw_reset(args['k'])
reset_key = args['k']
title = _('Reset password')
if email is None or CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 3:
return webuser.page_not_authorized(req, "../youraccount/resetpassword",
text=_("This request for resetting the password is not valid or"
" is expired."), navmenuid='youraccount')
if not args['reset']:
return page(title=title,
body=webaccount.perform_reset_password(args['ln'], email, reset_key),
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
elif args['password'] != args['password2']:
msg = _('The two provided passwords aren\'t equal.')
return page(title=title,
body=webaccount.perform_reset_password(args['ln'], email, reset_key, msg),
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
run_sql('UPDATE user SET password=AES_ENCRYPT(email,%s) WHERE email=%s', (args['password'], email))
mail_cookie_delete_cookie(reset_key)
return page(title=title,
body=webaccount.perform_back(
_("The password was successfully set! "
"You can now proceed with the login."),
'login', _('login'), args['ln']),
req=req,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def display(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/display",
navmenuid='youraccount')
if webuser.isGuestUser(uid):
return page(title=_("Your Account"),
body=webaccount.perform_info(req, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
username = webuser.get_nickname_or_email(uid)
bask = webbasket.account_list_baskets(uid, ln=args['ln'])
aler = webalert.account_list_alerts(uid, ln=args['ln'])
sear = webalert.account_list_searches(uid, ln=args['ln'])
msgs = account_new_mail(uid, ln=args['ln'])
grps = webgroup.account_group(uid, ln=args['ln'])
return page(title=_("Your Account"),
body=webaccount.perform_display_account(req,username,bask,aler,sear,msgs,grps,args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def edit(self, req, form):
args = wash_urlargd(form, {"verbose" : (int, 0)})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/edit",
navmenuid='youraccount')
if webuser.isGuestUser(uid):
return webuser.page_not_authorized(req, "../youraccount/edit",
text=_("This functionality is forbidden to guest users."),
navmenuid='youraccount')
body = ''
if args['verbose'] == 9:
user_info = webuser.collect_user_info(req)
for key, value in user_info.items():
body += "<b>%s</b>:%s<br />" % (key, value)
return page(title= _("Your Settings"),
body=body+webaccount.perform_set(webuser.get_email(uid),
args['ln'], verbose=args['verbose']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description=_("%s Personalize, Your Settings") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def change(self, req, form):
args = wash_urlargd(form, {
'nickname': (str, None),
'email': (str, None),
'old_password': (str, None),
'password': (str, None),
'password2': (str, None),
'login_method': (str, ""),
'group_records' : (int, None),
'latestbox' : (int, None),
'helpbox' : (int, None),
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/change",
navmenuid='youraccount')
prefs = webuser.get_user_preferences(uid)
if args['email']:
args['email'] = args['email'].lower()
if args['login_method'] and CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 4 \
and args['login_method'] in CFG_EXTERNAL_AUTHENTICATION.keys():
title = _("Settings edited")
act = "display"
linkname = _("Show account")
if prefs['login_method'] != args['login_method']:
if not CFG_EXTERNAL_AUTHENTICATION[args['login_method']][0]:
# Switching to internal authentication: we drop any external datas
p_email = webuser.get_email(uid)
webuser.drop_external_settings(uid)
webgroup_dblayer.drop_external_groups(uid)
prefs['login_method'] = args['login_method']
webuser.set_user_preferences(uid, prefs)
mess = "<p>" + _("Switched to internal login method.") + " "
mess += _("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 clic the "
"following button to obtain a password reset request "
"link sent to you via email:") + '</p>'
mess += """<p><form method="post" action="../youraccount/send_email">
<input type="hidden" name="p_email" value="%s">
<input class="formbutton" type="submit" value="%s">
</form></p>""" % (p_email, _("Send Password"))
else:
query = """SELECT email FROM user
WHERE id = %i"""
res = run_sql(query % uid)
if res:
email = res[0][0]
else:
email = None
if not email:
mess = _("Unable to switch to external login method %s, because your email address is unknown.") % args['login_method']
else:
try:
if not CFG_EXTERNAL_AUTHENTICATION[args['login_method']][0].user_exists(email):
mess = _("Unable to switch to external login method %s, because your email address is unknown to the external login system.") % args['login_method']
else:
prefs['login_method'] = args['login_method']
webuser.set_user_preferences(uid, prefs)
mess = _("Login method successfully selected.")
except AttributeError:
mess = _("The external login method %s does not support email address based logins. Please contact the site administrators.") % args['login_method']
elif args['login_method'] and CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4:
return webuser.page_not_authorized(req, "../youraccount/change",
navmenuid='youraccount')
elif args['email']:
# We should ignore the password if the authentication method is an
# external one.
ignore_password_p = CFG_EXTERNAL_AUTHENTICATION[prefs['login_method']][0] != None
uid2 = webuser.emailUnique(args['email'])
uid_with_the_same_nickname = webuser.nicknameUnique(args['nickname'])
if (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 2 or (CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS <= 1 and \
webuser.email_valid_p(args['email']))) \
and (args['nickname'] is None or webuser.nickname_valid_p(args['nickname'])) \
and uid2 != -1 and (uid2 == uid or uid2 == 0) \
and uid_with_the_same_nickname != -1 and (uid_with_the_same_nickname == uid or uid_with_the_same_nickname == 0):
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 3:
change = webuser.updateDataUser(uid,
args['email'],
args['nickname'])
else:
return webuser.page_not_authorized(req, "../youraccount/change",
navmenuid='youraccount')
if change:
mess = _("Settings successfully edited.")
act = "display"
linkname = _("Show account")
title = _("Settings edited")
elif args['nickname'] is not None and not webuser.nickname_valid_p(args['nickname']):
mess = _("Desired nickname %s is invalid.") % args['nickname']
mess += " " + _("Please try again.")
act = "edit"
linkname = _("Edit settings")
title = _("Editing settings failed")
elif not webuser.email_valid_p(args['email']):
mess = _("Supplied email address %s is invalid.") % args['email']
mess += " " + _("Please try again.")
act = "edit"
linkname = _("Edit settings")
title = _("Editing settings failed")
elif uid2 == -1 or uid2 != uid and not uid2 == 0:
mess = _("Supplied email address %s already exists in the database.") % args['email']
mess += " " + websession_templates.tmpl_lost_your_password_teaser(args['ln'])
mess += " " + _("Or please try again.")
act = "edit"
linkname = _("Edit settings")
title = _("Editing settings failed")
elif uid_with_the_same_nickname == -1 or uid_with_the_same_nickname != uid and not uid_with_the_same_nickname == 0:
mess = _("Desired nickname %s is already in use.") % args['nickname']
mess += " " + _("Please try again.")
act = "edit"
linkname = _("Edit settings")
title = _("Editing settings failed")
elif args['old_password'] != None and CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 3:
res = run_sql("SELECT id FROM user "
"WHERE AES_ENCRYPT(email,%s)=password AND id=%s",
(args['old_password'], uid))
if res:
if args['password'] == args['password2']:
webuser.updatePasswordUser(uid, args['password'])
mess = _("Password successfully edited.")
act = "display"
linkname = _("Show account")
title = _("Password edited")
else:
mess = _("Both passwords must match.")
mess += " " + _("Please try again.")
act = "edit"
linkname = _("Edit settings")
title = _("Editing password failed")
else:
mess = _("Wrong old password inserted.")
mess += " " + _("Please try again.")
act = "edit"
linkname = _("Edit settings")
title = _("Editing password failed")
elif args['group_records']:
prefs = webuser.get_user_preferences(uid)
prefs['websearch_group_records'] = args['group_records']
prefs['websearch_latestbox'] = args['latestbox']
prefs['websearch_helpbox'] = args['helpbox']
webuser.set_user_preferences(uid, prefs)
title = _("Settings edited")
act = "display"
linkname = _("Show account")
mess = _("User settings saved correctly.")
else:
mess = _("Unable to update settings.")
act = "edit"
linkname = _("Edit settings")
title = _("Editing settings failed")
return page(title=title,
body=webaccount.perform_back(mess, act, linkname, args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def lost(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/lost",
navmenuid='youraccount')
return page(title=_("Lost your password?"),
body=webaccount.perform_lost(args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def send_email(self, req, form):
# set all the declared query fields as local variables
args = wash_urlargd(form, {'p_email': (str, None)})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/send_email",
navmenuid='youraccount')
user_prefs = webuser.get_user_preferences(webuser.emailUnique(args['p_email']))
if user_prefs:
if CFG_EXTERNAL_AUTHENTICATION.has_key(user_prefs['login_method']) and \
CFG_EXTERNAL_AUTHENTICATION[user_prefs['login_method']][0] is not None:
eMsg = _("Cannot send password reset request since you are using external authentication system.")
return page(title=_("Your Account"),
body=webaccount.perform_emailMessage(eMsg, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME)),
uid=uid, req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
try:
reset_key = mail_cookie_create_pw_reset(args['p_email'], cookie_timeout=timedelta(days=CFG_WEBSESSION_RESET_PASSWORD_EXPIRE_IN_DAYS))
except InvenioWebAccessMailCookieError:
reset_key = None
if reset_key is None:
eMsg = _("The entered email address does not exist in the database.")
return page(title=_("Your Account"),
body=webaccount.perform_emailMessage(eMsg, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid, req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
ip_address = req.connection.remote_host or req.connection.remote_ip
if not send_email(CFG_SITE_SUPPORT_EMAIL, args['p_email'], "%s %s"
% (_("Password reset request for"),
CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME)),
websession_templates.tmpl_account_reset_password_email_body(
args['p_email'],reset_key, ip_address, args['ln'])):
eMsg = _("The entered email address is incorrect, please check that it is written correctly (e.g. johndoe@example.com).")
return page(title=_("Incorrect email address"),
body=webaccount.perform_emailMessage(eMsg, args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
return page(title=_("Reset password link sent"),
body=webaccount.perform_emailSent(args['p_email'], args['ln']),
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid, req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def youradminactivities(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.getUid(req)
user_info = webuser.collect_user_info(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/youradminactivities",
navmenuid='admin')
return page(title=_("Your Administrative Activities"),
body=webaccount.perform_youradminactivities(user_info, args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='admin')
def delete(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/delete",
navmenuid='youraccount')
return page(title=_("Delete Account"),
body=webaccount.perform_delete(args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def logout(self, req, form):
args = wash_urlargd(form, {})
uid = webuser.logoutUser(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../youraccount/logout",
navmenuid='youraccount')
if CFG_EXTERNAL_AUTH_USING_SSO:
return redirect_to_url(req, CFG_EXTERNAL_AUTH_LOGOUT_SSO)
return page(title=_("Logout"),
body=webaccount.perform_logout(req, args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords=_("%s, personalize") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def login(self, req, form):
args = wash_urlargd(form, {
'p_un': (str, None),
'p_pw': (str, None),
'login_method': (str, None),
'action': (str, ''),
'referer': (str, '')})
if args['p_un']:
args['p_un'] = args['p_un'].strip()
locals().update(args)
if CFG_ACCESS_CONTROL_LEVEL_SITE > 0:
return webuser.page_not_authorized(req, "../youraccount/login?ln=%s" % args['ln'],
navmenuid='youraccount')
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
apache_msg = ""
if args['action']:
cookie = args['action']
try:
action, arguments = mail_cookie_check_authorize_action(cookie)
apache_msg = make_apache_message(action, arguments, args['referer'])
# FIXME: Temporary Hack to help CDS current migration
if CFG_CERN_SITE:
roles = make_list_apache_firerole(action, arguments)
if len(roles) == 1:
# There's only one role enabled to see this collection
# Let's redirect to log to it!
return redirect_to_url(req, '%s%s' % (CFG_SITE_SECURE_URL, make_canonical_urlargd({'realm' : roles[0][0], 'referer' : args['referer']}, {})))
except InvenioWebAccessMailCookieError:
pass
if not CFG_EXTERNAL_AUTH_USING_SSO:
if args['p_un'] is None or not args['login_method']:
return page(title=_("Login"),
body=webaccount.create_login_page_box(args['referer'], apache_msg, args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
(iden, args['p_un'], args['p_pw'], msgcode) = webuser.loginUser(req, args['p_un'], args['p_pw'], args['login_method'])
else:
# Fake parameters for p_un & p_pw because SSO takes them from the environment
(iden, args['p_un'], args['p_pw'], msgcode) = webuser.loginUser(req, '', '', CFG_EXTERNAL_AUTH_USING_SSO)
if len(iden)>0:
uid = webuser.update_Uid(req, args['p_un'])
uid2 = webuser.getUid(req)
if uid2 == -1:
webuser.logoutUser(req)
return webuser.page_not_authorized(req, "../youraccount/login?ln=%s" % args['ln'], uid=uid,
navmenuid='youraccount')
# login successful!
if args['referer']:
req.err_headers_out.add("Location", args['referer'])
raise apache.SERVER_RETURN, apache.HTTP_MOVED_PERMANENTLY
else:
return self.display(req, form)
else:
mess = CFG_WEBACCESS_WARNING_MSGS[msgcode] % args['login_method']
if msgcode == 14:
if webuser.username_exists_p(args['p_un']):
mess = CFG_WEBACCESS_WARNING_MSGS[15] % args['login_method']
act = "login"
return page(title=_("Login"),
body=webaccount.perform_back(mess, act, _("login"), args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description="%s Personalize, Main page" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
def register(self, req, form):
args = wash_urlargd(form, {
'p_nickname': (str, None),
'p_email': (str, None),
'p_pw': (str, None),
'p_pw2': (str, None),
'action': (str, "login"),
'referer': (str, "")})
if CFG_ACCESS_CONTROL_LEVEL_SITE > 0:
return webuser.page_not_authorized(req, "../youraccount/register?ln=%s" % args['ln'],
navmenuid='youraccount')
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(args['ln'])
if args['p_nickname'] is None or args['p_email'] is None:
return page(title=_("Register"),
body=webaccount.create_register_page_box(args['referer'], args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description=_("%s Personalize, Main page") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
mess = ""
act = ""
if args['p_pw'] == args['p_pw2']:
ruid = webuser.registerUser(req, args['p_email'], args['p_pw'], args['p_nickname'])
else:
ruid = -2
if ruid == 0:
mess = _("Your account has been successfully created.")
title = _("Account created")
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT == 1:
mess += " " + _("In order to confirm its validity, an email message containing an account activation key has been sent to the given email address.")
mess += " " + _("Please follow instructions presented there in order to complete the account registration process.")
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 1:
mess += " " + _("A second email will be sent when the account has been activated and can be used.")
elif CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT != 1:
uid = webuser.update_Uid(req, args['p_email'])
mess += " " + _("You can now access your %(x_url_open)saccount%(x_url_close)s.") %\
{'x_url_open': '<a href="' + CFG_SITE_SECURE_URL + '/youraccount/display?ln=' + args['ln'] + '">',
'x_url_close': '</a>'}
elif ruid == -2:
mess = _("Both passwords must match.")
mess += " " + _("Please try again.")
act = "register"
title = _("Registration failure")
elif ruid == 1:
mess = _("Supplied email address %s is invalid.") % args['p_email']
mess += " " + _("Please try again.")
act = "register"
title = _("Registration failure")
elif ruid == 2:
mess = _("Desired nickname %s is invalid.") % args['p_nickname']
mess += " " + _("Please try again.")
act = "register"
title = _("Registration failure")
elif ruid == 3:
mess = _("Supplied email address %s already exists in the database.") % args['p_email']
mess += " " + websession_templates.tmpl_lost_your_password_teaser(args['ln'])
mess += " " + _("Or please try again.")
act = "register"
title = _("Registration failure")
elif ruid == 4:
mess = _("Desired nickname %s already exists in the database.") % args['p_nickname']
mess += " " + _("Please try again.")
act = "register"
title = _("Registration failure")
elif ruid == 5:
mess = _("Users cannot register themselves, only admin can register them.")
act = "register"
title = _("Registration failure")
elif ruid == 6:
mess = _("The site is having troubles in sending you an email for confirming your email address.") + _("The error has been logged and will be taken in consideration as soon as possibile.")
act = "register"
title = _("Registration failure")
else:
# this should never happen
mess = _("Internal Error")
act = "register"
title = _("Registration failure")
return page(title=title,
body=webaccount.perform_back(mess,act, (act == 'register' and _("register") or ""), args['ln']),
navtrail="""<a class="navtrail" href="%s/youraccount/display?ln=%s">""" % (CFG_SITE_SECURE_URL, args['ln']) + _("Your Account") + """</a>""",
description=_("%s Personalize, Main page") % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
keywords="%s , personalize" % CFG_SITE_NAME_INTL.get(args['ln'], CFG_SITE_NAME),
uid=uid,
req=req,
secure_page_p = 1,
language=args['ln'],
lastupdated=__lastupdated__,
navmenuid='youraccount')
class WebInterfaceYourGroupsPages(WebInterfaceDirectory):
_exports = ['', 'display', 'create', 'join', 'leave', 'edit', 'members']
def index(self, req, form):
redirect_to_url(req, '/yourgroups/display')
def display(self, req, form):
"""
Displays groups the user is admin of
and the groups the user is member of(but not admin)
@param ln: language
@return the page for all the groups
"""
argd = wash_urlargd(form, {})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/display",
navmenuid='yourgroups')
(body, errors, warnings) = webgroup.perform_request_groups_display(uid=uid,
ln=argd['ln'])
return page(title = _("Your Groups"),
body = body,
navtrail = webgroup.get_navtrail(argd['ln']),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def create(self, req, form):
"""create(): interface for creating a new group
@param group_name : name of the new webgroup.Must be filled
@param group_description : description of the new webgroup.(optionnal)
@param join_policy : join policy of the new webgroup.Must be chosen
@param *button: which button was pressed
@param ln: language
@return the compose page Create group
"""
argd = wash_urlargd(form, {'group_name': (str, ""),
'group_description': (str, ""),
'join_policy': (str, ""),
'create_button':(str, ""),
'cancel':(str, "")
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/create",
navmenuid='yourgroups')
if argd['cancel']:
- url = weburl + '/yourgroups/display?ln=%s'
+ url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
if argd['create_button'] :
(body, errors, warnings)= webgroup.perform_request_create_group(uid=uid,
group_name=argd['group_name'],
group_description=argd['group_description'],
join_policy=argd['join_policy'],
ln = argd['ln'])
else:
(body, errors, warnings) = webgroup.perform_request_input_create_group(group_name=argd['group_name'],
group_description=argd['group_description'],
join_policy=argd['join_policy'],
ln=argd['ln'])
title = _("Create new group")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def join(self, req, form):
"""join(): interface for joining a new group
@param grpID : list of the group the user wants to become a member.
The user must select only one group.
@param group_name : will search for groups matching group_name
@param *button: which button was pressed
@param ln: language
@return the compose page Join group
"""
argd = wash_urlargd(form, {'grpID':(list, []),
'group_name':(str, ""),
'find_button':(str, ""),
'join_button':(str, ""),
'cancel':(str, "")
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/join",
navmenuid='yourgroups')
if argd['cancel']:
- url = weburl + '/yourgroups/display?ln=%s'
+ url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
if argd['join_button']:
search = 0
if argd['group_name']:
search = 1
(body, errors, warnings) = webgroup.perform_request_join_group(uid,
argd['grpID'],
argd['group_name'],
search,
argd['ln'])
else:
search = 0
if argd['find_button']:
search = 1
(body, errors, warnings) = webgroup.perform_request_input_join_group(uid,
argd['group_name'],
search,
ln=argd['ln'])
title = _("Join New Group")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def leave(self, req, form):
"""leave(): interface for leaving a group
@param grpID : group the user wants to leave.
@param group_name : name of the group the user wants to leave
@param *button: which button was pressed
@param confirmed : the user is first asked to confirm
@param ln: language
@return the compose page Leave group
"""
argd = wash_urlargd(form, {'grpID':(str, ""),
'group_name':(str, ""),
'leave_button':(str, ""),
'cancel':(str, ""),
'confirmed': (int, 0)
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/leave",
navmenuid='yourgroups')
if argd['cancel']:
- url = weburl + '/yourgroups/display?ln=%s'
+ url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
if argd['leave_button']:
(body, errors, warnings) = webgroup.perform_request_leave_group(uid,
argd['grpID'],
argd['confirmed'],
argd['ln'])
else:
(body, errors, warnings) = webgroup.perform_request_input_leave_group(uid=uid,
ln=argd['ln'])
title = _("Leave Group")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def edit(self, req, form):
"""edit(): interface for editing group
@param grpID : group ID
@param group_name : name of the new webgroup.Must be filled
@param group_description : description of the new webgroup.(optionnal)
@param join_policy : join policy of the new webgroup.Must be chosen
@param update: button update group pressed
@param delete: button delete group pressed
@param cancel: button cancel pressed
@param confirmed : the user is first asked to confirm before deleting
@param ln: language
@return the main page displaying all the groups
"""
argd = wash_urlargd(form, {'grpID': (str, ""),
'update': (str, ""),
'cancel': (str, ""),
'delete': (str, ""),
'group_name': (str, ""),
'group_description': (str, ""),
'join_policy': (str, ""),
'confirmed': (int, 0)
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/display",
navmenuid='yourgroups')
if argd['cancel']:
- url = weburl + '/yourgroups/display?ln=%s'
+ url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
elif argd['delete']:
(body, errors, warnings) = webgroup.perform_request_delete_group(uid=uid,
grpID=argd['grpID'],
confirmed=argd['confirmed'])
elif argd['update']:
(body, errors, warnings) = webgroup.perform_request_update_group(uid= uid,
grpID=argd['grpID'],
group_name=argd['group_name'],
group_description=argd['group_description'],
join_policy=argd['join_policy'],
ln=argd['ln'])
else :
(body, errors, warnings)= webgroup.perform_request_edit_group(uid=uid,
grpID=argd['grpID'],
ln=argd['ln'])
title = _("Edit Group")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
def members(self, req, form):
"""member(): interface for managing members of a group
@param grpID : group ID
@param add_member: button add_member pressed
@param remove_member: button remove_member pressed
@param reject_member: button reject__member pressed
@param delete: button delete group pressed
@param member_id : ID of the existing member selected
@param pending_member_id : ID of the pending member selected
@param cancel: button cancel pressed
@param info : info about last user action
@param ln: language
@return the same page with data updated
"""
argd = wash_urlargd(form, {'grpID': (int, 0),
'cancel': (str, ""),
'add_member': (str, ""),
'remove_member': (str, ""),
'reject_member': (str, ""),
'member_id': (int, 0),
'pending_member_id': (int, 0)
})
uid = webuser.getUid(req)
# load the right message language
_ = gettext_set_language(argd['ln'])
if uid == -1 or webuser.isGuestUser(uid) or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return webuser.page_not_authorized(req, "../yourgroups/display",
navmenuid='yourgroups')
if argd['cancel']:
- url = weburl + '/yourgroups/display?ln=%s'
+ url = CFG_SITE_URL + '/yourgroups/display?ln=%s'
url %= argd['ln']
redirect_to_url(req, url)
if argd['remove_member']:
(body, errors, warnings) = webgroup.perform_request_remove_member(uid=uid,
grpID=argd['grpID'],
member_id=argd['member_id'],
ln=argd['ln'])
elif argd['reject_member']:
(body, errors, warnings) = webgroup.perform_request_reject_member(uid=uid,
grpID=argd['grpID'],
user_id=argd['pending_member_id'],
ln=argd['ln'])
elif argd['add_member']:
(body, errors, warnings) = webgroup.perform_request_add_member(uid=uid,
grpID=argd['grpID'],
user_id=argd['pending_member_id'],
ln=argd['ln'])
else:
(body, errors, warnings)= webgroup.perform_request_manage_member(uid=uid,
grpID=argd['grpID'],
ln=argd['ln'])
title = _("Edit group members")
return page(title = title,
body = body,
navtrail = webgroup.get_navtrail(argd['ln'], title),
uid = uid,
req = req,
language = argd['ln'],
lastupdated = __lastupdated__,
errors = errors,
warnings = warnings,
navmenuid = 'yourgroups')
diff --git a/modules/websession/lib/webuser.py b/modules/websession/lib/webuser.py
index 2d3f193e4..aaca8284c 100644
--- a/modules/websession/lib/webuser.py
+++ b/modules/websession/lib/webuser.py
@@ -1,945 +1,945 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
This file implements all methods necessary for working with users and
sessions in CDS Invenio. Contains methods for logging/registration
when a user log/register into the system, checking if it is a guest
user or not.
At the same time this presents all the stuff it could need with
sessions managements, working with websession.
It also contains Apache-related user authentication stuff.
"""
__revision__ = "$Id$"
try:
from mod_python import apache
except ImportError:
pass
from socket import gethostbyname
import os
import crypt
import socket
import smtplib
import re
import random
import base64
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS, \
CFG_ACCESS_CONTROL_LEVEL_GUESTS, \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN, \
CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS, \
CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT, \
CFG_APACHE_GROUP_FILE, \
CFG_APACHE_PASSWORD_FILE, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
CFG_TMPDIR, \
- weburl
+ CFG_SITE_URL
from invenio import session
from invenio.dbquery import run_sql, OperationalError, \
serialize_via_marshal, deserialize_via_marshal
from invenio.websession import pSession, pSessionMapping
from invenio.session import SessionError
from invenio.access_control_admin import acc_find_user_role_actions, acc_get_role_id
from invenio.access_control_mailcookie import mail_cookie_create_mail_activation
from invenio.access_control_firerole import acc_firerole_check_user, load_role_definition
from invenio.messages import gettext_set_language
from invenio.mailutils import send_email
from invenio.errorlib import register_exception
from invenio.webgroup_dblayer import get_groups
from invenio.external_authentication import InvenioWebAccessExternalAuthError
from invenio.access_control_config import CFG_EXTERNAL_AUTHENTICATION, \
CFG_WEBACCESS_MSGS, CFG_WEBACCESS_WARNING_MSGS
import invenio.template
tmpl = invenio.template.load('websession')
re_invalid_nickname = re.compile(""".*[,'@]+.*""")
# pylint: disable-msg=C0301
def createGuestUser():
"""Create a guest user , insert into user null values in all fields
createGuestUser() -> GuestUserID
"""
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0:
try:
return run_sql("insert into user (email, note) values ('', '1')")
except OperationalError:
return None
else:
try:
return run_sql("insert into user (email, note) values ('', '0')")
except OperationalError:
return None
def page_not_authorized(req, referer='', uid='', text='', navtrail='', ln=CFG_SITE_LANG,
navmenuid=""):
"""Show error message when user is not authorized to do something.
@param referer: in case the displayed message propose a login link, this
is the url to return to after logging in.
@param uid: the uid of the user. If not specified it is guessed from req.
@param text: the message to be displayed. If not specified it will be
guessed from the context.
"""
from invenio.webpage import page
_ = gettext_set_language(ln)
if not CFG_ACCESS_CONTROL_LEVEL_SITE:
title = CFG_WEBACCESS_MSGS[5]
if not uid:
uid = getUid(req)
try:
res = run_sql("SELECT email FROM user WHERE id=%s AND note=1" % uid)
if res and res[0][0]:
if text:
body = text
else:
body = "%s %s" % (CFG_WEBACCESS_WARNING_MSGS[9] % res[0][0],
("%s %s" % (CFG_WEBACCESS_MSGS[0] % referer, CFG_WEBACCESS_MSGS[1])))
else:
if text:
body = text
else:
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 1:
body = CFG_WEBACCESS_MSGS[3]
else:
body = CFG_WEBACCESS_WARNING_MSGS[4] + CFG_WEBACCESS_MSGS[2]
except OperationalError, e:
body = _("Database problem") + ': ' + str(e)
elif CFG_ACCESS_CONTROL_LEVEL_SITE == 1:
title = CFG_WEBACCESS_MSGS[8]
body = "%s %s" % (CFG_WEBACCESS_MSGS[7], CFG_WEBACCESS_MSGS[2])
elif CFG_ACCESS_CONTROL_LEVEL_SITE == 2:
title = CFG_WEBACCESS_MSGS[6]
body = "%s %s" % (CFG_WEBACCESS_MSGS[4], CFG_WEBACCESS_MSGS[2])
return page(title=title,
uid=getUid(req),
body=body,
navtrail=navtrail,
req=req,
navmenuid=navmenuid)
def getApacheUser(req):
"""Return the ApacheUser taking it from the cookie of the request."""
sm = session.MPSessionManager(pSession, pSessionMapping())
try:
s = sm.get_session(req)
except SessionError:
sm.revoke_session_cookie (req)
s = sm.get_session(req)
apache_user = s.getApacheUser()
sm.maintain_session(req, s)
return apache_user
def getUid (req):
"""Return user ID taking it from the cookie of the request.
Includes control mechanism for the guest users, inserting in
the database table when need be, raising the cookie back to the
client.
User ID is set to 0 when client refuses cookie or we are in the
read-only site operation mode.
User ID is set to -1 when we are in the permission denied site
operation mode.
getUid(req) -> userId
"""
if CFG_ACCESS_CONTROL_LEVEL_SITE == 1: return 0
if CFG_ACCESS_CONTROL_LEVEL_SITE == 2: return -1
guest = 0
sm = session.MPSessionManager(pSession, pSessionMapping())
try:
s = sm.get_session(req)
except SessionError:
sm.revoke_session_cookie (req)
s = sm.get_session(req)
userId = s.getUid()
if userId == -1: # first time, so create a guest user
s.setUid(createGuestUser())
userId = s.getUid()
guest = 1
sm.maintain_session(req, s)
if guest == 0:
guest = isGuestUser(userId)
if guest:
if CFG_ACCESS_CONTROL_LEVEL_GUESTS == 0:
return userId
elif CFG_ACCESS_CONTROL_LEVEL_GUESTS >= 1:
return -1
else:
res = run_sql("SELECT note FROM user WHERE id=%s" % userId)
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 0:
return userId
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 1 and res and res[0][0] in [1, "1"]:
return userId
else:
return -1
def setApacheUser(req, apache_user):
"""It sets the apache_user into the session, and raise the cookie to the client.
"""
sm = session.MPSessionManager(pSession, pSessionMapping())
try:
s = sm.get_session(req)
except SessionError:
sm.revoke_session_cookie(req)
s = sm.get_session(req)
s.setApacheUser(apache_user)
sm.maintain_session(req, s)
return apache_user
def setUid(req, uid):
"""It sets the userId into the session, and raise the cookie to the client.
"""
sm = session.MPSessionManager(pSession, pSessionMapping())
try:
s = sm.get_session(req)
except SessionError:
sm.revoke_session_cookie(req)
s = sm.get_session(req)
s.setUid(uid)
sm.maintain_session(req, s)
return uid
def get_user_info(uid, ln=CFG_SITE_LANG):
"""Get infos for a given user.
@param uid: user id (int)
@return tuple: (uid, nickname, display_name)
"""
_ = gettext_set_language(ln)
query = """SELECT id, nickname
FROM user
WHERE id=%i"""
res = run_sql(query%uid)
if res:
if res[0]:
user = list(res[0])
if user[1]:
user.append(user[1])
else:
user[1] = str(user[0])
user.append(_("user") + ' #' + str(user[0]))
return tuple(user)
return (uid, '', _("N/A"))
def isGuestUser(uid):
"""It Checks if the userId corresponds to a guestUser or not
isGuestUser(uid) -> boolean
"""
out = 1
try:
res = run_sql("select email from user where id=%s", (uid,))
if res:
if res[0][0]:
out = 0
except OperationalError:
pass
return out
def isUserSubmitter(user_info):
"""Return True if the user is a submitter for something; False otherwise."""
u_email = get_email(user_info['uid'])
res = run_sql("select email from sbmSUBMISSIONS where email=%s", (u_email,))
return len(res) > 0
def isUserReferee(user_info):
"""Return True if the user is a referee for something; False otherwise."""
from invenio.access_control_engine import acc_authorize_action
res = run_sql("select sdocname from sbmDOCTYPE")
for row in res:
doctype = row[0]
categ = "*"
(auth_code, auth_message) = acc_authorize_action(user_info, "referee", doctype=doctype, categ=categ)
if auth_code == 0:
return 1
res2 = run_sql("select sname from sbmCATEGORIES where doctype=%s", (doctype,))
for row2 in res2:
categ = row2[0]
(auth_code, auth_message) = acc_authorize_action(user_info, "referee", doctype=doctype, categ=categ)
if auth_code == 0:
return 1
return 0
def isUserAdmin(user_info):
"""Return True if the user has some admin rights; False otherwise."""
return acc_find_user_role_actions(user_info)
def nickname_valid_p(nickname):
"""Check whether wanted NICKNAME supplied by the user is valid.
At the moment we just check whether it is not empty, does not
contain blanks or @, is not equal to `guest', etc.
This check relies on re_invalid_nickname regexp (see above)
Return 1 if nickname is okay, return 0 if it is not.
"""
if nickname and \
not(nickname.startswith(' ') or nickname.endswith(' ')) and \
nickname.lower() != 'guest':
if not re_invalid_nickname.match(nickname):
return 1
return 0
def email_valid_p(email):
"""Check whether wanted EMAIL address supplied by the user is valid.
At the moment we just check whether it contains '@' and whether
it doesn't contain blanks. We also check the email domain if
CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN is set.
Return 1 if email is okay, return 0 if it is not.
"""
if (email.find("@") <= 0) or (email.find(" ") > 0):
return 0
elif CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN:
if not email.endswith(CFG_ACCESS_CONTROL_LIMIT_REGISTRATION_TO_DOMAIN):
return 0
return 1
def confirm_email(email):
"""Confirm the email. It returns None when there are problems, otherwise
it return the uid involved."""
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 0:
activated = 1
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
activated = 0
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 2:
return -1
run_sql('UPDATE user SET note=%s where email=%s', (activated, email))
res = run_sql('SELECT id FROM user where email=%s', (email, ))
if res:
if CFG_ACCESS_CONTROL_NOTIFY_ADMIN_ABOUT_NEW_ACCOUNTS:
send_new_admin_account_warning(email, CFG_SITE_ADMIN_EMAIL)
return res[0][0]
else:
return None
def registerUser(req, email, passw, nickname, register_without_nickname=False,
login_method=None, ln=CFG_SITE_LANG):
"""Register user with the desired values of NICKNAME, EMAIL and
PASSW.
If REGISTER_WITHOUT_NICKNAME is set to True, then ignore
desired NICKNAME and do not set any. This is suitable for
external authentications so that people can login without
having to register an internal account first.
Return 0 if the registration is successful, 1 if email is not
valid, 2 if nickname is not valid, 3 if email is already in the
database, 4 if nickname is already in the database, 5 when
users cannot register themselves because of the site policy, 6 when the
site is having problem contacting the user.
If login_method is None or is equal to the key corresponding to local
authentication, then CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS is taken
in account for deciding the behaviour about registering.
"""
# is email valid?
email = email.lower()
if not email_valid_p(email):
return 1
_ = gettext_set_language(ln)
# is email already taken?
res = run_sql("SELECT email FROM user WHERE email=%s", (email,))
if len(res) > 0:
return 3
if register_without_nickname:
# ignore desired nick and use default empty string one:
nickname = ""
else:
# is nickname valid?
if not nickname_valid_p(nickname):
return 2
# is nickname already taken?
res = run_sql("SELECT nickname FROM user WHERE nickname=%s", (nickname,))
if len(res) > 0:
return 4
activated = 1 # By default activated
if not login_method or not CFG_EXTERNAL_AUTHENTICATION[login_method][0]: # local login
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 2:
return 5
elif CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT:
activated = 2 # Email confirmation required
elif CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 1:
activated = 0 # Administrator confirmation required
if CFG_ACCESS_CONTROL_NOTIFY_USER_ABOUT_NEW_ACCOUNT:
address_activation_key = mail_cookie_create_mail_activation(email)
ip_address = req.connection.remote_host or req.connection.remote_ip
try:
if not send_email(CFG_SITE_SUPPORT_EMAIL, email, _("Account registration at %s") % CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
tmpl.tmpl_account_address_activation_email_body(email,
address_activation_key, ip_address, ln)):
return 1
except (smtplib.SMTPException, socket.error):
return 6
# okay, go on and register the user:
user_preference = get_default_user_preferences()
uid = run_sql("INSERT INTO user (nickname, email, password, note, settings, last_login) "
"VALUES (%s,%s,AES_ENCRYPT(email,%s),%s,%s, NOW())",
(nickname, email, passw, activated, serialize_via_marshal(user_preference)))
if activated == 1: # Ok we consider the user as logged in :-)
setUid(req, uid)
return 0
def updateDataUser(uid, email, nickname):
"""
Update user data. Used when a user changed his email or password
or nickname.
"""
email = email.lower()
if email == 'guest':
return 0
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 2:
run_sql("update user set email=%s where id=%s", (email, uid))
if nickname and nickname != '':
run_sql("update user set nickname=%s where id=%s", (nickname, uid))
return 1
def updatePasswordUser(uid, password):
"""Update the password of a user."""
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 3:
run_sql("update user set password=AES_ENCRYPT(email,%s) where id=%s", (password, uid))
return 1
def loginUser(req, p_un, p_pw, login_method):
"""It is a first simple version for the authentication of user. It returns the id of the user,
for checking afterwards if the login is correct
"""
# p_un passed may be an email or a nickname:
p_email = get_email_from_username(p_un)
# go on with the old stuff based on p_email:
if not CFG_EXTERNAL_AUTHENTICATION.has_key(login_method):
return ([], p_email, p_pw, 12)
if CFG_EXTERNAL_AUTHENTICATION[login_method][0]: # External Authenthication
try:
p_email = CFG_EXTERNAL_AUTHENTICATION[login_method][0].auth_user(p_email, p_pw, req)
if p_email:
p_email = p_email.lower()
else:
raise InvenioWebAccessExternalAuthError
except InvenioWebAccessExternalAuthError:
return([], p_email, p_pw, 15)
if p_email: # Authenthicated externally
query_result = run_sql("SELECT id from user where email=%s", (p_email,))
if not query_result: # First time user
p_pw_local = int(random.random() * 1000000)
p_nickname = ''
if CFG_EXTERNAL_AUTHENTICATION[login_method][0].enforce_external_nicknames:
try: # Let's discover the external nickname!
p_nickname = CFG_EXTERNAL_AUTHENTICATION[login_method][0].fetch_user_nickname(p_email, p_pw, req)
except (AttributeError, NotImplementedError):
pass
res = registerUser(req, p_email, p_pw_local, p_nickname,
register_without_nickname=p_nickname == '',
login_method=login_method)
if res == 4 or res == 2: # The nickname was already taken
res = registerUser(req, p_email, p_pw_local, '',
register_without_nickname=True,
login_method=login_method)
elif res == 0: # Everything was ok, with or without nickname.
query_result = run_sql("SELECT id from user where email=%s", (p_email,))
elif res == 6: # error in contacting the user via email
return([], p_email, p_pw_local, 19)
else:
return([], p_email, p_pw_local, 13)
try:
groups = CFG_EXTERNAL_AUTHENTICATION[login_method][0].fetch_user_groups_membership(p_email, p_pw, req)
# groups is a dictionary {group_name : group_description,}
new_groups = {}
for key, value in groups.items():
new_groups[key + " [" + str(login_method) + "]"] = value
groups = new_groups
except (AttributeError, NotImplementedError):
pass
except InvenioWebAccessExternalAuthError:
return([], p_email, p_pw, 16)
else: # Groups synchronization
if groups != 0:
userid = query_result[0][0]
from invenio.webgroup import synchronize_external_groups
synchronize_external_groups(userid, groups, login_method)
user_prefs = get_user_preferences(query_result[0][0])
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS >= 4:
# Let's prevent the user to switch login_method
if user_prefs.has_key("login_method") and \
user_prefs["login_method"] != login_method:
return([], p_email, p_pw, 11)
user_prefs["login_method"] = login_method
# Cleaning external settings
for key in user_prefs.keys():
if key.startswith('EXTERNAL_'):
del user_prefs[key]
try:
# Importing external settings
new_prefs = CFG_EXTERNAL_AUTHENTICATION[login_method][0].fetch_user_preferences(p_email, p_pw, req)
for key, value in new_prefs.items():
user_prefs['EXTERNAL_' + key] = value
except (AttributeError, NotImplementedError):
pass
except InvenioWebAccessExternalAuthError:
return([], p_email, p_pw, 16)
# Storing settings
set_user_preferences(query_result[0][0], user_prefs)
else:
return ([], p_un, p_pw, 10)
else: # Internal Authenthication
if not p_pw:
p_pw = ''
query_result = run_sql("SELECT id,email,note from user where email=%s and password=AES_ENCRYPT(email,%s)", (p_email, p_pw,))
if query_result:
#FIXME drop external groups and settings
note = query_result[0][2]
if note == '1': # Good account
preferred_login_method = get_user_preferences(query_result[0][0])['login_method']
p_email = query_result[0][1].lower()
if login_method != preferred_login_method:
if CFG_EXTERNAL_AUTHENTICATION.has_key(preferred_login_method):
return ([], p_email, p_pw, 11)
elif note == '2': # Email address need to be confirmed by user
return ([], p_email, p_pw, 17)
elif note == '0': # Account need to be confirmed by administrator
return ([], p_email, p_pw, 18)
else:
return ([], p_email, p_pw, 14)
# Login successful! Updating the last access time
run_sql("UPDATE user SET last_login=NOW() WHERE email=%s", (p_email, ))
return (query_result, p_email, p_pw, 0)
def drop_external_settings(userId):
"""Drop the external (EXTERNAL_) settings of userid."""
prefs = get_user_preferences(userId)
for key in prefs.keys():
if key.startswith('EXTERNAL_'):
del prefs[key]
set_user_preferences(userId, prefs)
def logoutUser(req):
"""It logout the user of the system, creating a guest user.
"""
getUid(req)
sm = session.MPSessionManager(pSession, pSessionMapping())
try:
s = sm.get_session(req)
except SessionError:
sm.revoke_session_cookie(req)
s = sm.get_session(req)
id1 = createGuestUser()
s.setUid(id1)
sm.maintain_session(req, s)
return id1
def username_exists_p(username):
"""Check if USERNAME exists in the system. Username may be either
nickname or email.
Return 1 if it does exist, 0 if it does not.
"""
if username == "":
# return not exists if asked for guest users
return 0
res = run_sql("SELECT email FROM user WHERE email=%s", (username,)) + \
run_sql("SELECT email FROM user WHERE nickname=%s", (username,))
if len(res) > 0:
return 1
return 0
def emailUnique(p_email):
"""Check if the email address only exists once. If yes, return userid, if not, -1
"""
query_result = run_sql("select id, email from user where email=%s", (p_email,))
if len(query_result) == 1:
return query_result[0][0]
elif len(query_result) == 0:
return 0
return -1
def nicknameUnique(p_nickname):
"""Check if the nickname only exists once. If yes, return userid, if not, -1
"""
query_result = run_sql("select id, nickname from user where nickname=%s", (p_nickname,))
if len(query_result) == 1:
return query_result[0][0]
elif len(query_result) == 0:
return 0
return -1
def update_Uid(req, p_email):
"""It updates the userId of the session. It is used when a guest user is logged in succesfully in the system
with a given email and password
"""
query_ID = int(run_sql("select id from user where email=%s",
(p_email,))[0][0])
setUid(req, query_ID)
return query_ID
def send_new_admin_account_warning(new_account_email, send_to, ln=CFG_SITE_LANG):
"""Send an email to the address given by send_to about the new account new_account_email."""
_ = gettext_set_language(ln)
sub = _("New account on") + " '%s'" % CFG_SITE_NAME
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
sub += " - " + _("PLEASE ACTIVATE")
body = _("A new account has been created on") + " '%s'" % CFG_SITE_NAME
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS == 1:
body += _(" and is awaiting activation")
body += ":\n\n"
body += _(" Username/Email") + ": %s\n\n" % new_account_email
- body += _("You can approve or reject this account request at") + ": %s/admin/webaccess/webaccessadmin.py/manageaccounts\n" % weburl
+ body += _("You can approve or reject this account request at") + ": %s/admin/webaccess/webaccessadmin.py/manageaccounts\n" % CFG_SITE_URL
return send_email(CFG_SITE_SUPPORT_EMAIL, send_to, subject=sub, content=body)
def get_email(uid):
"""Return email address of the user uid. Return string 'guest' in case
the user is not found."""
out = "guest"
res = run_sql("SELECT email FROM user WHERE id=%s", (uid,), 1)
if res and res[0][0]:
out = res[0][0].lower()
return out
def get_email_from_username(username):
"""Return email address of the user corresponding to USERNAME.
The username may be either nickname or email. Return USERNAME
untouched if not found in the database or if found several
matching entries.
"""
if username == '':
return ''
out = username
res = run_sql("SELECT email FROM user WHERE email=%s", (username,), 1) + \
run_sql("SELECT email FROM user WHERE nickname=%s", (username,), 1)
if res and len(res) == 1:
out = res[0][0].lower()
return out
#def get_password(uid):
#"""Return password of the user uid. Return None in case
#the user is not found."""
#out = None
#res = run_sql("SELECT password FROM user WHERE id=%s", (uid,), 1)
#if res and res[0][0] != None:
#out = res[0][0]
#return out
def get_nickname(uid):
"""Return nickname of the user uid. Return None in case
the user is not found."""
out = None
res = run_sql("SELECT nickname FROM user WHERE id=%s", (uid,), 1)
if res and res[0][0]:
out = res[0][0]
return out
def get_nickname_or_email(uid):
"""Return nickname (preferred) or the email address of the user uid.
Return string 'guest' in case the user is not found."""
out = "guest"
res = run_sql("SELECT nickname, email FROM user WHERE id=%s", (uid,), 1)
if res and res[0]:
if res[0][0]:
out = res[0][0]
elif res[0][1]:
out = res[0][1].lower()
return out
def create_userinfobox_body(req, uid, language="en"):
"""Create user info box body for user UID in language LANGUAGE."""
if req:
if req.subprocess_env.has_key('HTTPS') \
and req.subprocess_env['HTTPS'] == 'on':
url_referer = CFG_SITE_SECURE_URL + req.unparsed_uri
else:
- url_referer = weburl + req.unparsed_uri
+ url_referer = CFG_SITE_URL + req.unparsed_uri
if '/youraccount/logout' in url_referer:
url_referer = ''
else:
- url_referer = weburl
+ url_referer = CFG_SITE_URL
try:
return tmpl.tmpl_create_userinfobox(ln=language,
url_referer=url_referer,
guest = isGuestUser(uid),
username = get_nickname_or_email(uid),
submitter = True, # FIXME isUserSubmitter(uid),
referee = True, # FIXME isUserReferee(req),
admin = True # FIXME isUserAdmin(req),
)
except OperationalError:
return ""
def list_registered_users():
"""List all registered users."""
return run_sql("SELECT id,email FROM user where email!=''")
def list_users_in_role(role):
"""List all users of a given role (see table accROLE)
@param role: role of user (string)
@return list of uids
"""
res = run_sql("""SELECT uacc.id_user
FROM user_accROLE uacc JOIN accROLE acc
ON uacc.id_accROLE=acc.id
WHERE acc.name=%s""",
(role,))
if res:
return map(lambda x: int(x[0]), res)
return []
def list_users_in_roles(role_list):
"""List all users of given roles (see table accROLE)
@param role_list: list of roles [string]
@return list of uids
"""
if not(type(role_list) is list or type(role_list) is tuple):
role_list = [role_list]
query = """SELECT DISTINCT(uacc.id_user)
FROM user_accROLE uacc JOIN accROLE acc
ON uacc.id_accROLE=acc.id
"""
query_addons = ""
query_params = ()
if len(role_list) > 0:
query_params = role_list
query_addons = " WHERE "
for role in role_list[:-1]:
query_addons += "acc.name=%s OR "
query_addons += "acc.name=%s"
res = run_sql(query + query_addons, query_params)
if res:
return map(lambda x: int(x[0]), res)
return []
def get_user_preferences(uid):
pref = run_sql("SELECT id, settings FROM user WHERE id=%s", (uid,))
if pref:
try:
return deserialize_via_marshal(pref[0][1])
except:
pass
return get_default_user_preferences() # empty dict mean no preferences
def set_user_preferences(uid, pref):
assert(type(pref) == type({}))
run_sql("UPDATE user SET settings=%s WHERE id=%s",
(serialize_via_marshal(pref), uid))
def get_default_user_preferences():
user_preference = {
'login_method': ''}
for system in CFG_EXTERNAL_AUTHENTICATION.keys():
if CFG_EXTERNAL_AUTHENTICATION[system][1]:
user_preference['login_method'] = system
break
return user_preference
def collect_user_info(req):
"""Given the mod_python request object rec or a uid it returns a dictionary
containing at least the keys uid, nickname, email, groups, plus any external keys in
the user preferences (collected at login time and built by the different
external authentication plugins) and if the mod_python request object is
provided, also the remote_ip, remote_host, referer, agent fields.
If the user is authenticated with Apache should provide also
apache_user and apache_group.
"""
user_info = {
'remote_ip' : '',
'remote_host' : '',
'referer' : '',
'uri' : '',
'agent' : '',
'apache_user' : '',
'apache_group' : [],
'uid' : -1,
'nickname' : '',
'email' : '',
'group' : [],
'guest' : '1'
}
try:
if req is None:
uid = -1
elif type(req) in (type(1), type(1L)):
## req is infact a user identification
uid = req
elif type(req) is dict:
## req is by mistake already a user_info
try:
assert(req.has_key('uid'))
assert(req.has_key('email'))
assert(req.has_key('nickname'))
except AssertionError:
## mmh... misuse of collect_user_info. Better warn the admin!
register_exception(alert_admin=True)
user_info.update(req)
return user_info
else:
uid = getUid(req)
user_info['remote_ip'] = gethostbyname(req.connection.remote_ip)
user_info['remote_host'] = req.connection.remote_host or ''
user_info['referer'] = req.headers_in.get('Referer', '')
user_info['uri'] = req.unparsed_uri or ''
user_info['agent'] = req.headers_in.get('User-Agent', 'N/A')
try:
user_info['apache_user'] = getApacheUser(req)
if user_info['apache_user']:
user_info['apache_group'] = auth_apache_user_in_groups(user_info['apache_user'])
except AttributeError:
pass
user_info['uid'] = uid
user_info['nickname'] = get_nickname(uid) or ''
user_info['email'] = get_email(uid) or ''
user_info['group'] = []
user_info['guest'] = str(isGuestUser(uid))
if uid:
user_info['group'] = [group[1] for group in get_groups(uid)]
prefs = get_user_preferences(uid)
if prefs:
for key, value in prefs.items():
user_info[key.lower()] = value
except Exception, e:
register_exception()
return user_info
## --- follow some functions for Apache user/group authentication
def auth_apache_user_p(user, password, apache_password_file=CFG_APACHE_PASSWORD_FILE):
"""Check whether user-supplied credentials correspond to valid
Apache password data file."""
try:
if not apache_password_file.startswith("/"):
apache_password_file = CFG_TMPDIR + "/" + apache_password_file
dummy, pipe_output = os.popen2(["grep", "^" + user + ":", apache_password_file], 'r')
line = pipe_output.readlines()[0]
password_apache = line.strip().split(":")[1]
except: # no pw found, so return not-allowed status
return False
salt = password_apache[:2]
return crypt.crypt(password, salt) == password_apache
def auth_apache_user_in_groups(user, apache_group_file=CFG_APACHE_GROUP_FILE):
"""Return list of Apache groups to which Apache user belong."""
out = []
try:
if not apache_group_file.startswith("/"):
apache_group_file = CFG_TMPDIR + "/" + apache_group_file
dummy, pipe_output = os.popen2(["grep", user, apache_group_file], 'r')
for line in pipe_output.readlines():
out.append(line.strip().split(":")[0])
except: # no groups found, so return empty list
pass
return out
def http_get_credentials(req):
if req.headers_in.has_key("Authorization"):
try:
s = req.headers_in["Authorization"][6:]
s = base64.decodestring(s)
user, passwd = s.split(":", 1)
except (ValueError, base64.binascii.Error, base64.binascii.Incomplete):
raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST
return (user, passwd)
return (None, None)
def http_check_credentials(req, role):
"""Retrieve Apache password and check user credential with the
check_auth function. If this function returns True check if the user
is enabled to the given role. If this is True, return, otherwise
popup a new apache login box.
"""
authorized = False
while True:
if req.headers_in.has_key("Authorization"):
try:
s = req.headers_in["Authorization"][6:]
s = base64.decodestring(s)
user, passwd = s.split(":", 1)
except (ValueError, base64.binascii.Error, base64.binascii.Incomplete):
raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST
authorized = auth_apache_user_p(user, passwd)
if authorized:
setApacheUser(req, user)
authorized = acc_firerole_check_user(collect_user_info(req), load_role_definition(acc_get_role_id(role)))
setApacheUser(req, '')
if not authorized:
# note that Opera supposedly doesn't like spaces around "=" below
s = 'Basic realm="%s"' % role
req.err_headers_out["WWW-Authenticate"] = s
raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED
else:
setApacheUser(req, user)
return
diff --git a/modules/webstat/doc/admin/webstat-admin-guide.webdoc b/modules/webstat/doc/admin/webstat-admin-guide.webdoc
index 0cc7c6343..33528ab87 100644
--- a/modules/webstat/doc/admin/webstat-admin-guide.webdoc
+++ b/modules/webstat/doc/admin/webstat-admin-guide.webdoc
@@ -1,193 +1,193 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebStat Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<pre>
CDS Invenio Web usage analyzer
==========================
Purpose:
--------
Walk through the Apache combined log and analyse the usage of the
site with respect to the searching habits, alert/basket pages, etc.
Note: Please analyze only moderately-sized logs, e.g. for a day or a week.
Usage:
------
$ webstat example_access_log
or specify your preferred Common Lisp implementation:
$ LISP=sbcl webstat example_access_log
Supported Common Lisp implementations:
-------------------------------------
CMUCL, SBCL, CLISP.
GCL doesn't work at the moment.
Use CMUCL for fastest results.
Configuration:
--------------
Edit the config file:
$ vim /opt/cds-invenio/etc/webstat/webstat.cfg
The file is self-explanatory.
Example output:
--------------
** APACHE LOG FILE ANALYSIS
Filename: example_access_log
Excluding search engine hits from (137.138.249.162).
** GENERAL STATS
There were 36109 website hits dating from 20041014 to 20041014.
This makes an average of 36109.0 website hits per day.
There were 1706 unique visitors in 1 days.
Visitor ............................................................ no. of hits
w1.x1.y1.z1 ............................................................... 1844
w2.x2.y2.z2 ............................................................... 1761
w3.x3.y3.z3 ............................................................... 1012
w4.x4.y4.z4 ................................................................ 913
w5.x5.y5.z5 ................................................................ 889
w6.x6.y6.z6 ................................................................ 562
w7.x7.y7.z7 ................................................................ 449
w8.x8.y8.z8 ................................................................ 373
w9.x9.y9.z9 ................................................................ 335
** SEARCH COLLECTIONS USAGE ANALYSIS
There were 9059 visits of search interface pages.
There were 8688 visits of non-Home search interface pages. ( 4.1%)
Collection ....................................................... no. of visits
CERN Document Server ....................................................... 371
Photos ..................................................................... 207
Articles & Preprints ....................................................... 149
HEP Institutes .............................................................. 98
Periodicals ................................................................. 87
Weekly Bulletin ............................................................. 85
Preprints ................................................................... 84
Theses ...................................................................... 80
CERN PhotoLab ............................................................... 78
CERN Yellow Reports ......................................................... 77
Books ....................................................................... 75
ATLAS Photos ................................................................ 72
ATLAS ....................................................................... 68
CERN Archives ............................................................... 68
Academic Training Lectures .................................................. 66
Internet Resources .......................................................... 65
Books & Proceedings ......................................................... 62
Published Articles .......................................................... 60
Summer Student Lectures ..................................................... 60
Standards ................................................................... 59
** SEARCH COLLECTIONS USAGE ANALYSIS
There were 5935 search engine hits.
There were 2586 searches originating from non-Home collections. (56.4%)
Originating collection ......................................... no. of searches
CERN Document Server ...................................................... 3349
News Articles .............................................................. 649
ATLAS eNews ................................................................ 481
Photos ..................................................................... 335
Weekly Bulletin ............................................................ 205
General Information ........................................................ 102
Articles & Preprints ........................................................ 85
CERN PhotoLab ............................................................... 76
CERN Yellow Reports ......................................................... 59
Official News ............................................................... 58
Training and Development .................................................... 50
Staff Association ........................................................... 45
Pension Fund ................................................................ 38
ATLAS Photos ................................................................ 36
CMS Photos .................................................................. 29
Books ....................................................................... 28
Preprints ................................................................... 21
Videos ...................................................................... 18
CERN Committee Documents .................................................... 17
Translation and Minutes ..................................................... 17
** SEARCH ENGINE QUERY PATTERN ANALYSIS
Found 8692 search engine hits.
First search engine hit log is dated 20041014.
Last search engine hit log is dated 20041014.
This makes an average of 8692.0 search engine hits per day.
There were 5424 simple searches out of 8692 search engine hits. (62.4%)
There were 1886 advanced searches out of 8692 search engine hits. (21.7%)
There were 1382 detailed record pages out of 8692 search engine hits. (15.9%)
There are 2523 different query patterns for 8692 search engine hits. (29.0%)
There are 169 empty query pattern searches out of 8692 search engine hits. (1.94%)
There are 1264 phrase searches out of 8692 search engine hits. (14.5%)
There are 263 phrase query patterns out of 2523 query patterns. (10.4%)
There are 1748 one-time event searches out of 8692 search engine hits. (20.1%)
There are 1748 one-time query patterns out of 2523 query patterns. (69.3%)
There are 4276 one-word searches out of 8692 search engine hits. (49.2%)
There are 1602 one-word query patterns out of 2523 query patterns. (63.5%)
There are 223 wildcard searches out of 8692 search engine hits. (2.57%)
There are 35 wildcard query patterns out of 2523 query patterns. (1.39%)
There are 1466 punctuation-like searches out of 8692 search engine hits. (16.9%)
There are 407 punctuation-like query patterns out of 2523 query patterns. (16.1%)
There are 2424 any-field query patterns out of 2523 query patterns. (96.1%)
User query .................................................. no. of occurrences
200409 ..................................................................... 432
........................................................................... 169
"42/2004" ................................................................... 64
"43/2004" ................................................................... 48
"40/2004" ................................................................... 46
internalnote:press internalnote:'Views*' .................................... 44
ALICE ....................................................................... 29
"27/2004" ................................................................... 28
0002235ATLATL ............................................................... 28
"36/2004" ................................................................... 26
"24/2004" ................................................................... 25
internet .................................................................... 24
intranet .................................................................... 24
0021317ADMBUL ............................................................... 23
internalnote:press internalnote:'physics diagrams*' ......................... 23
"19/2004" ................................................................... 22
"39/2004" ................................................................... 22
0005179UDCCER ............................................................... 22
"23/2004" ................................................................... 21
"38/2004" ................................................................... 21
** USER BASKETS STATS
There were 196 user basket page hits.
This makes an average of 196.0 user basket page hits per day.
There were 47 unique basket page users in 1 days.
There were 82 additions to baskets.
There were 114 displays of baskets, out of which 39 public baskets accesses.
** USER ALERTS STATS
There were 7 user alert page hits.
This makes an average of 7.0 user alert page hits per day.
There were 5 unique alert page users in 1 days.
There were 3 displays of user alerts.
There were 4 displays of user searches history.
</pre>
diff --git a/modules/webstat/lib/webstat_engine.py b/modules/webstat/lib/webstat_engine.py
index 33a791371..02a2b848f 100644
--- a/modules/webstat/lib/webstat_engine.py
+++ b/modules/webstat/lib/webstat_engine.py
@@ -1,660 +1,660 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
__lastupdated__ = "$Date$"
import calendar, commands, datetime, time, os, cPickle
-from invenio.config import CFG_TMPDIR, weburl
+from invenio.config import CFG_TMPDIR, CFG_SITE_URL
from invenio.urlutils import redirect_to_url
from invenio.search_engine import perform_request_search
from invenio.dbquery import run_sql
WEBSTAT_SESSION_LENGTH = 48*60*60 # seconds
WEBSTAT_GRAPH_TOKENS = '-=#+@$%&XOSKEHBC'
# KEY EVENT TREND SECTION
def get_keyevent_trend_collection_population(args):
"""
Returns the quantity of documents in CDS Invenio for
the given timestamp range.
@param args['collection']: A collection name
@type args['collection']: str
@param args['t_start']: Date and time of start point
@type args['t_start']: str
@param args['t_end']: Date and time of end point
@type args['t_end']: str
@param args['granularity']: Granularity of date and time
@type args['granularity']: str
@param args['t_format']: Date and time formatting string
@type args['t_format']: str
"""
# Collect list of timestamps of insertion in the specific collection
ids = perform_request_search(cc=args['collection'])
if len(ids) == 0:
return []
sql = ("SELECT creation_date FROM bibrec WHERE id IN %s ORDER BY " + \
"creation_date DESC") % str(ids).replace('[', '(').replace(']', ')')
action_dates = [x[0] for x in run_sql(sql)]
initial_quantity = run_sql("SELECT COUNT(id) FROM bibrec WHERE creation_date < '%s'" %
_to_datetime(args['t_start'], args['t_format']).isoformat())[0][0]
return _get_trend_from_actions(action_dates, initial_quantity,
args['t_start'], args['t_end'], args['granularity'], args['t_format'])
def get_keyevent_trend_search_frequency(args):
"""
Returns the number of searches (of any kind) carried out
during the given timestamp range.
@param args['t_start']: Date and time of start point
@type args['t_start']: str
@param args['t_end']: Date and time of end point
@type args['t_end']: str
@param args['granularity']: Granularity of date and time
@type args['granularity']: str
@param args['t_format']: Date and time formatting string
@type args['t_format']: str
"""
sql = "SELECT date FROM query INNER JOIN user_query ON id=id_query ORDER BY date DESC"
action_dates = [x[0] for x in run_sql(sql)]
return _get_trend_from_actions(action_dates, 0, args['t_start'], args['t_end'], args['granularity'], args['t_format'])
def get_keyevent_trend_search_type_distribution(args):
"""
Returns the number of searches carried out during the given
timestamp range, but also partion them by type Simple and
Advanced.
@param args['t_start']: Date and time of start point
@type args['t_start']: str
@param args['t_end']: Date and time of end point
@type args['t_end']: str
@param args['granularity']: Granularity of date and time
@type args['granularity']: str
@param args['t_format']: Date and time formatting string
@type args['t_format']: str
"""
# SQL to determine all simple searches:
sql = "SELECT date FROM query INNER JOIN user_query ON id=id_query \
WHERE urlargs LIKE '%p=%' ORDER BY date DESC"
simple = [x[0] for x in run_sql(sql)]
# SQL to determine all advanced searches:
sql = "SELECT date FROM query INNER JOIN user_query ON id=id_query \
WHERE urlargs LIKE '%as=1%' ORDER BY date DESC"
advanced = [x[0] for x in run_sql(sql)]
# Compute the trend for both types
s_trend = _get_trend_from_actions(simple, 0, args['t_start'], args['t_end'], args['granularity'], args['t_format'])
a_trend = _get_trend_from_actions(advanced, 0, args['t_start'], args['t_end'], args['granularity'], args['t_format'])
# Assemble, according to return type
return [(s_trend[i][0], (s_trend[i][1], a_trend[i][1])) for i in range(len(s_trend))]
def get_keyevent_trend_download_frequency(args):
"""
Returns the number of full text downloads carried out
during the given timestamp range.
@param args['t_start']: Date and time of start point
@type args['t_start']: str
@param args['t_end']: Date and time of end point
@type args['t_end']: str
@param args['granularity']: Granularity of date and time
@type args['granularity']: str
@param args['t_format']: Date and time formatting string
@type args['t_format']: str
"""
sql = "SELECT download_time FROM rnkDOWNLOADS ORDER BY download_time DESC"
actions = [x[0] for x in run_sql(sql)]
return _get_trend_from_actions(actions, 0, args['t_start'], args['t_end'], args['granularity'], args['t_format'])
# KEY EVENT SNAPSHOT SECTION
def get_keyevent_snapshot_uptime_cmd():
"""
A specific implementation of get_current_event().
@return: The std-out from the UNIX command 'uptime'.
@type: str
"""
return _run_cmd('uptime').strip().replace(' ', ' ')
def get_keyevent_snapshot_apache_processes():
"""
A specific implementation of get_current_event().
@return: The std-out from the UNIX command 'uptime'.
@type: str
"""
# The number of Apache processes (root+children)
return _run_cmd('ps -e | grep apache2 | grep -v grep | wc -l')
def get_keyevent_snapshot_bibsched_status():
"""
A specific implementation of get_current_event().
@return: Information about the number of tasks in the different status modes.
@type: [(str, int)]
"""
sql = "SELECT status, COUNT(status) FROM schTASK GROUP BY status"
return [(x[0], int(x[1])) for x in run_sql(sql)]
def get_keyevent_snapshot_sessions():
"""
A specific implementation of get_current_event().
@return: The current number of website visitors (guests, logged in)
@type: (int, int)
"""
# SQL to retrieve sessions in the Guests
sql = "SELECT COUNT(session_expiry) FROM session INNER JOIN user ON uid=id " + \
"WHERE email = '' AND " + \
"session_expiry-%d < unix_timestamp() AND " % WEBSTAT_SESSION_LENGTH + \
"unix_timestamp() < session_expiry"
guests = run_sql(sql)[0][0]
# SQL to retrieve sessions in the Logged in users
sql = "SELECT COUNT(session_expiry) FROM session INNER JOIN user ON uid=id " + \
"WHERE email <> '' AND " + \
"session_expiry-%d < unix_timestamp() AND " % WEBSTAT_SESSION_LENGTH + \
"unix_timestamp() < session_expiry"
logged_ins = run_sql(sql)[0][0]
# Assemble, according to return type
return (guests, logged_ins)
# CUSTOM EVENT SECTION
def get_customevent_trend(args):
"""
Returns trend data for a custom event over a give
timestamp range.
@param args['id']: The event id
@type args['id']: str
@param args['t_start']: Date and time of start point
@type args['t_start']: str
@param args['t_end']: Date and time of end point
@type args['t_end']: str
@param args['granularity']: Granularity of date and time
@type args['granularity']: str
@param args['t_format']: Date and time formatting string
@type args['t_format']: str
"""
sql = "SELECT creation_time FROM %s ORDER BY creation_time DESC" % get_customevent_table(args['id'])
dates = [x[0] for x in run_sql(sql)]
return _get_trend_from_actions(dates, 0, args['t_start'], args['t_end'], args['granularity'], args['t_format'])
def get_customevent_dump(args):
"""
Similar to a get_event_trend implemention, but NO refining aka frequency
handling is carried out what so ever. This is just a dump. A dump!
@param args['id']: The event id
@type args['id']: str
@param args['t_start']: Date and time of start point
@type args['t_start']: str
@param args['t_end']: Date and time of end point
@type args['t_end']: str
@param args['granularity']: Granularity of date and time
@type args['granularity']: str
@param args['t_format']: Date and time formatting string
@type args['t_format']: str
"""
# Mapping of event id and column names
event_cols = {}
run_sql("CREATE TEMPORARY TABLE staTEMP " + \
"(event VARCHAR(255), creation_time TIMESTAMP, arguments VARCHAR(255)) " + \
"SELECT '%s' event, creation_time, arguments FROM %s"
% (args['ids'][0], get_customevent_table(args['ids'][0])))
try:
event_cols[args['ids'][0]] = cPickle.loads(run_sql("SELECT cols FROM staEVENT WHERE id = '%s'" % args['ids'][0])[0][0])
except TypeError:
event_cols[args['ids'][0]] = ["Unnamed"]
for id in args['ids'][1:]:
tbl_name = get_customevent_table(id)
run_sql("INSERT INTO staTEMP SELECT '%s', creation_time, arguments FROM %s"
% (id, tbl_name))
try:
event_cols[id] = cPickle.loads(run_sql("SELECT cols FROM staEVENT WHERE id = '%s'" % id)[0][0])
except TypeError:
event_cols[id] = ["Unnamed"]
# Get a MySQL friendly date
lower = _to_datetime(args['t_start'], args['t_format']).isoformat()
upper = _to_datetime(args['t_end'], args['t_format']).isoformat()
sql = "SELECT event,creation_time,arguments FROM staTEMP WHERE creation_time > '%s' " % lower + \
"AND creation_time < '%s' ORDER BY creation_time DESC" % upper
output = []
for row in run_sql(sql):
temp = [row[0], row[1].strftime('%Y-%m-%d %H:%M:%S')]
if row[2] is not None:
arguments = cPickle.loads(row[2])
else:
arguments = [None]
arguments = ["%s: %s" % (event_cols[row[0]][i], arguments[i]) for i in range(len(arguments))]
temp.extend(arguments)
output.append(tuple(temp))
return output
def get_customevent_table(id):
"""
Helper function that for a certain event id retrives the corresponding
event table name.
"""
res = run_sql("SELECT CONCAT('staEVENT', number) FROM staEVENT WHERE id = '%s'" % id)
try:
return res[0][0]
except IndexError:
# No such event table
return None
def get_customevent_args(id):
"""
Helper function that for a certain event id retrives the corresponding
event argument (column) names.
"""
res = run_sql("SELECT arguments FROM staEVENT WHERE id = '%s'" % id)
try:
return cPickle.loads(res[0][0])
except IndexError:
# No such event table
return None
# GRAPHER
def create_graph_trend(trend, path, settings):
"""
Creates a graph representation out of data produced from get_event_trend.
@param trend: The trend data
@type trend: [(str, str|int|(str|int,...))]
@param path: Where to store the graph
@type path: str
@param settings: Dictionary of graph parameters
@type settings: dict
"""
# If no input, we don't bother about anything
if len(trend) == 0:
return
# If no filename is given, we'll assume STD-out format and ASCII.
if path == '':
settings["format"] = 'asciiart'
if settings["format"] == 'asciiart':
out = ""
if settings["multiple"] is not None:
# Tokens that will represent the different data sets (maximum 16 sets)
# Set index (=100) to the biggest of the histogram sums
index = max([sum(x[1]) for x in trend])
# Print legend box
out += "Legend: %s\n\n" % ", ".join(["%s (%s)" % x for x in zip(settings["multiple"], WEBSTAT_GRAPH_TOKENS)])
else:
index = max([x[1] for x in trend])
width = 82
# Figure out the max length of the xtics, in order to left align
xtic_max_len = max([len(_to_datetime(x[0]).strftime(settings["xtic_format"])) for x in trend])
for row in trend:
# Print the xtic
xtic = _to_datetime(row[0]).strftime(settings["xtic_format"])
out_row = xtic + ': ' + ' '*(xtic_max_len-len(xtic)) + '|'
try:
col_width = (1.0*width/index)
except ZeroDivisionError:
col_width = 0
if settings["multiple"] is not None:
# The second value of the row-tuple, represents the n values from the n data
# sets. Each set, will be represented by a different ASCII character, chosen
# from the randomized string 'WEBSTAT_GRAPH_TOKENS'. NOTE: Only up to 16 (len(WEBSTAT_GRAPH_TOKENS)) data
# sets are supported.
total = sum(row[1])
for i in range(len(row[1])):
col = row[1][i]
try:
out_row += WEBSTAT_GRAPH_TOKENS[i]*int(1.0*col*col_width)
except ZeroDivisionError:
break
if len([i for i in row[1] if type(i) is int and i > 0]) - 1 > 0:
out_row += out_row[-1]
else:
total = row[1]
try:
out_row += '-'*int(1.0*total*col_width)
except ZeroDivisionError:
break
# Print sentinel, and the total
out += out_row + '>' + ' '*(xtic_max_len+4+width-len(out_row)) + str(total) + '\n'
# Write to destination file
if path == '':
print out
else:
open(path, 'w').write(out)
elif settings["format"] == 'gnuplot':
import Gnuplot
g = Gnuplot.Gnuplot()
g('set style data linespoints')
g('set terminal png small')
g('set output "%s"' % path)
if settings["title"] != '':
g.title(settings["title"])
if settings["xlabel"] != '':
g.xlabel(settings["xlabel"])
if settings["ylabel"] != '':
g.ylabel(settings["ylabel"])
if settings["xtic_format"] != '':
xtics = 'set xtics ('
xtics += ', '.join(['"%s" %d' %
(_to_datetime(trend[i][0], '%Y-%m-%d \
%H:%M:%S').strftime(settings["xtic_format"]), i)
for i in range(len(trend))]) + ')'
g(xtics)
# If we have multiple data sets, we need to do some magic to make Gnuplot eat it,
# This is basically a matrix transposition, and the addition of index numbers.
if settings["multiple"] is not None:
cols = len(trend[0][1])
rows = len(trend)
plot_items = []
for col in range(cols):
data = []
for row in range(rows):
data.append([row, trend[row][1][col]])
plot_items.append(Gnuplot.PlotItems.Data(data, title=settings["multiple"][col]))
g.plot(*plot_items)
else:
g.plot([x[1] for x in trend])
def create_graph_dump(dump, path, settings):
"""
Creates a graph representation out of data produced from get_event_trend.
@param dump: The dump data
@type dump: [(str|int,...)]
@param path: Where to store the graph
@type path: str
@param graph_settings: Dictionary of graph parameters
@type graph_settings: dict
"""
out = ""
if len(dump) == 0:
out += "No actions for this custom event are registered in the given time range."
else:
# Make every row in dump equally long, insert None if appropriate.
max_len = max([len(x) for x in dump])
events = [tuple(list(x) + [None]*(max_len-len(x))) for x in dump]
cols = ["Event", "Date and time"] + ["Argument %d" % i for i in range(max_len-2)]
column_widths = [max([len(str(x[i])) for x in events + cols])+3 for i in range(len(events[0]))]
for i in range(len(cols)):
out += cols[i] + ' '*(column_widths[i] - len(cols[i]))
out += "\n"
for i in range(len(cols)):
out += '='*(len(cols[i])) + ' '*(column_widths[i] - len(cols[i]))
out += "\n\n"
for action in dump:
for i in range(len(action)):
if action[i] is None:
temp = ''
else:
temp = action[i]
out += str(temp) + ' '*(column_widths[i] - len(str(temp)))
out += "\n"
# Write to destination file
if path == '':
print out
else:
open(path, 'w').write(out)
# EXPORTER
def export_to_python(data, req):
"""
Exports the data to Python code.
@param data: The Python data that should be exported
@type data: []
@param req: The Apache request object
@type req:
"""
_export("text/x-python", str(data), req)
def export_to_csv(data, req):
"""
Exports the data to CSV.
@param data: The Python data that should be exported
@type data: []
@param req: The Apache request object
@type req:
"""
csv_list = [""""%s",%s""" % (x[0], ",".join([str(y) for y in ((type(x[1]) is tuple) and x[1] or (x[1],))])) for x in data]
_export('text/csv', '\n'.join(csv_list), req)
# INTERNAL
def _export(mime, content, req):
"""
Helper function to pass on the export call. Create a
temporary file in which the content is stored, then let
redirect to the export web interface.
"""
filename = CFG_TMPDIR + "/webstat_export_" + str(time.time()).replace('.', '')
open(filename, 'w').write(content)
- redirect_to_url(req, '%s/stats/export?filename=%s&mime=%s' % (weburl, os.path.basename(filename), mime))
+ redirect_to_url(req, '%s/stats/export?filename=%s&mime=%s' % (CFG_SITE_URL, os.path.basename(filename), mime))
def _get_trend_from_actions(action_dates, initial_value, t_start, t_end, granularity, format):
"""
Given a list of dates reflecting some sort of action/event, and some additional parameters,
an internal data format is returned. 'initial_value' set to zero, means that the frequency
will not be accumulative, but rather non-causal.
@param action_dates: A list of dates, indicating some sort of action/event.
@type action_dates: [datetime.datetime]
@param initial_value: The numerical offset the first action's value should make use of.
@type initial_value: int
@param t_start: Start time for the time domain in format %Y-%m-%d %H:%M:%S
@type t_start: str
@param t_stop: End time for the time domain in format %Y-%m-%d %H:%M:%S
@type t_stop: str
@param granularity: The granularity of the time domain, span between values.
Possible values are [year,month,day,hour,minute,second].
@type granularity: str
@param format: Format of the 't_start' and 't_stop' parameters
@type format: str
@return: A list of tuples zipping a time-domain and a value-domain
@type: [(str, int)]
"""
# Append the maximum date as a sentinel indicating we're done
action_dates.insert(0, datetime.datetime.max)
# Create an iterator running from the first day of activity
dt_iter = _get_datetime_iter(t_start, granularity, format)
# Construct the datetime tuple for the stop time
stop_at = _to_datetime(t_end, format) - datetime.timedelta(seconds=1)
# If our t_start is more recent than the initial action_dates, we need to
# drop those.
t_start_dt = _to_datetime(t_start, format)
while action_dates[-1] < t_start_dt:
action_dates = action_dates[:-1]
vector = [(None, initial_value)]
old = dt_iter.next()
upcoming_action = action_dates.pop()
for current in dt_iter:
# Counter of action_dates in the current span, set the initial value to
# zero to avoid accumlation.
if initial_value != 0:
actions_here = vector[-1][1]
else:
actions_here = 0
# Check to see if there's an action date in the current span
while old <= upcoming_action < current:
actions_here += 1
try:
upcoming_action = action_dates.pop()
except IndexError:
upcoming_action = datetime.datetime.max
vector.append((old.strftime('%Y-%m-%d %H:%M:%S'), actions_here))
old = current
# Make sure to stop the iteration at the end time
if current > stop_at:
break
# Remove the first bogus tuple, and return
return vector[1:]
def _get_datetime_iter(t_start, granularity='day', format='%Y-%m-%d %H:%M:%S'):
"""
Returns an iterator over datetime elements starting at an arbitrary time,
with granularity of a [year,month,day,hour,minute,second].
@param t_start: An arbitrary starting time in format %Y-%m-%d %H:%M:%S
@type t_start: str
@param granularity: The span between iterable elements, default is 'days'.
Possible values are [year,month,day,hour,minute,second].
@type granularity: str
@param format: Format of the 't_start' parameter
@type format: str
@return: An iterator of points in time
@type: iterator over datetime elements
"""
t = _to_datetime(t_start, format)
# Make a time increment depending on the granularity and the current time
# (the length of years and months vary over time)
span = ""
while True:
yield t
if granularity == "year":
span = (calendar.isleap(t.year) and ["days=366"] or ["days=365"])[0]
elif granularity == "month":
span = "days=" + str(calendar.monthrange(t.year, t.month)[1])
elif granularity == "day":
span = "days=1"
elif granularity == "hour":
span = "hours=1"
elif granularity == "minute":
span = "minutes=1"
elif granularity == "second":
span = "seconds=1"
else:
# Default just in case
span = "days=1"
t += eval("datetime.timedelta(" + span + ")")
def _to_datetime(dt, format='%Y-%m-%d %H:%M:%S'):
return datetime.datetime(*time.strptime(dt, format)[:6])
def _run_cmd(command):
"""
Runs a certain command and returns the string output. If the command is
not found a string saying so will be returned. Use with caution!
@param command: The UNIX command to execute.
@type command: str
@return: The std-out from the command.
@type: str
"""
return commands.getoutput(command)
diff --git a/modules/webstat/lib/webstat_templates.py b/modules/webstat/lib/webstat_templates.py
index e866176bc..530b1b82a 100644
--- a/modules/webstat/lib/webstat_templates.py
+++ b/modules/webstat/lib/webstat_templates.py
@@ -1,279 +1,279 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
__lastupdated__ = "$Date$"
-from invenio.config import CFG_BINDIR, CFG_WEBDIR, weburl
+from invenio.config import CFG_BINDIR, CFG_WEBDIR, CFG_SITE_URL
class Template:
def tmpl_welcome(self, ):
"""
Generates a welcome page for the Webstat module.
"""
return """<p>On these pages, you can review measurements of CDS Invenio usage
and performance. Output is available in several formats, and its
raw data can be exported for offline processing. Further on, a general
overview is presented below under the label Current System Health.</p>"""
def tmpl_system_health(self, health_statistics):
"""
Generates a box with current information from the system providing the administrator
an easy way of overlooking the 'health', i.e. the current performance/efficency, of
the system.
"""
out = """<h3>Current system health</h3>"""
temp_out = ""
for statistic in health_statistics:
if statistic is None:
temp_out += '\n'
elif statistic[1] is None:
temp_out += statistic[0] + '\n'
else:
temp_out += statistic[0] + \
'.'*(85 - len(str(statistic[0])) - len(str(statistic[1]))) + \
str(statistic[1]) + '\n'
out += "<pre>" + temp_out + "</pre>"
return out
def tmpl_keyevent_list(self):
"""
Generates a list of available key statistics.
"""
return """<h3>Key statistics</h3>
<p>Please choose a statistic from below to review it in detail.</p>
<ul>
<li><a href="%s/stats/collection_population">Collection population</a></li>
<li><a href="%s/stats/search_frequency">Search frequency</a></li>
<li><a href="%s/stats/search_type_distribution">Search type distribution</a></li>
<li><a href="%s/stats/download_frequency">Download frequency</a></li>
- </ul>""" % ((weburl,)*4)
+ </ul>""" % ((CFG_SITE_URL,)*4)
def tmpl_customevent_list(self, customevents):
"""
Generates a list of available custom statistics.
"""
out = """<h3>Custom events</h3>
<p>The Webstat module supplies a mean for the administrators of CDS Invenio
to define their own custom events, more abstract than the Key Statistics above.
A technical walk-through how to create these, is available <a href="%s/stats/customevent_help">here</a>.
When a custom event has been made available, it is displayed below.</p>
- """ % weburl
+ """ % CFG_SITE_URL
temp_out = ""
for event in customevents:
temp_out += """<li><a href="%s/stats/customevent?ids=%s">%s</a></li>""" \
- % (weburl, event[0], (event[1] is None) and event[0] or event[1])
+ % (CFG_SITE_URL, event[0], (event[1] is None) and event[0] or event[1])
if len(customevents) == 0:
out += self.tmpl_error("There are currently no custom events available.")
else:
out += "<ul>" + temp_out + "</ul>"
return out
def tmpl_customevent_help(self):
"""
Display help for custom events.
"""
return """<h3>General overview</h3>
<p>A custom event is a measure indicating the frequency of some kind of
"action", such as e.g. the number of advanced searches carried out using
the Swedish language interface. The custom event functionality is intended
to give administrators a mean to log abstract activity, as opposed to
trivial measurements like "collection population" and "search frequency".
Thus, a custom event is fully customizable and defined by an administrator
but it is important to understand that the Webstat module merely supplies
the mean to register an action and associate it with a predefined custom event,
while the actual use case leading up to the very registration of the action
is left to the user.</p>
<p>After a custom event has been created and the process of collecting data
has started, the event is accessible for review through the Webstat webpage.</p>
<h3>How to create a new custom event</h3>
<p>The listing below reflects the most basic usage. See
<strong>%(bindir)s/webstatadmin --help</strong> for more options.</p>
<ol>
<li>Execute the following command, where ID is the unique name you want to use
when referring to the event:
<pre>%(bindir)s/webstatadmin -n ID</pre>
</li>
<li>If the command completed succesfully, output similar to the following
is generated:
<pre>$ %(bindir)s/webstatadmin -n test
Event table [staEVENT01] succesfully created.
Please use event id [test] when registering an event.</pre>
</li>
<li>You can now use the code snippet:
<pre>from invenio.webstat import register_customevent
register_customevent('test') </pre>
from anywhere in your CDS Invenio sources in order to start logging
the custom event 'test' using the use case logic of your choice!
</li>
</ol>""" % { "bindir": CFG_BINDIR, }
def tmpl_error(self, msg):
"""
Provides a common way of outputting error messages.
"""
return """<div class="important">%s</div>""" % msg
def tmpl_event_box(self, options, order, choosed):
"""
Generates a FORM box with dropdowns for events.
@param options: { parameter name: [(argument internal, argument full)]}
@type options: { str: [(str, str)]}
@param order: A permutation of the keys in options, for design purpose.
@type order: [str]
@param options: The selected parameters, and its values.
@type options: { str: str }
"""
# Create the FORM's header
formheader = """<form method="get">"""
# Create the headers using the options permutation
headers = [options[param][0] for param in order]
# Create all SELECT boxes
sels = [self._tmpl_select_box(options[param][1], # SELECT box data
" - select " + options[param][0], # first item info
param, # name
choosed[param], # selected value (perhaps several)
type(choosed[param]) is list) # multiple box?
for param in order]
# Create all buttons
buttons = []
buttons.append("""<input class="formbutton" type="submit" name="action_gen" value="Generate">""")
return self._tmpl_box(formheader, headers, sels, buttons)
def tmpl_display_event_trend_ascii(self, title, filename):
"""Displays an image graph representing a trend"""
return self.tmpl_display_trend(title, "<div><pre>%s</pre></div>" % open(filename, 'r').read())
def tmpl_display_event_trend_image(self, title, filename):
"""Displays a ASCII graph represnting a trend"""
- return self.tmpl_display_trend(title, """<div><img src="%s" /></div>""" % filename.replace(CFG_WEBDIR, weburl))
+ return self.tmpl_display_trend(title, """<div><img src="%s" /></div>""" % filename.replace(CFG_WEBDIR, CFG_SITE_URL))
# INTERNALS
def tmpl_display_trend(self, title, html):
"""
Generates a generic display box for showing graphs (ASCII and IMGs)
alongside to some metainformational boxes.
"""
return """<table class="narrowsearchbox">
<thead><tr><th colspan="2" class="narrowsearchboxheader" align="left">%s</th></tr></thead>
<tbody><tr><td class="narrowsearchboxbody" valign="top">%s</td></tr></tbody>
</table> """ % (title, html)
def _tmpl_box(self, formheader, headers, selectboxes, buttons):
"""
Aggregates together the parameters in order to generate the
corresponding box.
@param formheader: Start tag for the FORM element.
@type formheader: str
@param headers: Headers for the SELECT boxes
@type headers: list<str>
@param selectboxes: The actual HTML drop-down boxes, with appropriate content.
@type selectboxes: list<str>
@param buttons: Buttons to attach to the FORM.
@type buttons: list<str>
@return: HTML describing a particular FORM box.
@type: str
"""
out = formheader + """<table class="searchbox"><thead><tr>"""
# Zip together the lists to achieve symmetry (some items might be discarded!)
combined = zip(headers, selectboxes)
# Append the headers
for header in [x[0] for x in combined]:
if header == combined[-1][0]:
colspan = ("""colspan="%i" """ % (len(buttons)+1)).strip()
else:
colspan = ""
out += """<th %s class="searchboxheader">%s</th>""" % (colspan, header)
out += """</tr></thead><tbody><tr valign="bottom">"""
# Append the SELECT boxes
for selectbox in [x[1] for x in combined]:
out += """<td class="searchboxbody" valign="top">%s</td>""" % selectbox
# Append all the buttons in a row
out += """<td class="searchboxbody" valign="top" align="left">""" + "".join(buttons)
out += "</td></tr></tbody></table></form>"
return out
def _tmpl_select_box(self, iterable, explaination, name, preselected, multiple=False):
"""
Generates a HTML SELECT drop-down menu.
@param iterable: A list of values and tag content to be used in the SELECT list
@type iterable: [(str, str)]
@param explaination: An explainatory string put as the tag content for the first OPTION.
@type explaination: str
@param name: The name of the SELECT tag. Important for FORM-parsing.
@type name: str
@param preselected: The value, or list of values, of the OPTION that should be
preselected. Blank or empty list for none.
@type preselected: str | []
@param multiple: Optionally sets the SELECT box to accept multiple entries.
@type multiple: bool
"""
sel = """<select name="%s">""" % name
if multiple is True:
sel = sel.replace("<select ", """<select multiple="multiple" size="5" """)
else:
sel += """<option value="">%s</option>""" % explaination
for realname, printname in [(x[0], x[1]) for x in iterable]:
if printname is None:
printname = realname
option = """<option value="%s">%s</option>""" % (realname, printname)
if realname == preselected or (type(preselected) is list and realname in preselected):
option = option.replace('">', '" selected="selected">')
sel += option
return sel + "</select>"
diff --git a/modules/webstat/lib/webstat_webinterface.py b/modules/webstat/lib/webstat_webinterface.py
index 57cc27352..093fb0811 100644
--- a/modules/webstat/lib/webstat_webinterface.py
+++ b/modules/webstat/lib/webstat_webinterface.py
@@ -1,174 +1,174 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
__lastupdated__ = "$Date$"
import os
-from invenio.config import CFG_TMPDIR, weburl, CFG_SITE_NAME
+from invenio.config import CFG_TMPDIR, CFG_SITE_URL, CFG_SITE_NAME
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.webpage import page
from invenio import template
from invenio.webstat import perform_request_index
from invenio.webstat import perform_display_keyevent
from invenio.webstat import perform_display_customevent
from invenio.webstat import perform_display_customevent_help
def detect_suitable_graph_format():
"""
Return suitable graph format default argument: gnuplot if it is
present, otherwise asciiart.
"""
try:
import Gnuplot
suitable_graph_format = "gnuplot"
except ImportError:
suitable_graph_format = "asciiart"
return suitable_graph_format
SUITABLE_GRAPH_FORMAT = detect_suitable_graph_format()
class WebInterfaceStatsPages(WebInterfaceDirectory):
"""Defines the set of stats pages."""
_exports = [ '',
'collection_population', 'search_frequency', 'search_type_distribution',
'download_frequency', 'customevent', 'customevent_help',
'export' ]
def index(self, req, _):
"""Index page."""
return page(title="Statistics",
body=perform_request_index(),
description="CDS, Statistics",
keywords="CDS, statistics",
req=req,
lastupdated=__lastupdated__,
navmenuid='stats')
# KEY EVENT SECTION
def collection_population(self, req, form):
"""Collection population statistics page."""
argd = wash_urlargd(form, {'collection': (str, CFG_SITE_NAME),
'timespan': (str, "today"),
'format': (str, SUITABLE_GRAPH_FORMAT)})
return page(title="Collection population",
body=perform_display_keyevent('collection population', argd, req),
- navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % weburl,
+ navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % CFG_SITE_URL,
description="CDS, Statistics, Collection population",
keywords="CDS, statistics, collection population",
req=req,
lastupdated=__lastupdated__,
navmenuid='collection population')
def search_frequency(self, req, form):
"""Search frequency statistics page."""
argd = wash_urlargd(form, {'timespan': (str, "today"),
'format': (str, SUITABLE_GRAPH_FORMAT)})
return page(title="Search frequency",
body=perform_display_keyevent('search frequency', argd, req),
- navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % weburl,
+ navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % CFG_SITE_URL,
description="CDS, Statistics, Search frequency",
keywords="CDS, statistics, search frequency",
req=req,
lastupdated=__lastupdated__,
navmenuid='search frequency')
def search_type_distribution(self, req, form):
"""Search type distribution statistics page."""
argd = wash_urlargd(form, {'timespan': (str, "today"),
'format': (str, SUITABLE_GRAPH_FORMAT)})
return page(title="Search type distribution",
body=perform_display_keyevent('search type distribution', argd, req),
- navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % weburl,
+ navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % CFG_SITE_URL,
description="CDS, Statistics, Search type distribution",
keywords="CDS, statistics, search type distribution",
req=req,
lastupdated=__lastupdated__,
navmenuid='search type distribution')
def download_frequency(self, req, form):
"""Download frequency statistics page."""
argd = wash_urlargd(form, {'timespan': (str, "today"),
'format': (str, SUITABLE_GRAPH_FORMAT)})
return page(title="Download frequency",
body=perform_display_keyevent('download frequency', argd, req),
- navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % weburl,
+ navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % CFG_SITE_URL,
description="CDS, Statistics, Download frequency",
keywords="CDS, statistics, download frequency",
req=req,
lastupdated=__lastupdated__,
navmenuid='download frequency')
# CUSTOM EVENT SECTION
def customevent(self, req, form):
"""Custom event statistics page"""
argd = wash_urlargd(form, {'ids': (list, []),
'timespan': (str, ""),
'format': (str, SUITABLE_GRAPH_FORMAT)})
return page(title="Custom event",
body=perform_display_customevent(argd['ids'], argd, req=req),
- navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % weburl,
+ navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % CFG_SITE_URL,
description="CDS Personalize, Statistics, Custom event",
keywords="CDS, statistics, custom event",
req=req,
lastupdated=__lastupdated__,
navmenuid='custom event')
def customevent_help(self, req, form):
"""Custom event help page"""
return page(title="Custom event help",
body=perform_display_customevent_help(),
- navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % weburl,
+ navtrail="""<a class="navtrail" href="%s/stats">Statistics</a>""" % CFG_SITE_URL,
description="CDS Personalize, Statistics, Custom event help",
keywords="CDS, statistics, custom event help",
req=req,
lastupdated=__lastupdated__,
navmenuid='custom event help')
# EXPORT SECTION
def export(self, req, form):
"""Exports data"""
argd = wash_urlargd(form, {"filename": (str, ""),
"mime": (str, "")})
# Check that the particular file exists and that it's OK to export
webstat_files = [x for x in os.listdir(CFG_TMPDIR) if x.startswith("webstat")]
if argd["filename"] not in webstat_files:
return "Bad file."
# Set correct header type
req.content_type = argd["mime"]
req.send_http_header()
# Rebuild path, send it to the user, and clean up.
filename = CFG_TMPDIR + '/' + argd["filename"]
req.sendfile(filename)
os.remove(filename)
diff --git a/modules/webstyle/doc/admin/webstyle-admin-guide.webdoc b/modules/webstyle/doc/admin/webstyle-admin-guide.webdoc
index 806cddf47..794c070c2 100644
--- a/modules/webstyle/doc/admin/webstyle-admin-guide.webdoc
+++ b/modules/webstyle/doc/admin/webstyle-admin-guide.webdoc
@@ -1,474 +1,474 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: WebStyle Admin Guide -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<h2>Contents</h2>
<ul style="list-style-type:None">
<li><strong>1. <a href="#overview_page_layout">Overview</a></strong>
<ul style="list-style-type:None">
<li>1.1&nbsp;&nbsp;<a href="#overview_css">CSS Style Sheet and Images</a></li>
<li>1.2&nbsp;&nbsp;<a href="#overview_page_layout">HTML Page Layout</a>
<ul style="list-style-type:None">
<li>1.2.1&nbsp;&nbsp;&nbsp;&nbsp;<a href="#overview_page_layout_stat">Layout of HTML Static Pages</a></li>
<li>1.2.3&nbsp;&nbsp;&nbsp;&nbsp;<a href="#overview_page_layout_dyn">Layout of Python Dynamic Pages</a></li>
</ul>
</li>
<li>1.3&nbsp;&nbsp;<a href="#overview_bib">Look of Bibliographic References</a></li>
<li>1.4&nbsp;&nbsp;<a href="#overview_spec_conf">Specific Configurations</a></li>
</ul>
</li>
<li><strong>2. <a href="#det_page">Detailed Record Pages</a></strong>
<ul style="list-style-type:None">
<li>2.1&nbsp;&nbsp;<a href="#det_page">Available tabs</a></li>
<li>2.2&nbsp;&nbsp;<a href="#det_show_hide_tabs">Showing/Hiding tabs</a></li>
<li>2.3&nbsp;&nbsp;<a href="#det_page_cust_cont_tabs">Customizing content of tabs</a></li>
<li>2.4&nbsp;&nbsp;<a href="#det_page_cust_look_tabs">Customizing look of tabs</a></li>
</ul>
</li>
</ul>
<h2><a name="overview">1. Overview</a></h2>
<p>This document describes how to change the look and feel of your CDS
Invenio installation.
<h3><a name="overview_css">1.1 CSS Style Sheet and Images</a></h3>
<p>The most obvious modification you may want to do is the
-modification of <a href="<WEBURL>/img/cds.css">CSS style sheet</a>.
-You may also customize default <a href="<WEBURL>/img/">images</a>.</p>
+modification of <a href="<CFG_SITE_URL>/img/cds.css">CSS style sheet</a>.
+You may also customize default <a href="<CFG_SITE_URL>/img/">images</a>.</p>
<h3><a name="overview_page_layout">1.2 HTML Page Layout</a></h3>
<p>The customization of the general page look and feel is currently
different depending on whether you customize HTML-like static pages
or dynamic Python pages.</p>
<p>Dynamic HTML pages are used to build the 'interactive' parts of the
website, such as the search and browse pages, as well as the admin
pages. The content of these pages is defined at run time, depending on
the users parameters. You can modify them to provide a totally
different experience to your users when browsing <CFG_SITE_NAME>. Most
probably, you will only want to customize 'webstyle_templates.py',
which define the headers and footer of a page.</p>
<p>Static HTML pages are used for basic pages that do not embed
dynamic content, such as this guide. They help reducing the load of
the server and speed up pages serving. As you will see, even HTML
static pages can still contain some values defined at run time, but
these are reduced to the minimum compared to dynamic pages. Most
probably you will not want to modify these pages, since the small
amount of dynamic content they have allow these pages to inherit from
the customizations you have made elsewhere, such as in the CSS, and the
page header and footer.</p>
<h4><a name="overview_page_layout_stat">1.2.1 Layout of HTML Static Pages</a></h4>
<p>Static HTML pages are all located in the /opt/cds-invenio/lib/webdoc/
installation directory. These files are organized in 3 directories:</p>
<ul>
<li><b>help:</b> Help pages available to users of your website</li>
<li><b>admin:</b> Mostly guides for admin users</li>
<li><b>hacking:</b> Mostly guides for administrators and developers</li>
</ul>
<p>
These directories do not contain the <code>.html</code> files, but
<code>.webdoc</code> files. These '<em>WebDoc</em>' files are
basically HTML with the following main differences:
</p>
<ul>
<li>They only contain the body of your page (only content of the
<code>&lt;body&gt;</code> tag)
</li>
<li>You can make use of special tags such as
- <code>&lt;WEBURL&gt;</code> and <code>&lt;CFG_SITE_SUPPORT_EMAIL&gt;</code>,
- that will be replaced by <WEBURL> and <CFG_SITE_SUPPORT_EMAIL>, the values that
+ <code>&lt;CFG_SITE_URL&gt;</code> and <code>&lt;CFG_SITE_SUPPORT_EMAIL&gt;</code>,
+ that will be replaced by <CFG_SITE_URL> and <CFG_SITE_SUPPORT_EMAIL>, the values that
you should have configured in your <code>config.py</code> file.
</li>
<li>You can internationalize the content. For example:
<pre>
&lt;lang&gt;
&lt;en&gt;Book&lt;/en&gt;
&lt;fr&gt;Livre&lt;/fr&gt;
&lt;de&gt;Buch&lt;/de&gt;
&lt;/lang&gt;
</pre>
will be replaced by "Book" if the user has chosen to display the English version of your site,
"Livre" in French or "Buch" in German.
</li>
</ul>
<p>Read the (FIXME) to learn more about details of the WebDoc syntax</p>
<p>The advantage of not using raw HTML for these static pages is that
they can for example reuse the header and footer that you have defined
for dynamic pages, and make use of the variables you have defined in
the config.py file. In that way you should not need to adapt them to
your needs: <b>they</b> will adapt themselves to your needs.</p>
<p>Any modification should be immediatly visible when looking at the
pages from the web: the pages are built "dynamically" and then cached.
If you want to force the cache of the pages, use the WebDoc utility.
Read (FIXME) to learn more about it.</p>
<h4><a name="overview_page_layout_dyn">1.2.2 Layout of Python Dynamic Pages</a></h4>
<p>The dynamic Python-powered pages can be customized by making use of
CDS Invenio templating system that uses a notion of a template skin.
How this works?</p>
<p>During the compile time, when you edit <code>config.py</code>, you
may choose to use your own templates instead of the provided default
ones by editing
<code>CFG_WEBSTYLE_TEMPLATE_SKIN</code> variable. Let us say you
put <code>ithaca</code> there in order to use your
own <code>ithaca</code> style. Now, when you start Apache, then
instead of CDS Invenio's usual template files such
as <code>webbasket_templates.py</code> the system will look for file
named <code>webbasket_templates_ithaca.py</code> and will load the
template functions from there instead, provided that they exist.
(Otherwise it would fall back to the default ones.)</p>
<p>How do you create such an <code>ithaca</code> style templates file?
We do not use one of many existing templating frameworks in Python but
a very simple programmer-friendly templating system that enables you
to use the full power of Python to inherit from the default templates
the output generating functions you want to reuse and to write anew
only the functions you would like to modify.</p>
<p>Let's show an example of how to modify the page footer. Create a
file named <code>webstyle_templates_ithaca.py</code> with the
following content:</p>
<blockquote>
<pre>
from invenio.config import CFG_SITE_LANG
from invenio.webstyle_templates import Template as DefaultTemplate
class Template(DefaultTemplate):
"""Ithaca style templates."""
def tmpl_pagefooter(self, req=None, ln=CFG_SITE_LANG, lastupdated=None,
pagefooteradd=""):
"""
Ithaca style page footer. See the default function for
the meaning of parameters.
"""
return "&lt;hr>This site has no footer."
</pre>
</blockquote>
<p>After the file was created, restart Apache and lo, your new ithaca
style footer will be seen in action.</p>
<p>Some further remarks on this templating system:</p>
<ul>
<li>We have observed that in practice the HTML page designers were
ofter Python programmers, therefore we have adopted a
programmer-friendly templating system.</li>
<li>You have to know a bit of Python in order to use it. If you don't
know Python, do not worry, because you can basically copy and
paste the original <code>tmpl_foo()</code> function definition "as
is" into the above-cited example and then you would only modify
its HTML snippets. The important thing is to preserve the imports
(<code>from invenio.config import CFG_SITE_LANG</code>) as in the original
<code>webstyle_templates.py</code> file and to preserve the
leading whitespace Pythonic indentation.
<li>You do not have to learn "yet another templating language", you
can use the full power of Python. The <code>tmpl_foo()</code>
functions do not contain any business logic in them, their only
purpose is to make the HTML presentation of data supplied to them.
But, should you need to carry out a little data transformation,
you can do it within the <code>tmpl_foo()</code> function itself,
thanks to the full Python power.</li>
<li>If you feel like doing so, you can modify all
the <code>tmpl_foo()</code> functions across all CDS Invenio
modules in a way that will completely change the presentation of
elements including their content, position and order on the
screen.</li>
<li>In practice, it is sufficient to modify the CSS and the
webstyle_templates_ithaca.py (and possibly
websearch_templates_ithaca.py) files to achieve most important
customizations.</li>
<li>We expect to provide possibly more than one skin with the default
distribution, so if you have modified CDS Invenio look and feel in
an interesting way, please consider donating us your templates.</li>
<li>When upgrading from one CDS Invenio release to another, you may
find out that the default templates have changed in a way that
requires changes to your templates (such as an addition of
parameters to cover the new functionality). This is inevitable in
any templating system; unless you introduce new parameters, you
would not see them being printed. Therefore, if you have
modified <code>tmpl_foo()</code> and <code>tmpl_bar()</code>, and
you are ugrading to a new release, you may at least briefly check
whether the function arguments are the same. A quick check of the
body would be helpful too, in case the new release fixed some
display-related problems in these functions.
<br/><br/>
In order to help you in this task, we provide a tool to check
incompatibilities between your customized templates and the default
templates.<br/>
This tool can be run before doing a <code>'make install'</code>,
therefore giving you a chance to fix your templates before
upgrading. Just run <code>'make check-custom-templates'</code> to
get the list of problems found with your templates. <br /><br />
You can also run this tool any time after the new default
templates have been installed, in order to ensure that
modifications you have done to your templates are valid. To do so
move to your CDS Invenio installation directory, and run: <br/>
<code>$python /opt/cds-invenio/lib/python/invenio/template.py
--check-custom-templates</code>
</li>
</ul>
<h3><a name="overview_bib">1.3 Look of Bibliographic
References</a></h3>
<p>Bibliographic metadata is formatted using
<a href="bibformat-admin">BibFormat</a>. Read the
<a href="bibformat-admin-guide">BibFormat documentation</a>
for more information.</p>
<h3><a name="overview_spec_conf">1.4 Specific Configurations</a></h3>
<p>Note that the search interface pages may be modified to a large
extent in the <a href="websearch-admin">WebSearch Admin
Interface</a> by adding HTML portalboxes on various places on the page
(right top, before/after page title, before/after narrow by collection
boxes, etc).</p>
<h2><a name="det_page">2. Detailed Record Pages</a></h2>
<p>The web pages displaying the details of a record (such as
-<a href="<WEBURL>/record/1"><WEBURL>/record/1</a>) do not only show metadata, but
+<a href="<CFG_SITE_URL>/record/1"><CFG_SITE_URL>/record/1</a>) do not only show metadata, but
also users' comments and reviews, statistics, etc. This information is
organized into tabs.</p>
<p>The content of these tabs can be customized on a collection basis. It
is also possible to show/hide tabs depending on the displayed
collection.</p>
<p>The detailed record pages also feature a mini panel at the bottom of
the page that links to popular functions (The mini panel is only
displayed when <em>Information</em> tab is selected).</p>
<pre>
+--------------Detailed record page-------------+
| header |
|nav. breadcrumb |
| |
| .--------------------------------------. |
| .-|Info.|Ref.|Discussion.|Stats.|Fulltext|-. |
| | '--------------------------------------' | |
| | | |
| | content | |
| | | |
| '------------------------------------------' |
| |
| .---------------(Mini Panel)---------------. |
| | Mini | Mini | Mini | |
| | File | Review | Actions | |
| '------------------------------------------' |
+-----------------------------------------------+
</pre>
<h4><a name="det_page_avail_tabs">2.1 Available tabs</a></h4>
The following tabs are available:
<table border="1">
<tr>
<th>Name</th>
<th>Description</th>
<th>URL (eg. for record '10')</th>
</tr>
<tr>
<td>Information</td>
<td>Show the formatted metadata of the record</td>
- <td><a href="<WEBURL>/record/10"><WEBURL>/record/10</a></td>
+ <td><a href="<CFG_SITE_URL>/record/10"><CFG_SITE_URL>/record/10</a></td>
</tr>
<tr>
<td>References</td>
<td>Displays the references (bibliography) of the record</td>
- <td><a href="<WEBURL>/record/10/references"><WEBURL>/record/10/references</a></td>
+ <td><a href="<CFG_SITE_URL>/record/10/references"><CFG_SITE_URL>/record/10/references</a></td>
</tr>
<tr>
<td>Discussion</td>
<td>Displays the users' comments and reviews</td>
- <td><a href="<WEBURL>/record/10/comments"><WEBURL>/record/10/comments</a><br/>
- <a href="<WEBURL>/record/10/reviews"><WEBURL>/record/10/reviews</a></td>
+ <td><a href="<CFG_SITE_URL>/record/10/comments"><CFG_SITE_URL>/record/10/comments</a><br/>
+ <a href="<CFG_SITE_URL>/record/10/reviews"><CFG_SITE_URL>/record/10/reviews</a></td>
</tr>
<tr>
<td>Usage Statistics</td>
<td>Statistics data and graph about file downloads/views</td>
- <td><a href="<WEBURL>/record/10/statistics"><WEBURL>/record/10/statistics</a></td>
+ <td><a href="<CFG_SITE_URL>/record/10/statistics"><CFG_SITE_URL>/record/10/statistics</a></td>
</tr>
<tr><td>Fulltext</td>
<td>Link(s) to fulltext file(s)</td>
- <td><a href="<WEBURL>/record/10/files"><WEBURL>/record/10/files</a></td>
+ <td><a href="<CFG_SITE_URL>/record/10/files"><CFG_SITE_URL>/record/10/files</a></td>
</tr>
</table>
<p>The mini panel is only displayed when the <em>Information</em> tab is
selected. It is divided into the following sections:</p>
<ul>
<li>Files : quick access to fulltext file(s)</li>
<li>Review : quick access to reviewing feature</li>
<li>Actions: quick access to several other features</li>
</ul>
<h4><a name="det_page_show_hide_tabs">2.2 Showing/Hiding tabs</a></h4>
<p>The <a href="websearch-admin">WebSearch admin web
interface</a> lets you decide for each collection which tabs are to be
displayed. Choose a collection to edit in the collection tree and go
to its <em>detailed record page options</em>. From there you can select which
tabs to show for that collection.</p>
<p>If you want to apply these settings to the subcollections, select
<em><input type="checkbox" checked="checked" disabled="disabled">Also apply to subcollections</input></em>
before you click on the
<input type="submit" value="Modify" class="formbutton" disabled="disabled"/>
button.</p>
<p>Note that these settings only affect the tabs, not the content of the
tabs: even if a tab is not displayed, it is still possible to access
its content using its usual url. This is useful if you decide to
completely change the detailed record pages, dropping the tab-metaphor
(eg. for a side bar) but still want to access the comments, reviews,
etc pages.</p>
<p>Here are some behaviours you should expect when changing the tabs
configuration:</p>
<ul>
- <li>Given that search results pages always link to <WEBURL>/record/10,
+ <li>Given that search results pages always link to <CFG_SITE_URL>/record/10,
and given the above comment about accessibility of tabs when they
are not displayed, the content of the <em>Information</em> will always be
show when clicking on <a href="#" class="moreinfo">detailed record</a> link in search results, even
if the <em>Information</em> tab is set not to be displayed.</li>
<li>If you select only 1 tab, none of the tabs will be displayed at the
top of the page. This also means that whatever tabs you have
selected, you users will always see the content of the 'Information'
tabs (see above behaviour).</li>
<li>If you select 0 tab, only the content of <em>Information</em> tab is shown.
None of the tabs, nor the border that usually surrounds the content
of the tabs, nor the minipanel are shown. You should choose this
option if you decide to drop the tabs metaphor for the detailed
record pages. You can then build your own user interface on this
almost blank page (See <a href="#det_page_cust_cont_tabs">Customizing content of tabs</a>).</li>
<li>Note that <em>Discussion</em> tab will not be shown if you have disabled
both the commenting and reviewing features in your installation
(<code>CFG_WEBCOMMENT_ALLOW_COMMENTS</code> and
<code>CFG_WEBCOMMENT_ALLOW_REVIEWS</code> variable in your config file).</li>
</ul>
<h4><a name="det_page_cust_cont_tabs">2.3 Customizing content of tabs</a></h4>
<p>The contents of tabs are defined in the following ways:</p>
<dl>
<dt><strong><em>Information</em> tab</strong></dt>
<dd>The content of this tab is defined by function
<code>tmpl_detailed_record_metadata(..)</code> in <code>websearch_templates.py</code>.
By default <code>tmpl_detailed_record_metadata</code> simply returns the result
of the formatting of the metadata by BibFormat using the "HD" output
format. It can therefore be collection-specific.</dd>
<dt><strong><em>References</em> tab</strong></dt>
<dd>The content of this tab is defined by function
<code>tmpl_detailed_record_references(..)</code> in <code>websearch_templates.py</code>.
By default <code>tmpl_detailed_record_metadata</code> simply returns the result
of the formatting of the metadata by BibFormat using the "HDREF"
output format. If the result returned by BibFormat is empty, the tab
is disabled (visible, but not clickable). It can therefore be
collection-specific.</dd>
<dt><strong><em>Discussion</em> tab</strong></dt>
<dd>The content of this tab is mainly defined by function
<code>tmpl_get_comments(..)</code> in <code>webcomment_templates.py</code>. Other
functions in this file are also involved in the display.</dd>
<dt><strong><em>Usage Statistics</em> tab</strong></dt>
<dd>The content of this tab is defined by function
<code>tmpl_detailed_record_statistics(..)</code> in <code>websearch_templates.py</code>.
If the returned content is empty, then the tabs will be disabled
(visible, but cannot be clicked).</dd>
<dt><strong><em>Fulltext</em> tab</strong></dt>
<dd>The content of this tab is defined by function <code>tmpl_filelist(..)</code>
in <code>websubmit_templates.py</code>.</dd>
</dl>
<br/>
<p>The content of the mini panel is defined in the following ways:</p>
<dl>
<dt><strong><em>Files</em></strong></dt>
<dd>The content of this section is defined by the output format
'HDFILE'. It can therefore be collection-specific.</dd>
<dt><strong><em>Review</em></strong></dt>
<dd>The content of this section is defined by function
<code>tmpl_mini_review(..)</code> inside <code>webcomment_templates.py</code></dd>
<dt><strong><em>Actions</em></strong></dt>
<dd>The content of this section is defined by the output format
<code>HDACT</code>. It can therefore be collection-specific.</dd>
</dl>
<h4><a name="det_page_cust_look_tabs">2.4 Customizing look of tabs</a></h4>
<p>You can customize how tabs look like, as well change the look of the
border that surrounds the content of tabs. The mini panel can
similarly be customized.</p>
<p>Have a look at the following classes in the CDS css stylesheet:</p>
<ul>
<li><code>detailedrecordtabs</code></li>
<li><code>detailedrecordbox</code></li>
<li><code>detailedrecordminipanel</code></li>
<li><code>top-left, top-right, bottom-left, bottom-right</code></li>
<li><code>detailedrecordminipanel{actions,review,file}, detailedrecordshortreminder</code></li>
</ul>
<p>Note that a tab might be greyed out (disabled) when its content is
empty. This is the case for the <em>References</em> tab (see <a href="#det_page_cust_cont_tabs">Customizing
content of tabs</a> -&gt; 'References tab') and the <em>Fulltext</em> tab (if no
file could be found for the record).<p>
<p>For more advanced modifications (like changing the HTML code of the
tabs), you can modify the <code>detailed_record_container(..)</code> and
<code>detailed_record_mini_panel(..)</code> functions inside your
<code>webstyle_templates.py</code> file.</p>
diff --git a/modules/webstyle/lib/webdoc.py b/modules/webstyle/lib/webdoc.py
index eb60b87df..13ab2108d 100644
--- a/modules/webstyle/lib/webdoc.py
+++ b/modules/webstyle/lib/webdoc.py
@@ -1,894 +1,894 @@
# -*- coding: utf-8 -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
WebDoc -- Transform webdoc sources into static html files
"""
__revision__ = \
"$Id$"
from invenio.config import \
CFG_PREFIX, \
CFG_SITE_LANG, \
CFG_SITE_LANGS, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_ADMIN_EMAIL, \
- weburl, \
+ CFG_SITE_URL, \
CFG_SITE_SECURE_URL, \
CFG_VERSION, \
CFG_SITE_NAME_INTL, \
CFG_CACHEDIR
from invenio.dateutils import \
convert_datestruct_to_datetext, \
convert_datestruct_to_dategui, \
convert_datecvs_to_datestruct
from invenio.messages import \
gettext_set_language, \
wash_language
import re
import getopt
import os
import sys
import time
# List of (webdoc_source_dir, webdoc_cache_dir)
webdoc_dirs = {'help':('%s/lib/webdoc/help' % CFG_PREFIX, \
'%s/webdoc/help-pages' % CFG_CACHEDIR),
'admin':('%s/lib/webdoc/admin' % CFG_PREFIX, \
'%s/webdoc/admin-pages' % CFG_CACHEDIR),
'hacking':('%s/lib/webdoc/hacking' % CFG_PREFIX, \
'%s/webdoc/hacking-pages' % CFG_CACHEDIR)}
# Regular expression for finding text to be translated
translation_pattern = re.compile(r'_\((?P<word>.*?)\)_', \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# # Regular expression for finding comments
comments_pattern = re.compile(r'^\s*#.*$', \
re.MULTILINE)
# Regular expression for finding <lang:current/> tag
pattern_lang_current = re.compile(r'<lang \s*:\s*current\s*\s*/>', \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding <lang:link/> tag
pattern_lang_link_current = re.compile(r'<lang \s*:\s*link\s*\s*/>', \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding <!-- %s: %s --> tag
# where %s will be replaced at run time
pattern_tag = r'''
<!--\s*(?P<tag>%s) #<!-- %%s tag (no matter case)
\s*:\s*
(?P<value>.*?) #description value. any char that is not end tag
(\s*-->) #end tag
'''
# List of available tags in webdoc, and the pattern to find it
pattern_tags = {'WebDoc-Page-Title': '',
'WebDoc-Page-Navtrail': '',
'WebDoc-Page-Description': '',
'WebDoc-Page-Keywords': '',
'WebDoc-Page-Header-Add': '',
'WebDoc-Page-Box-Left-Top-Add': '',
'WebDoc-Page-Box-Left-Bottom-Add': '',
'WebDoc-Page-Box-Right-Top-Add': '',
'WebDoc-Page-Box-Right-Bottom-Add': '',
'WebDoc-Page-Footer-Add': '',
'WebDoc-Page-Revision': ''
}
for tag in pattern_tags.keys():
pattern_tags[tag] = re.compile(pattern_tag % tag, \
re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding <lang>...</lang> tag
pattern_lang = re.compile(r'''
<lang #<lang tag (no matter case)
\s*
(?P<keep>keep=all)*
\s* #any number of white spaces
> #closing <lang> start tag
(?P<langs>.*?) #anything but the next group (greedy)
(</lang\s*>) #end tag
''', re.IGNORECASE | re.DOTALL | re.VERBOSE)
# Regular expression for finding <en>...</en> tag (particular case of
# pattern_lang)
pattern_CFG_SITE_LANG = re.compile(r"<("+CFG_SITE_LANG+ \
r")\s*>(.*?)(</"+CFG_SITE_LANG+r"\s*>)",
re.IGNORECASE | re.DOTALL)
# Builds regular expression for finding each known language in <lang> tags
ln_pattern_text = r"<(?P<lang>"
ln_pattern_text += r"|".join(CFG_SITE_LANGS)
ln_pattern_text += r')\s*(revision="[^"]"\s*)?>(?P<translation>.*?)</\1>'
ln_pattern = re.compile(ln_pattern_text, re.IGNORECASE | re.DOTALL)
defined_tags = {'<CFG_SITE_NAME>': CFG_SITE_NAME,
'<CFG_SITE_SUPPORT_EMAIL>': CFG_SITE_SUPPORT_EMAIL,
'<CFG_SITE_ADMIN_EMAIL>': CFG_SITE_ADMIN_EMAIL,
- '<WEBURL>': weburl,
+ '<CFG_SITE_URL>': CFG_SITE_URL,
'<CFG_SITE_SECURE_URL>': CFG_SITE_SECURE_URL,
'<CFG_VERSION>': CFG_VERSION,
'<CFG_SITE_NAME_INTL>': CFG_SITE_NAME_INTL}
def get_webdoc_parts(webdoc,
parts=['title', \
'keywords', \
'navtrail', \
'body',
'lastupdated',
'description'],
categ="",
update_cache_mode=1,
ln=CFG_SITE_LANG,
verbose=0):
"""
Returns the html of the specified 'webdoc' part(s).
Also update the cache if 'update_cache' is True.
Parameters:
webdoc - *string* the name of a webdoc that can be
found in standard webdoc dir, or a webdoc
filepath. Priority is given to filepath if
both match.
parts - *list(string)* the parts that should be
returned by this function. Can be in:
'title', 'keywords', 'navtrail', 'body',
'description', 'lastupdated'.
categ - *string* (optional) The category to which
the webdoc file belongs. 'help', 'admin'
or 'hacking'. If "", look in all categories.
update_cache_mode - *int* update the cached version of the
given 'webdoc':
- 0 : do not update
- 1 : update if needed
- 2 : always update
Returns : *dictionary* with keys being in 'parts' input parameter and values
being the corresponding html part.
"""
html_parts = {}
if update_cache_mode in [1, 2]:
update_webdoc_cache(webdoc, update_cache_mode, verbose)
def get_webdoc_cached_part_path(webdoc_cache_dir, webdoc, ln, part):
"Build path for given webdoc, ln and part"
return webdoc_cache_dir + os.sep + webdoc + \
os.sep + webdoc + '.' + part + '-' + \
ln + '.html'
for part in parts:
if categ != "":
locations = [webdoc_dirs.get(categ, ('',''))]
else:
locations = webdoc_dirs.values()
for (_webdoc_source_dir, _web_doc_cache_dir) in locations:
webdoc_cached_part_path = None
if os.path.exists(get_webdoc_cached_part_path(_web_doc_cache_dir,
webdoc, ln, part)):
# Check given language
webdoc_cached_part_path = get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, ln, part)
elif os.path.exists(get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, CFG_SITE_LANG, part)):
# Check CFG_SITE_LANG
webdoc_cached_part_path = get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, 'en', part)
elif os.path.exists(get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, ln, part)):
# Check English
webdoc_cached_part_path = get_webdoc_cached_part_path(_web_doc_cache_dir, webdoc, 'en', part)
if webdoc_cached_part_path is not None:
try:
webdoc_cached_part = file(webdoc_cached_part_path, 'r').read()
html_parts[part] = webdoc_cached_part
except IOError:
# Could not read cache file. Generate on-the-fly
(webdoc_source_path, \
webdoc_cache_dir, \
webdoc_name,\
webdoc_source_modification_date, \
webdoc_cache_modification_date) = get_webdoc_info(webdoc)
webdoc_source = file(webdoc_source_path, 'r').read()
htmls = transform(webdoc_source, languages=[ln])
(lang, body, title, keywords, \
navtrail, lastupdated, description) = htmls[-1]
html_parts = {'body': body or '',
'title': title or '',
'keywords': keywords or '',
'navtrail': navtrail or '',
'lastupdated': lastupdated or '',
'description': description or ''}
break
else:
# Could not find/read the folder where cache should
# be. Generate on-the-fly
(webdoc_source_path, \
webdoc_cache_dir, \
webdoc_name,\
webdoc_source_modification_date, \
webdoc_cache_modification_date) = get_webdoc_info(webdoc)
if webdoc_source_path is not None:
try:
webdoc_source = file(webdoc_source_path, 'r').read()
htmls = transform(webdoc_source, languages=[ln])
(lang, body, title, keywords, \
navtrail, lastupdated, description) = htmls[-1]
html_parts = {'body': body or '',
'title': title or '',
'keywords': keywords or '',
'navtrail': navtrail or '',
'lastupdated': lastupdated or '',
'description': description or ''}
break
except IOError:
# Nothing we can do..
continue
return html_parts
def update_webdoc_cache(webdoc, mode=1, verbose=0, languages=CFG_SITE_LANGS):
"""
Update the cache (on disk) of the given webdoc.
Parameters:
webdoc - *string* the name of a webdoc that can be
found in standard webdoc dir, or a webdoc
filepath.
mode - *int* update cache mode:
- 0 : do not update
- 1 : only if necessary (webdoc source
is newer than its cache)
- 2 : always update
"""
if mode in [1, 2]:
(webdoc_source_path, \
webdoc_cache_dir, \
webdoc_name,\
webdoc_source_modification_date, \
webdoc_cache_modification_date) = get_webdoc_info(webdoc)
if mode == 1 and \
webdoc_source_modification_date < webdoc_cache_modification_date and \
get_mo_last_modification() < webdoc_cache_modification_date:
# Cache was updated after source. No need to update
return
(webdoc_source, \
webdoc_cache_dir, \
webdoc_name) = read_webdoc_source(webdoc)
if webdoc_source is not None:
htmls = transform(webdoc_source, languages=languages)
for (lang, body, title, keywords, \
navtrail, lastupdated, description) in htmls:
# Body
if body is not None:
try:
write_cache_file('%(name)s.body%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
body,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Title
if title is not None:
try:
write_cache_file('%(name)s.title%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
title,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Keywords
if keywords is not None:
try:
write_cache_file('%(name)s.keywords%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
keywords,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Navtrail
if navtrail is not None:
try:
write_cache_file('%(name)s.navtrail%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
navtrail,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Description
if description is not None:
try:
write_cache_file('%(name)s.description%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
description,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Last updated timestamp (CVS timestamp)
if lastupdated is not None:
try:
write_cache_file('%(name)s.lastupdated%(lang)s.html' % \
{'name': webdoc_name,
'lang': '-'+lang},
webdoc_cache_dir,
lastupdated,
verbose)
except IOError, e:
print e
except OSError, e:
print e
# Last updated cache file
try:
write_cache_file('last_updated',
webdoc_cache_dir,
convert_datestruct_to_dategui(time.localtime()),
verbose=0)
except IOError, e:
print e
except OSError, e:
print e
if verbose > 0:
print 'Written cache in %s' % webdoc_cache_dir
def read_webdoc_source(webdoc):
"""
Returns the source of the given webdoc, along with the path to its
cache directory.
Returns (None, None, None) if webdoc cannot be found.
Parameters:
webdoc - *string* the name of a webdoc that can be
found in standard webdoc dir, or a webdoc
filepath. Priority is given to filepath if
both match.
Returns: *tuple* (webdoc_source, webdoc_cache_dir, webdoc_name)
"""
(webdoc_source_path, \
webdoc_cache_dir, \
webdoc_name,\
webdoc_source_modification_date, \
webdoc_cache_modification_date) = get_webdoc_info(webdoc)
if webdoc_source_path is not None:
try:
webdoc_source = file(webdoc_source_path, 'r').read()
except IOError:
webdoc_source = None
else:
webdoc_source = None
return (webdoc_source, webdoc_cache_dir, webdoc_name)
def get_webdoc_info(webdoc):
"""
Locate the file corresponding to given webdoc and returns its
path, the path to its cache directory (even if it does not exist
yet), the last modification dates of the source and the cache, and
the webdoc name (i.e. webdoc id)
Parameters:
webdoc - *string* the name of a webdoc that can be found in
standard webdoc dir, or a webdoc filepath. Priority
is given to filepath if both match.
Returns: *tuple* (webdoc_source_path, webdoc_cache_dir,
webdoc_name webdoc_source_modification_date,
webdoc_cache_modification_date)
"""
webdoc_source_path = None
webdoc_cache_dir = None
webdoc_name = None
last_updated_date = None
webdoc_source_modification_date = 1
webdoc_cache_modification_date = 0
# Search at given path or in webdoc cache dir
if os.path.exists(os.path.abspath(webdoc)):
webdoc_source_path = os.path.abspath(webdoc)
(webdoc_cache_dir, webdoc_name) = os.path.split(webdoc_source_path)
(webdoc_name, extension) = os.path.splitext(webdoc_name)
webdoc_source_modification_date = os.stat(webdoc_source_path).st_mtime
else:
for (_webdoc_source_dir, _web_doc_cache_dir) in webdoc_dirs.values():
webdoc_source_path = _webdoc_source_dir + os.sep + \
webdoc + '.webdoc'
if os.path.exists(webdoc_source_path):
webdoc_cache_dir = _web_doc_cache_dir + os.sep + webdoc
webdoc_name = webdoc
webdoc_source_modification_date = os.stat(webdoc_source_path).st_mtime
break
else:
webdoc_source_path = None
webdoc_name = None
webdoc_source_modification_date = 1
if webdoc_cache_dir is not None and \
os.path.exists(webdoc_cache_dir + os.sep + 'last_updated'):
webdoc_cache_modification_date = os.stat(webdoc_cache_dir + \
os.sep + \
'last_updated').st_mtime
return (webdoc_source_path, webdoc_cache_dir, webdoc_name,
webdoc_source_modification_date, webdoc_cache_modification_date)
def get_webdoc_topics(sort_by='name', sc=0, limit=-1,
categ=['help', 'admin', 'hacking'],
ln=CFG_SITE_LANG):
"""
List the available webdoc files in html format.
sort_by - *string* Sort topics by 'name' or 'date'.
sc - *int* Split the topics by categories if sc=1.
limit - *int* Max number of topics to be printed.
No limit if limit < 0.
categ - *list(string)* the categories to consider
ln - *string* Language of the page
"""
_ = gettext_set_language(ln)
topics = {}
ln_link = (ln != CFG_SITE_LANG and '?ln=' + ln) or ''
for category in categ:
if not webdoc_dirs.has_key(category):
continue
(source_path, cache_path) = webdoc_dirs[category]
if not topics.has_key(category):
topics[category] = []
# Build list of tuples(webdoc_name, webdoc_date, webdoc_url)
for webdocfile in [path for path in \
os.listdir(source_path) \
if path.endswith('.webdoc')]:
webdoc_name = webdocfile[:-7]
- webdoc_url = weburl + "/help/" + \
+ webdoc_url = CFG_SITE_URL + "/help/" + \
((category != 'help' and category + '/') or '') + \
webdoc_name
try:
webdoc_date = time.strptime(get_webdoc_parts(webdoc_name,
parts=['lastupdated']).get('lastupdated', "1970-01-01 00:00:00"),
"%Y-%m-%d %H:%M:%S")
except:
webdoc_date = time.strptime("1970-01-01 00:00:00", "%Y-%m-%d %H:%M:%S")
topics[category].append((webdoc_name, webdoc_date, webdoc_url))
# If not split by category, merge everything
if sc == 0:
all_topics = []
for topic in topics.values():
all_topics.extend(topic)
topics.clear()
topics[''] = all_topics
# Sort topics
if sort_by == 'name':
for topic in topics.values():
topic.sort()
elif sort_by == 'date':
for topic in topics.values():
topic.sort(lambda x, y:cmp(x[1], y[1]))
topic.reverse()
out = ''
for category, topic in topics.iteritems():
if category != '' and len(categ) > 1:
out += '<strong>'+ _("%(category)s Pages") % \
{'category': _(category).capitalize()} + '</strong>'
if limit < 0:
limit = len(topic)
out += '<ul><li>' + \
'</li><li>'.join(['%s <a href="%s%s">%s</a>' % \
((sort_by == 'date' and time.strftime('%Y-%m-%d', topic_item[1])) or '', \
topic_item[2], \
ln_link, \
get_webdoc_parts(topic_item[0], \
parts=['title'], \
ln=ln).get('title', '')) \
for topic_item in topic[:limit]]) + \
'</li></ul>'
return out
def transform(webdoc_source, verbose=0, req=None, languages=CFG_SITE_LANGS):
"""
Transform a WebDoc into html
This is made through a serie of transformations, mainly substitutions.
Parameters:
- webdoc_source : *string* the WebDoc input to transform to HTML
"""
parameters = {} # Will store values for specified parameters, such
# as 'Title' for <!-- WebDoc-Page-Title: Title -->
def get_param_and_remove(match):
"""
Analyses 'match', get the parameter and return empty string to
remove it.
Called by substitution in 'transform(...)', used to collection
parameters such as <!-- WebDoc-Page-Title: Title -->
@param match a match object corresponding to the special tag
that must be interpreted
"""
tag = match.group("tag")
value = match.group("value")
parameters[tag] = value
return ''
def translate(match):
"""
Translate matching values
"""
word = match.group("word")
translated_word = _(word)
return translated_word
# 1 step
## First filter, used to remove comments
## and <protect> tags
uncommented_webdoc = ''
for line in webdoc_source.splitlines(True):
if not line.strip().startswith('#'):
uncommented_webdoc += line
webdoc_source = uncommented_webdoc.replace('<protect>', '')
webdoc_source = webdoc_source.replace('</protect>', '')
html_texts = {}
# Language dependent filters
for ln in languages:
_ = gettext_set_language(ln)
# Check if translation is really needed
## Just a quick check. Might trigger false negative, but it is
## ok.
if ln != CFG_SITE_LANG and \
translation_pattern.search(webdoc_source) is None and \
pattern_lang_link_current.search(webdoc_source) is None and \
pattern_lang_current.search(webdoc_source) is None and \
'<%s>' % ln not in webdoc_source and \
('_(') not in webdoc_source:
continue
# 2 step
## Filter used to translate string in _(..)_
localized_webdoc = translation_pattern.sub(translate, webdoc_source)
# 3 step
## Print current language 'en', 'fr', .. instead of
## <lang:current /> tags and '?ln=en', '?ln=fr', .. instead of
## <lang:link /> if ln is not default language
if ln != CFG_SITE_LANG:
localized_webdoc = pattern_lang_link_current.sub('?ln=' + ln,
localized_webdoc)
else:
localized_webdoc = pattern_lang_link_current.sub('',
localized_webdoc)
localized_webdoc = pattern_lang_current.sub(ln, localized_webdoc)
# 4 step
## Filter out languages
localized_webdoc = filter_languages(localized_webdoc, ln, defined_tags)
# 5 Step
## Replace defined tags with their value from config file
- ## Eg. replace <weburl> with 'http://cdsweb.cern.ch/':
+ ## Eg. replace <CFG_SITE_URL> with 'http://cdsweb.cern.ch/':
for defined_tag, value in defined_tags.iteritems():
if defined_tag.upper() == '<CFG_SITE_NAME_INTL>':
localized_webdoc = localized_webdoc.replace(defined_tag, \
value.get(ln, value['en']))
else:
localized_webdoc = localized_webdoc.replace(defined_tag, value)
# 6 step
## Get the parameters defined in HTML comments, like
## <!-- WebDoc-Page-Title: My Title -->
localized_body = localized_webdoc
for tag, pattern in pattern_tags.iteritems():
localized_body = pattern.sub(get_param_and_remove, localized_body)
out = localized_body
# Pre-process date
last_updated = parameters.get('WebDoc-Page-Revision', '')
last_updated = convert_datecvs_to_datestruct(last_updated)
last_updated = convert_datestruct_to_datetext(last_updated)
html_texts[ln] = (ln,
out,
parameters.get('WebDoc-Page-Title'),
parameters.get('WebDoc-Page-Keywords'),
parameters.get('WebDoc-Page-Navtrail'),
last_updated,
parameters.get('WebDoc-Page-Description'))
# Remove duplicates
filtered_html_texts = []
if html_texts.has_key(CFG_SITE_LANG):
filtered_html_texts = [(html_text[0], \
(html_text[1] != html_texts[CFG_SITE_LANG][1] and html_text[1]) or None, \
(html_text[2] != html_texts[CFG_SITE_LANG][2] and html_text[2]) or None, \
(html_text[3] != html_texts[CFG_SITE_LANG][3] and html_text[3]) or None, \
(html_text[4] != html_texts[CFG_SITE_LANG][4] and html_text[4]) or None, \
(html_text[5] != html_texts[CFG_SITE_LANG][5] and html_text[5]) or None, \
(html_text[6] != html_texts[CFG_SITE_LANG][6] and html_text[6]) or None)
for html_text in html_texts.values() \
if html_text[0] != CFG_SITE_LANG]
filtered_html_texts.append(html_texts[CFG_SITE_LANG])
else:
filtered_html_texts = html_texts.values()
return filtered_html_texts
def mymkdir(newdir, mode=0777):
"""works the way a good mkdir should :)
- already exists, silently complete
- regular file in the way, raise an exception
- parent directory(ies) does not exist, make them as well
"""
if os.path.isdir(newdir):
pass
elif os.path.isfile(newdir):
raise OSError("a file with the same name as the desired " \
"dir, '%s', already exists." % newdir)
else:
head, tail = os.path.split(newdir)
if head and not os.path.isdir(head):
mymkdir(head, mode)
if tail:
os.umask(022)
os.mkdir(newdir, mode)
def write_cache_file(filename, webdoc_cache_dir, filebody, verbose=0):
"""Write a file inside WebDoc cache dir.
Raise an exception if not possible
"""
# open file:
mymkdir(webdoc_cache_dir)
fullfilename = webdoc_cache_dir + os.sep + filename
os.umask(022)
f = open(fullfilename, "w")
f.write(filebody)
f.close()
if verbose > 2:
print 'Written %s' % fullfilename
def get_mo_last_modification():
"""
Returns the timestamp of the most recently modified mo (compiled
po) file
"""
# Take one of the mo files. They are all installed at the same
# time, so last modication date should be the same
mo_file = '%s/share/locale/%s/LC_MESSAGES/cds-invenio.mo' % (CFG_PREFIX, CFG_SITE_LANG)
if os.path.exists(os.path.abspath(mo_file)):
return os.stat(mo_file).st_mtime
else:
return 0
def filter_languages(text, ln='en', defined_tags=None):
"""
Filters the language tags that do not correspond to the specified language.
Eg: <lang><en>A book</en><de>Ein Buch</de></lang> will return
- with ln = 'de': "Ein Buch"
- with ln = 'en': "A book"
- with ln = 'fr': "A book"
- Also replace variables such as <WEBURL> and <CFG_SITE_NAME_INTL> inside
+ Also replace variables such as <CFG_SITE_URL> and <CFG_SITE_NAME_INTL> inside
<lang><..><..></lang> tags in order to print them with the correct
language
@param text the input text
@param ln the language that is NOT filtered out from the input
@return the input text as string with unnecessary languages filtered out
@see bibformat_engine.py, from where this function was originally extracted
"""
# First define search_lang_tag(match) and clean_language_tag(match), used
# in re.sub() function
def search_lang_tag(match):
"""
Searches for the <lang>...</lang> tag and remove inner localized tags
such as <en>, <fr>, that are not current_lang.
If current_lang cannot be found inside <lang> ... </lang>, try to use 'CFG_SITE_LANG'
@param match a match object corresponding to the special tag that must be interpreted
"""
current_lang = ln
# If <lang keep=all> is used, keep all languages
keep = False
if match.group("keep") is not None:
keep = True
def clean_language_tag(match):
"""
Return tag text content if tag language of match is output language.
Called by substitution in 'filter_languages(...)'
@param match a match object corresponding to the special tag that must be interpreted
"""
if match.group('lang') == current_lang or \
keep == True:
return match.group('translation')
else:
return ""
# End of clean_language_tag(..)
lang_tag_content = match.group("langs")
# Try to find tag with current lang. If it does not exists,
# then try to look for CFG_SITE_LANG. If still does not exist, use
# 'en' as current_lang
pattern_current_lang = re.compile(r"<(" + current_lang + \
r")\s*>(.*?)(</"+current_lang+r"\s*>)",
re.IGNORECASE | re.DOTALL)
if re.search(pattern_current_lang, lang_tag_content) is None:
current_lang = CFG_SITE_LANG
# Can we find translation in 'CFG_SITE_LANG'?
if re.search(pattern_CFG_SITE_LANG, lang_tag_content) is None:
current_lang = 'en'
cleaned_lang_tag = ln_pattern.sub(clean_language_tag, lang_tag_content)
# Remove empty lines
# Only if 'keep' has not been set
if keep == False:
stripped_text = ''
for line in cleaned_lang_tag.splitlines(True):
if line.strip():
stripped_text += line
cleaned_lang_tag = stripped_text
return cleaned_lang_tag
# End of search_lang_tag(..)
filtered_text = pattern_lang.sub(search_lang_tag, text)
return filtered_text
def usage(exitcode=1, msg=""):
"""Prints usage info."""
if msg:
sys.stderr.write("Error: %s.\n" % msg)
sys.stderr.write("Usage: %s [options] webdocfile\n" % sys.argv[0])
sys.stderr.write(" -h, --help \t\t Print this help.\n")
sys.stderr.write(" -V, --version \t\t Print version information.\n")
sys.stderr.write(" -v, --verbose=LEVEL \t\t Verbose level (0=min,1=normal,9=max).\n")
sys.stderr.write(" -l, --language=LN1,LN2,.. \t\t Language(s) to process (default all)\n")
sys.stderr.write(" -m, --mode=MODE \t\t Update cache mode(0=Never,1=if necessary,2=always) (default 2)\n")
sys.stderr.write("\n")
sys.stderr.write(" Example: webdoc help-pages\n")
sys.stderr.write(" Example: webdoc -l en,fr help-pages\n")
sys.stderr.write(" Example: webdoc -m 1 help-pages")
sys.stderr.write("\n")
sys.exit(exitcode)
def main():
"""
main entry point for webdoc via command line
"""
options = {'language':CFG_SITE_LANGS, 'verbose':1, 'mode':2}
try:
opts, args = getopt.getopt(sys.argv[1:],
"hVv:l:m:",
["help",
"version",
"verbose=",
"language=",
"mode="])
except getopt.GetoptError, err:
usage(1, err)
try:
for opt in opts:
if opt[0] in ["-h", "--help"]:
usage(0)
elif opt[0] in ["-V", "--version"]:
print __revision__
sys.exit(0)
elif opt[0] in ["-v", "--verbose"]:
options["verbose"] = int(opt[1])
elif opt[0] in ["-l", "--language"]:
options["language"] = [wash_language(lang.strip().lower()) \
for lang in opt[1].split(',') \
if lang in CFG_SITE_LANGS]
elif opt[0] in ["-m", "--mode"]:
options["mode"] = opt[1]
except StandardError, e:
usage(e)
try:
options["mode"] = int(options["mode"])
except ValueError:
usage(1, "Mode must be an integer")
options["webdoc"] = args[0]
if not options.has_key("webdoc"):
usage(0)
# check if webdoc exists
infos = get_webdoc_info(options["webdoc"])
if infos[0] is None:
usage(1, "Could not find %s" % options["webdoc"])
update_webdoc_cache(webdoc=options["webdoc"],
mode=options["mode"],
verbose=options["verbose"],
languages=options["language"])
if __name__ == "__main__":
main()
diff --git a/modules/webstyle/lib/webdoc_webinterface.py b/modules/webstyle/lib/webdoc_webinterface.py
index fb200c30f..ceaf113a8 100644
--- a/modules/webstyle/lib/webdoc_webinterface.py
+++ b/modules/webstyle/lib/webdoc_webinterface.py
@@ -1,223 +1,223 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
WebDoc web interface, handling URLs such as </help/foo?ln=el>.
"""
__revision__ = \
"$Id$"
__lastupdated__ = """$Date$"""
import cgi
-from invenio.config import weburl, CFG_SITE_LANG, CFG_SITE_NAME, CFG_SITE_NAME_INTL, CFG_SITE_LANGS
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG, CFG_SITE_NAME, CFG_SITE_NAME_INTL, CFG_SITE_LANGS
from invenio.messages import gettext_set_language
from invenio.webpage import page
from invenio.webuser import getUid
from invenio.webdoc import get_webdoc_parts, get_webdoc_topics
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import redirect_to_url
class WebInterfaceDocumentationPages(WebInterfaceDirectory):
"""Defines the set of documentation pages, usually installed under /help."""
def __init__(self, webdocname='', categ='help'):
"""Constructor."""
self.webdocname = webdocname
self.categ = categ
self.legacy_urls_mappings = {'tips': 'search-tips',
'guide': 'search-guide',
'submit': 'submit-guide',
'index': '',
'': ''}
def _lookup(self, component, path):
"""This handler parses dynamic URLs (/help/component)."""
if component in ['admin', 'hacking'] and len(path) == 1:
if path[0] != '':
webdocname = path[0] # /help/hacking/coding-style use
# coding-style.webdoc
elif component == 'admin':
webdocname = 'admin' # /help/admin/ use admin.webdoc
else:
webdocname = 'hacking' # /help/hacking/ use
# hacking.webdoc
return WebInterfaceDocumentationPages(webdocname, component), []
elif len(path) == 0 and \
component != 'search' and \
component != 'submit':
# Accept any other 'leaf' pages ('help' pages),
# excepted /help/search/ and /help/submit/ which are legacy urls
if component == '':
component = 'help-central'
return WebInterfaceDocumentationPages(component), []
else:
# This is maybe a wrong url eg. /help/help-central/foo
# or a legacy url eg. /help/search/tips.en.html
# /help/search/
if ((component == 'submit' or \
component == 'search') and (len(path) == 0 or path[0] == '')) or \
path[0].endswith('.html'):
# Legacy url?
return WebInterfaceDocumentationPages(), []
# Wrong url
return None, []
def __call__(self, req, form):
"""Serve webdoc page in the given language."""
argd = wash_urlargd(form, {'ln': (str, CFG_SITE_LANG)})
if self.webdocname in ['admin', 'hacking', ''] and \
self.categ == 'help' and \
not (req.uri.endswith('.html') or \
req.uri.endswith('/help/search/') or \
req.uri.endswith('/help/submit/')):
# Eg. /help/hacking -> /help/hacking/
# /help -> /help/
ln_link = (argd['ln'] != CFG_SITE_LANG and '?ln=' + argd['ln']) or ''
redirect_to_url(req, req.uri + "/" + ln_link)
elif req.uri.endswith('.html') or \
req.uri.endswith('/help/search/') or \
req.uri.endswith('/help/submit/'):
# Legacy urls
path = req.uri.split('/')
parts = path[-1].split('.')
title = parts[0]
ln = CFG_SITE_LANG
if len(parts) > 1 and parts[1] in CFG_SITE_LANGS:
ln = parts[1]
category = path[-2]
webdocname = self.legacy_urls_mappings.get(title, '')
if category == 'submit':
webdocname = 'submit-guide'
category = ''
elif category == 'help' or category == 'search':
category = ''
if category != '':
category += '/'
- url = weburl + '/help/' + category + webdocname
+ url = CFG_SITE_URL + '/help/' + category + webdocname
ln_link = (ln != CFG_SITE_LANG and '?ln=' + ln) or ''
redirect_to_url(req, url + ln_link)
else:
return display_webdoc_page(self.webdocname, categ=self.categ, ln=argd['ln'], req=req)
index = __call__
def display_webdoc_page(webdocname, categ="help", ln=CFG_SITE_LANG, req=None):
"""Display webdoc page WEBDOCNAME in language LN."""
_ = gettext_set_language(ln)
uid = getUid(req)
# wash arguments:
if not webdocname:
webdocname = 'help-central'
ln_link = (ln != CFG_SITE_LANG and '?ln=' + ln) or ''
# get page parts in given language:
if webdocname != 'contents':
page_parts = get_webdoc_parts(webdocname, parts=['title', 'body',
'navtrail', 'lastupdated',
'description', 'keywords'],
categ=categ,
ln=ln)
else:
# Print Table of Contents
see_also_links = {'admin': '<a href="%s/help/admin/contents%s">%s</a>' % \
- (weburl, ln_link, _('Admin Pages Index')),
+ (CFG_SITE_URL, ln_link, _('Admin Pages Index')),
'help':'<a href="%s/help/contents%s">%s</a>' % \
- (weburl, ln_link, _('Help Pages Index')),
+ (CFG_SITE_URL, ln_link, _('Help Pages Index')),
'hacking':'<a href="%s/help/hacking/contents%s">%s</a>' % \
- (weburl, ln_link, _('Hacking Pages Index'))}
+ (CFG_SITE_URL, ln_link, _('Hacking Pages Index'))}
titles = {'admin': _("Admin Pages Index"),
'help': _("Help Pages Index"),
'hacking': _("Hacking Pages Index")}
navtrails = {'admin': '<a class="navtrail" href="%s/help/admin%s">%s</a>' % \
- (weburl, ln_link, _("Admin Area")),
+ (CFG_SITE_URL, ln_link, _("Admin Area")),
'help': '<a class="navtrail" href="%s/help/%s">%s</a>' % \
- (weburl, ln_link, _("Help Central")),
+ (CFG_SITE_URL, ln_link, _("Help Central")),
'hacking': '<a class="navtrail" href="%s/help/hacking%s">%s</a>' % \
- (weburl, ln_link, _("Hacking CDS Invenio"))}
+ (CFG_SITE_URL, ln_link, _("Hacking CDS Invenio"))}
body = '''<div style="float:right;clear:none;font-size:small;color:#666;width:auto;margin-right:30px;padding:5px" class="mini-panel"><strong>''' + \
_("Last modifications") + '</strong>' + \
get_webdoc_topics(sort_by='date', sc=0, limit=5,
categ=[categ], ln=ln) + \
'</div>' + '<p>' +_('This is the index of the %(category)s pages.') % {'category': categ}
if categ != 'help':
body += ' ' + _('See also') + ' ' + \
', '.join([ link for (category, link) in \
see_also_links.iteritems() \
if category != categ])
body += '</p>' + get_webdoc_topics(sort_by='name', sc=1,
categ=[categ], ln=ln)
page_parts = {'title': titles.get(categ, ''),
'body': body,
'navtrail': navtrails.get(categ, '')
}
# set page title:
page_title = page_parts.get('title', '')
if not page_title:
page_title = _("Page %s Not Found") % cgi.escape(webdocname)
# set page navtrail:
page_navtrail = page_parts.get('navtrail', '')
# set page body:
page_body = page_parts.get('body' , '')
if not page_body:
page_body = '<p>' + (_("Sorry, page %s does not seem to exist.") % \
('<strong>' + cgi.escape(webdocname) + '</strong>')) + \
'</p>'
page_body += '<p>' + (_("You may want to start browsing from %(rooturl)s or have a look at the %(x_url_open)s index of the %(category)s pages%(x_url_close)s.") % \
{'rooturl':'<a href="%s%s">%s</a>' % \
- (weburl, ln_link, CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)),
+ (CFG_SITE_URL, ln_link, CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME)),
'category': _(categ),
'x_url_open': '<a href="%s/help/%scontents%s">' % \
- (weburl, ((categ != 'help' and categ + '/') or ''), ln_link),
+ (CFG_SITE_URL, ((categ != 'help' and categ + '/') or ''), ln_link),
'x_url_close': '</a>'}) + \
'</p>'
# set page description:
page_description = page_parts.get('description' , '')
# set page keywords:
page_keywords = page_parts.get('keywords' , '')
# set page last updated timestamp:
page_last_updated = page_parts.get('lastupdated' , '')
if categ == 'hacking':
categ = 'help'
# display page:
return page(title=page_title,
body=page_body,
navtrail=page_navtrail,
description=page_description,
keywords=page_keywords,
uid=uid,
language=ln,
req=req,
lastupdated=page_last_updated,
navmenuid=categ)
diff --git a/modules/webstyle/lib/webinterface_handler.py b/modules/webstyle/lib/webinterface_handler.py
index 8999860be..0f20c1ff9 100644
--- a/modules/webstyle/lib/webinterface_handler.py
+++ b/modules/webstyle/lib/webinterface_handler.py
@@ -1,390 +1,390 @@
# -*- coding: utf-8 -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""
Apache request handler mechanism.
It gives the tools to map url to functions, handles the legacy url
scheme (/search.py queries), HTTP/HTTPS switching, language
specification,...
"""
__revision__ = "$Id$"
import urlparse
import cgi
import sys
# The following mod_python imports are done separately in a particular
# order (util first) because I was getting sometimes publisher import
# error when testing weird situations, preventing util from being
# imported and leading to a traceback later. When this happened,
# importing util was okay, only publisher import caused troubles, so
# that importing in special order prevents these problems.
try:
from mod_python import util
from mod_python import apache
from mod_python import publisher
except ImportError:
pass
-from invenio.config import CFG_SITE_LANG, weburl, CFG_SITE_SECURE_URL, CFG_TMPDIR
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, CFG_SITE_SECURE_URL, CFG_TMPDIR
from invenio.messages import wash_language
from invenio.urlutils import redirect_to_url
from invenio.errorlib import register_exception
-has_https_support = weburl != CFG_SITE_SECURE_URL
+has_https_support = CFG_SITE_URL != CFG_SITE_SECURE_URL
DEBUG = False
def _debug(msg):
if DEBUG:
apache.log_error(msg, apache.APLOG_WARNING)
return
def _check_result(req, result):
""" Check that a page handler actually wrote something, and
properly finish the apache request."""
if result or req.bytes_sent > 0 or req.next:
if result is None:
result = ""
else:
result = str(result)
# unless content_type was manually set, we will attempt
# to guess it
if not req._content_type_set:
# make an attempt to guess content-type
if result[:100].strip()[:6].lower() == '<html>' \
or result.find('</') > 0:
req.content_type = 'text/html'
else:
req.content_type = 'text/plain'
if req.method != "HEAD":
req.write(result)
else:
req.write("")
return apache.OK
else:
req.log_error("mod_python.publisher: %s returned nothing." % `object`)
return apache.HTTP_INTERNAL_SERVER_ERROR
class TraversalError(Exception):
pass
class WebInterfaceDirectory(object):
""" A directory groups web pages, and can delegate dispatching of
requests to the actual handler. This has been heavily borrowed
from Quixote's dispatching mechanism, with specific adaptations."""
# Lists the valid URLs contained in this directory.
_exports = []
# Set this to True in order to redirect queries over HTTPS
_force_https = False
def _translate(self, component):
"""(component : string) -> string | None
Translate a path component into a Python identifier. Returning
None signifies that the component does not exist.
"""
if component in self._exports:
if component == '':
return 'index' # implicit mapping
else:
return component
else:
# check for an explicit external to internal mapping
for value in self._exports:
if isinstance(value, tuple):
if value[0] == component:
return value[1]
else:
return None
def _lookup(self, component, path):
""" Override this method if you need to map dynamic URLs.
It can eat up as much of the remaining path as needed, and
return the remaining parts, so that the traversal can
continue.
"""
return None, path
def _traverse(self, req, path):
""" Locate the handler of an URI by traversing the elements of
the path."""
_debug('traversing %r' % path)
component, path = path[0], path[1:]
name = self._translate(component)
if name is None:
obj, path = self._lookup(component, path)
else:
obj = getattr(self, name)
if obj is None:
_debug('could not resolve %s' % repr((component, path)))
raise TraversalError()
# We have found the next segment. If we know that from this
# point our subpages are over HTTPS, do the switch.
if has_https_support and self._force_https:
is_over_https = req.subprocess_env.has_key('HTTPS') \
and req.subprocess_env['HTTPS'] == 'on'
if not is_over_https:
# We need to isolate the part of the URI that is after
- # weburl, and append that to our CFG_SITE_SECURE_URL.
+ # CFG_SITE_URL, and append that to our CFG_SITE_SECURE_URL.
original_parts = urlparse.urlparse(req.unparsed_uri)
- plain_prefix_parts = urlparse.urlparse(weburl)
+ plain_prefix_parts = urlparse.urlparse(CFG_SITE_URL)
secure_prefix_parts = urlparse.urlparse(CFG_SITE_SECURE_URL)
# Compute the new path
plain_path = original_parts[2]
plain_path = secure_prefix_parts[2] + plain_path[len(plain_prefix_parts[2]):]
# ...and recompose the complete URL
final_parts = list(secure_prefix_parts)
final_parts[2] = plain_path
final_parts[-3:] = original_parts[-3:]
target = urlparse.urlunparse(final_parts)
redirect_to_url(req, target)
# Continue the traversal. If there is a path, continue
# resolving, otherwise call the method as it is our final
# renderer. We even pass it the parsed form arguments.
if path:
return obj._traverse(req, path)
form = util.FieldStorage(req, keep_blank_values=True)
result = obj(req, form)
return _check_result(req, result)
def __call__(self, req, form):
""" Maybe resolve the final / of a directory """
# When this method is called, we either are a directory which
# has an 'index' method, and we redirect to it, or we don't
# have such a method, in which case it is a traversal error.
if "" in self._exports:
if not form:
# Fix missing trailing slash as a convenience, unless
# we are processing a form (in which case it is better
# to fix the form posting).
util.redirect(req, req.uri + "/", permanent=True)
_debug('directory %r is not callable' % self)
raise TraversalError()
def create_handler(root):
""" Return a handler function that will dispatch apache requests
through the URL layout passed in parameter."""
def _profiler(req):
""" This handler wrap the default handler with a profiler.
Profiling data is written into CFG_TMPDIR/invenio-profile-stats-datetime.raw, and
is displayed at the bottom of the webpage.
To use add profile=1 to your url. To change sorting algorithm you
can provide profile=algorithm_name. You can add more than one
profile requirement like ?profile=time&profile=cumulative.
The list of available algorithm is displayed at the end of the profile.
"""
if req.args and cgi.parse_qs(req.args).has_key('profile'):
from cStringIO import StringIO
try:
import pstats
except ImportError:
ret = _handler(req)
req.write("<pre>%s</pre>" % "The Python Profiler is not installed!")
return ret
import datetime
date = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
filename = '%s/invenio-profile-stats-%s.raw' % (CFG_TMPDIR, date)
existing_sorts = pstats.Stats.sort_arg_dict_default.keys()
required_sorts = []
profile_dump = []
for sort in cgi.parse_qs(req.args)['profile']:
if sort not in existing_sorts:
sort = 'cumulative'
if sort not in required_sorts:
required_sorts.append(sort)
if sys.hexversion < 0x02050000:
import hotshot, hotshot.stats
pr = hotshot.Profile(filename)
ret = pr.runcall(_handler, req)
for sort_type in required_sorts:
tmp_out = sys.stdout
sys.stdout = StringIO()
hotshot.stats.load(filename).strip_dirs().sort_stats(sort_type).print_stats()
profile_dump.append(sys.stdout.getvalue())
sys.stdout = tmp_out
else:
import cProfile
pr = cProfile.Profile()
ret = pr.runcall(_handler, req)
pr.dump_stats(filename)
for sort_type in required_sorts:
strstream = StringIO()
pstats.Stats(filename, stream=strstream).strip_dirs().sort_stats(sort_type).print_stats()
profile_dump.append(strstream.getvalue())
profile_dump = ''.join(["<pre>%s</pre>" % single_dump for single_dump in profile_dump])
profile_dump += '\nYou can use profile=%s' % existing_sorts
req.write("<pre>%s</pre>" % profile_dump)
return ret
else:
return _handler(req)
def _handler(req):
""" This handler is invoked by mod_python with the apache request."""
req.allow_methods(["GET", "POST"])
if req.method not in ["GET", "POST"]:
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
try:
uri = req.uri
if uri == '/':
path = ['']
else:
path = uri[1:].split('/')
return root._traverse(req, path)
except TraversalError:
return apache.HTTP_NOT_FOUND
except apache.SERVER_RETURN:
## This is one of mod_python way of communicating
raise
except IOError, exc:
if 'Write failed, client closed connection' in "%s" % exc:
## Workaround for considering as false positive exceptions
## rised by mod_python when the user close the connection
## or in some other rare and not well identified cases.
raise
else:
register_exception(req=req, alert_admin=True)
except Exception:
register_exception(req=req, alert_admin=True)
raise
# Serve an error by default.
return apache.HTTP_NOT_FOUND
return _profiler
def wash_urlargd(form, content):
"""
Wash the complete form based on the specification in
content. Content is a dictionary containing the field names as a
key, and a tuple (type, default) as value.
'type' can be list, str, int, tuple, or mod_python.util.Field (for
file uploads).
The specification automatically includes the 'ln' field, which is
common to all queries.
Arguments that are not defined in 'content' are discarded.
Note that in case {list,tuple} were asked for, we assume that
{list,tuple} of strings is to be returned. Therefore beware when
you want to use wash_urlargd() for multiple file upload forms.
@Return: argd dictionary that can be used for passing function
parameters by keywords.
"""
result = {}
content['ln'] = (str, CFG_SITE_LANG)
for k, (dst_type, default) in content.items():
try:
value = form[k]
except KeyError:
result[k] = default
continue
src_type = type(value)
# First, handle the case where we want all the results. In
# this case, we need to ensure all the elements are strings,
# and not Field instances.
if src_type in (list, tuple):
if dst_type is list:
result[k] = [str(x) for x in value]
continue
if dst_type is tuple:
result[k] = tuple([str(x) for x in value])
continue
# in all the other cases, we are only interested in the
# first value.
value = value[0]
# Maybe we already have what is expected? Then don't change
# anything.
if src_type is dst_type:
result[k] = value
continue
# Since we got here, 'value' is sure to be a single symbol,
# not a list kind of structure anymore.
if dst_type in (str, int):
try:
result[k] = dst_type(value)
except:
result[k] = default
elif dst_type is tuple:
result[k] = (str(value),)
elif dst_type is list:
result[k] = [str(value)]
else:
raise ValueError('cannot cast form into type %r' % dst_type)
result['ln'] = wash_language(result['ln'])
return result
diff --git a/modules/webstyle/lib/webpage.py b/modules/webstyle/lib/webpage.py
index dfdba570c..ecfe92730 100644
--- a/modules/webstyle/lib/webpage.py
+++ b/modules/webstyle/lib/webpage.py
@@ -1,240 +1,240 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""CDS Invenio Web Page Functions"""
__revision__ = "$Id$"
from invenio.config import \
CFG_WEBSTYLE_CDSPAGEBOXLEFTBOTTOM, \
CFG_WEBSTYLE_CDSPAGEBOXLEFTTOP, \
CFG_WEBSTYLE_CDSPAGEBOXRIGHTBOTTOM, \
CFG_WEBSTYLE_CDSPAGEBOXRIGHTTOP, \
CFG_SITE_LANG, \
CFG_SITE_SUPPORT_EMAIL, \
- weburl
+ CFG_SITE_URL
from invenio.messages import gettext_set_language
from invenio.webuser import create_userinfobox_body
from invenio.errorlib import get_msgs_for_code_list, register_errors
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
from xml.dom.minidom import parseString, getDOMImplementation
def create_navtrailbox_body(title,
previous_links,
prolog="",
separator=""" &gt; """,
epilog="",
language=CFG_SITE_LANG):
"""Create navigation trail box body
input: title = page title;
previous_links = the trail content from site title until current page (both ends exlusive).
output: text containing the navtrail
"""
return webstyle_templates.tmpl_navtrailbox_body(ln = language,
title = title,
previous_links = \
previous_links,
separator = separator,
prolog = prolog,
epilog = epilog)
def page(title, body, navtrail="", description="", keywords="", uid=0,
cdspageheaderadd="", cdspageboxlefttopadd="",
cdspageboxleftbottomadd="", cdspageboxrighttopadd="",
cdspageboxrightbottomadd="", cdspagefooteradd="", lastupdated="",
language=CFG_SITE_LANG, verbose=1, titleprologue="",
titleepilogue="", secure_page_p=0, req=None, errors=[], warnings=[], navmenuid="admin",
- navtrail_append_title_p=1, of="", rssurl=weburl+"/rss"):
+ navtrail_append_title_p=1, of="", rssurl=CFG_SITE_URL+"/rss"):
"""page(): display CDS web page
input: title of the page
body of the page in html format
description goes to the metadata in the header of the HTML page
keywords goes to the metadata in the header of the html page
cdspageheaderadd is a message to be displayed just under the page header
cdspageboxlefttopadd is a message to be displayed in the page body on left top
cdspageboxleftbottomadd is a message to be displayed in the page body on left bottom
cdspageboxrighttopadd is a message to be displayed in the page body on right top
cdspageboxrightbottomadd is a message to be displayed in the page body on right bottom
cdspagefooteradd is a message to be displayed on the top of the page footer
lastupdated is a text containing the info on last update (optional)
language is the language version of the page
verbose is verbosity of the page (useful for debugging)
titleprologue is to be printed right before page title
titleepilogue is to be printed right after page title
req is the mod_python request object
errors is the list of error codes as defined in the moduleName_config.py file of the calling module
log is the string of data that should be appended to the log file (errors automatically logged)
secure_page_p is 0 or 1 and tells whether we are to use HTTPS friendly page elements or not
navmenuid the section of the website this page belongs (search, submit, baskets, etc.)
navtrail_append_title_p is 0 or 1 and tells whether page title is appended to navtrail
of is an output format (use xx for xml output (e.g. AJAX))
rssfeed is the url of the RSS feed for this page
output: the final cds page with header, footer, etc.
"""
_ = gettext_set_language(language)
if of == 'xx':
#xml output (e.g. AJAX calls) => of=xx
req.content_type = 'text/xml'
impl = getDOMImplementation()
output = impl.createDocument(None, "invenio-message", None)
root = output.documentElement
body_node = output.createElement('body')
body_text = output.createCDATASection(unicode(body, 'utf_8'))
body_node.appendChild(body_text)
root.appendChild(body_node)
if errors:
errors_node = output.createElement('errors')
errors = get_msgs_for_code_list(errors, 'error', language)
register_errors(errors, 'error', req)
for (error_code, error_msg) in errors:
error_node = output.createElement('error')
error_node.setAttribute('code', error_code)
error_text = output.createTextNode(error_msg)
error_node.appendChild(error_text)
errors_node.appendChild(error_node)
root.appendChild(errors_node)
if warnings:
warnings_node = output.createElement('warnings')
warnings = get_msgs_for_code_list(warnings, 'warning', language)
register_errors(warnings, 'warning')
for (warning_code, warning_msg) in warnings:
warning_node = output.createElement('warning')
warning_node.setAttribute('code', warning_code)
warning_text = output.createTextNode(warning_msg)
warning_node.appendChild(warning_text)
warnings_node.appendChild(warning_node)
root.appendChild(warnings_node)
return output.toprettyxml(encoding="utf-8" )
else:
#usual output
# if there are event
if warnings:
warnings = get_msgs_for_code_list(warnings, 'warning', language)
register_errors(warnings, 'warning')
# if there are errors
if errors:
errors = get_msgs_for_code_list(errors, 'error', language)
register_errors(errors, 'error', req)
body = create_error_box(req, errors=errors, ln=language)
return webstyle_templates.tmpl_page(req, ln=language,
description = description,
keywords = keywords,
userinfobox = create_userinfobox_body(req, uid, language),
navtrailbox = create_navtrailbox_body(navtrail_append_title_p \
and title or '',
navtrail,
language=language),
uid = uid,
secure_page_p = secure_page_p,
pageheaderadd = cdspageheaderadd,
boxlefttop = CFG_WEBSTYLE_CDSPAGEBOXLEFTTOP,
boxlefttopadd = cdspageboxlefttopadd,
boxleftbottomadd = cdspageboxleftbottomadd,
boxleftbottom = CFG_WEBSTYLE_CDSPAGEBOXLEFTBOTTOM,
boxrighttop = CFG_WEBSTYLE_CDSPAGEBOXRIGHTTOP,
boxrighttopadd = cdspageboxrighttopadd,
boxrightbottomadd = cdspageboxrightbottomadd,
boxrightbottom = CFG_WEBSTYLE_CDSPAGEBOXRIGHTBOTTOM,
titleprologue = titleprologue,
title = title,
titleepilogue = titleepilogue,
body = body,
lastupdated = lastupdated,
pagefooteradd = cdspagefooteradd,
navmenuid = navmenuid,
rssurl = rssurl)
def pageheaderonly(title, navtrail="", description="", keywords="", uid=0,
cdspageheaderadd="", language=CFG_SITE_LANG, req=None,
secure_page_p=0, verbose=1, navmenuid="admin",
navtrail_append_title_p=1, metaheaderadd="",
- rssurl=weburl+"/rss"):
+ rssurl=CFG_SITE_URL+"/rss"):
"""Return just the beginning of page(), with full headers.
Suitable for the search results page and any long-taking scripts."""
return webstyle_templates.tmpl_pageheader(req,
ln = language,
headertitle = title,
description = description,
keywords = keywords,
metaheaderadd = metaheaderadd,
userinfobox = create_userinfobox_body(req, uid, language),
navtrailbox = create_navtrailbox_body(navtrail_append_title_p \
and title or '',
navtrail,
language=language),
uid = uid,
secure_page_p = secure_page_p,
pageheaderadd = cdspageheaderadd,
navmenuid = navmenuid,
rssurl = rssurl)
def pagefooteronly(cdspagefooteradd="", lastupdated="",
language=CFG_SITE_LANG, req=None, verbose=1):
"""Return just the ending of page(), with full footer.
Suitable for the search results page and any long-taking scripts."""
return webstyle_templates.tmpl_pagefooter(req,
ln=language,
lastupdated = lastupdated,
pagefooteradd = cdspagefooteradd)
def create_error_box(req, title=None, verbose=1, ln=CFG_SITE_LANG, errors=None):
"""Analyse the req object and the sys traceback and return a text
message box with internal information that would be suitful to
display when something bad has happened.
"""
_ = gettext_set_language(ln)
return webstyle_templates.tmpl_error_box(title = title,
ln = ln,
verbose = verbose,
req = req,
errors = errors)
def adderrorbox(header='', datalist=[]):
"""used to create table around main data on a page, row based"""
try:
perc = str(100 // len(datalist)) + '%'
except ZeroDivisionError:
perc = 1
output = '<table class="errorbox">'
output += '<thead><tr><th class="errorboxheader" colspan="%s">%s</th></tr></thead>' % (len(datalist), header)
output += '<tbody>'
for row in [datalist]:
output += '<tr>'
for data in row:
output += '<td style="vertical-align: top; margin-top: 5px; width: %s;">' % (perc, )
output += data
output += '</td>'
output += '</tr>'
output += '</tbody></table>'
return output
diff --git a/modules/webstyle/lib/webstyle_templates.py b/modules/webstyle/lib/webstyle_templates.py
index 1fa122c34..6e967d64a 100644
--- a/modules/webstyle/lib/webstyle_templates.py
+++ b/modules/webstyle/lib/webstyle_templates.py
@@ -1,794 +1,794 @@
## $Id$
## CDS Invenio WebStyle templates.
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = \
"$Id$"
import time
import cgi
import traceback
import urllib
import sys
import string
from invenio.config import \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_NAME_INTL, \
CFG_SITE_SUPPORT_EMAIL, \
CFG_SITE_SECURE_URL, \
- weburl, \
+ CFG_SITE_URL, \
CFG_VERSION
from invenio.messages import gettext_set_language, language_list_long
from invenio.urlutils import make_canonical_urlargd, create_html_link
from invenio.dateutils import convert_datecvs_to_datestruct, \
convert_datestruct_to_dategui
from invenio.bibformat import format_record
from invenio import template
websearch_templates = template.load('websearch')
class Template:
def tmpl_navtrailbox_body(self, ln, title, previous_links,
separator, prolog, epilog):
"""Create navigation trail box body
Parameters:
- 'ln' *string* - The language to display
- 'title' *string* - page title;
- 'previous_links' *string* - the trail content from site title until current page (both ends exlusive)
- 'prolog' *string* - HTML code to prefix the navtrail item with
- 'epilog' *string* - HTML code to suffix the navtrail item with
- 'separator' *string* - HTML code that separates two navtrail items
Output:
- text containing the navtrail
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if title != CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME):
- out += create_html_link(weburl, {'ln': ln},
+ out += create_html_link(CFG_SITE_URL, {'ln': ln},
_("Home"), {'class': 'navtrail'})
if previous_links:
if out:
out += separator
out += previous_links
if title:
if out:
out += separator
if title == CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME): # hide site name, print Home instead
out += cgi.escape(_("Home"))
else:
out += cgi.escape(title)
return cgi.escape(prolog) + out + cgi.escape(epilog)
def tmpl_page(self, req=None, ln=CFG_SITE_LANG, description="",
keywords="", userinfobox="", navtrailbox="",
pageheaderadd="", boxlefttop="", boxlefttopadd="",
boxleftbottom="", boxleftbottomadd="",
boxrighttop="", boxrighttopadd="",
boxrightbottom="", boxrightbottomadd="",
titleprologue="", title="", titleepilogue="",
body="", lastupdated=None, pagefooteradd="", uid=0,
secure_page_p=0, navmenuid="", metaheaderadd="",
- rssurl=weburl+"/rss"):
+ rssurl=CFG_SITE_URL+"/rss"):
"""Creates a complete page
Parameters:
- 'ln' *string* - The language to display
- 'description' *string* - description goes to the metadata in the header of the HTML page
- 'keywords' *string* - keywords goes to the metadata in the header of the HTML page
- 'userinfobox' *string* - the HTML code for the user information box
- 'navtrailbox' *string* - the HTML code for the navigation trail box
- 'pageheaderadd' *string* - additional page header HTML code
- 'boxlefttop' *string* - left-top box HTML code
- 'boxlefttopadd' *string* - additional left-top box HTML code
- 'boxleftbottom' *string* - left-bottom box HTML code
- 'boxleftbottomadd' *string* - additional left-bottom box HTML code
- 'boxrighttop' *string* - right-top box HTML code
- 'boxrighttopadd' *string* - additional right-top box HTML code
- 'boxrightbottom' *string* - right-bottom box HTML code
- 'boxrightbottomadd' *string* - additional right-bottom box HTML code
- 'title' *string* - the title of the page
- 'titleprologue' *string* - what to print before page title
- 'titleepilogue' *string* - what to print after page title
- 'body' *string* - the body of the page
- 'lastupdated' *string* - when the page was last updated
- 'uid' *int* - user ID
- 'pagefooteradd' *string* - additional page footer HTML code
- 'secure_page_p' *int* (0 or 1) - are we to use HTTPS friendly page elements or not?
- 'navmenuid' *string* - the id of the navigation item to highlight for this page
- 'metaheaderadd' *string* - list of further tags to add to the <HEAD></HEAD> part of the page
- 'rssurl' *string* - the url of the RSS feed for this page
Output:
- HTML code of the page
"""
# load the right message language
_ = gettext_set_language(ln)
out = self.tmpl_pageheader(req,
ln = ln,
headertitle = title,
description = description,
keywords = keywords,
metaheaderadd = metaheaderadd,
userinfobox = userinfobox,
navtrailbox = navtrailbox,
pageheaderadd = pageheaderadd,
secure_page_p = secure_page_p,
navmenuid=navmenuid,
rssurl=rssurl) + """
<div class="pagebody">
<div class="pagebodystripeleft">
<div class="pageboxlefttop">%(boxlefttop)s</div>
<div class="pageboxlefttopadd">%(boxlefttopadd)s</div>
<div class="pageboxleftbottomadd">%(boxleftbottomadd)s</div>
<div class="pageboxleftbottom">%(boxleftbottom)s</div>
</div>
<div class="pagebodystriperight">
<div class="pageboxrighttop">%(boxrighttop)s</div>
<div class="pageboxrighttopadd">%(boxrighttopadd)s</div>
<div class="pageboxrightbottomadd">%(boxrightbottomadd)s</div>
<div class="pageboxrightbottom">%(boxrightbottom)s</div>
</div>
<div class="pagebodystripemiddle">
%(titleprologue)s
<h1 class="headline">%(title)s</h1>
%(titleepilogue)s
%(body)s
</div>
</div>
""" % {
'boxlefttop' : boxlefttop,
'boxlefttopadd' : boxlefttopadd,
'boxleftbottom' : boxleftbottom,
'boxleftbottomadd' : boxleftbottomadd,
'boxrighttop' : boxrighttop,
'boxrighttopadd' : boxrighttopadd,
'boxrightbottom' : boxrightbottom,
'boxrightbottomadd' : boxrightbottomadd,
'titleprologue' : titleprologue,
'title' : cgi.escape(title),
'titleepilogue' : titleepilogue,
'body' : body,
} + self.tmpl_pagefooter(req, ln = ln,
lastupdated = lastupdated,
pagefooteradd = pagefooteradd)
return out
def tmpl_pageheader(self, req, ln=CFG_SITE_LANG, headertitle="",
description="", keywords="", userinfobox="",
navtrailbox="", pageheaderadd="", uid=0,
secure_page_p=0, navmenuid="admin", metaheaderadd="",
- rssurl=weburl+"/rss"):
+ rssurl=CFG_SITE_URL+"/rss"):
"""Creates a page header
Parameters:
- 'ln' *string* - The language to display
- 'headertitle' *string* - the second part of the page HTML title
- 'description' *string* - description goes to the metadata in the header of the HTML page
- 'keywords' *string* - keywords goes to the metadata in the header of the HTML page
- 'userinfobox' *string* - the HTML code for the user information box
- 'navtrailbox' *string* - the HTML code for the navigation trail box
- 'pageheaderadd' *string* - additional page header HTML code
- 'uid' *int* - user ID
- 'secure_page_p' *int* (0 or 1) - are we to use HTTPS friendly page elements or not?
- 'navmenuid' *string* - the id of the navigation item to highlight for this page
- 'metaheaderadd' *string* - list of further tags to add to the <HEAD></HEAD> part of the page
- 'rssurl' *string* - the url of the RSS feed for this page
Output:
- HTML code of the page headers
"""
# load the right message language
_ = gettext_set_language(ln)
if headertitle == CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME):
headertitle = _("Home")
out = """\
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>%(sitename)s: %(headertitle)s</title>
<link rev="made" href="mailto:%(sitesupportemail)s" />
<link rel="stylesheet" href="%(cssurl)s/img/cds.css" type="text/css" />
<link rel="alternate" type="application/rss+xml" title="%(sitename)s RSS" href="%(rssurl)s" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="%(description)s" />
<meta name="keywords" content="%(keywords)s" />
%(metaheaderadd)s
</head>
<body>
<div class="pageheader">
<!-- replaced page header -->
<div style="background-image: url(%(cssurl)s/img/header_background.gif);">
<table class="headerbox">
<tr>
<td class="headerboxbodylogo">
%(sitename)s
</td>
<td align="right" valign="top" class="userinfoboxbody">
%(userinfobox)s
</td>
</tr>
<tr>
<td class="headerboxbody" valign="bottom" align="left">
<table class="headermodulebox" width="100%%"><tr><td class="headermoduleboxbodyblanklast">&nbsp;</td></tr></table>
</td>
<td class="headerboxbody" valign="bottom" align="left">
<table class="headermodulebox">
<tr>
<td class="headermoduleboxbodyblank">
&nbsp;
</td>
<td class="headermoduleboxbodyblank">
&nbsp;
</td>
<td class="headermoduleboxbody%(search_selected)s">
- <a class="header%(search_selected)s" href="%(weburl)s/?ln=%(ln)s">%(msg_search)s</a>
+ <a class="header%(search_selected)s" href="%(siteurl)s/?ln=%(ln)s">%(msg_search)s</a>
</td>
<td class="headermoduleboxbodyblank">
&nbsp;
</td>
<td class="headermoduleboxbody%(submit_selected)s">
- <a class="header%(submit_selected)s" href="%(weburl)s/submit?ln=%(ln)s">%(msg_submit)s</a>
+ <a class="header%(submit_selected)s" href="%(siteurl)s/submit?ln=%(ln)s">%(msg_submit)s</a>
</td>
<td class="headermoduleboxbodyblank">
&nbsp;
</td>
<td class="headermoduleboxbody%(personalize_selected)s">
<a class="header%(personalize_selected)s" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(msg_personalize)s</a>
</td>
<td class="headermoduleboxbodyblank">
&nbsp;
</td>
<td class="headermoduleboxbody%(help_selected)s">
- <a class="header%(help_selected)s" href="%(weburl)s/help/%(langlink)s">%(msg_help)s</a>
+ <a class="header%(help_selected)s" href="%(siteurl)s/help/%(langlink)s">%(msg_help)s</a>
</td>
<td class="headermoduleboxbodyblanklast">
&nbsp;
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<table class="navtrailbox">
<tr>
<td class="navtrailboxbody">
%(navtrailbox)s
</td>
</tr>
</table>
<!-- end replaced page header -->
%(pageheaderadd)s
</div>
""" % {
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'sitesecureurl' : CFG_SITE_SECURE_URL,
- 'cssurl' : secure_page_p and CFG_SITE_SECURE_URL or weburl,
+ 'cssurl' : secure_page_p and CFG_SITE_SECURE_URL or CFG_SITE_URL,
'rssurl': rssurl,
'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'sitename' : CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
'headertitle' : cgi.escape(headertitle),
'sitesupportemail' : CFG_SITE_SUPPORT_EMAIL,
'description' : cgi.escape(description),
'keywords' : cgi.escape(keywords),
'metaheaderadd' : metaheaderadd,
'userinfobox' : userinfobox,
'navtrailbox' : navtrailbox,
'pageheaderadd' : pageheaderadd,
'search_selected': navmenuid == 'search' and "selected" or "",
'submit_selected': navmenuid == 'submit' and "selected" or "",
'personalize_selected': navmenuid.startswith('your') and "selected" or "",
'help_selected': navmenuid == 'help' and "selected" or "",
'msg_search' : _("Search"),
'msg_submit' : _("Submit"),
'msg_personalize' : _("Personalize"),
'msg_help' : _("Help"),
'languagebox' : self.tmpl_language_selection_box(req, ln),
}
return out
def tmpl_pagefooter(self, req=None, ln=CFG_SITE_LANG, lastupdated=None,
pagefooteradd=""):
"""Creates a page footer
Parameters:
- 'ln' *string* - The language to display
- 'lastupdated' *string* - when the page was last updated
- 'pagefooteradd' *string* - additional page footer HTML code
Output:
- HTML code of the page headers
"""
# load the right message language
_ = gettext_set_language(ln)
if lastupdated:
if lastupdated.startswith("$Date: ") or \
lastupdated.startswith("$Id: "):
lastupdated = convert_datestruct_to_dategui(\
convert_datecvs_to_datestruct(lastupdated),
ln=ln)
msg_lastupdated = _("Last updated") + ": " + lastupdated
else:
msg_lastupdated = ""
out = """
<div class="pagefooter">
%(pagefooteradd)s
<!-- replaced page footer -->
<div class="pagefooterstripeleft">
- %(sitename)s&nbsp;::&nbsp;<a class="footer" href="%(weburl)s/?ln=%(ln)s">%(msg_search)s</a>&nbsp;::&nbsp;<a class="footer" href="%(weburl)s/submit?ln=%(ln)s">%(msg_submit)s</a>&nbsp;::&nbsp;<a class="footer" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(msg_personalize)s</a>&nbsp;::&nbsp;<a class="footer" href="%(weburl)s/help/%(langlink)s">%(msg_help)s</a>
+ %(sitename)s&nbsp;::&nbsp;<a class="footer" href="%(siteurl)s/?ln=%(ln)s">%(msg_search)s</a>&nbsp;::&nbsp;<a class="footer" href="%(siteurl)s/submit?ln=%(ln)s">%(msg_submit)s</a>&nbsp;::&nbsp;<a class="footer" href="%(sitesecureurl)s/youraccount/display?ln=%(ln)s">%(msg_personalize)s</a>&nbsp;::&nbsp;<a class="footer" href="%(siteurl)s/help/%(langlink)s">%(msg_help)s</a>
<br />
%(msg_poweredby)s <a class="footer" href="http://cdsware.cern.ch/">CDS Invenio</a> v%(version)s
<br />
%(msg_maintainedby)s <a class="footer" href="mailto:%(sitesupportemail)s">%(sitesupportemail)s</a>
<br />
%(msg_lastupdated)s
</div>
<div class="pagefooterstriperight">
%(languagebox)s
</div>
<!-- replaced page footer -->
</div>
</body>
</html>
""" % {
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'sitesecureurl' : CFG_SITE_SECURE_URL,
'ln' : ln,
'langlink': ln != CFG_SITE_LANG and '?ln=' + ln or '',
'sitename' : CFG_SITE_NAME_INTL.get(ln, CFG_SITE_NAME),
'sitesupportemail' : CFG_SITE_SUPPORT_EMAIL,
'msg_search' : _("Search"),
'msg_submit' : _("Submit"),
'msg_personalize' : _("Personalize"),
'msg_help' : _("Help"),
'msg_poweredby' : _("Powered by"),
'msg_maintainedby' : _("Maintained by"),
'msg_lastupdated' : msg_lastupdated,
'languagebox' : self.tmpl_language_selection_box(req, ln),
'version' : CFG_VERSION,
'pagefooteradd' : pagefooteradd,
}
return out
def tmpl_language_selection_box(self, req, language=CFG_SITE_LANG):
"""Take URLARGS and LANGUAGE and return textual language
selection box for the given page.
Parameters:
- 'req' - The mod_python request object
- 'language' *string* - The selected language
"""
# load the right message language
_ = gettext_set_language(language)
# Work on a copy in order not to bork the arguments of the caller
argd = {}
if req and req.args:
argd.update(cgi.parse_qs(req.args))
parts = []
for (lang, lang_namelong) in language_list_long():
if lang == language:
parts.append('<span class="langinfo">%s</span>' % lang_namelong)
else:
# Update the 'ln' argument in the initial request
argd['ln'] = lang
if req and req.uri:
args = urllib.quote(req.uri, '/:?') + make_canonical_urlargd(argd, {})
else:
args = ""
parts.append(create_html_link(args,
{}, lang_namelong,
{'class': "langinfo"}))
return _("This site is also available in the following languages:") + \
"<br />" + ' &nbsp;'.join(parts)
def tmpl_error_box(self, ln, title, verbose, req, errors):
"""Produces an error box.
Parameters:
- 'title' *string* - The title of the error box
- 'ln' *string* - The selected language
- 'verbose' *bool* - If lots of information should be displayed
- 'req' *object* - the request object
- 'errors' list of tuples (error_code, error_message)
"""
# load the right message language
_ = gettext_set_language(ln)
info_not_available = _("N/A")
if title is None:
if errors:
title = _("Error") + ': %s' % errors[0][1]
else:
title = _("Internal Error")
browser_s = _("Browser")
if req:
try:
if req.headers_in.has_key('User-Agent'):
browser_s += ': ' + req.headers_in['User-Agent']
else:
browser_s += ': ' + info_not_available
host_s = req.hostname
page_s = req.unparsed_uri
client_s = req.connection.remote_ip
except: # FIXME: bad except
browser_s += ': ' + info_not_available
host_s = page_s = client_s = info_not_available
else:
browser_s += ': ' + info_not_available
host_s = page_s = client_s = info_not_available
error_s = ''
sys_error_s = ''
traceback_s = ''
if verbose >= 1:
if sys.exc_info()[0]:
sys_error_s = _("System Error") + ': %s %s\n' % \
(sys.exc_info()[0], sys.exc_info()[1])
if errors:
errs = ''
for error_tuple in errors:
try:
errs += "%s%s : %s\n " % (' '*6, error_tuple[0],
error_tuple[1])
except:
errs += "%s%s\n" % (' '*6, error_tuple)
errs = errs[6:-2] # get rid of trainling ','
error_s = _("Error") + ': %s")' % errs + "\n"
else:
error_s = _("Error") + ': ' + info_not_available
if verbose >= 9:
traceback_s = _("Traceback") + ': \n%s' % \
string.join(traceback.format_tb(sys.exc_info()[2]),
"\n")
out = """
<table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
<p> %(title)s %(sys1)s %(sys2)s</p>
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
<p>%(contact)s</p>
<blockquote><pre>
URI: http://%(host)s%(page)s
%(time_label)s: %(time)s
%(browser)s
%(client_label)s: %(client)s
%(error)s%(sys_error)s%(traceback)s
</pre></blockquote>
</td>
</tr>
<tr>
<td>
- <form action="%(weburl)s/error/send" method="post">
+ <form action="%(siteurl)s/error/send" method="post">
%(send_error_label)s
<input class="adminbutton" type="submit" value="%(send_label)s" />
<input type="hidden" name="header" value="%(esc_title)s %(esc_sys1)s %(esc_sys2)s" />
<input type="hidden" name="url" value="URI: http://%(host)s%(page)s" />
<input type="hidden" name="time" value="Time: %(time)s" />
<input type="hidden" name="browser" value="%(browser)s" />
<input type="hidden" name="client" value="Client: %(client)s" />
<input type="hidden" name="error" value="%(esc_error)s" />
<input type="hidden" name="sys_error" value="%(sys_error)s" />
<input type="hidden" name="traceback" value="%(esc_traceback)s" />
<input type="hidden" name="referer" value="%(referer)s" />
</form>
</td>
</tr>
</tbody>
</table>
""" % {
'title' : title,
'esc_title' : title.replace('"', '&quot;'),
'time_label': _("Time"),
'client_label': _("Client"),
'send_error_label': \
_("Please send an error report to the Administrator."),
'send_label': _("Send error report"),
'sys1' : sys.exc_info()[0] or '',
'sys2' : sys.exc_info()[1] or '',
'esc_sys1' : str((sys.exc_info()[0] or '')).replace('"', '&quot;"'),
'esc_sys2' : str((sys.exc_info()[1] or '')).replace('"', '&quot;"'),
'contact' : \
_("Please contact %s quoting the following information:") % \
('<a href="mailto:' + urllib.quote(CFG_SITE_SUPPORT_EMAIL) +'">' + \
CFG_SITE_SUPPORT_EMAIL + '</a>'),
'host' : host_s,
'page' : page_s,
'time' : time.strftime("%d/%b/%Y:%H:%M:%S %z"),
'browser' : browser_s,
'client' : client_s,
'esc_error' : error_s,
'error' : error_s.replace('"', '&quot;"'),
'traceback' : traceback_s,
'esc_traceback' : traceback_s.replace('"', '&quot;"'),
'sys_error' : sys_error_s,
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'referer' : page_s!=info_not_available and \
("http://" + host_s + page_s) or \
info_not_available
}
return out
def detailed_record_container(self, content, recid, tabs, ln=CFG_SITE_LANG,
show_similar_rec_p=True,
creationdate=None,
modificationdate=None, show_short_rec_p=True):
"""Prints the box displayed in detailed records pages, with tabs at the top.
Parameters:
- content *string* - the content displayed inside the box
- recid *int* - the id of the displayed record
- tabs ** - the tabs displayed at the top of the box.
- ln *string* - the language of the page in which the box is displayed
- show_similar_rec_p *bool* print 'similar records' link in the box
- creationdate *string* - the creation date of the displayed record
- modificationdate *string* - the last modification date of the displayed record
- show_short_rec_p *boolean* - prints a very short version of the record as reminder.
"""
# load the right message language
_ = gettext_set_language(ln)
# If no tabs, simply returns the content
if len(tabs) == 0:
return content
# Build the tabs at the top of the page
out_tabs = ''
if len(tabs) > 1:
first_tab = True
for (label, url, selected, enabled) in tabs:
css_class = []
if selected:
css_class.append('on')
if first_tab:
css_class.append('first')
first_tab = False
if not enabled:
css_class.append('disabled')
css_class = ' class="%s"' % ' '.join(css_class)
if not enabled:
out_tabs += '<li%(class)s><a>%(label)s</a></li>' % \
{'class':css_class,
'label':label}
else:
out_tabs += '<li%(class)s><a href="%(url)s">%(label)s</a></li>' % \
{'class':css_class,
'url':url,
'label':label}
if out_tabs != '':
out_tabs = ''' <div class="detailedrecordtabs">
<div>
<ul class="detailedrecordtabs">%s</ul>
<div style="clear:both;height:00px">&nbsp;</div></div>
</div>''' % out_tabs
# Add the clip icon and the brief record reminder if necessary
if show_short_rec_p:
record_details = format_record(recID=recid, of='hs', ln=ln)
content = '''<div id="detailedrecordshortreminder">
<div id="clip">&nbsp;</div>
<div id="HB">
%(record_details)s
</div>
</div>
<div style="clear:both;height:1px">&nbsp;</div>
%(content)s
''' % {'content': content,
'record_details': record_details}
# Print the content
out = """
<div class="detailedrecordbox">
%(tabs)s
<div class="detailedrecordboxcontent">
<div class="top-left-folded"></div>
<div class="top-right-folded"></div>
<div class="inside">
<!--<div style="height:0.1em;">&nbsp;</div>
<p class="notopgap">&nbsp;</p>-->
%(content)s
<p class="nobottomgap" >&nbsp;</p>
</div>
<div class="bottom-left-folded">%(dates)s</div>
<div class="bottom-right-folded" style="text-align:right"><span class="moreinfo" style="margin-right:25px">%(similar)s</span></div>
</div>
</div>
<br/>
""" % {
'tabs':out_tabs,
'content':content,
'similar':create_html_link(
websearch_templates.build_search_url(p='recid:%d' % \
recid,
rm='wrd',
ln=ln),
{}, _("Similar records"),
{'class': "moreinfo"}),
'dates':creationdate and '<div class="recordlastmodifiedbox" style="float:left;position:relative;margin-left:1px">&nbsp;%(dates)s</div>' % {
'dates': _("Record created %(x_date_creation)s, last modified %(x_date_modification)s") % \
{'x_date_creation': creationdate,
'x_date_modification': modificationdate},
} or ''
}
return out
def detailed_record_mini_panel(self, recid, ln=CFG_SITE_LANG,
format='hd',
files='',
reviews='',
actions=''):
"""Displays the actions dock at the bottom of the detailed record
pages.
Parameters:
- recid *int* - the id of the displayed record
- ln *string* - interface language code
- format *string* - the format used to display the record
- files *string* - the small panel representing the fulltext
- reviews *string* - the small panel representing the reviews
- actions *string* - the small panel representing the possible user's action
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<br />
<div class="detailedrecordminipanel">
<div class="top-left"></div><div class="top-right"></div>
<div class="inside">
<div id="detailedrecordminipanelfile" style="width:33%%;float:left;text-align:center;margin-top:0">
%(files)s
</div>
<div id="detailedrecordminipanelreview" style="width:33%%;float:left;text-align:center">
%(reviews)s
</div>
<div id="detailedrecordminipanelactions" style="width:33%%;float:right;text-align:right;">
%(actions)s
</div>
<div style="clear:both;margin-bottom: 0;"></div>
</div>
<div class="bottom-left"></div><div class="bottom-right"></div>
</div>
""" % {
- 'weburl': weburl,
+ 'siteurl': CFG_SITE_URL,
'ln':ln,
'recid':recid,
'files': files,
'reviews':reviews,
'actions': actions,
}
return out
diff --git a/modules/websubmit/doc/admin/websubmit-admin-guide.webdoc b/modules/websubmit/doc/admin/websubmit-admin-guide.webdoc
index 022358483..5063ec07d 100644
--- a/modules/websubmit/doc/admin/websubmit-admin-guide.webdoc
+++ b/modules/websubmit/doc/admin/websubmit-admin-guide.webdoc
@@ -1,2217 +1,2217 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(WebSubmit Admin Guide)_ -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/admin<lang:link/>">_(Admin Area)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
<p><table class="errorbox">
<thead>
<tr>
<th class="errorboxheader">
WARNING: OLD WEBSUBMIT ADMIN GUIDE FOLLOWS
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="errorboxbody">
This WebSubmit Admin Guide was written for the previous PHP-based
version of the admin tool. The submission concepts and pipeline
description remain valid, but the interface snapshot examples
would now differ. The guide is to be updated soon.
</td>
</tr>
</tbody>
</table>
<h1>Table of Contents</h1>
<ul>
<li><b>Introduction</b>
<ul>
<li><a href="#introduction">General Overview of the Manager Tool</a>
<li><a href="#example">Using the manager through an example</a>
<li><a href="#philosophy">Philosophy behind the document submission system</a>
</ul>
<li><b>The Interface</b>
<ul>
<li><a href="#description">Description</a>
</ul>
<li><b><a href="#documents">Types of Document</a></b>
<ul>
<li><a href="#documentnew">Add a New Type of Document</a>
<li><a href="#documentremove">Remove a type of document</a>
<li><a href="#documentmodify">Modify an Existing Type of Document</a>
</ul>
<li><b><a href="#actions">Actions</a></b>
<ul>
<li><a href="#actionnew">Add a New Action</a>
<li><a href="#actionremove">Remove an Action</a>
<li><a href="#actionmodify">Modify an Existing Action</a>
<li><a href="#actionimplement">Implement an Action over a Document Type</a>
<ul>
<li><a href="#implementwebform">Create and Maintain the Web Form</a>
<li><a href="#implementfunctions">Create and Maintain the Data Treatment</a>
</ul>
</ul>
<li><b><a href="#functions">Functions</a></b>
<ul>
<li><a href="#functionnew">Create a New Function</a>
<li><a href="#functiondelete">Remove a Function</a>
<li><a href="#functionedit">Edit a Function</a>
<li><a href="#functiondescription">All Functions Explained</a>
</ul>
<li><b><a href="#protection">Protection</a></b>
<li><b><a href="#catalogues">Catalogues Organisation</a></b>
<li><b><a href="#bibconvert">BibConvert</a></b>
<li><b>Notes</b>
<ul>
</ul>
<li><b><a href="#faq">FAQ</a></b>
</ul>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="introduction"></a>
<h1>General Overview of the Manager Tool</h1>
<h3>Things to know before using the Manager:</h3>
<blockquote>
<ul>
&nbsp;<span class="guideheader">T</span>his manager tool allows you to administrate all the WebSubmit
interface. With it, you will be able to create new actions, new types of documents and edit the existing ones.
<br/><br/>
&nbsp;<span class="guideheader">T</span>he main objects in webSubmit are the "action" (such as
"Submit New Record", "Submit New File", "Modify Record"...) and the "type of document" (such as "preprint",
"photo"...).<br/><br/>
&nbsp;<span class="guideheader">T</span>o one given type of document can be attached several actions.
An action is the addition of two processes:
<ul>
<li>The first one is the <a href="#implementwebform">data gathering</a>. The manager
will allow you to create new web forms corresponding to the fields the user will have to fill in when using
webSubmit.
<li>The second one is the <a href="#implementfunctions">data treatement</a>.
Basically, what the program will do with the data gathered during the first phase. The treatment appears
in this tool as a sequence of functions. This manager will allow you to add functions to an action, edit the
existing functions, and reorder the functions.
</ul>
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#example">using the manager through an example</a><br/>
<li><a href="#description">interface description</a><br/>
<li><a href="#actions">actions</a><br/>
<li><a href="#documents">document types</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="example"></a>
<h1>Using the manager through an example</h1>
<h3>what is this?</h3>
<blockquote>
<ul>
This page presents you the typical situations a user could meet using WebSubmit, and for each situation how to use the manager to configure it.
</ul>
</blockquote>
<h3>The user reaches WebSubmit main page.</h3>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-main_menu.png" alt="Main Page" class="guideimg" align="left">
- &nbsp;<span class="guideheader">T</span>o add a document type to WebSubmit, you should go to the <a target=top href="<WEBURL>/admin/websubmit/index.php">main page</a>
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-main_menu.png" alt="Main Page" class="guideimg" align="left">
+ &nbsp;<span class="guideheader">T</span>o add a document type to WebSubmit, you should go to the <a target=top href="<CFG_SITE_URL>/admin/websubmit/index.php">main page</a>
and click on "New Doctype" in the left blue panel.<br/><br/>
&nbsp;<span class="guideheader">E</span>ven once created, a document type will not appear automatically on this page. To configure the list of catalogues and document
- types displayed on this page, the administrator shall go to the <a target=top href="<WEBURL>/admin/websubmit/editCatalogues.php">edit catalogues</a>
+ types displayed on this page, the administrator shall go to the <a target=top href="<CFG_SITE_URL>/admin/websubmit/editCatalogues.php">edit catalogues</a>
page. (see the <a href="#catalogues">guide section</a>)<br clear="all"/>
<h3>The user can then click on the document type he is interested in.</h3>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-menu_doc.png" alt="Document type Page" class="guideimg" align="left">
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-menu_doc.png" alt="Document type Page" class="guideimg" align="left">
&nbsp;<span class="guideheader">T</span>he text appearing under the header containing the name of the document
can be configured by going to the <a target=top href="websubmit-admin">main page</a>, click on
the title of the document type then on the "Edit Document Types Details" button.<br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can associate several categories to a document type which can be defined by going to the
<a target=top href="websubmit-admin">main page</a>, click on the title of the document type
then on the "View Categories" button. The selected category will be saved in a file named "comboXXX"
(where XXX is the short name of the document type) in the submission directory.<br/><br/>
&nbsp;<span class="guideheader">T</span>o add an action button to this page, first implement this action by going to the
<a target=top href="websubmit-admin">main page</a>, click on the title of the document type then
on the "Add a new submission" button. If the action is already implemented and the button still does not appear
on the submision page, then you should edit the details of this implementation: go to the
<a target=top href="websubmit-admin">main page</a>, click on the title of the document type then
on the icon in the "Edit Submission" column and in the line of the desired action. There you should set the
"Displayed" form field to "YES".<br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can also change the order of the buttons, by going to the <a target=top href="websubmit-admin">
main page</a>, click on the title of the document type then on the icon in the "Edit Submission" column and in the
line of the desired action. There you can set the "buttonorder" form field.<br/><br clear="all"/>
<h3>The user now may choose a category, then click on the action button he wishes.<br/>The submission starts, the first page of the web form appears.</h3>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-form.png" alt="Document type Page" class="guideimg" align="left">
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-form.png" alt="Document type Page" class="guideimg" align="left">
&nbsp;<span class="guideheader">T</span>his web form is composed of several pages, on each of these
pages form fields can be found. To modify the number of pages, add or withdraw form fields and modify
the texts before each form field, you shall go to the <a target=top href="websubmit-admin">main page</a>,
click on the title of the document type then on the icon in the "Edit Submission Pages" column and in the line of the
desired action. (see the <a href="#actionimplement">guide section</a>)<br/><br clear="all"/>
<h3>On the last page of the submission, there should be a button like in the following image which will
trigger the end script</h3>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-end_action.png" alt="Document type End Page" class="guideimg" align="left">
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-end_action.png" alt="Document type End Page" class="guideimg" align="left">
&nbsp;<span class="guideheader">T</span>his button is defined like any other form field. Its definition should include
a <i> onclick="finish();"</i> javascript attribute.<br/><br/>
&nbsp;<span class="guideheader">A</span>fter clicking this button, WebSubmit will apply the end script functions
to the gathered data. To modify the end script, you shall go to the <a target=top href="websubmit-admin">
main page</a>, click on the title of the document type then on the icon in the "Edit Functions" column and in the line
of the desired action. (see the <a href="#implementfunctions">guide section</a>)<br clear="all"/>
<h3>See also:</h3>
<blockquote>
<a href="#description">interface description</a><br/>
<a href="#actions">actions</a><br/>
<a href="#documents">document types</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="philosophy"></a>
<h1>Philosophy behind the document submission system</h1>
<p>This page will explain some philosophical issues behind the document submission system.
<h3>On the relation between a search collection and a submission doctype:</h3>
<blockquote>
<ul>
&nbsp;<span class="guideheader">T</span>he relation
between a search collection and a submission document
type may be prone to certain confusion for CDS Invenio
administrators. This comes from the fact that there is
no one-to-one direct mapping between them, as is usual
elsewhere. The relation is more flexible than that.<br/><br/>
&nbsp;<span class="guideheader">A</span> search
collection in CDS Invenio is defined through a search
query. For example, "all records where field F
contains the value V belong to collection C". Several
assertions can be deduced from this definition:<br/>
&nbsp;1/ A single record can appear in several collections.<br/>
&nbsp;2/ There is no limitation to the number of
collections in which a record can appear.<br/>
&nbsp;3/ Any query can be used to build a
collection. The query can also be a complex one using
logical operators, hence can rely on the value of
several fields.<br/><br/>
&nbsp;(In addition, a search collection can be defined
via a set of its subcollections in the hierarchy tree.
Refer to the <a
href="websearch-guide">WebSearch
Admin Guide</a> for that matter.)<br/><br/>
&nbsp;<span class="guideheader">T</span>he submission
system basically creates an XML MARC record and stores
it in the database. To which collection this new
record belongs depends exclusively on the content of
the XML MARC record. This XML MARC record is created
by the <a href="#Make_Record">Make_Record</a> function. So the
secret of the matching of a submitted record to a
particular collection lies in the configuration of
this function. Some examples will clarify this
point:<br/><br/>
&nbsp;<span class="guideheader">E</span>xample 1:
Let's consider a "Preprints" collection which is
defined by this query: "980__a:PREPRINT". We want to
create a submission document type from which all
records will go to this "Preprints" collection. For
this, the Make_Record function should be configured so
that a 980__a field containing "PREPRINT" will always
be created.<br/>
&nbsp;<span class="guideheader">E</span>xample 2:
Let's still consider the same "Preprints" collection,
and an additional "Theses" collection based on a
slightly different query "980__a:THESIS". We want to
create a single submission type from which the records
will go in the "Preprints" or "Theses" collections
depending on a field chosen by the submitter. In this
case, the Make_Record function should be configured so
that a 980__a field will contain either "PREPRINT" or
"THESIS" depending on the value entered by
the submitter.<br/><br/>
&nbsp;<span class="guideheader">T</span>he apparent
disconnection between a submission document type and a
search collection allows a great flexibility, allowing
administrators to create 1 to 1, 1 to n, n to 1 or
even 1 to 0 (not very useful!) relations.
</ul>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="description"></a>
<h1>Interface Description</h1>
<h3>Welcome to webSubmit Management tool:</h3>
<blockquote>
<ul>
&nbsp;<span class="guideheader">o</span>n the websubmit admin <a href="websubmit-admin">main page</a> you will find:<br/><br/>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-main_page.png" class="guideimg"><br/><br/>
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-main_page.png" class="guideimg"><br/><br/>
<ul>
<li>The list of all existing document type in the middle of the page. Click on one line in the list to have
access to the main document modification panel
<li>The right menu panel with the following links inside:
<ul>
<li>"<b>webSubmit Admin</b>": This links leads you back to the main page of the manager.
<li>"<b>New Doctype</b>": Click here if you wish to create a new document type.
<li>"<b>Remove Doctype</b>": Click here if you want to remove an existing document type.
<li>"<b>Available Actions</b>": Lists all existing actions
<li>"<b>Available Javascript Checks</b>": Lists all existing Javascript checking functions.
<li>"<b>Available Element Description</b>": Lists all existing html form element descriptions.
<li>"<b>Available Functions</b>": Lists all existing functions in CDS Submit.
<li>"<b>Organise Main Page</b>": Allows you to manage the appearance and order of the list of document
types on CDS Submit User main page.
</ul>
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<a href="#description">interface description</a><br/>
<a href="#actions">actions</a><br/>
<a href="#documents">document types</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="documents"></a>
<h1>Document Types</h1>
<blockquote>
<ul>
&nbsp;<span class="guideheader">W</span>ebSubmit can propose several actions on different document
types. Each of these document type may or may not implement all possible actions. The main difference
between each document type is the metadata which define each of them, and may also be the kind of fulltext
files attached to one record. <br/><br/>
&nbsp;<span class="guideheader">A</span> document type can be one of "Thesis", "Photos", "Videotapes"...
or whatever type of document you may invent. A document type is always defined by its metadata. It may or
may not have a fulltext file attached to it.<br/><br/>
&nbsp;<span class="guideheader">T</span>his tool leaves you free to create the web forms adapted to whatever type of document you want to
create (see "<a href="#implementwebform">Create and Maintain the Web Form</a>") as well as free
to determine what treatment you wish to apply to the collected data (see
"<a href="#implementfunctions">Create and Maintain the Data Treatment</a>").
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<a href="#documentnew">add a new type of document</a><br/>
<a href="#documentremove">remove a type of document</a><br/>
<a href="#documentmodify">modify a type of document</a><br/>
<a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="documentnew"></a>
<h1>Ading new type of document</h1>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "New Doctype" link in the webSubmit right menu.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">A</span> new document type is defined by 6 fields:<br/>
<ul>
<li><b>Creation Date</b> and <b>Modification Dates</b> are generated and modified automatically.<br/>
<li><b>Document Type ID</b>: This is the acronym for your new document type. We usually use a 3 letters
acronym.
<li><b>Document Type Name</b>: This is the full name of your new document. This is the text which will
appear on the list of available documents and catalogues on webSubmit main page.
<li><b>Document Type Description</b>: This is the text which will appear on the document type submission
page. This can be pure text or html.
<li><b>Doctype to clone</b>: Here you can choose to create your document type as a clone of another
existing document type. If so, the new document type will implement all actions implemented by the chosen
one. The web forms will be the same, and the functions also, as well as the values of the parameters for
these functions. Of course once cloned, you will be able to modify the implemented actions.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#documentremove">remove a type of document</a><br/>
<li><a href="#documentmodify">modify a type of document</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="documentremove"></a>
<h1>Removing a Document Type</h1>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Remove Doctype" link in the
webSubmit admin right menu
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">S</span>elect the document type to delete then click on the "Remove Doctype" button. Remember by doing this, you
will delete this document type as well as all the implementation of actions for this document type!
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#documentnew">create a type of document</a><br/>
<li><a href="#documentmodify">modify a type of document</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="documentmodify"></a>
<h1>Modifying a Document Type</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">M</span>odifying a document type in webSubmit - this will modify its
general data description, not the implementations of
the actions on this document type. For the later, please see <a href="#actionimplement">
implement an action over a type of document</a>.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the main page of the manager, click on the title of the
document type you want to modify, then click on the "Edit Document Type Details".
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">O</span>nce here, you can modify 2 fields:<br/>
<li><b>Document Type Name</b>: This is the full name of your new document. This is the text which will appear
on the list of available documents and catalogues on webSubmit main page.
<li><b>Document Type Description</b>: This is the text which will appear on the right of the screen when the user
moves the mouse over the document type title and on the document type submission page. This can be pure
text or html.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#documentremove">remove a type of document</a><br/>
<li><a href="#documentnew">create a type of document</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actions"></a>
<h1>Actions</h1>
<blockquote>
&nbsp;<span class="guideheader">I</span>n webSubmit you can create several actions (for example
"Submit New Record", "Submit a New File", "Send to a Distribution List", etc. in fact any action you can imagine
to perform on a document stored in your database). The creation of an action is very simple and consists in
filling in a name, description and associating a directory to this action. The directory parameter indicates where
the collected data will be stored when the action is carried on.<br/><br/>
&nbsp;<span class="guideheader">O</span>nce an action is created, you have to implement it over a document
type. Implementing an action means defining the web form which will be displayed to a user, and defining the
treatment (set of functions) applied to the data which have been gathered. The implementation of the same action
over two document types can be very different. The fields in the web form can be different as well as the functions
applied at the end of this action.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#actionnew">create a new action</a><br/>
<li><a href="#actionremove">remove an action</a><br/>
<li><a href="#actionmodify">modify an action</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actionnew"></a>
<h1>Adding a New Action</h1>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Available Actions" link in the websubmit right menu,
then on the "Add an Action" button.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">A</span> new action is defined by 6 fields:<br/><br/>
<ul>
<li><b>Creation Date</b> and <b>Modification Dates</b> are generated and modified automatically.<br/>
<li><b>Action Code</b>: This is the acronym for your new action. We usually use a 3 letters acronym.
<li><b>Action Description</b>: This is a short description of the new action.
<li><b>dir</b>: This is the name of the directory in which the submission data will be stored temporarily. If
the dir value is "running" as for the "Submit New Record" action (SBI), then the submission data for a
Text Document (document acronym "TEXT") will be stored in the
/opt/cds-invenio/var/data/submit/storage/running/TEXT/9089760_90540 directory (where 9089760_90540 is what we call
the submission number. It is a string automatically generated at the beginning of each submission). Once
finished, the submission data will be moved to the
/opt/cds-invenio/var/data/submit/storage/done/running/TEXT/ directory by the "Move_to_Done" function.
<li><b>statustext</b>: text displayed in the status bar of the browser when the user moves his mouse upon
the action button.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#actionremove">remove an action</a><br/>
<li><a href="#actionmodify">modify an action</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actionremove"></a>
<h1>Removing an Action</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">R</span>emoving the implementation of an action over a document type -
Please note the removal of the action itself is not allowed with this tool.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the websubmit admin main page, click on the title of the
relevant document type. Then click on the red cross corresponding to the line of the action you want to remove.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#actionnew">create an action</a><br/>
<li><a href="#actionmodify">modify an action</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actionmodify"></a>
<h1>Modifying an Action</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">T</span>his page is about how to modify the general data about an
action - for modifying the implementation of an action over a document type, see
<a href="#actionimplement">implement an action over a type of document</a>
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "View Actions" link in the right menu of the websubmit
admin, then on the title of the action you want to modify...
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">Y</span>ou may modify 3 fields:<br/>
<ul>
<li><b>Action Description</b>: This is a short description of the new action.
<li><b>dir</b>: This is the name of the directory in which the submission data will be stored temporarily.
See the meaning of this parameter in <a href="#actionnew">create an action</a>.
<li><b>statustext</b>: text displayed in the status bar of the browser when the user moves his mouse
upon the action button.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#actionremove">remove an action</a><br/>
<li><a href="#actionnew">create an action</a><br/>
<li><a href="#actionimplement">implement an action over a type of document</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="actionimplement"></a>
<h1>Implement an action over a document type</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>mplement an action over a document type. Create the web forms
and the treatment process.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the main page of the manager, click on the title of the
relevant document type.<br/>Then click on the "Add a New Submission" button.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">J</span>ust select the name of the action you want to implement. When you
select an action, the list of document which already implement this action appears. Then you can select from
this list the document from which you want to clone the implementation, or just choose "No Clone" if you want
to build this implementation from scratch.<br/><br/>
&nbsp;<span class="guideheader">A</span>fter selecting the correct fields, click on the "Add Submission"
button.<br/><br/>
&nbsp;<span class="guideheader">Y</span>ou then go back to the document type manager page where you
can see that in the bottom array your newly implemented action appears (check the acronym in the first
column).<br/><br/>
- <img src="<WEBURL>/img/admin/websubmit-admin-guide-implement.png" class="guideimg"><br/><br/>
+ <img src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-implement.png" class="guideimg"><br/><br/>
<ul>
<li>Clicking on the action acronym will allow you to modify the general data about the action (remember
in this case that all the other implementations of this particular action will also be changed).
<li>The second column indicates whether the button representing this action will appear on the submission page.
<li>The third column shows you the number of pages composing the web form for this implementation.
(see <a href="#implementwebform">create and maintain the web form</a>).
<li>The 4th and 5th columns indicate the creation and last modification dates for this implementation.
<li>In the 6th column, you can find the order in which the button will be displayed on the submission page
of this document type.<br/>
<li>The following 4 columns (level, score, stpage, endtxt) deal with the insertion of this action in an action
set.<br/><br/>
<table border=0 bgcolor="eeeeff"><tr><td><small><br/>
An action set is a succession of actions which should be done in a given order when a user starts.<br/>
For example the submission of a document is usually composed of two actions: Submission of Bibliographic
Information (SBI) and Fulltext Transfer (FTT) which should be done one after the other.<br/>
When the user starts the submission, we want CDS Submit to get him first in SBI and when he finishes SBI to
carry him to FTT.<br/>
SBI and FTT are in this case in the same action set.<br/>
They will both have a level of 1 ("level" is a bad name, it should be "action set number"), SBI will have a
score of 1, and FTT a score of 2 (which means it will be started after SBI). If you set the stpage of FTT to 2,
the user will be directly carried to the 2nd page of the FTT web form. This value is usually set to 1.<br/>
The endtxt field contains the text which will be display to the user at the end of the first action (here
it could be "you now have to transfer your files")
<br/><br/>
A single action like "Modify Bibliographic Information" should have the 3 columns to 0,0 and 1.<br/>&nbsp;
</small></td></tr></TABLE>
<br/><br/>
<li>Click on the icon in the 12th column ("Edit Submission Pages") to
<a href="#implementwebform">create or edit the web form</a>.
<li>Click on the icon in the 13th column ("Edit Functions") to
<a href="#implementfunctions">create or edit the function list</a>.
<li>The "Edit Submission" column allows you to modify the data (level, status text...) for this implementation.
<li> Finally the last column allows you to delete this implementation.<br/>&nbsp;
</ul><br/>
&nbsp;<span class="guideheader">I</span>f you chose to clone the implementation from an existing one,
the web form as well as the functions list will already be defined. Else you will have to create them from scratch.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#implementwebform">create and maintain the web form</a><br/>
<li><a href="#implementfunctions">create and maintain the data treatment</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="implementwebform"></a>
<h1>Create and maintain the web form</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>reate and define the web form used during an action.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the main page of the manager, click on the title of the relevant
document type. Then click on the icon in the "Edit Submission Pages" column of the relevant line.
</blockquote>
<h3>List of the form pages</h3>
<blockquote>
&nbsp;<span class="guideheader">A</span> web form can be split over several pages. This is a matter
of easiness for the user: he will have an overview of all form fields present on the page without having to scroll it.
Moreover, each time the user goes from one page to the other, all entered data are saved. If he wants to stop
then come back later (or if the browser crashes!) he will be able to get back to the submission at the exact
moment he left it.<br/><br/>
&nbsp;<span class="guideheader">O</span>nce here:<br/><br/>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-menu_page.png" class="guideimg"><br/><br/>
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-menu_page.png" class="guideimg"><br/><br/>
you can see the ordered list of already existing pages in the web form. In this example there are 4 pages.
You can then:
<ul>
<li> Move one page from one place to an other, using the small blue arrows under each page number.
<li>Suppress one page by clicking on the relevant red cross.
<li>Add a page, by clicking the "ADD A PAGE" button!
<li><a href="#onepage">Edit the content of one page</a> by clicking on the page number.
<li>Go back to the document main page.
</ul>
</blockquote>
<a name="onepage"></a>
<h3>Edit one form page</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on a page number, you then arrive to a place where you can
edit this form page.<br/><br/>
&nbsp;<span class="guideheader">A</span> form page is composed of a list of form elements. Each of these
form elements is roughly made of an html template and a text displayed before the form field.<br/><br/>
&nbsp;<span class="guideheader">I</span>n the first part of the page, you have a preview of what the form
will look like to the user:<br/>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-preview.png" class="guideimg"><br/><br/>
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-preview.png" class="guideimg"><br/><br/>
&nbsp;<span class="guideheader">T</span>hen the second table shows you the list of the form elements
present on the page:<br/>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-elements.png" class="guideimg"><br/><br/>
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-elements.png" class="guideimg"><br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can then:
<ul>
<li>Move one element from one place to another using the drop-down menus in the first
column ("Item No") of the table, or the little blue arrows in the second column.
<li><a href="#edittemplate">Edit the html template of one form element</a> by clicking on the name of the
template in the 3rd column ("Name").
<li><a href="#editelement">Edit one of the form elements</a> by clicking on the icon in the 10th column.
<li>delete one form element by clicking on the relevant red cross.
<li><a href="#addelement">Add an element to the page</a> by clicking the "ADD ELEMENT TO PAGE" button.
</ul>
</blockquote>
<a name="edittemplate"></a>
<h3>Edit the html template of one form element</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>n the html template edition page, you can modify the following values:
<ul>
<li><b>Element type</b>: indicates which html form element to create
<li><b>Aleph code</b>: <font color=red>Aleph users only!</font> - This indicates in which field of the Aleph
document database to retrieve the original value when modifying this information (function
Create_Modify_Interface of action MBI).
<li><b>Marc Code</b>: <font color=red>MySQL users only!</font> - This indicates in which field of the MySQL
document database to retrieve the original value when modifying this information (function
Create_Modify_Interface of action MBI).
<li><b>Cookies</b>: indicates whether WebSubmit will set a cookie on the value filled in by the user. If yes,
next time the user will come to this submission, the value he has entered last time will be filled in
automatically. <span style="font-weight: bold;">Note:</span> <span style="color: red; font-weight: bold;">
This feature has been REMOVED.</span>
<li><b>other fields</b>: The other fields help defining the html form element.
</ul>
<font color=red>Important warning!</font> Please remember this is a template! This means it can be used in
many different web forms/implementations. When you modify this template the modification will take place in
each of the implementations this template has been used.
</blockquote>
<a name="editelement"></a>
<h3>Edit one form element</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>n the form element edition page, you may modify the following values:
<ul>
<li><b>element label</b>: This is the text displayed before the actual form field.
<li><b>level</b>: can be one of "mandatory" or "optional". If mandatory, the user won't be able to leave
this page before filling this field in.
<li><b>short desc</b>: This is the text displayed in the summary window when it is opened.
<li><b>Check</b>: Select here the <a href="#addcheck">javascript checking function</a> to be applied to
the submitted value of this field
<li><b>Modify Text</b>: This text will be displayed before the form field when modifying the value (action
"Modify Record", function "Create_Modify_Interface")
</ul>
</blockquote>
<a name="addelement"></a>
<h3>Add one form element</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "ADD ELEMENT TO PAGE" button. There you will have
to decide which <a href="#addtemplate">html template field</a> to use ("Element Description code"), and
also the field mentioned <a href="#editelement">above</a>.
</blockquote>
<a name="addtemplate"></a>
<h3>Create a new html template</h3>
<blockquote>
&nbsp;<span class="guideheader">Y</span>ou have access to the list of all existing html templates by clicking
on the "View element descriptions" link in the websubmit admin right menu.<br/>
By clicking on one of them, you will have access to its description.<br/>
If no template corresponds to the one you seek, click on the "ADD NEW ELEMENT DESCRIPTION" button to
create one.<br/>
&nbsp;<span class="guideheader">T</span>he fields you have to enter in the creation form are the one
described in the <a href="#edittemplate">Edit the html template of one form element</a> section.<br/>
You also have to choose a name for this new element.<br/>
<font color=red>IMPORTANT!</font> The name you choose for your html element is also the name of the file
in which webSubmit will save the value entered in this field. This is also the one you will use in your
<a href="#bibconvert">BibConvert</a> configuration. Bibconvert is the program which will
convert the data gathered in webSubmit in a formatted XML file for insertion in the documents database.
<br/>
&nbsp;<span class="guideheader">T</span>ips:
<li>Elements of type "select box" which are used as a mandatory field in a form must start with "&lt;option&gt;Select:&lt;/option&gt;"
</blockquote>
<a name="addcheck"></a>
<h3>Create and edit a checking function.</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "View Checks" link in the websubmit admin right menu.
You then have access to a list of all the defined javascript functions.<br/>
You can then click on the name of the function you want to modify, or click on the "ADD NEW CHECK" button
to create a new javascript function.<br/>
These functions are inserted in the web page when the user is doing his submission. When he clicks on
"next page", this function will be called with the value entered by the user as a parameter. If the function returns
false, the page does not change and an error message should be output. If the function returns true, everything
is correct, so page can be changed.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#implementfunctions">create and maintain the data treatment</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="implementfunctions"></a>
<h1>Setup the Data Treatment</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">A</span>t the end of a submission, we have to tell webSubmit what to do
with the data it has gathered. This is expressed through one or several lists of functions (we call this the
"end script").
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">F</span>rom the main page of the manager, click on the title of the relevant
document type.<br/>Then click on the icon in the "Edit Functions" column of the relevant line.
</blockquote>
<h3>List of functions</h3>
<blockquote>
&nbsp;<span class="guideheader">H</span>ere is what you may see then (this is the end script list of functions
for a document type named "TEST" and action "FTT" - Fulltext Transfer):<br/><br/>
- <IMG src="<WEBURL>/img/admin/websubmit-admin-guide-list_functions.png" class="guideimg"><br/><br/>
+ <IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-list_functions.png" class="guideimg"><br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can see the ordered list of all the functions in the end script.
This end script is composed of 2 steps (see the "step" column). The functions composing the first step are called,
then there should be action from the user which would trigger step 2 - in the present case the
<a href="#Upload_Files">Upload_Files</a> function (last of step 1) allows
the user to upload additional files by creating a web form, then when the user finishes, he presses another
button created by the function, which ends the process. Functions of step 2 are then called.<br/><br/>
&nbsp;<span class="guideheader">W</span>hy implement multiple steps? The reason can vary with the task
you want to accomplish. For example with the example above (Fulltext Transfer), we use the first step to allow
the upload of multiple additional files (dynamic action) which could not be done in the
<a href="#implementwebform">static web form</a>. In the case of the
"Modify Bibliographic Information" action, the first step is used to display the fields the user wants to modify,
prefilled with the existing values. The reason is once again that the task we want to realise is dynamic.<br/><br/>
&nbsp;<span class="guideheader">T</span>he "score" column is used to order the functions. The function
which has the smallest score will be called first, and the largest score will be called last.<br/><br/>
&nbsp;<span class="guideheader">Y</span>ou can then:
<ul>
<li> View and edit the parameters of each function by clicking on the name of the function.
<li> Move one function up and down, by using the small blue arrows.
<li> Suppress one function by clicking on the relevant red cross.
<li> Add a function to the list by clicking the "ADD FUNCTION" button.
<li> Go back to the document main page ("FINISHED" button).
</ul>
&nbsp;<span class="guideheader">P</span>lease note: To pass one function from one step to another,
you have to delete it then add it again in the proper step.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functions">all about functions</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functions"></a>
<h1>Functions</h1>
<h3>Description:</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>n webSubmit, each action process is divided into two phases: the
gathering of data (through a web form) and the treatment of the data.<br/><br/>
&nbsp;<span class="guideheader">T</span>he treatment is organised in a succession of functions, each of
which has its own input and output.<br/><br/>
&nbsp;<span class="guideheader">T</span>he functions themselves are stored in separate files (one per
function) in the /opt/cds-invenio/lib/python/invenio/websubmit_functions directory. A file containing a function MUST
be named after the function name itself. For example, a function called "Move_to_Done" MUST be stored in a
file called Move_to_Done.py. The case is important here.<br/><br/>
&nbsp;<span class="guideheader">F</span>or a description of what should be inside the file, have a look to
the "create a new function" page of this guide.<br/><br/>
&nbsp;<span class="guideheader">T</span>o each function you can associate one or several parameters,
which may have different values according to the document type the function is used for. One parameter may
be used for different functions. For example one standard parameter used in several functions is called "edsrn".
It contains the name of the file in which the reference of the document is stored.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionnew">create a new function</a><br/>
<li><a href="#functiondelete">delete a function</a><br/>
<li><a href="#functionedit">edit a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functionnew"></a>
<h1>Creating a New Function</h1>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Available Functions" link in the websubmit admin right
menu. Then click on the "Add New Function" button.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">E</span>nter the name of the new function as well as a text description if
you wish.<br/>
&nbsp;<span class="guideheader">Y</span>ou will then reach a page where you can add parameters to your
new function.<br/><br/>
&nbsp;<span class="guideheader">D</span>on't forget to add the function file inside the
/opt/cds-invenio/lib/python/invenio/websubmit_functions directory and to name the file after the function. Functions must
be written in Python. Here is an example implementation of a function:<br/><br/>
/opt/cds-invenio/lib/python/invenio/websubmit_functions/Get_Report_Number.py:</small>
<table border=0 width=75% bgcolor="eeeeff"><tr><td><small><pre><br/>
def Get_Report_Number (parameters,curdir,form):
global rn
#Path of file containing report number
if os.path.exists("%s/%s" % (curdir,parameters['edsrn'])):
fp = open("%s/%s" % (curdir,parameters['edsrn']),"r")
rn = fp.read()
rn = rn.replace("/","_")
rn = re.sub("[\n\r ]+","",rn)
else:
rn = ""
return ""
<pre></small></td></tr></TABLE>
<br/>
The function parameters are passed to the function through the parameters dictionary.<br/>
The curdir parameter contains the current submission directory path.<br/>
The form parameter contains the form passed to the current web page for possible reference from inside the
function.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionedit">edit a function</a><br/>
<li><a href="#functiondelete">delete a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functiondelete"></a>
<h1>Removing a Function</h1>
<h3>Note</h3>
<blockquote>
&nbsp;<span class="guideheader">T</span>here are currently no way of deleting a function through this
interface. Use the direct MySQL command line interface for this.
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionedit">edit a function</a><br/>
<li><a href="#functionnew">create a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functionedit"></a>
<h1>Editing a Function</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">E</span>dit a function, add parameters to it...
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Available Functions" link in the websubmit admin
right menu.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">O</span>n this page appears a list of all functions defined into the system.
Two columns give you access to some features:
<ul>
<li><font color=green>View function usage</font> Click here to have access to the list of all document
types and all actions in which this function is used. Then by clicking on one of the items, you will be given a
chance to modify the parameters value for the given document type.
<li><font color=green>View/Edit function details</font> There you will be able to modify the function
description, as well as add/withdraw parameters for this function.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionnew">create a new function</a><br/>
<li><a href="#functiondelete">delete a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="functiondescription"></a>
<h1>All functions explained</h1>
<h3>Description:</h3>
<blockquote>
&nbsp;<span class="guideheader">T</span>his page lists and explains all the functions used in the demo
provided with the CDS Invenio package. This list is not exhaustive since you can add any new function you need.<br/>
&nbsp;<span class="guideheader">C</span>lick on one function name to get its description.<br/>
&nbsp;<span class="guideheader">P</span>lease note in this page when we refer to [param] this means the
value of the parameter 'param' for a given document type.<br/><br/>
<table cellspacing=5><tr>
<td valign="top">
<a href="#CaseEDS">CaseEDS</a><br/>
<a href="#Create_Modify_Interface">Create_Modify_Interface</a><br/>
<a href="#Create_Recid">Create_Recid</a><br/>
<a href="#Finish_Submission">Finish_Submission</a><br/>
<a href="#Get_Info">Get_Info</a><br/>
<a href="#Get_Recid">Get_Recid</a><br/>
<a href="#Get_Report_Number">Get_Report_Number</a><br/>
<a href="#Get_Sysno">Get_Sysno</a><br/>
<a href="#Get_TFU_Files">Get_TFU_Files</a><br/>
<a href="#Insert_Modify_Record">Insert_Modify_Record</a><br/>
<a href="#Insert_Record">Insert_Record</a><br/>
</td>
<td valign="top">
<a href="#Is_Original_Submitter">Is_Original_Submitter</a><br/>
<a href="#Is_Referee">Is_Referee</a><br/>
<a href="#Mail_Submitter">Mail_Submitter</a><br/>
<a href="#Make_Modify_Record">Make_Modify_Record</a><br/>
<a href="#Make_Record">Make_Record</a><br/>
<a href="#Move_From_Pending">Move_From_Pending</a><br/>
<a href="#Move_to_Done">Move_to_Done</a><br/>
<a href="#Move_to_Pending">Move_to_Pending</a><br/>
<a href="#Print_Success">Print_Success</a><br/>
<a href="#Print_Success_APP">Print_Success_APP</a><br/>
<a href="#Print_Success_MBI">Print_Success_MBI</a><br/>
</td>
<td valign="top">
<a href="#Print_Success_SRV">Print_Success_SRV</a><br/>
<a href="#Report_Number_Generation">Report_Number_Generation</a><br/>
<a href="#Send_Approval_Request">Send_Approval_Request</a><br/>
<a href="#Send_APP_Mail">Send_APP_Mail</a><br/>
<a href="#Send_Modify_Mail">Send_Modify_Mail</a><br/>
<a href="#Send_SRV_Mail">Send_SRV_Mail</a><br/>
<a href="#Test_Status">Test_Status</a><br/>
<a href="#Update_Approval_DB">Update_Approval_DB</a><br/>
<a href="#Upload_Files">Upload_Files</a><br/>
</td>
</tr></table>
</blockquote>
<br/><br/><a name="CaseEDS">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>CaseEDS</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function may be used if the treatment to be done after a submission depends on a field entered by
the user. Typically this is used in an approval interface. If the referee approves then we do this. If he rejects,
then we do other thing.<br/>
More specifically, the function gets the value from the file named [casevariable] and compares it with the
values stored in [casevalues]. If a value matches, the function directly goes to the corresponding step stored
in [casesteps]. If no value is matched, it goes to step [casedefault].
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>casevariable</b></small></td>
<td><small>
This parameters contains the name of the file in which the function will get the chosen value.<br/>
Eg: "decision"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>casevalues</b></small></td>
<td><small>
Contains the list of recognized values to match with the chosen value. Should be a comma separated list of words.<br/>
Eg: "approve,reject"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>casesteps</b></small></td>
<td><small>
Contains the list of steps corresponding to the values matched in [casevalue]. It should be a comma
separated list of numbers<br/>
Eg: "2,3"<br/>
<i>In this example, if the value stored in the file named "decision" is "approved", then the function launches
step 2 of this action. If it is "reject", then step 3 is launched.</i>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>casedefault</b></small></td>
<td><small>
Contains the step number to go by default if no match is found.<br/>
Eg: "4"<br/>
<i>In this example, if the value stored in the file named "decision" is not "approved" nor "reject", then
step 4 is launched.</i>
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Create_Modify_Interface">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Create_Modify_Interface</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
To be used in the MBI-Modify Record action.
It displays a web form allowing the user to modify the fields he chose. The fields are prefilled with the existing
values extracted from the documents database.
This functions takes the values stored in the [fieldnameMBI] file. This file contains a list of field name separated
with "+" (it is usually generated from a multiple select form field). Then the function retrieves the corresponding
tag name (marc-21) stored in the element definition. Finally it displays the web form and fills it with the existing
values found in the documents database.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>fieldnameMBI</b></small></td>
<td><small>
Contains the name of the file in which the function will find the list of fields the user wants to modify. Depends
on the web form configuration.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Create_Recid">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Create_Recid</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function retrieves a new record id from the records database. This record id will then be used to create the
XML record afterwards, or to link with the fulltext files. The created id is stored in a file named "SN".
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</small></td>
</tr>
</TABLE>
<br/><br/><a name="Finish_Submission">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Finish_Submission</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function stops the data treatment process even if further steps exist. This is used for example in the
approval action. In the first step, the program determines whether the user approved or rejected the
document (see <a href="#CaseEDS">CaseEDS</a> function description). Then depending on the result, it
executes step 2 or step 3. If it executes step 2, then it should continue with step 3 if nothing stopped it. The
Finish_Submission function plays this role.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Get_Info">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Get_Info</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function tries to retrieve in the "pending" directory or directly in the documents database, some information
about the document: title, original submitter's email and author(s).<br/>
If found, this information is stored in 3 global variables: $emailvalue, $titlevalue, $authorvalue to be used
in other functions.<br/>
If not found, an error message is displayed.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>authorFile</b></small></td>
<td><small>
Name of the file in which the author may be found if the document has not yet been integrated (in this case
it is still in the "pending" directory).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>emailFile</b></small></td>
<td><small>
Name of the file in which the email of the original submitter may be found if the document has not yet been
integrated (in this case it is still in the "pending" directory).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>titleFile</b></small></td>
<td><small>
Name of the file in which the title may be found if the document has not yet been integrated (in this case it is
still in the "pending" directory).
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Get_Recid">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Get_Recid</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function searches for the document in the database and stores the recid of this document in the "SN" file and in a global variable "sysno".<br/>
The function conducts the search based upon the document's report-number (and relies upon the global variable "rn") so the "Get_Report_Number" function should be called before this one.<br/>
<i>This function replaces the older function "Get_Sysno".</i><br/>
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Get_Report_Number">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Get_Report_Number</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function gets the value contained in the [edsrn] file and stores it in the reference global variable.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file which stores the reference.<br/>
This value depends on the web form configuration you did. It should contain the name of the form element used for storing the reference of the document.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Get_Sysno">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Get_Sysno</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function searches for the document in the database and stores the system number of this document in the "SN" file and in a global variable.<br/>
"Get_Report_Number" should be called before.<br/>
<span style="color: red; font-style: italic;">Deprecated: Use Get_Recid instead.</span>
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Insert_Modify_Record">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Insert_Modify_Record</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function gets the output of bibconvert and uploads it into the MySQL bibliographical database.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Insert_Record">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Insert_Record</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function gets the output of bibFormat and uploads it into the MySQL bibliographical database.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Is_Original_Submitter">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Is_Original_Submitter</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
If the authentication module (login) is active in webSubmit, this function compares the current login with the email of the original submitter. If it is the same (or if the current user has superuser rights), we go on. If it differs, an error message is issued.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Is_Referee">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Is_Referee</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function checks whether the currently logged user is a referee for this document.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Mail_Submitter">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Mail_Submitter</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function send an email to the submitter to warn him the document he has just submitted has been
correctly received.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>authorfile</b></small></td>
<td><small>
Name of the file containing the authors of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>titleFile</b></small></td>
<td><small>
Name of the file containing the title of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>emailFile</b></small></td>
<td><small>
Name of the file containing the email of the submitter of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>status</b></small></td>
<td><small>
Depending on the value of this parameter, the function adds an additional text to the email.<br/>
This parameter can be one of:<br/>
<b>ADDED</b>: The file has been integrated in the database.<br/>
<b>APPROVAL</b>: The file has been sent for approval to a referee.<br/>
or can stay empty.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file containing the reference of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>newrnin</b></small></td>
<td><small>
Name of the file containing the 2nd reference of the document (if any)<br/>
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Make_Modify_Record">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Make_Modify_Record</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function creates the record file formatted for a direct insertion in the documents database. It uses the
<a href="#bibconvert">BibConvert</a> tool.<br/>
The main difference between all the Make_..._Record functions are the parameters.<br/>
As its name says, this particular function should be used for the modification of a record. (MBI- Modify
Record action).
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>modifyTemplate</b></small></td>
<td><small>
Name of bibconvert's configuration file used for creating the mysql record.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>sourceTemplate</b></small></td>
<td><small>
Name of bibconvert's source file.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Make_Record">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Make_Record</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function creates the record file formatted for a direct insertion in the documents database. It uses the
<a href="#bibconvert">BibConvert</a> tool.<br/>
The main difference between all the Make_..._Record functions are the parameters.<br/>
As its name does not say :), this particular function should be used for the submission of a document.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>createTemplate</b></small></td>
<td><small>
Name of bibconvert's configuration file used for creating the mysql record.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>sourceTemplate</b></small></td>
<td><small>
Name of bibconvert's source file.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Move_From_Pending">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Move_From_Pending</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function retrieves the data of a submission which was temporarily stored in the "pending" directory
(waiting for an approval for example), and moves it to the current action directory.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Move_to_Done">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Move_to_Done</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function moves the existing submission directory to the /opt/cds-invenio/var/data/submit/storage/done directory. If the
Then it tars and gzips the directory.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Move_to_Pending">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Move_to_Pending</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function moves the existing submission directory to the /opt/cds-invenio/var/data/submit/storage/pending directory. It is
used to store temporarily this data until it is approved or...
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Print_Success">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Print_Success</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function simply displays a text on the screen, telling the user the submission went fine. To be used in
the "Submit New Record" action.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>status</b></small></td>
<td><small>
Depending on the value of this parameter, the function adds an additional text to the email.<br/>
This parameter can be one of:<br/>
<b>ADDED</b>: The file has been integrated in the database.<br/>
<b>APPROVAL</b>: The file has been sent for approval to a referee.<br/>
or can stay empty.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file containing the reference of the document<br/>
</small></td>
</tr>
<tr>
<td valign="top"><small><b>newrnin</b></small></td>
<td><small>
Name of the file containing the 2nd reference of the document (if any)<br/>
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Print_Success_APP">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Print_Success_APP</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function simply displays a text on the screen, telling the referee his decision has been taken into account.
To be used in the Approve (APP) action.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Print_Success_MBI">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Print_Success_MBI</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function simply displays a text on the screen, telling the user the modification went fine. To be used in
the Modify Record (MBI) action.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Print_Success_SRV">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Print_Success_SRV</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function simply displays a text on the screen, telling the user the revision went fine. To be used in the
Submit New File (SRV) action.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small>none</td>
</tr>
</TABLE>
<br/><br/><a name="Report_Number_Generation">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Report_Number_Generation</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function is used to automatically generate a reference number.<br/>
After generating the reference, the function saves it into the [newrnin] file and sets the global variable
containing this reference.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>autorngen</b></small></td>
<td><small>
If set to "<b>Y</b>": The reference number is generated.<br/>
If set to "<b>N</b>": The reference number is read from a file ([newrnin])<br/>
If set to "<b>A</b>": The reference number will be the access number of the submission.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>counterpath</b></small></td>
<td><small>
indicates the file in which the program will find the counter for this reference generation.<br/>
The value of this parameter may contain one of:<br/>
"<b>&lt;PA&gt;categ&lt;/PA&gt;</b>": in this case this string is replaced with the content of the file [altrnin]<br/>
"<b>&lt;PA&gt;yy&lt;/PA&gt;</b>": in this case this string is replaced by the current year (4 digits) if [altyeargen]
is set to "AUTO", or by the content of the [altyeargen] file in any other case. (this content should be formatted
as a date (dd/mm/yyyy).<br/>
"<b>&lt;PA&gt;file:<i>name_of_file</i>&lt;/PA&gt;</b>": in this case, this string is replaced by the first line of the given file<br />
"<b>&lt;PA&gt;file*:<i>name_of_file</i>&lt;/PA&gt;</b>": in this case, this string is replaced by all the lines of the given file, separated by a dash ('-') character.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>rnformat</b></small></td>
<td><small>
This is the format used by the program to create the reference. The program computes the value of the
parameter and appends a "-" followed by the current value of the counter increased by 1.<br/>
The value of this parameter may contain one of:<br/>
"<b>&lt;PA&gt;categ&lt;/PA&gt;</b>": in this case this string is replaced with the content of the file [altrnin]<br/>
"<b>&lt;PA&gt;yy&lt;/PA&gt;</b>": in this case this string is replaced by the current year (4 digits) if [altyeargen]
is set to "AUTO", or by the content of the [altyeargen] file in any other case. (this content should be formatted
as a date (dd/mm/yyyy).
<br/>
"<b>&lt;PA&gt;file:<i>name_of_file</i>&lt;/PA&gt;</b>": in this case, this string is replaced by the first line of the given file<br />
"<b>&lt;PA&gt;file*:<i>name_of_file</i>&lt;/PA&gt;</b>": in this case, this string is replaced by all the lines of the given file, separated by a dash ('-') character.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>rnin</b></small></td>
<td><small>
This parameter contains the name of the file in which the program will find the category if needed. The content
of thif file will then replace the string &lt;PA&gt;categ&lt;/PA&gt; in the reference format or in the counter
path.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>yeargen</b></small></td>
<td><small>
This parameter can be one of:<br/>
"<b>AUTO</b>": in this case the program takes the current 4 digit year.<br/>
"<b>&lt;filename&gt;</b>": in this case the program extract the year from the file which name is
&lt;filename&gt;. This file should contain a date (dd/mm/yyyy).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file in which the created reference will be stored.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Send_Approval_Request">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Send_Approval_Request</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function sends an email to the referee in order to start the simple approval process.<br/>
This function is very CERN-specific and should be changed in case of external use.<br/>
Must be called after the <a href=#Get_Report_Number>Get_Report_Number</a> function.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>addressesDAM</b></small></td>
<td><small>
email addresses of the people who will receive this email (comma separated list). this parameter may contain the <b>&lt;CATEG&gt;</b> string. In which case the variable computed from the [categformatDAM] parameter replaces this string.<br/>
eg.: "&lt;CATEG&gt;-email@cern.ch"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>categformatDAM</b></small></td>
<td><small>
contains a regular expression used to compute the category of the document given the reference of the document.<br/>
eg.: if [categformatAFP]="TEST-&lt;CATEG&gt;-.*" and the reference of the document is "TEST-CATEGORY1-2001-001", then the computed category equals "CATEGORY1"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>authorfile</b></small></td>
<td><small>
name of the file in which the authors are stored
</small></td>
</tr>
<tr>
<td valign="top"><small><b>titlefile</b></small></td>
<td><small>
name of the file in which the title is stored.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>directory</b></small></td>
<td><small>
parameter used to create the URL to access the files.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Send_APP_Mail">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Send_APP_Mail</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
Sends an email to warn people that a document has been approved.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>addressesAPP</b></small></td>
<td><small>
email addresses of the people who will receive this email (comma separated list). this parameter may contain
the <b>&lt;CATEG&gt;</b> string. In which case the variable computed from the [categformatAFP] parameter
replaces this string.<br/>
eg.: "&lt;CATEG&gt;-email@cern.ch"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>categformatAPP</b></small></td>
<td><small>
contains a regular expression used to compute the category of the document given the reference of the
document.<br/>
eg.: if [categformatAFP]="TEST-&lt;CATEG&gt;-.*" and the reference of the document is
"TEST-CATEGORY1-2001-001", then the computed category equals "CATEGORY1"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>newrnin</b></small></td>
<td><small>
Name of the file containing the 2nd reference of the approved document (if any).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>edsrn</b></small></td>
<td><small>
Name of the file containing the reference of the approved document.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Send_Modify_Mail">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Send_Modify_Mail</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function sends an email to warn people a document has been modified and the user his modifications
have been taken into account..
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>addressesMBI</b></small></td>
<td><small>
email addresses of the people who will receive this email (comma separated list).
</small></td>
</tr>
<tr>
<td valign="top"><small><b>fieldnameMBI</b></small></td>
<td><small>
name of the file containing the modified fields.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>sourceDoc</b></small></td>
<td><small>
Long name for the type of document. This name will be displayed in the mail.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>emailfile</b></small></td>
<td><small>
name of the file in which the email of the modifier will be found.
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Send_SRV_Mail">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Send_SRV_Mail</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function sends an email to warn people a revision has been carried out.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>notefile</b></small></td>
<td><small>
name of the file in which the note can be found
</small></td>
</tr>
<tr>
<td valign="top"><small><b>emailfile</b></small></td>
<td><small>
name of the file containing the submitter's email
</small></td>
</tr>
<tr>
<td valign="top"><small><b>addressesSRV</b></small></td>
<td><small>
email addresses of the people who will receive this email (comma separated list). this parameter may contain the <b>&lt;CATEG&gt;</b> string. In which case the variable computed from the [categformatDAM] parameter replaces this string.<br/>
eg.: "&lt;CATEG&gt;-email@cern.ch"
</small></td>
</tr>
<tr>
<td valign="top"><small><b>categformatDAM</b></small></td>
<td><small>
contains a regular expression used to compute the category of the document given the reference of the
document.<br/>
eg.: if [categformatAFP]="TEST-&lt;CATEG&gt;-.*" and the reference of the document is
"TEST-CATEGORY1-2001-001", then the computed category equals "CATEGORY1"
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Test_Status">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Test_Status</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function checks whether the considered document has been requested for approval and is still waiting
for approval. It also checks whether the password stored in file "password" of the submission directory
corresponds to the password associated with the document..
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top" colspan="2"><small><b>none</b></small></td>
</tr>
</TABLE>
<br/><br/><a name="Update_Approval_DB">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Update_Approval_DB</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function updates the approval database when a document has just been approved or rejected. It uses
the [categformatDAM] parameter to compute the category of the document.<br/>
Must be called after the <a href=#Get_Report_Number>Get_Report_Number</a> function.
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>categformatDAM</b></small></td>
<td><small>
It contains the regular expression which allows the retrieval of the category from the reference number.<br/>
Eg: if [categformatDAM]="TEST-&lt;CATEG&gt;-.*" and the reference is "TEST-CATEG1-2001-001" then the
category will be recognized as "CATEG1".
</small></td>
</tr>
</TABLE>
<br/><br/><a name="Upload_Files">
<table border="1" width="80%">
<tr><td colspan="2" bgcolor="#ddddff"><small><b>Upload_Files</b></small></td></tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>description</small></td></tr>
<tr>
<td colspan="2">
<small>
This function displays the list of already transfered files (main and additional ones), and also outputs an html
form for uploading other files (pictures or fulltexts).
</small>
</td>
</tr>
<tr><td colspan="2" bgcolor="#eeeeff" align="center"><small>parameters</small></td></tr>
<tr>
<td valign="top"><small><b>maxsize</b></small></td>
<td><small>
Maximum allowed size for the transfered files (size in bits)
</small></td>
</tr>
<tr>
<td valign="top"><small><b>minsize</b></small></td>
<td><small>
Minimum allowed size for the transfered files (size in bits)
</small></td>
</tr>
<tr>
<td valign="top"><small><b>iconsize</b></small></td>
<td><small>
In case the transfered files are pictures (jpg, gif or pdf), the function will automatically try to create icons from them.
This parameter indicates the size in pixel of the created icon.
</small></td>
</tr>
<tr>
<td valign="top"><small><b>type</b></small></td>
<td><small>
This can be one of "fulltext" or "picture". If the type is set to "picture" then the function will try to create icons
(uses the ImageMagick's "convert" tool)
</small></td>
</tr>
</TABLE>
<h3>See also:</h3>
<blockquote>
<li><a href="#functionnew">create a new function</a><br/>
<li><a href="#functiondelete">delete a function</a><br/>
<li><a href="#functionedit">edit a function</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="protection"></a>
<h1>Protection and Restriction</h1>
<h3>Description:</h3>
<blockquote>
&nbsp;<span class="guideheader">I</span>n webSubmit, you can restrict the use of some actions on a given
document type to a list of users. You can use the <a href="webaccess-admin">webAccess</a>
manager for this.<br/><br/>
&nbsp;<span class="guideheader">L</span>et's say you want to restrict the submission of new TEXT documents
to a given user. You should then create a role in webAccess which will authorize the action "submit" over doctype
"TEXT" and act "SBI" (Submit new record). You can call this role "submitter_TEXT_SBI" for example.
Then link the role to the proper users.<br/>
&nbsp;<span class="guideheader">A</span>nother example: if you wish to authorize a user to Modify the
bibliographic data of PICT documents, you have to create a role which authorize the action "submit" over doctype
"PICT" and act "MBI". This role can be called "submitter_PICT_MBI" or whatever you want.<br/><br/>
&nbsp;<span class="guideheader">I</span>f no role is defined for a given action and a given document type,
then all users will be allowed to use it.
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="catalogues"></a>
<h1>Submission Catalogue Organisation</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">T</span>his feature allows you to organise the way webSubmit main page
will look like. You will be able to group document types inside catalogues and order the catalogues the way you
wish.
</blockquote>
<h3>How to get there?</h3>
<blockquote>
&nbsp;<span class="guideheader">C</span>lick on the "Organisation" link in the websubmit admin right menu.
</blockquote>
<h3>How to do this?</h3>
<blockquote>
&nbsp;<span class="guideheader">O</span>nce on the "Edit Catalogues page", you will find the currently
defined organisation chart in the middle of the page. To the right, one form allows you to create a new catalogue
("Add a Catalogue") and one to add a document type to an existing catalogue ("Add a document type").
<br/>&nbsp;<br/>
<ul>
<li><font color=green>To add a catalogue:</font> Enter the name of your new catalogue in the
"Catalogue Name" free text field then choose to which existing catalogue this one will be attached to. If you
attach the new one to an already existing catalogue, you can create a sub-catalogue. To actually create it,
click on "ADD".
<li><font color=green>To add a document type to a catalogue:</font> Choose in the list of existing
"Document type names" the one you want to add to the chart. Then choose to which catalogue the document
type will be associated. Click on "ADD" to finalise this action.
<li><font color=green>To withdraw a document type or a catalogue from the chart:</font> Click on the
red cross next to the item you want to withdraw. If you withdraw a catalogue all document types attached to
it will be withdrawn also (of course the actual document types in webSubmit won't be destroyed!).
<li><font color=green>To move a document type or a catalogue in the chart:</font> Use the small up
and down arrows next to the document type/catalogue title.
</ul>
</blockquote>
<h3>See also:</h3>
<blockquote>
<li><a href="#newtype">Create a New Document Type</a><br/>
<li><a href="#documents">document types</a><br/>
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="bibconvert"></a>
<h1>BibConvert</h1>
<h3>What is it?</h3>
<blockquote>
&nbsp;<span class="guideheader">W</span>ebSubmit stores the data gathered during a submission in a
directory. In this directory each file corresponds to a field saved during the submission. <br/>
&nbsp;<span class="guideheader">B</span>ibConvert is used to create a formatted file which will be easy to
upload in the bibliographical database from this directory.<br/>
&nbsp;<span class="guideheader">T</span>his BibConvert program is called from the
<a href="#Make_Record">Make_Record</a> and
<a href="#Make_Modify_Record">Make_Modify_Record</a> functions
from the <a href="#functions">end script</a> system of webSubmit.<br/>
&nbsp;<span class="guideheader">T</span>he BibConvert configuration files used by webSubmit are in the
<ETCDIR>/bibconvert/config directory.<br/><br/>
&nbsp;<span class="guideheader">F</span>or more info about bibconvert, please see the dedicated
<a href="bibconvert-admin">guide</a>.
</blockquote>
<p>&nbsp;<p>
<p>&nbsp;<p>
<a name="faq"></a>
<h1>FAQ</h1>
&nbsp;<span class="guideheader">Q</span>1. <a href="#Q1">I'd like to be warned each time there is an error, or an important
action is made through the manager. Is this possible?
</a><br/>
&nbsp;<span class="guideheader">Q</span>2. <a href="#Q2">Where are all the files stored in this system?
</a><br/>
&nbsp;<span class="guideheader">Q</span>3. <a href="#Q3">How is the documents archive organised?
</a><br/><br/><br/><br/>
<a name="Q1"></a>
<i>&nbsp;<span class="guideheader">Q</span>1. I'd like to be warned each time there is an error, or an important
action is made through the manager. Is this possible?
</i>
<blockquote>
Yes, it is. Edit the invenio-local.conf file, the "CFG_SITE_ADMIN_EMAIL" definition and set it to your email
address. You will then receive all the warning emails issued by the manager.
</blockquote>
<a name="Q2"></a>
<i>&nbsp;<span class="guideheader">Q</span>2. Where are all the files stored in this system?
</i>
<blockquote>
<li>the counter files are here: /opt/cds-invenio/var/data/submit/counters. There are used by the
<a href="#Report_Number_Generation">Report_Number_Generation</a>
function.
<li>all running and completed submissions are stored here: /opt/cds-invenio/var/data/submit/storage.
<li>all the document files attached to records are stored here: /opt/cds-invenio/var/data/files.
<li>all python functions used by webSubmit are stored here: /opt/cds-invenio/lib/python/invenio/websubmit_functions
</blockquote>
<a name="Q3"></a>
<i>&nbsp;<span class="guideheader">Q</span>3. How is the documents archive organised?
</i>
<blockquote>
First of all, the documents files attached to records are stored here: /opt/cds-invenio/var/data/files. <br/><br/>
The <a href="#Upload_Files">Upload_Files</a> webSubmit function is used
to link a document with a record.<br/><br/>
All documents get an id from the system and are stored in the "bibdoc" table in the database. The link between a
document and a record is stored using the "bibdoc_bibrec" table.<br/><br/>
The document id is used to determine where the files are stored. For example the files of document #14 will be
stored here: /opt/cds-invenio/var/data/files/g0/14<br/><br/>
The subdirectory g0 is used to split the documents accross the filesystem. The CFG_FILE_DIR_SIZE variable from
invenio.conf determines how many documents will be stored under one subdirectory.<br/><br/>
Several files may be stored under the same document directory: they are the different formats and versions of the
same document. Versions are indicated by a string of the form ";1.0" concatenated to the name of the file.
</blockquote>
<h3>See also:</h3>
<blockquote>
notes
</blockquote>
diff --git a/modules/websubmit/doc/submit-guide.webdoc b/modules/websubmit/doc/submit-guide.webdoc
index 27a3f4a98..18890f2e8 100644
--- a/modules/websubmit/doc/submit-guide.webdoc
+++ b/modules/websubmit/doc/submit-guide.webdoc
@@ -1,25 +1,25 @@
## -*- mode: html; coding: utf-8; -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
<!-- WebDoc-Page-Title: _(Submit Guide)_ -->
-<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<WEBURL>/help/<lang:link/>">_(Help Central)_</a> -->
+<!-- WebDoc-Page-Navtrail: <a class="navtrail" href="<CFG_SITE_URL>/help/<lang:link/>">_(Help Central)_</a> -->
<!-- WebDoc-Page-Revision: $Id$ -->
FIXME.
diff --git a/modules/websubmit/lib/bibdocfile.py b/modules/websubmit/lib/bibdocfile.py
index 88e7b2f83..5b7886578 100644
--- a/modules/websubmit/lib/bibdocfile.py
+++ b/modules/websubmit/lib/bibdocfile.py
@@ -1,1384 +1,1382 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import os
import re
import shutil
import md5
import filecmp
import time
from datetime import datetime
from xml.sax.saxutils import quoteattr
from mimetypes import MimeTypes
from invenio.dbquery import run_sql, DatabaseError
from invenio.errorlib import register_exception
from invenio.access_control_engine import acc_authorize_action
-from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, weburl, CFG_WEBDIR, CFG_WEBSUBMIT_FILEDIR, CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT, CFG_SITE_SECURE_URL
+from invenio.config import CFG_SITE_LANG, CFG_SITE_URL, CFG_SITE_URL, CFG_WEBDIR, CFG_WEBSUBMIT_FILEDIR, CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT, CFG_SITE_SECURE_URL
import invenio.template
websubmit_templates = invenio.template.load('websubmit')
websearch_templates = invenio.template.load('websearch')
CFG_BIBDOCFILE_MD5_THRESHOLD = 256 * 1024
CFG_BIBDOCFILE_MD5_BUFFER = 1024 * 1024
CFG_BIBDOCFILE_MD5SUM_EXISTS = os.system('which md5sum 2>&1 > /dev/null') == 0
_mimes = MimeTypes()
_mimes.suffix_map.update({'.tbz2' : '.tar.bz2'})
_mimes.encodings_map.update({'.bz2' : 'bzip2'})
_extensions = _mimes.encodings_map.keys() + \
_mimes.suffix_map.keys() + \
_mimes.types_map[1].keys()
_extensions.sort()
_extensions.reverse()
_extensions = [ext.lower() for ext in _extensions]
class InvenioWebSubmitFileError(Exception):
pass
def file_strip_ext(file):
"""Strip in the best way the extension from a filename"""
lowfile = file.lower()
ext = '.'
while ext:
ext = ''
for c_ext in _extensions:
if lowfile.endswith(c_ext):
lowfile = lowfile[0:-len(c_ext)]
ext = c_ext
break
return file[:len(lowfile)]
def normalize_format(format):
"""Normalize the format."""
#format = format.lower()
if format and format[0] != '.':
format = '.' + format
#format = format.replace('.jpg', '.jpeg')
return format
_docname_re = re.compile(r'[^-\w.]*')
def normalize_docname(docname):
"""Normalize the docname (only digit and alphabetic letters and underscore are allowed)"""
#return _docname_re.sub('', docname)
return docname
def normalize_version(version):
"""Normalize the version."""
try:
int(version)
except ValueError:
if version.lower().strip() == 'all':
return 'all'
else:
return ''
return str(version)
_path_re = re.compile(r'.*[\\/:]')
def decompose_file(file):
"""Decompose a file into dirname, basename and extension"""
basename = _path_re.sub('', file)
dirname = file[:-len(basename)-1]
base = file_strip_ext(basename)
extension = basename[len(base) + 1:]
return (dirname, base, extension)
def propose_unique_name(file, use_version=False):
"""Propose a unique name, taking in account the version"""
if use_version:
version = ';'+re.sub('.*;', '', file)
file = file[:-len(version)]
else:
version = ''
(basedir, basename, extension) = decompose_file(file)
if extension: # Sometimes the extension wasn't guessed
extension = '.' + extension
goodname = "%s%s%s" % (basename, extension, version)
i = 1
listdir = os.listdir(basedir)
while goodname in listdir:
i += 1
goodname = "%s_%s%s%s" % (basename, i, extension, version)
return "%s/%s" % (basedir, goodname)
class BibRecDocs:
"""this class represents all the files attached to one record"""
def __init__(self, recid):
self.id = recid
self.bibdocs = []
self.build_bibdoc_list()
def __repr__(self):
return 'BibRecDocs(%s)' % self.id
def __str__(self):
out = '%i::::total bibdocs attached=%i\n' % (self.id, len(self.bibdocs))
out += '%i::::total size latest version=%s\n' % (self.id, nice_size(self.get_total_size_latest_version()))
out += '%i::::total size all files=%s\n' % (self.id, nice_size(self.get_total_size()))
for bibdoc in self.bibdocs:
out += str(bibdoc)
return out
def get_total_size_latest_version(self):
"""Return the total size used on disk of all the files belonging
to this record and corresponding to the latest version."""
size = 0
for bibdoc in self.bibdocs:
size += bibdoc.get_total_size_latest_version()
return size
def get_total_size(self):
"""Return the total size used on disk of all the files belonging
to this record of any version."""
size = 0
for bibdoc in self.bibdocs:
size += bibdoc.get_total_size()
return size
def build_bibdoc_list(self):
"""This function must be called everytime a bibdoc connected to this
recid is added, removed or modified.
"""
self.bibdocs = []
res = run_sql("""SELECT id_bibdoc, type, status FROM bibrec_bibdoc JOIN
bibdoc ON id=id_bibdoc WHERE id_bibrec=%s AND
status<>'DELETED' ORDER BY docname ASC""", (self.id,))
for row in res:
cur_doc = BibDoc(docid=row[0], recid=self.id, doctype=row[1])
self.bibdocs.append(cur_doc)
def list_bibdocs(self, doctype=''):
"""Returns the list all bibdocs object belonging to a recid.
If doctype is set, it returns just the bibdocs of that doctype.
"""
if not doctype:
return self.bibdocs
else:
return [bibdoc for bibdoc in self.bibdocs if doctype == bibdoc.doctype]
def get_bibdoc_names(self, doctype=''):
"""Returns the names of the files associated with the bibdoc of a
paritcular doctype"""
return [bibdoc.docname for bibdoc in self.list_bibdocs(doctype)]
def check_file_exists(self, path):
"""Returns 1 if the recid has a file identical to the one stored in path."""
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 = [file for file in files if file.get_size() == size]
if potential:
checksum = calculate_md5(path)
# Let's consider all the latest files with the same size and the
# same checksum
potential = [file for file in potential if file.get_checksum() == checksum]
if potential:
potential = [file for file in potential if filecmp.cmp(file.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):
"""Propose a unique docname."""
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 get_docid(self, docname):
"""Returns the docid corresponding to the given docname, if the docname
is valid.
"""
for bibdoc in self.bibdocs:
if bibdoc.docname == docname:
return bibdoc.id
raise InvenioWebSubmitFileError, "Recid '%s' is not connected with a " \
"docname '%s'" % (self.id, docname)
def get_docname(self, docid):
"""Returns the docname corresponding to the given docid, if the docid
is valid.
"""
for bibdoc in self.bibdocs:
if bibdoc.id == docid:
return bibdoc.docname
raise InvenioWebSubmitFileError, "Recid '%s' is not connected with a " \
"docid '%s'" % (self.id, docid)
def get_bibdoc(self, docname):
"""Returns the bibdoc with a particular docname associated with
this recid"""
for bibdoc in self.bibdocs:
if bibdoc.docname == docname:
return bibdoc
raise InvenioWebSubmitFileError, "Recid '%s' is not connected with " \
" docname '%s'" % (self.id, docname)
def delete_bibdoc(self, docname):
"""Deletes a docname associated with the recid."""
for bibdoc in self.bibdocs:
if bibdoc.docname == docname:
bibdoc.delete()
self.build_bibdoc_list()
def add_bibdoc(self, doctype="Main", docname='file', never_fail=False):
"""Creates a new bibdoc associated with the recid, with a file
called docname and a particular doctype. It returns the bibdoc object
which was just created.
If never_fail is True then the system will always be able
to create a bibdoc.
"""
docname = normalize_docname(docname)
if never_fail:
docname = self.propose_unique_docname(docname)
if docname in self.get_bibdoc_names():
raise InvenioWebSubmitFileError, "%s has already a bibdoc with docname %s" % (self.id, docname)
else:
bibdoc = BibDoc(recid=self.id, doctype=doctype, docname=docname)
self.build_bibdoc_list()
return bibdoc
def add_new_file(self, fullpath, doctype="Main", docname='', never_fail=False):
"""Adds a new file with the following policy: if the docname is not set
it is retrieved from the name of the file. If bibdoc with the given
docname doesn't 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 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. The elaborated bibdoc
is returned.
"""
if not docname:
docname = decompose_file(fullpath)[1]
docname = normalize_docname(docname)
try:
bibdoc = self.get_bibdoc(docname)
except InvenioWebSubmitFileError:
# bibdoc doesn't already exists!
bibdoc = self.add_bibdoc(doctype, docname, False)
bibdoc.add_file_new_version(fullpath)
else:
try:
bibdoc.add_file_new_format(fullpath)
except InvenioWebSubmitFileError, e:
# Format already exist!
if never_fail:
bibdoc = self.add_bibdoc(doctype, docname, True)
bibdoc.add_file_new_version(fullpath)
else:
raise e
return bibdoc
def add_new_version(self, fullpath, docname):
"""Adds a new fullpath file to an already existent docid making the
previous files associated with the same bibdocids obsolete.
It returns the bibdoc object.
"""
bibdoc = self.get_bibdoc(docname=docname)
bibdoc.add_file_new_version(fullpath)
return bibdoc
def add_new_format(self, fullpath, docname):
"""Adds a new format for a fullpath file to an already existent
docid along side already there files.
It returns the bibdoc object.
"""
bibdoc = self.get_bibdoc(docname=docname)
bibdoc.add_file_new_format(fullpath)
return bibdoc
def list_latest_files(self, doctype=''):
"""Returns a list which is made up by all the latest docfile of every
bibdoc (of a particular doctype).
"""
docfiles = []
for bibdoc in self.list_bibdocs(doctype):
docfiles += bibdoc.list_latest_files()
return docfiles
def display(self, docname="", version="", doctype="", ln=CFG_SITE_LANG):
"""Returns a formatted panel with information and links about a given
docid of a particular version (or any), of a particular doctype (or any)
"""
t = ""
if docname:
try:
bibdocs = [self.get_bibdoc(docname)]
except InvenioWebSubmitFileError:
bibdocs = self.list_bibdocs(doctype)
else:
bibdocs = self.list_bibdocs(doctype)
if bibdocs:
types = list_types_from_array(bibdocs)
fulltypes = []
for mytype in types:
fulltype = {
'name' : mytype,
'content' : [],
}
for bibdoc in bibdocs:
if mytype == bibdoc.get_type():
fulltype['content'].append(bibdoc.display(version,
ln = ln))
fulltypes.append(fulltype)
t = websubmit_templates.tmpl_bibrecdoc_filelist(
ln=ln,
types = fulltypes,
)
return t
def fix(self, docname):
"""Algorithm that transform an a broken/old bibdoc into a coherent one:
i.e. the corresponding folder will have files named after the bibdoc
name. Proper .recid, .type, .md5 files will be created/updated.
In case of more than one file with the same format revision a new bibdoc
will be created in order to put does files.
Returns the list of newly created bibdocs if any.
"""
bibdoc = self.get_bibdoc(docname)
versions = {}
res = []
new_bibdocs = [] # List of files with the same version/format of
# existing file which need new bibdoc.
counter = 0
zero_version_bug = False
if os.path.exists(bibdoc.basedir):
for filename in os.listdir(bibdoc.basedir):
if filename[0] != '.' and ';' in filename:
name, version = filename.split(';')
try:
version = int(version)
except ValueError:
# Strange name, let's skip it...
register_exception()
continue
if version == 0:
zero_version_bug = True
format = name[len(file_strip_ext(name)):]
format = normalize_format(format)
if not versions.has_key(version):
versions[version] = {}
new_name = 'FIXING-%s-%s' % (str(counter), name)
try:
shutil.move('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, new_name))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in renaming '%s' to '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, new_name), e)
if versions[version].has_key(format):
new_bibdocs.append((new_name, version))
else:
versions[version][format] = new_name
counter += 1
if not versions:
bibdoc.delete()
else:
for version, formats in versions.iteritems():
if zero_version_bug:
version += 1
for format, filename in formats.iteritems():
destination = '%s%s;%i' % (docname, format, version)
try:
shutil.move('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, destination))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in renaming '%s' to '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, destination), e)
try:
open("%s/.recid" % bibdoc.basedir, "w").write(str(self.id))
open("%s/.type" % bibdoc.basedir, "w").write(str(bibdoc.doctype))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in creating .recid and .type file for '%s' folder: '%s'" % (bibdoc.basedir, e)
self.build_bibdoc_list()
res = []
for (filename, version) in new_bibdocs:
if zero_version_bug:
version += 1
new_bibdoc = self.add_bibdoc(doctype=bibdoc.doctype, docname=docname, never_fail=True)
new_bibdoc.add_file_new_format('%s/%s' % (bibdoc.basedir, filename), version)
res.append(new_bibdoc)
try:
os.remove('%s/%s' % (bibdoc.basedir, filename))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Error in removing '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), e)
Md5Folder(bibdoc.basedir).update(only_new=False)
bibdoc._build_file_list()
self.build_bibdoc_list()
return res
class BibDoc:
"""this class represents one file attached to a record
there is a one to one mapping between an instance of this class and
an entry in the bibdoc db table"""
def __init__ (self, docid="", recid="", docname="file", doctype="Main"):
"""Constructor of a bibdoc. At least the docid or the recid/docname
pair is needed."""
# docid is known, the document already exists
docname = normalize_docname(docname)
self.docfiles = []
self.md5s = None
self.related_files = []
if docid != "":
if recid == "":
recid = None
self.doctype = ""
res = run_sql("select id_bibrec,type from bibrec_bibdoc "
"where id_bibdoc=%s", (docid,))
if len(res) > 0:
recid = res[0][0]
self.doctype = res[0][1]
else:
res = run_sql("select id_bibdoc1 from bibdoc_bibdoc "
"where id_bibdoc2=%s", (docid,))
if len(res) > 0 :
main_bibdoc = res[0][0]
res = run_sql("select id_bibrec,type from bibrec_bibdoc "
"where id_bibdoc=%s", (main_bibdoc,))
if len(res) > 0:
recid = res[0][0]
self.doctype = res[0][1]
else:
res = run_sql("select type from bibrec_bibdoc "
"where id_bibrec=%s and id_bibdoc=%s", (recid, docid,))
if len(res) > 0:
self.doctype = res[0][0]
else:
#this bibdoc isn't associated with the corresponding bibrec.
raise InvenioWebSubmitFileError, "No docid associated with the recid %s" % recid
# gather the other information
res = run_sql("select id,status,docname,creation_date,"
"modification_date from bibdoc where id=%s", (docid,))
if len(res) > 0:
self.cd = res[0][3]
self.md = res[0][4]
self.recid = recid
self.docname = res[0][2]
self.id = docid
self.status = res[0][1]
self.basedir = _make_base_dir(self.id)
else:
# this bibdoc doesn't exist
raise InvenioWebSubmitFileError, "The docid %s does not exist." % docid
# else it is a new document
else:
if docname == "" or doctype == "":
raise InvenioWebSubmitFileError, "Argument missing for creating a new bibdoc"
else:
self.recid = recid
self.doctype = doctype
self.docname = docname
self.status = ''
if recid:
res = run_sql("SELECT b.id FROM bibrec_bibdoc bb JOIN bibdoc b on bb.id_bibdoc=b.id WHERE bb.id_bibrec=%s AND b.docname=%s", (recid, docname))
if res:
raise InvenioWebSubmitFileError, "A bibdoc called %s already exists for recid %s" % (docname, recid)
self.id = run_sql("INSERT INTO bibdoc (status,docname,creation_date,modification_date) "
"values(%s,%s,NOW(),NOW())", (self.status, docname,))
if self.id is not None:
# we link the document to the record if a recid was
# specified
if self.recid != "":
run_sql("INSERT INTO bibrec_bibdoc (id_bibrec, id_bibdoc, type) VALUES (%s,%s,%s)",
(recid, self.id, self.doctype,))
else:
raise InvenioWebSubmitFileError, "New docid cannot be created"
self.basedir = _make_base_dir(self.id)
# we create the corresponding storage directory
if not os.path.exists(self.basedir):
old_umask = os.umask(022)
os.makedirs(self.basedir)
# and save the father record id if it exists
try:
if self.recid != "":
open("%s/.recid" % self.basedir, "w").write(str(self.recid))
if self.doctype != "":
open("%s/.type" % self.basedir, "w").write(str(self.doctype))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, e
os.umask(old_umask)
# build list of attached files
self._build_file_list('init')
# link with related_files
self._build_related_file_list()
def __repr__(self):
return 'BibDoc(%i, %i, %s, %s)' % (self.id, self.recid, repr(self.docname), repr(self.doctype))
def __str__(self):
out = '%s:%i:::docname=%s\n' % (self.recid or '', self.id, self.docname)
out += '%s:%i:::doctype=%s\n' % (self.recid or '', self.id, self.doctype)
out += '%s:%i:::status=%s\n' % (self.recid or '', self.id, self.status)
out += '%s:%i:::basedir=%s\n' % (self.recid or '', self.id, self.basedir)
out += '%s:%i:::creation date=%s\n' % (self.recid or '', self.id, self.cd)
out += '%s:%i:::modification date=%s\n' % (self.recid or '', self.id, self.md)
out += '%s:%i:::total file attached=%s\n' % (self.recid or '', self.id, len(self.docfiles))
out += '%s:%i:::total size latest version=%s\n' % (self.recid or '', self.id, nice_size(self.get_total_size_latest_version()))
out += '%s:%i:::total size all files=%s\n' % (self.recid or '', self.id, nice_size(self.get_total_size()))
for docfile in self.docfiles:
out += str(docfile)
icon = self.get_icon()
if icon:
out += str(self.get_icon())
return out
def get_status(self):
"""Retrieve the status."""
return self.status
def touch(self):
"""Update the modification time of the bibdoc."""
run_sql('UPDATE bibdoc SET modification_date=NOW() WHERE id=%s', (self.id, ))
if self.recid:
run_sql('UPDATE bibrec SET modification_date=NOW() WHERE id=%s', (self.recid, ))
def set_status(self, new_status):
"""Set a new status."""
run_sql('UPDATE bibdoc SET status=%s WHERE id=%s', (new_status, self.id))
self.status = new_status
self.touch()
self._build_file_list()
self._build_related_file_list()
def add_file_new_version(self, filename):
"""Add a new version of a file."""
try:
latestVersion = self.get_latest_version()
if latestVersion == 0:
myversion = 1
else:
myversion = latestVersion + 1
if os.path.exists(filename):
dummy, dummy, format = decompose_file(filename)
format = normalize_format(format)
destination = "%s/%s%s;%i" % (self.basedir, self.docname, format, myversion)
try:
shutil.copyfile(filename, destination)
os.chmod(destination, 0644)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while copying '%s' to '%s': '%s'" % (file, destination, e)
else:
raise InvenioWebSubmitFileError, "'%s' does not exists!" % file
finally:
self.touch()
Md5Folder(self.basedir).update()
self._build_file_list()
def purge(self):
"""Phisically Remove all the previous version of the given bibdoc"""
version = self.get_latest_version()
if version > 1:
for file in self.docfiles:
if file.get_version() < version:
try:
os.remove(file.get_full_path())
except Exception, e:
register_exception()
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list()
def expunge(self):
"""Phisically remove all the traces of a given bibdoc"""
for file in self.docfiles:
try:
os.remove(file.get_full_path())
except Exception, e:
register_exception()
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list()
def revert(self, version):
"""Revert to a given version by copying its differnt formats to a new
version."""
try:
version = int(version)
new_version = self.get_latest_version() + 1
for docfile in self.list_version_files(version):
destination = "%s/%s%s;%i" % (self.basedir, self.docname, docfile.get_format(), new_version)
if os.path.exists(destination):
raise InvenioWebSubmitFileError, "A file for docname '%s' for the recid '%s' already exists for the format '%s'" % (self.docname, self.recid, docfile.get_format())
try:
shutil.copyfile(docfile.get_full_path(), destination)
os.chmod(destination, 0644)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while copying '%s' to '%s': '%s'" % (file, destination, e)
finally:
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list()
def add_file_new_format(self, filename, version=""):
"""add a new format of a file to an archive"""
try:
if version == "":
version = self.get_latest_version()
if version == 0:
version = 1
if os.path.exists(filename):
dummy, dummy, format = decompose_file(filename)
format = normalize_format(format)
destination = "%s/%s%s;%i" % (self.basedir, self.docname, format, version)
if os.path.exists(destination):
raise InvenioWebSubmitFileError, "A file for docname '%s' for the recid '%s' already exists for the format '%s'" % (self.docname, self.recid, format)
try:
shutil.copyfile(filename, destination)
os.chmod(destination, 0644)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while copying '%s' to '%s': '%s'" % (file, destination, e)
else:
raise InvenioWebSubmitFileError, "'%s' does not exists!" % file
finally:
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list()
def get_icon(self):
"""Returns the bibdoc corresponding to an icon of the given bibdoc."""
if self.related_files.has_key('Icon'):
return self.related_files['Icon'][0]
else:
return None
def add_icon(self, filename, basename=''):
"""Links an icon with the bibdoc object. Return the icon bibdoc"""
#first check if an icon already exists
existing_icon = self.get_icon()
if existing_icon is not None:
existing_icon.delete()
#then add the new one
if not basename:
basename = decompose_file(filename)[1]
newicon = BibDoc(doctype='Icon', docname=basename)
newicon.add_file_new_version(filename)
run_sql("INSERT INTO bibdoc_bibdoc (id_bibdoc1, id_bibdoc2, type) VALUES (%s,%s,'Icon')",
(self.id, newicon.get_id(),))
try:
try:
old_umask = os.umask(022)
open("%s/.docid" % newicon.get_base_dir(), "w").write(str(self.id))
open("%s/.type" % newicon.get_base_dir(), "w").write(str(self.doctype))
os.umask(old_umask)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while writing .docid and .doctype for folder '%s': '%s'" % (newicon.get_base_dir(), e)
finally:
Md5Folder(newicon.basedir).update()
self.touch()
self._build_related_file_list()
return newicon
def delete_icon(self):
"""Removes the current icon if it exists."""
existing_icon = self.get_icon()
if existing_icon is not None:
existing_icon.delete()
self.touch()
self._build_related_file_list()
def display(self, version="", ln = CFG_SITE_LANG):
"""Returns a formatted representation of the files linked with
the bibdoc.
"""
t = ""
if version == "all":
docfiles = self.list_all_files()
elif version != "":
version = int(version)
docfiles = self.list_version_files(version)
else:
docfiles = self.list_latest_files()
existing_icon = self.get_icon()
if existing_icon is not None:
existing_icon = existing_icon.list_all_files()[0]
imageurl = "%s/record/%s/files/%s" % \
- (weburl, self.recid, existing_icon.get_full_name())
+ (CFG_SITE_URL, self.recid, existing_icon.get_full_name())
else:
imageurl = "%s/img/smallfiles.gif" % CFG_SITE_URL
versions = []
for version in list_versions_from_array(docfiles):
currversion = {
'version' : version,
'previous' : 0,
'content' : []
}
if version == self.get_latest_version() and version != 1:
currversion['previous'] = 1
for docfile in docfiles:
if docfile.get_version() == version:
currversion['content'].append(docfile.display(ln = ln))
versions.append(currversion)
t = websubmit_templates.tmpl_bibdoc_filelist(
ln = ln,
- weburl = weburl,
versions = versions,
imageurl = imageurl,
docname = self.docname,
recid = self.recid
)
return t
def change_name(self, newname):
"""Rename the bibdoc name. New name must not be already used by the linked
bibrecs."""
newname = normalize_docname(newname)
res = run_sql("SELECT b.id FROM bibrec_bibdoc bb JOIN bibdoc b on bb.id_bibdoc=b.id WHERE bb.id_bibrec=%s AND b.docname=%s", (self.recid, newname))
if res:
raise InvenioWebSubmitFileError, "A bibdoc called %s already exists for recid %s" % (newname, self.recid)
run_sql("update bibdoc set docname=%s where id=%s", (newname, self.id,))
for f in os.listdir(self.basedir):
if f.startswith(self.docname):
shutil.move('%s/%s' % (self.basedir, f), '%s/%s' % (self.basedir, f.replace(self.docname, newname, 1)))
self.docname = newname
Md5Folder(self.basedir).update()
self.touch()
self._build_file_list('rename')
self._build_related_file_list()
def get_docname(self):
"""retrieve bibdoc name"""
return self.docname
def get_base_dir(self):
"""retrieve bibdoc base directory, e.g. /soft/cdsweb/var/data/files/123"""
return self.basedir
def get_type(self):
"""retrieve bibdoc doctype"""
return self.doctype
def get_recid(self):
"""retrieve bibdoc recid"""
return self.recid
def get_id(self):
"""retrieve bibdoc id"""
return self.id
def get_file(self, format, version=""):
"""Return a DocFile with docname name, with format (the extension), and
with the given version.
"""
if version == "":
docfiles = self.list_latest_files()
else:
version = int(version)
docfiles = self.list_version_files(version)
format = normalize_format(format)
for docfile in docfiles:
if (docfile.get_format()==format or not format):
return docfile
raise InvenioWebSubmitFileError, "No file called '%s' of format '%s', version '%s'" % (self.docname, format, version)
def list_versions(self):
"""Returns the list of existing version numbers for a given bibdoc."""
versions = []
for docfile in self.docfiles:
if not docfile.get_version() in versions:
versions.append(docfile.get_version())
return versions
def delete(self):
"""delete the current bibdoc instance"""
try:
self.change_name('DELETED-%s-%s' % (datetime.today().strftime('%Y%m%d%H%M%S'), self.docname))
run_sql("UPDATE bibdoc SET status='DELETED' WHERE id=%s", (self.id,))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "It's impossible to delete bibdoc %s: %s" % (self.id, e)
def undelete(self, previous_status=''):
"""undelete a deleted file (only if it was actually deleted). The
previous status, i.e. the restriction key can be provided.
Otherwise the bibdoc will pe public."""
try:
run_sql("UPDATE bibdoc SET status=%s WHERE id=%s AND status='DELETED'", (self.id, previous_status))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "It's impossible to undelete bibdoc %s: %s" % (self.id, e)
if self.docname.startswith('DELETED-'):
try:
# Let's remove DELETED-20080214144322- in front of the docname
original_name = '-'.join(self.docname.split('-')[2:])
self.change_name(original_name)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "It's impossible to restore the previous docname %s. %s kept as docname because:" % (original_name, self.docname, e)
else:
raise InvenioWebSubmitFileError, "Strange just undeleted docname isn't called DELETED-somedate-docname but %s" % self.docname
def _build_file_list(self, context=''):
"""Lists all files attached to the bibdoc. This function should be
called everytime the bibdoc is modified.
As a side effect it log everything that has happened to the bibdocfiles
in the log facility, according to the context:
"init": means that the function has been called;
for the first time by a constructor, hence no logging is performed
"": by default means to log every deleted file as deleted and every
added file as added;
"rename": means that every appearently deleted file is logged as
renamef and every new file as renamet.
"""
def log_action(action, docid, docname, format, version, size, checksum, timestamp=''):
"""Log an action into the bibdoclog table."""
try:
if timestamp:
run_sql('INSERT DELAYED INTO hstDOCUMENT(action, id_bibdoc, docname, docformat, docversion, docsize, docchecksum, doctimestamp) VALUES(%s, %s, %s, %s, %s, %s, %s, %s)', (action, docid, docname, format, version, size, checksum, time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(timestamp))))
else:
run_sql('INSERT DELAYED INTO hstDOCUMENT(action, id_bibdoc, docname, docformat, docversion, docsize, docchecksum, doctimestamp) VALUES(%s, %s, %s, %s, %s, %s, %s, NOW())', (action, docid, docname, format, version, size, checksum))
except DatabaseError:
register_exception()
def make_removed_added_bibdocfiles(previous_file_list):
"""Internal function for build the log of changed files."""
# Let's rebuild the previous situation
old_files = {}
for bibdocfile in previous_file_list:
old_files[(bibdocfile.name, bibdocfile.format, bibdocfile.version)] = (bibdocfile.size, bibdocfile.checksum, bibdocfile.md)
# Let's rebuild the new situation
new_files = {}
for bibdocfile in self.docfiles:
new_files[(bibdocfile.name, bibdocfile.format, bibdocfile.version)] = (bibdocfile.size, bibdocfile.checksum, bibdocfile.md)
# Let's subtract from added file all the files that are present in
# the old list, and let's add to deleted files that are not present
# added file.
added_files = dict(new_files)
deleted_files = {}
for key, value in old_files.iteritems():
if added_files.has_key(key):
del added_files[key]
else:
deleted_files[key] = value
return (added_files, deleted_files)
if context != 'init':
previous_file_list = list(self.docfiles)
self.docfiles = []
if os.path.exists(self.basedir):
self.md5s = Md5Folder(self.basedir)
files = os.listdir(self.basedir)
files.sort()
for fil in files:
if not fil.startswith('.'):
try:
filepath = "%s/%s" % (self.basedir, fil)
fileversion = int(re.sub(".*;", "", fil))
fullname = fil.replace(";%s" % fileversion, "")
checksum = self.md5s.get_checksum(fil)
(dirname, basename, format) = decompose_file(fullname)
# we can append file:
self.docfiles.append(BibDocFile(filepath, self.doctype,
fileversion, basename, format,
self.recid, self.id, self.status, checksum))
except Exception, e:
register_exception()
if context == 'init':
return
else:
added_files, deleted_files = make_removed_added_bibdocfiles(previous_file_list)
deletedstr = "DELETED"
addedstr = "ADDED"
if context == 'rename':
deletedstr = "RENAMEDFROM"
addedstr = "RENAMEDTO"
for (docname, format, version), (size, checksum, md) in added_files.iteritems():
if context == 'rename':
md = '' # No modification time
log_action(addedstr, self.id, docname, format, version, size, checksum, md)
for (docname, format, version), (size, checksum, md) in deleted_files.iteritems():
if context == 'rename':
md = '' # No modification time
log_action(deletedstr, self.id, docname, format, version, size, checksum, md)
def _build_related_file_list(self):
"""Lists all files attached to the bibdoc. This function should be
called everytime the bibdoc is modified within e.g. its icon.
"""
self.related_files = {}
res = run_sql("SELECT ln.id_bibdoc2,ln.type,bibdoc.status FROM "
"bibdoc_bibdoc AS ln,bibdoc WHERE id=ln.id_bibdoc2 AND "
"ln.id_bibdoc1=%s", (self.id,))
for row in res:
docid = row[0]
doctype = row[1]
if row[2] != 'DELETED':
if not self.related_files.has_key(doctype):
self.related_files[doctype] = []
cur_doc = BibDoc(docid=docid)
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):
"""Returns all the docfiles linked with the given bibdoc."""
return self.docfiles
def list_latest_files(self):
"""Returns all the docfiles within the last version."""
return self.list_version_files(self.get_latest_version())
def list_version_files(self, version):
"""Return all the docfiles of a particular version."""
version = int(version)
return [docfile for docfile in self.docfiles if docfile.get_version() == version]
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'.
"""
if len(self.docfiles) > 0:
self.docfiles.sort(order_files_with_version)
return self.docfiles[0].get_version()
else:
return 0
def get_file_number(self):
"""Return the total number of files."""
return len(self.docfiles)
def register_download(self, ip_address, version, format, userid=0):
"""Register the information about a download of a particular file."""
format = normalize_format(format)
return run_sql("INSERT INTO rnkDOWNLOADS "
"(id_bibrec,id_bibdoc,file_version,file_format,"
"id_user,client_host,download_time) VALUES "
"(%s,%s,%s,%s,%s,INET_ATON(%s),NOW())",
(self.recid, self.id, version, format,
userid, ip_address,))
def readfile(filename):
"""Used only for backward compatibility."""
try:
return open(filename).read()
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "It's impossible to read %s: %s" % (filename, e)
class BibDocFile:
"""This class represents a physical file in the CDS Invenio filesystem.
It should never be instantiated directly"""
def __init__(self, fullpath, doctype, version, name, format, recid, docid, status, checksum):
self.fullpath = fullpath
self.doctype = doctype
self.docid = docid
self.recid = recid
self.version = version
self.status = status
self.checksum = checksum
self.size = os.path.getsize(fullpath)
self.md = os.path.getmtime(fullpath)
try:
self.cd = os.path.getctime(fullpath)
except OSError:
self.cd = self.md
self.name = name
self.format = normalize_format(format)
self.dir = os.path.dirname(fullpath)
if format == "":
self.mime = "text/plain"
self.encoding = ""
self.fullname = name
else:
self.fullname = "%s%s" % (name, self.format)
(self.mime, self.encoding) = _mimes.guess_type(self.fullname)
if self.mime is None:
self.mime = "text/plain"
def __repr__(self):
return ('BibDocFile(%s, %s, %i, %s, %s, %i, %i, %s, %s)' % (repr(self.fullpath), repr(self.doctype), self.version, repr(self.name), repr(self.format), self.recid, self.docid, repr(self.status), repr(self.checksum)))
def __str__(self):
out = '%s:%s:%s:%s:fullpath=%s\n' % (self.recid, self.docid, self.version, self.format, self.fullpath)
out += '%s:%s:%s:%s:fullname=%s\n' % (self.recid, self.docid, self.version, self.format, self.fullname)
out += '%s:%s:%s:%s:name=%s\n' % (self.recid, self.docid, self.version, self.format, self.name)
out += '%s:%s:%s:%s:status=%s\n' % (self.recid, self.docid, self.version, self.format, self.status)
out += '%s:%s:%s:%s:checksum=%s\n' % (self.recid, self.docid, self.version, self.format, self.checksum)
out += '%s:%s:%s:%s:size=%s\n' % (self.recid, self.docid, self.version, self.format, nice_size(self.size))
out += '%s:%s:%s:%s:creation time=%s\n' % (self.recid, self.docid, self.version, self.format, self.cd)
out += '%s:%s:%s:%s:modification time=%s\n' % (self.recid, self.docid, self.version, self.format, self.md)
out += '%s:%s:%s:%s:encoding=%s\n' % (self.recid, self.docid, self.version, self.format, self.encoding)
return out
def display(self, ln = CFG_SITE_LANG):
"""Returns a formatted representation of this docfile."""
return websubmit_templates.tmpl_bibdocfile_filelist(
ln = ln,
- weburl = weburl,
recid = self.recid,
version = self.version,
name = self.name,
format = self.format,
size = self.size,
)
def is_restricted(self, req):
"""Returns restriction state. (see acc_authorize_action return values)"""
if self.status not in ('', 'DELETED'):
return acc_authorize_action(req, 'viewrestrdoc', status=self.status)
elif self.status == 'DELETED':
return (1, 'File has ben deleted')
else:
return (0, '')
def get_type(self):
return self.doctype
def get_path(self):
return self.fullpath
def get_bibdocid(self):
return self.docid
def get_name(self):
return self.name
def get_full_name(self):
return self.fullname
def get_full_path(self):
return self.fullpath
def get_format(self):
return self.format
def get_size(self):
return self.size
def get_version(self):
return self.version
def get_checksum(self):
return self.checksum
def get_content(self):
"""Returns the binary content of the file."""
return open(self.fullpath, 'rb').read()
def get_recid(self):
"""Returns the recid connected with the bibdoc of this file."""
return self.recid
def get_status(self):
"""Returns the status of the file, i.e. either '', 'DELETED' or a
restriction keyword."""
return self.status
def stream(self, req):
"""Stream the file."""
if self.status:
(auth_code, auth_message) = acc_authorize_action(req, 'viewrestrdoc', status=self.status)
else:
auth_code = 0
if auth_code == 0:
if os.path.exists(self.fullpath):
if calculate_md5(self.fullpath) != self.checksum:
raise InvenioWebSubmitFileError, "File %s, version %i, for record %s is corrupted!" % (self.fullname, self.version, self.recid)
req.content_type = self.mime
req.encoding = self.encoding
req.filename = self.fullname
req.headers_out["Content-Disposition"] = \
"inline; filename=%s" % quoteattr(self.fullname)
req.set_content_length(self.size)
req.send_http_header()
try:
req.sendfile(self.fullpath)
return ""
except IOError, e:
register_exception(req=req)
raise InvenioWebSubmitFileError, "Encountered exception while reading '%s': '%s'" % (self.fullpath, e)
else:
raise InvenioWebSubmitFileError, "%s does not exists!" % self.fullpath
else:
raise InvenioWebSubmitFileError, "You are not authorized to download %s: %s" % (self.fullname, auth_message)
def stream_restricted_icon(req):
"""Return the content of the "Restricted Icon" file."""
req.content_type = 'image/gif'
req.encoding = None
req.filename = 'restricted'
req.headers_out["Content-Disposition"] = \
"inline; filename=%s" % quoteattr('restricted')
req.set_content_length(os.path.getsize('%s/img/restricted.gif' % CFG_WEBDIR))
req.send_http_header()
try:
req.sendfile('%s/img/restricted.gif' % CFG_WEBDIR)
return ""
except Exception, e:
register_exception(req=req)
raise InvenioWebSubmitFileError, "Encountered exception while streaming restricted icon: '%s'" % (e, )
def list_types_from_array(bibdocs):
"""Retrieves the list of types from the given bibdoc list."""
types = []
for bibdoc in bibdocs:
if not bibdoc.get_type() in types:
types.append(bibdoc.get_type())
return types
def list_versions_from_array(docfiles):
"""Retrieve the list of existing versions from the given docfiles list."""
versions = []
for docfile in docfiles:
if not docfile.get_version() in versions:
versions.append(docfile.get_version())
return versions
def order_files_with_version(docfile1, docfile2):
"""order docfile objects according to their version"""
version1 = docfile1.get_version()
version2 = docfile2.get_version()
return cmp(version2, version1)
def _make_base_dir(docid):
"""Given a docid it returns the complete path that should host its files."""
group = "g" + str(int(int(docid) / CFG_WEBSUBMIT_FILESYSTEM_BIBDOC_GROUP_LIMIT))
return "%s/%s/%s" % (CFG_WEBSUBMIT_FILEDIR, group, docid)
class Md5Folder:
"""Manage all the Md5 checksum about a folder"""
def __init__(self, folder):
"""Initialize the class from the md5 checksum of a given path"""
self.folder = folder
try:
self.load()
except InvenioWebSubmitFileError:
self.md5s = {}
self.update()
def update(self, only_new = True):
"""Update the .md5 file with the current files. If only_new
is specified then only not already calculated file are calculated."""
if os.path.exists(self.folder):
for filename in os.listdir(self.folder):
if not only_new or self.md5s.get(filename, None) is None and \
not filename.startswith('.'):
self.md5s[filename] = calculate_md5("%s/%s" % (self.folder, filename))
self.store()
def store(self):
"""Store the current md5 dictionary into .md5"""
try:
old_umask = os.umask(022)
md5file = open("%s/.md5" % self.folder, "w")
for key, value in self.md5s.items():
md5file.write('%s *%s\n' % (value, key))
os.umask(old_umask)
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while storing .md5 for folder '%s': '%s'" % (self.folder, e)
def load(self):
"""Load .md5 into the md5 dictionary"""
self.md5s = {}
try:
for row in open("%s/.md5" % self.folder, "r"):
md5hash = row[:32]
filename = row[34:].strip()
self.md5s[filename] = md5hash
except IOError:
self.update()
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while loading .md5 for folder '%s': '%s'" % (self.folder, e)
def check(self, filename = ''):
"""Check the specified file or all the files for which it exists a hash
for being coherent with the stored hash."""
if filename and filename in self.md5s.keys():
try:
return self.md5s[filename] == calculate_md5("%s/%s" %
(self.folder, filename))
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while loading '%s/%s': '%s'" % (self.folder, filename, e)
else:
for filename, md5hash in self.md5s.items():
try:
if calculate_md5("%s/%s" % (self.folder, filename)) != md5hash:
return False
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while loading '%s/%s': '%s'" % (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('md5sum --binary "%s"' % filename)
ret = md5_result.read()[:32]
md5_result.close()
if len(ret) != 32:
# Error in running md5sum. Let's fallback to internal
# algorithm.
return calculate_md5(filename, force_internal=True)
else:
return ret
except Exception, e:
raise InvenioWebSubmitFileError, "Encountered an exception while calculating md5 for file '%s': '%s'" % (filename, e)
def calculate_md5(filename, force_internal=False):
"""Calculate the md5 of a physical file. This is suitable for files smaller
than 256Kb."""
if not CFG_BIBDOCFILE_MD5SUM_EXISTS or force_internal or os.path.getsize(filename) < CFG_BIBDOCFILE_MD5_THRESHOLD:
try:
to_be_read = open(filename, "rb")
computed_md5 = md5.new()
while True:
buf = to_be_read.read(CFG_BIBDOCFILE_MD5_BUFFER)
if buf:
computed_md5.update(buf)
else:
break
return computed_md5.hexdigest()
except Exception, e:
register_exception()
raise InvenioWebSubmitFileError, "Encountered an exception while calculating md5 for file '%s': '%s'" % (filename, e)
else:
return calculate_md5_external(filename)
def bibdocfile_url_to_bibrecdocs(url):
- """Given a url in the form (s)weburl/record/xxx/files/... it returns
+ """Given an URL in the form CFG_SITE_[SECURE_]URL/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 a url in the form (s)weburl/record/xxx/files/... it returns
+ """Given an URL in the form CFG_SITE_[SECURE_]URL/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 a url in the form (s)weburl/record/xxx/files/... it returns
+ """Given an URL in the form CFG_SITE_[SECURE_]URL/record/xxx/files/... it returns
a BibDocFile object for the corresponding recid/docname/format."""
dummy, dummy, format = decompose_bibdocfile_url(url)
return bibdocfile_url_to_bibdoc(url).get_file(format)
def bibdocfile_url_to_fullpath(url):
- """Given a url in the form (s)weburl/record/xxx/files/... it returns
+ """Given an URL in the form CFG_SITE_[SECURE_]URL/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 not (url.startswith('%s/record/' % weburl) or url.startswith('%s/record/' % CFG_SITE_SECURE_URL)):
+ if not (url.startswith('%s/record/' % CFG_SITE_URL) or url.startswith('%s/record/' % CFG_SITE_SECURE_URL)):
return False
splitted_url = url.split('/files/')
return len(splitted_url) == 2 and splitted_url[0] != '' and splitted_url[1] != ''
def decompose_bibdocfile_url(url):
"""Given a bibdocfile_url return a triple (recid, docname, format)."""
- if url.startswith('%s/record/' % weburl):
- recid_file = url[len('%s/record/' % weburl):]
+ if url.startswith('%s/record/' % CFG_SITE_URL):
+ recid_file = url[len('%s/record/' % CFG_SITE_URL):]
elif url.startswith('%s/record/' % CFG_SITE_SECURE_URL):
recid_file = url[len('%s/record/' % CFG_SITE_SECURE_URL):]
else:
raise InvenioWebSubmitFileError, "Url %s doesn't correspond to a valid record inside the system." % url
recid_file = recid_file.replace('/files/', '/')
recid, docname, format = decompose_file(recid_file)
return (int(recid), docname, format)
def nice_size(size):
"""Return a nicely printed size in kilo."""
unit = 'B'
if size > 1024:
size /= 1024.0
unit = 'KB'
if size > 1024:
size /= 1024.0
unit = 'MB'
if size > 1024:
size /= 1024.0
unit = 'GB'
return '%s %s' % (websearch_templates.tmpl_nice_number(size, max_ndigits_after_dot=3), unit)
diff --git a/modules/websubmit/lib/functions/Mail_New_Record_Notification.py b/modules/websubmit/lib/functions/Mail_New_Record_Notification.py
index a6f11d552..b4d27d245 100644
--- a/modules/websubmit/lib/functions/Mail_New_Record_Notification.py
+++ b/modules/websubmit/lib/functions/Mail_New_Record_Notification.py
@@ -1,289 +1,289 @@
# -*- coding: utf-8 -*-
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""This module contains the WebSubmit function "Mail_New_Record_Notification",
which should be called when a new record has been submitted to the repository
and notified of the fact should be sent by mail to the submitters/requester/
admins/other general managers.
"""
__revision__ = "$Id$"
-from invenio.config import CFG_SITE_NAME, CFG_SITE_SUPPORT_EMAIL, weburl, CFG_SITE_ADMIN_EMAIL
+from invenio.config import CFG_SITE_NAME, CFG_SITE_SUPPORT_EMAIL, CFG_SITE_URL, CFG_SITE_ADMIN_EMAIL
from invenio.webuser import email_valid_p
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
CFG_EMAIL_FROM_ADDRESS = '%s Submission Engine <%s>' % (CFG_SITE_NAME, CFG_SITE_SUPPORT_EMAIL)
def Mail_New_Record_Notification(parameters, curdir, form, user_info=None):
"""This function sends a mail giving notification about the submission
of a new item to the relevant recipients, including:
+ The record's Submitter(s);
+ The site ADMIN;
+ The record-type's "managers" (signified by the "submit_managers"
parameter);
The mail contains details of the new item's reference number(s), its
title and its author(s). It also contains a link to the item in the
CDS Invenio repository.
@param parameters: (dictionary) - contains the following parameter
strings used by this function:
+ item_status: (string) - the status of the new item. It can be
either "ADDED" (in which case the new item has been integrated
into the repository), or "APPROVAL" (in which case the item is
awaiting a referee's approval before being integrated into the
repository, and the mail should state this fact);
+ mail_submitters: (string) - a flag containing "Y" or "N" (defaulting
to "Y"). Determines whether or not the notification mail will be
sent to the submitters;
+ item_managers: (string) - a comma-separated list of email
addresses, each of which corresponds to a "manager" for the class
of item that has been submitted. These managers will receive the
notification message sent by this function;
+ author_file: (string) - the name of a file that contains the names
of the item's authors (one author per line);
+ title_file: (string) - the name of a file that contains the title
of the new item;
+ owners_file: (string) - the name of a file that contains the email
addresses of the "owners" of the submitted item. I.e. those who
will be classed as "submitters" of the item and will therefore
have modification rights over it. The mail will be sent to these
people. There should be one email-address per line in this file;
+ rn_file1: (string) - the name of the the file containing the item's
principal reference number;
+ rn_file2: (string) - the name of the file containing the item's
additional reference number(s) (e.g. sometimes two reference numbers
are allocated during the submission process;
@param curdir: (string) - the current submission's working directory. All
files containing data related to the submission are stored here and
therefore all of the files referred to in the "parameters" dictionary
are considered to be within "curdir";
@param form: (string) - a dictionary-like structure containing the fields
that were present in the WebSubmit submission form;
@return: (string) - an empty string;
"""
global sysno ## (I'm really sorry for that! :-O )
## Read items from the parameters array into local vars:
item_status = parameters["item_status"]
mail_submitters = parameters["mail_submitters"]
item_managers = parameters["item_managers"]
author_file = parameters["author_file"]
title_file = parameters["title_file"]
owners_file = parameters["owners_file"]
rn_file1 = parameters["rn_file1"]
rn_file2 = parameters["rn_file2"]
## Now wash the parameters' values:
##
## item_status:
try:
## If item_status isn't "added" or "approval", make it "added" by
## default. Else, keep its value:
item_status = (item_status.upper() in ("ADDED", "APPROVAL") \
and item_status.upper()) or "ADDED"
except AttributeError:
## Oops - item_status wasn't a string (NoneType?) Anyway, default
## it to "ADDED".
item_status = "ADDED"
## mail_submitters:
try:
## If mail_submitters isn't "Y" or "N", make it "Y" by
## default. Else, keep its value:
mail_submitters = (mail_submitters.upper() in ("Y", "N") \
and mail_submitters.upper()) or "Y"
except AttributeError:
## Oops - mail_submitters wasn't a string (NoneType?) Anyway, default
## it to "Y".
mail_submitters = "Y"
## item_managers:
## A string in which the item_managers' email addresses will be stored:
managers_email = ""
try:
## We assume that the email addresses of item managers are
## separated by commas.
item_managers_list = item_managers.split(",")
for manager in item_managers_list:
manager_address = manager.strip()
## Test that this manager's email address is OK, adding it if so:
if email_valid_p(manager_address):
## This address is OK - add it to the string of manager
## addresses:
managers_email += "%s," % manager_address
## Strip the trailing comma from managers_email (if there is one):
managers_email = managers_email.strip().rstrip(",")
except AttributeError:
## Oops - item_managers doesn't seem to be a string? Treat it as
## though it were empty:
managers_email = ""
## author_file:
authors = ""
try:
## Read in the authors from author_file, putting them into the "authors"
## variable, one per line:
fp_author_file = open("%s/%s" % (curdir, author_file), "r")
for author in fp_author_file:
authors += "%s\n" % author.strip()
fp_author_file.close()
except IOError:
## Unable to correctly read from "author_file", Skip it as though
## there were no authors:
authors = "-"
## title_file:
title = ""
try:
## Read in the lines from title_file, putting them into the "title"
## variable on one line:
fp_title_file = open("%s/%s" % (curdir, title_file), "r")
for line in fp_title_file:
title += "%s " % line.strip()
fp_title_file.close()
title = title.strip()
except IOError:
## Unable to correctly read from "title_file", Skip it as though
## there were no title:
title = "-"
## owners_file:
## A string in which the item_owners' email addresses will be stored:
owners_email = ""
try:
fp_owners_file = open("%s/%s" % (curdir, owners_file), "r")
for line in fp_owners_file:
owner_address = line.strip()
## Test that this owner's email address is OK, adding it if so:
if email_valid_p(owner_address):
## This address is OK - add it to the string of item owner
## addresses:
owners_email += "%s," % owner_address
## Strip the trailing comma from owners_email (if there is one):
owners_email = owners_email.strip().rstrip(",")
except IOError:
## Unable to correctly read from "owners_file", Skip it as though
## there were no title:
owners_email = ""
## Add "SuE" (the submitter) into the list of document "owners":
try:
fp_sue = open("%s/SuE" % curdir, "r")
sue = fp_sue.readline()
fp_sue.close()
except IOError:
sue = ""
else:
if sue.lower() not in owners_email.lower().split(","):
## The submitter is not listed in the "owners" mails,
## add her:
owners_email = "%s,%s" % (sue, owners_email)
owners_email = owners_email.strip().rstrip(",")
## rn_file1 & rn_file2:
reference_numbers = ""
try:
fp_rnfile1 = open("%s/%s" % (curdir, rn_file1), "r")
for line in fp_rnfile1:
reference_number = line.strip()
reference_number = \
reference_number.replace("\n", "").replace("\r", "").\
replace(" ", "")
if reference_number != "":
## Add this reference number into the "reference numbers"
## variable:
reference_numbers += "%s " % reference_number
fp_rnfile1.close()
except IOError:
reference_numbers = ""
try:
fp_rnfile2 = open("%s/%s" % (curdir, rn_file2), "r")
for line in fp_rnfile2:
reference_number = line.strip()
reference_number = \
reference_number.replace("\n", "").replace("\r", "").\
replace(" ", "")
if reference_number != "":
## Add this reference number into the "reference numbers"
## variable:
reference_numbers += "%s " % reference_number
fp_rnfile2.close()
except IOError:
pass
## Strip any trailing whitespace from the reference numbers:
reference_numbers = reference_numbers.strip()
## Now build the email from the information we've collected:
email_txt = """
The following item has been submitted to %(sitename)s:
Reference(s): %(reference)s
Title: %(title)s
Author(s): %(author)s
""" % { 'sitename' : CFG_SITE_NAME,
'reference' : reference_numbers,
'title' : title,
'author' : authors,
}
if item_status == "ADDED":
## The item has been added into the repository.
email_txt += """
It will soon be made available and you will be able to check it at the
following URL:
- <%(weburl)s/record/%(record-id)s>
+ <%(siteurl)s/record/%(record-id)s>
Please report any problems to <%(sitesupportemail)s>.
-""" % { 'weburl' : weburl,
+""" % { 'siteurl' : CFG_SITE_URL,
'record-id' : sysno,
'sitesupportemail' : CFG_SITE_SUPPORT_EMAIL,
}
else:
## The item has not yet been added - instead it awaits the
## approval of a referee. Let the email reflect this detail:
email_txt += """
The item is now awaiting a referee's approval before being integrated
into the repository. You will be alerted by email as soon as a decision
has been taken.
"""
## Finish the message with a signature:
email_txt += """
Thank you for submitting your item into %(sitename)s.
""" % { 'sitename' : CFG_SITE_NAME, }
## Send the email:
if mail_submitters == "Y" and len(owners_email) != "":
## Mail-to is "owners_email":
if managers_email != "":
## Managers should also be copied into the mail:
owners_email += ",%s" % managers_email
## Post the mail:
send_email(CFG_EMAIL_FROM_ADDRESS, owners_email, \
"[%s] Submitted" % reference_numbers, \
email_txt, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
elif CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN:
## We don't want to mail the "owners". Let's mail the admin instead:
send_email(CFG_EMAIL_FROM_ADDRESS, CFG_SITE_ADMIN_EMAIL, \
"[%s] Submitted" % reference_numbers, email_txt)
## Return an empty string
return ""
diff --git a/modules/websubmit/lib/functions/Send_Modify_Mail.py b/modules/websubmit/lib/functions/Send_Modify_Mail.py
index e20e786ff..1a324c593 100644
--- a/modules/websubmit/lib/functions/Send_Modify_Mail.py
+++ b/modules/websubmit/lib/functions/Send_Modify_Mail.py
@@ -1,78 +1,78 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
## Description: function Send_Modify_Mail
## This function sends an email saying the document has been
## correctly updated
## Author: T.Baron
## PARAMETERS: addressesMBI: email addresses to which the mail is sent
## fieldnameMBI: name of the file containing the modified
## fields
## sourceDoc: name of the type of document
## emailFile: name of the file containing the email of the
## user
import os
import re
from invenio.config import \
CFG_SITE_URL, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
- weburl
+ CFG_SITE_URL
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
def Send_Modify_Mail (parameters, curdir, form, user_info=None):
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
global sysno,rn
if parameters['emailFile'] is not None and parameters['emailFile']!= "" and os.path.exists("%s/%s" % (curdir,parameters['emailFile'])):
fp = open("%s/%s" % (curdir,parameters['emailFile']),"r")
sub = fp.read()
fp.close()
sub = sub.replace ("\n","")
else:
sub = ""
# Copy mail to:
addresses = parameters['addressesMBI']
addresses = addresses.strip()
m_fields = parameters['fieldnameMBI']
type = parameters['sourceDoc']
rn = re.sub("[\n\r ]+","",rn)
if os.path.exists("%s/%s" % (curdir,m_fields)):
fp = open("%s/%s" % (curdir,m_fields),"r")
fields = fp.read()
fp.close()
fields = fields.replace ("\n"," | ")
fields = re.sub("[| \n\r]+$","",fields)
else:
fields = ""
email_txt = "Dear Sir or Madam, \n%s %s has just been modified.\nModified fields: %s\n\n" % (type,rn,fields)
if CFG_SITE_URL != "" and sysno != "":
email_txt += "You can check the modified document here:\n"
- email_txt += "<%s/record/%s>\n\n" % (weburl,sysno)
+ email_txt += "<%s/record/%s>\n\n" % (CFG_SITE_URL,sysno)
email_txt += "Please note that the modifications will be taken into account in a couple of minutes.\n\nBest regards,\nThe %s Server support Team" % CFG_SITE_NAME
# send the mail
send_email(FROMADDR,sub,"%s modified" % rn,email_txt,copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/functions/Send_SRV_Mail.py b/modules/websubmit/lib/functions/Send_SRV_Mail.py
index 98e68ab2c..142c5883d 100644
--- a/modules/websubmit/lib/functions/Send_SRV_Mail.py
+++ b/modules/websubmit/lib/functions/Send_SRV_Mail.py
@@ -1,74 +1,74 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
## Description: function Send_SRV_Mail
## This function sends an email confirming the revision
## has been carried on with success
## Author: T.Baron
## PARAMETERS: addressesSRV: list of addresses to send this email to.
## categformatDAM: variable used to derive the category of
## the document from its reference. This value might then
## be used to derive the list of addresses
## emailFile: name of the file in which the user's email is
## noteFile: name of the file containing a note from the user
import os
import re
from invenio.config import \
CFG_SITE_URL, \
CFG_SITE_ADMIN_EMAIL, \
CFG_SITE_NAME, \
CFG_SITE_SUPPORT_EMAIL, \
- weburl
+ CFG_SITE_URL
from invenio.websubmit_config import CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN
from invenio.mailutils import send_email
from invenio.websubmit_functions.Retrieve_Data import Get_Field
def Send_SRV_Mail(parameters, curdir, form, user_info=None):
global rn,doctype,sysno
# variables declaration
FROMADDR = '%s Submission Engine <%s>' % (CFG_SITE_NAME,CFG_SITE_SUPPORT_EMAIL)
addresses = parameters['addressesSRV']
addresses = addresses.strip()
if parameters['emailFile'] is not None and parameters['emailFile']!="" and os.path.exists("%s/%s" % (curdir,parameters['emailFile'])):
fp = open("%s/%s" % (curdir,parameters['emailFile']), "r")
SuE = fp.read()
fp.close()
else:
SuE = ""
SuE = SuE.replace("\n",",")
if parameters['noteFile'] is not None and parameters['noteFile']!= "" and os.path.exists("%s/%s" % (curdir,parameters['noteFile'])):
fp = open("%s/%s" % (curdir,parameters['noteFile']), "r")
note = fp.read()
fp.close()
else:
note = ""
title = Get_Field("245__a",sysno)
author = Get_Field('100__a',sysno)
author += Get_Field('700__a',sysno)
# create message
- message = "A revised version of document %s has been submitted.\n\nTitle: %s\nAuthor(s): %s\nURL: <%s/record/%s>%s" % (rn,title,author,weburl,sysno,note)
+ message = "A revised version of document %s has been submitted.\n\nTitle: %s\nAuthor(s): %s\nURL: <%s/record/%s>%s" % (rn,title,author,CFG_SITE_URL,sysno,note)
# send the email
send_email(FROMADDR, SuE, "%s revised" % rn, message, copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
return ""
diff --git a/modules/websubmit/lib/websubmit_engine.py b/modules/websubmit/lib/websubmit_engine.py
index e8cb1e24e..84b390ef0 100644
--- a/modules/websubmit/lib/websubmit_engine.py
+++ b/modules/websubmit/lib/websubmit_engine.py
@@ -1,1556 +1,1554 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSubmit: the mechanism for the submission of new records into CDS Invenio
via a Web interface.
"""
__revision__ = "$Id$"
## import interesting modules:
import string
import os, os.path
import sys
import time
import types
import re
try:
from mod_python import apache
except ImportError:
pass
from invenio.config import \
CFG_BINDIR, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_URL, \
CFG_PYLIBDIR, \
CFG_WEBSUBMIT_STORAGEDIR, \
- CFG_VERSION, \
- weburl
+ CFG_VERSION
from invenio.dbquery import run_sql, Error
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_admin import acc_is_role
from invenio.webpage import page, create_error_box
from invenio.webuser import getUid, get_email, collect_user_info
from invenio.websubmit_config import *
from invenio.messages import gettext_set_language, wash_language
from websubmit_dblayer import \
get_storage_directory_of_action, \
get_longname_of_doctype, \
get_longname_of_action, \
get_num_pages_of_submission, \
get_parameter_value_for_doctype, \
submission_exists_in_log, \
log_new_pending_submission, \
log_new_completed_submission, \
update_submission_modified_date_in_log, \
update_submission_reference_in_log, \
update_submission_reference_and_status_in_log, \
get_form_fields_on_submission_page, \
get_element_description, \
get_element_check_description, \
get_form_fields_not_on_submission_page, \
function_step_is_last, \
get_collection_children_of_submission_collection, \
get_submission_collection_name, \
get_doctype_children_of_submission_collection, \
get_categories_of_doctype, \
get_doctype_details, \
get_actions_on_submission_page_for_doctype, \
get_action_details, \
get_parameters_of_function, \
get_details_of_submission, \
get_functions_for_submission_step, \
get_submissions_at_level_X_with_score_above_N, \
submission_is_finished
import invenio.template
websubmit_templates = invenio.template.load('websubmit')
def interface(req,
c=CFG_SITE_NAME,
ln=CFG_SITE_LANG,
doctype="",
act="",
startPg=1,
indir="",
access="",
mainmenu="",
fromdir="",
file="",
nextPg="",
nbPg="",
curpage=1):
"""This function is called after a user has visited a document type's
"homepage" and selected the type of "action" to perform. Having
clicked an action-button (e.g. "Submit a New Record"), this function
will be called . It performs the task of initialising a new submission
session (retrieving information about the submission, creating a
working submission-directory, etc), and "drawing" a submission page
containing the WebSubmit form that the user uses to input the metadata
to be submitted.
When a user moves between pages in the submission interface, this
function is recalled so that it can save the metadata entered into the
previous page by the user, and draw the current submission-page.
Note: During a submission, for each page refresh, this function will be
called while the variable "step" (a form variable, seen by
websubmit_webinterface, which calls this function) is 0 (ZERO).
In other words, this function handles the FRONT-END phase of a
submission, BEFORE the WebSubmit functions are called.
@param req: (apache request object) *** NOTE: Added into this object, is
a variable called "form" (req.form). This is added into the object in
the index function of websubmit_webinterface. It contains a
"mod_python.util.FieldStorage" instance, that contains the form-fields
found on the previous submission page.
@param c: (string), defaulted to CFG_SITE_NAME. The name of the CDS Invenio
installation.
@param ln: (string), defaulted to CFG_SITE_LANG. The language in which to
display the pages.
@param doctype: (string) - the doctype ID of the doctype for which the
submission is being made.
@param act: (string) - The ID of the action being performed (e.g.
submission of bibliographic information; modification of bibliographic
information, etc).
@param startPg: (integer) - Starting page for the submission? Defaults
to 1.
@param indir: (string) - the directory used to store all submissions
of the given "type" of this submission. For example, if the submission
is of the type "modify bibliographic information", this variable would
contain "modify".
@param access: (string) - the "access" number for the submission
(e.g. 1174062451_7010). This number is also used as the name for the
current working submission directory.
@param mainmenu: (string) - contains the URL (minus the CDS Invenio
home stem) for the submission's home-page. (E.g. If this submission
is "PICT", the "mainmenu" file would contain "/submit?doctype=PICT".
@param fromdir: (integer)
@param file: (string)
@param nextPg: (string)
@param nbPg: (string)
@param curpage: (integer) - the current submission page number. Defaults
to 1.
"""
ln = wash_language(ln)
# load the right message language
_ = gettext_set_language(ln)
sys.stdout = req
# get user ID:
try:
uid = getUid(req)
uid_email = get_email(uid)
except Error, e:
return errorMsg(e, req, c, ln)
# variable initialisation
t = ""
field = []
fieldhtml = []
level = []
fullDesc = []
text = []
check = []
select = []
radio = []
upload = []
txt = []
noPage = []
# Preliminary tasks
# check that the user is logged in
if uid_email == "" or uid_email == "guest":
return warningMsg(websubmit_templates.tmpl_warning_message(
ln = ln,
msg = _("Sorry, you must log in to perform this action.")
), req, ln)
# warningMsg("""<center><font color="red"></font></center>""",req, ln)
# check we have minimum fields
if "" in (doctype, act, access):
## We don't have all the necessary information to go ahead
## with this submission:
return errorMsg(_("Invalid parameter"), req, c, ln)
## Before continuing to display the submission form interface,
## verify that this submission has not already been completed:
if submission_is_finished(doctype, act, access, uid_email):
## This submission has already been completed.
## This situation can arise when, having completed a submission,
## the user uses the browser's back-button to go back to the form
## stage of the submission and then tries to submit once more.
## This is unsafe and should not be allowed. Instead of re-displaying
## the submission forms, display an error message to the user:
wrnmsg = """<b>This submission has been completed. Please go to the""" \
""" <a href="/submit?doctype=%(doctype)s&amp;ln=%(ln)s">""" \
"""main menu</a> to start a new submission.</b>""" \
% { 'doctype' : doctype, 'ln' : ln }
return warningMsg(wrnmsg, req)
## retrieve the action and doctype data:
## Concatenate action ID and doctype ID to make the submission ID:
subname = "%s%s" % (act, doctype)
if indir == "":
## Get the submission storage directory from the DB:
submission_dir = get_storage_directory_of_action(act)
if submission_dir not in ("", None):
indir = submission_dir
else:
## Unable to determine the submission-directory:
return errorMsg(_("Unable to find the submission directory."), req, c, ln)
## get the document type's long-name:
doctype_lname = get_longname_of_doctype(doctype)
if doctype_lname is not None:
## Got the doctype long-name: replace spaces with HTML chars:
docname = doctype_lname.replace(" ", "&nbsp;")
else:
## Unknown document type:
return errorMsg(_("Unknown document type"), req, c, ln)
## get the action's long-name:
actname = get_longname_of_action(act)
if actname is None:
## Unknown action:
return errorMsg(_("Unknown action"), req, c, ln)
## Get the number of pages for this submission:
num_submission_pages = get_num_pages_of_submission(subname)
if num_submission_pages is not None:
nbpages = num_submission_pages
else:
## Unable to determine the number of pages for this submission:
return errorMsg(_("Unable to determine the number of submission pages."), req, c, ln)
## If unknown, get the current page of submission:
if startPg != "" and curpage in ("", 0):
curpage = startPg
## retrieve the name of the file in which the reference of
## the submitted document will be stored
rn_filename = get_parameter_value_for_doctype(doctype, "edsrn")
if rn_filename is not None:
edsrn = rn_filename
else:
## Unknown value for edsrn - set it to an empty string:
edsrn = ""
## This defines the path to the directory containing the action data
curdir = "%s/%s/%s/%s" % (CFG_WEBSUBMIT_STORAGEDIR, indir, doctype, access)
## if this submission comes from another one (fromdir is then set)
## We retrieve the previous submission directory and put it in the proper one
if fromdir != "":
olddir = "%s/%s/%s/%s" % (CFG_WEBSUBMIT_STORAGEDIR, fromdir, doctype, access)
if os.path.exists(olddir):
os.rename(olddir, curdir)
## If the submission directory still does not exist, we create it
if not os.path.exists(curdir):
try:
os.makedirs(curdir)
except:
return errorMsg(_("Unable to create a directory for this submission."), req, c, ln)
# retrieve the original main menu url and save it in the "mainmenu" file
if mainmenu != "":
fp = open("%s/mainmenu" % curdir, "w")
fp.write(mainmenu)
fp.close()
# and if the file containing the URL to the main menu exists
# we retrieve it and store it in the $mainmenu variable
if os.path.exists("%s/mainmenu" % curdir):
fp = open("%s/mainmenu" % curdir, "r");
mainmenu = fp.read()
fp.close()
else:
mainmenu = "%s/submit" % (CFG_SITE_URL,)
# various authentication related tasks...
if uid_email != "guest" and uid_email != "":
#First save the username (email address) in the SuE file. This way bibconvert will be able to use it if needed
fp = open("%s/SuE" % curdir, "w")
fp.write(uid_email)
fp.close()
# is user authorized to perform this action?
(auth_code, auth_message) = acc_authorize_action(req, "submit", verbose=0, doctype=doctype, act=act)
if acc_is_role("submit", doctype=doctype, act=act) and auth_code != 0:
return warningMsg("""<center><font color="red">%s</font></center>""" % auth_message, req)
## update the "journal of submission":
## Does the submission already exist in the log?
submission_exists = \
submission_exists_in_log(doctype, act, access, uid_email)
if submission_exists == 1:
## update the modification-date of this submission in the log:
update_submission_modified_date_in_log(doctype, act, access, uid_email)
else:
## Submission doesn't exist in log - create it:
log_new_pending_submission(doctype, act, access, uid_email)
# Save the form fields entered in the previous submission page
# If the form was sent with the GET method
form = req.form
value = ""
# we parse all the form variables
for key in form.keys():
formfields = form[key]
if re.search("\[\]", key):
filename = key.replace("[]", "")
else:
filename = key
# the field is an array
if isinstance(formfields, types.ListType):
fp = open("%s/%s" % (curdir, filename), "w")
for formfield in formfields:
#stripslashes(value)
value = specialchars(formfield)
fp.write(value+"\n")
fp.close()
# the field is a normal string
elif isinstance(formfields, types.StringTypes) and formfields != "":
value = formfields
fp = open("%s/%s" % (curdir, filename), "w")
fp.write(specialchars(value))
fp.close()
# the field is a file
elif hasattr(formfields,"filename") and formfields.filename is not None:
if not os.path.exists("%s/files/%s" % (curdir, key)):
try:
os.makedirs("%s/files/%s" % (curdir, key))
except:
return errorMsg(_("Cannot create submission directory."), req, c, ln)
filename = formfields.filename
## Before saving the file to disc, wash the filename (in particular
## washing away UNIX and Windows (e.g. DFS) paths):
filename = os.path.basename(filename.split('\\')[-1])
filename = filename.strip()
if filename != "":
# This may be dangerous if the file size is bigger than the available memory
data = formfields.file.read()
fp = open("%s/files/%s/%s" % (curdir, key, filename), "w")
fp.write(data)
fp.close()
fp = open("%s/lastuploadedfile" % curdir, "w")
fp.write(filename)
fp.close()
fp = open("%s/%s" % (curdir, key), "w")
fp.write(filename)
fp.close()
## if the found field is the reference of the document,
## save this value in the "journal of submissions":
if uid_email != "" and uid_email != "guest":
if key == edsrn:
update_submission_reference_in_log(doctype, access, uid_email, value)
## create the interface:
subname = "%s%s" % (act, doctype)
## Get all of the form fields that appear on this page, ordered by fieldnum:
form_fields = get_form_fields_on_submission_page(subname, curpage)
full_fields = []
values = []
for field_instance in form_fields:
full_field = {}
## Retrieve the field's description:
element_descr = get_element_description(field_instance[3])
if element_descr is None:
## The form field doesn't seem to exist - return with error message:
return \
errorMsg(_("Unknown form field found on submission page."), \
req, c, ln)
if element_descr[8] is None:
val = ""
else:
val = element_descr[8]
## we also retrieve and add the javascript code of the checking function, if needed
## Set it to empty string to begin with:
full_field['javascript'] = ''
if field_instance[7] != '':
check_descr = get_element_check_description(field_instance[7])
if check_descr is not None:
## Retrieved the check description:
full_field['javascript'] = check_descr
full_field['type'] = element_descr[3]
full_field['name'] = field_instance[3]
full_field['rows'] = element_descr[5]
full_field['cols'] = element_descr[6]
full_field['val'] = val
full_field['size'] = element_descr[4]
full_field['maxlength'] = element_descr[7]
full_field['htmlcode'] = element_descr[9]
full_field['typename'] = field_instance[1] ## TODO: Investigate this, Not used?
## It also seems to refer to pagenum.
# The 'R' fields must be executed in the engine's environment,
# as the runtime functions access some global and local
# variables.
if full_field ['type'] == 'R':
co = compile (full_field ['htmlcode'].replace("\r\n","\n"), "<string>", "exec")
exec(co)
else:
text = websubmit_templates.tmpl_submit_field (ln = ln, field = full_field)
# we now determine the exact type of the created field
if full_field['type'] not in [ 'D','R']:
field.append(full_field['name'])
level.append(field_instance[5])
fullDesc.append(field_instance[4])
txt.append(field_instance[6])
check.append(field_instance[7])
# If the field is not user-defined, we try to determine its type
# (select, radio, file upload...)
# check whether it is a select field or not
if re.search("SELECT", text, re.IGNORECASE) is not None:
select.append(1)
else:
select.append(0)
# checks whether it is a radio field or not
if re.search(r"TYPE=[\"']?radio", text, re.IGNORECASE) is not None:
radio.append(1)
else:
radio.append(0)
# checks whether it is a file upload or not
if re.search(r"TYPE=[\"']?file", text, re.IGNORECASE) is not None:
upload.append(1)
else:
upload.append(0)
# if the field description contains the "<COMBO>" string, replace
# it by the category selected on the document page submission page
combofile = "combo%s" % doctype
if os.path.exists("%s/%s" % (curdir, combofile)):
f = open("%s/%s" % (curdir, combofile), "r")
combo = f.read()
f.close()
else:
combo=""
text = text.replace("<COMBO>", combo)
# if there is a <YYYY> tag in it, replace it by the current year
year = time.strftime("%Y");
text = text.replace("<YYYY>", year)
# if there is a <TODAY> tag in it, replace it by the current year
today = time.strftime("%d/%m/%Y");
text = text.replace("<TODAY>", today)
fieldhtml.append(text)
else:
select.append(0)
radio.append(0)
upload.append(0)
# field.append(value) - initial version, not working with JS, taking a submitted value
field.append(field_instance[3])
level.append(field_instance[5])
txt.append(field_instance[6])
fullDesc.append(field_instance[4])
check.append(field_instance[7])
fieldhtml.append(text)
full_field['fullDesc'] = field_instance[4]
full_field['text'] = text
# If a file exists with the name of the field we extract the saved value
text = ''
if os.path.exists("%s/%s" % (curdir, full_field['name'])):
file = open("%s/%s" % (curdir, full_field['name']), "r");
text = file.read()
text = re.compile("[\n\r]*$").sub("", text)
text = re.compile("\n").sub("\\n", text)
text = re.compile("\r").sub("", text)
file.close()
values.append(text)
full_fields.append(full_field)
returnto = {}
if int(curpage) == int(nbpages):
subname = "%s%s" % (act, doctype)
other_form_fields = \
get_form_fields_not_on_submission_page(subname, curpage)
nbFields = 0
message = ""
fullcheck_select = []
fullcheck_radio = []
fullcheck_upload = []
fullcheck_field = []
fullcheck_level = []
fullcheck_txt = []
fullcheck_noPage = []
fullcheck_check = []
for field_instance in other_form_fields:
if field_instance[5] == "M":
## If this field is mandatory, get its description:
element_descr = get_element_description(field_instance[3])
if element_descr is None:
## The form field doesn't seem to exist - return with error message:
return \
errorMsg(_("Unknown form field found on one of the submission pages."), \
req, c, ln)
if element_descr[3] in ['D', 'R']:
if element_descr[3] == "D":
text = element_descr[9]
else:
text = eval(element_descr[9])
formfields = text.split(">")
for formfield in formfields:
match = re.match("name=([^ <>]+)", formfield, re.IGNORECASE)
if match is not None:
names = match.groups
for value in names:
if value != "":
value = re.compile("[\"']+").sub("", value)
fullcheck_field.append(value)
fullcheck_level.append(field_instance[5])
fullcheck_txt.append(field_instance[6])
fullcheck_noPage.append(field_instance[1])
fullcheck_check.append(field_instance[7])
nbFields = nbFields + 1
else:
fullcheck_noPage.append(field_instance[1])
fullcheck_field.append(field_instance[3])
fullcheck_level.append(field_instance[5])
fullcheck_txt.append(field_instance[6])
fullcheck_check.append(field_instance[7])
nbFields = nbFields+1
# tests each mandatory field
fld = 0
res = 1
for i in range (0, nbFields):
res = 1
if not os.path.exists("%s/%s" % (curdir, fullcheck_field[i])):
res=0
else:
file = open("%s/%s" % (curdir, fullcheck_field[i]), "r")
text = file.read()
if text == '':
res=0
else:
if text == "Select:":
res=0
if res == 0:
fld = i
break
if not res:
returnto = {
'field' : fullcheck_txt[fld],
'page' : fullcheck_noPage[fld],
}
t += websubmit_templates.tmpl_page_interface(
ln = ln,
docname = docname,
actname = actname,
curpage = curpage,
nbpages = nbpages,
file = file,
nextPg = nextPg,
access = access,
nbPg = nbPg,
doctype = doctype,
act = act,
indir = indir,
fields = full_fields,
javascript = websubmit_templates.tmpl_page_interface_js(
ln = ln,
upload = upload,
field = field,
fieldhtml = fieldhtml,
txt = txt,
check = check,
level = level,
curdir = curdir,
values = values,
select = select,
radio = radio,
curpage = curpage,
nbpages = nbpages,
images = CFG_SITE_URL + '/img',
returnto = returnto,
),
images = CFG_SITE_URL + '/img',
mainmenu = mainmenu,
)
# start display:
req.content_type = "text/html"
req.send_http_header()
p_navtrail = """<a href="/submit" class="navtrail">%(submit)s</a>&nbsp;>&nbsp;<a href="/submit?doctype=%(doctype)s" class="navtrail">%(docname)s</a>&nbsp;""" % {
'submit' : _("Submit"),
'doctype' : doctype,
'docname' : docname,
}
return page(title= actname,
body = t,
navtrail = p_navtrail,
description = "submit documents",
keywords = "submit",
uid = uid,
language = ln,
req = req,
navmenuid='submit')
def endaction(req,
c=CFG_SITE_NAME,
ln=CFG_SITE_LANG,
doctype="",
act="",
startPg=1,
indir="",
access="",
mainmenu="",
fromdir="",
file="",
nextPg="",
nbPg="",
curpage=1,
step=1,
mode="U"):
"""Having filled-in the WebSubmit form created for metadata by the interface
function, the user clicks a button to either "finish the submission" or
to "proceed" to the next stage of the submission. At this point, a
variable called "step" will be given a value of 1 or above, which means
that this function is called by websubmit_webinterface.
So, during all non-zero steps of the submission, this function is called.
In other words, this function is called during the BACK-END phase of a
submission, in which WebSubmit *functions* are being called.
The function first ensures that all of the WebSubmit form field values
have been saved in the current working submission directory, in text-
files with the same name as the field elements have. It then determines
the functions to be called for the given step of the submission, and
executes them.
Following this, if this is the last step of the submission, it logs the
submission as "finished" in the journal of submissions.
@param req: (apache request object) *** NOTE: Added into this object, is
a variable called "form" (req.form). This is added into the object in
the index function of websubmit_webinterface. It contains a
"mod_python.util.FieldStorage" instance, that contains the form-fields
found on the previous submission page.
@param c: (string), defaulted to CFG_SITE_NAME. The name of the CDS Invenio
installation.
@param ln: (string), defaulted to CFG_SITE_LANG. The language in which to
display the pages.
@param doctype: (string) - the doctype ID of the doctype for which the
submission is being made.
@param act: (string) - The ID of the action being performed (e.g.
submission of bibliographic information; modification of bibliographic
information, etc).
@param startPg: (integer) - Starting page for the submission? Defaults
to 1.
@param indir: (string) - the directory used to store all submissions
of the given "type" of this submission. For example, if the submission
is of the type "modify bibliographic information", this variable would
contain "modify".
@param access: (string) - the "access" number for the submission
(e.g. 1174062451_7010). This number is also used as the name for the
current working submission directory.
@param mainmenu: (string) - contains the URL (minus the CDS Invenio
home stem) for the submission's home-page. (E.g. If this submission
is "PICT", the "mainmenu" file would contain "/submit?doctype=PICT".
@param fromdir:
@param file:
@param nextPg:
@param nbPg:
@param curpage: (integer) - the current submission page number. Defaults
to 1.
@param step: (integer) - the current step of the submission. Defaults to
1.
@param mode:
"""
global rn, sysno, dismode, curdir, uid, uid_email, last_step, action_score
# load the right message language
_ = gettext_set_language(ln)
try:
rn
except NameError:
rn = ""
dismode = mode
ln = wash_language(ln)
sys.stdout = req
t = ""
# get user ID:
try:
uid = getUid(req)
uid_email = get_email(uid)
except Error, e:
return errorMsg(e, req, c, ln)
# Preliminary tasks
# check that the user is logged in
if uid_email == "" or uid_email == "guest":
return warningMsg(websubmit_templates.tmpl_warning_message(
ln = ln,
msg = _("Sorry, you must log in to perform this action.")
), req, ln)
## check we have minimum fields
if "" in (doctype, act, access):
## We don't have all the necessary information to go ahead
## with this submission:
return errorMsg(_("Invalid parameter"), req, c, ln)
## Before continuing to process the submitted data, verify that
## this submission has not already been completed:
if submission_is_finished(doctype, act, access, uid_email):
## This submission has already been completed.
## This situation can arise when, having completed a submission,
## the user uses the browser's back-button to go back to the form
## stage of the submission and then tries to submit once more.
## This is unsafe and should not be allowed. Instead of re-processing
## the submitted data, display an error message to the user:
wrnmsg = """<b>This submission has been completed. Please go to the""" \
""" <a href="/submit?doctype=%(doctype)s&amp;ln=%(ln)s">""" \
"""main menu</a> to start a new submission.</b>""" \
% { 'doctype' : doctype, 'ln' : ln }
return warningMsg(wrnmsg, req)
## retrieve the action and doctype data
if indir == "":
## Get the submission storage directory from the DB:
submission_dir = get_storage_directory_of_action(act)
if submission_dir not in ("", None):
indir = submission_dir
else:
## Unable to determine the submission-directory:
return errorMsg(_("Unable to find the submission directory."), \
req, c, ln)
# The following words are reserved and should not be used as field names
reserved_words = ["stop", "file", "nextPg", "startPg", "access", "curpage", "nbPg", "act", \
"indir", "doctype", "mode", "step", "deleted", "file_path", "userfile_name"]
# This defines the path to the directory containing the action data
curdir = "%s/%s/%s/%s" % (CFG_WEBSUBMIT_STORAGEDIR, indir, doctype, access)
# If the submission directory still does not exist, we create it
if not os.path.exists(curdir):
try:
os.makedirs(curdir)
except:
return errorMsg(_("Cannot create submission directory."), req, c, ln)
# retrieve the original main menu url ans save it in the "mainmenu" file
if mainmenu != "":
fp = open("%s/mainmenu" % curdir, "w")
fp.write(mainmenu)
fp.close()
# and if the file containing the URL to the main menu exists
# we retrieve it and store it in the $mainmenu variable
if os.path.exists("%s/mainmenu" % curdir):
fp = open("%s/mainmenu" % curdir, "r");
mainmenu = fp.read()
fp.close()
else:
mainmenu = "%s/submit" % (CFG_SITE_URL,)
## retrieve the name of the file in which the reference of
## the submitted document will be stored
rn_filename = get_parameter_value_for_doctype(doctype, "edsrn")
if rn_filename is not None:
edsrn = rn_filename
else:
## Unknown value for edsrn - set it to an empty string:
edsrn = ""
## Determine whether the action is finished
## (ie there are no other steps after the current one):
finished = function_step_is_last(doctype, act, step)
# Save the form fields entered in the previous submission page
# If the form was sent with the GET method
form = req.form
value = ""
# we parse all the form variables
for key in form.keys():
formfields = form[key]
if re.search("\[\]", key):
filename = key.replace("[]", "")
else:
filename = key
# the field is an array
if isinstance(formfields,types.ListType):
fp = open("%s/%s" % (curdir, filename), "w")
for formfield in formfields:
#stripslashes(value)
value = specialchars(formfield)
fp.write(value+"\n")
fp.close()
# the field is a normal string
elif isinstance(formfields, types.StringTypes) and formfields != "":
value = formfields
fp = open("%s/%s" % (curdir, filename), "w")
fp.write(specialchars(value))
fp.close()
# the field is a file
elif hasattr(formfields, "filename") and formfields.filename is not None:
if not os.path.exists("%s/files/%s" % (curdir, key)):
try:
os.makedirs("%s/files/%s" % (curdir, key))
except:
return errorMsg("can't create submission directory", req, CFG_SITE_NAME, ln)
filename = formfields.filename
## Before saving the file to disc, wash the filename (in particular
## washing away UNIX and Windows (e.g. DFS) paths):
filename = os.path.basename(filename.split('\\')[-1])
filename = filename.strip()
if filename != "":
# This may be dangerous if the file size is bigger than the available memory
data = formfields.file.read()
fp = open("%s/files/%s/%s" % (curdir, key, filename), "w")
fp.write(data)
fp.close()
fp = open("%s/lastuploadedfile" % curdir, "w")
fp.write(filename)
fp.close()
fp = open("%s/%s" % (curdir, key), "w")
fp.write(filename)
fp.close()
## if the found field is the reference of the document
## we save this value in the "journal of submissions"
if uid_email != "" and uid_email != "guest":
if key == edsrn:
update_submission_reference_in_log(doctype, access, uid_email, value)
## get the document type's long-name:
doctype_lname = get_longname_of_doctype(doctype)
if doctype_lname is not None:
## Got the doctype long-name: replace spaces with HTML chars:
docname = doctype_lname.replace(" ", "&nbsp;")
else:
## Unknown document type:
return errorMsg(_("Unknown document type"), req, c, ln)
## get the action's long-name:
actname = get_longname_of_action(act)
if actname is None:
## Unknown action:
return errorMsg(_("Unknown action"), req, c, ln)
## Get the number of pages for this submission:
subname = "%s%s" % (act, doctype)
num_submission_pages = get_num_pages_of_submission(subname)
if num_submission_pages is not None:
nbpages = num_submission_pages
else:
## Unable to determine the number of pages for this submission:
return errorMsg(_("Unable to determine the number of submission pages."), \
req, CFG_SITE_NAME, ln)
## Determine whether the action is finished
## (ie there are no other steps after the current one):
last_step = function_step_is_last(doctype, act, step)
next_action = '' ## The next action to be proposed to the user
# Prints the action details, returning the mandatory score
action_score = action_details(doctype, act)
current_level = get_level(doctype, act)
# Calls all the function's actions
function_content = ''
try:
## Handle the execution of the functions for this
## submission/step:
start_time = time.time()
function_content = print_function_calls(req=req, doctype=doctype,
action=act,
step=step,
form=form,
start_time=start_time,
ln=ln)
except InvenioWebSubmitFunctionError, e:
## There was a serious function-error. Execution ends.
return errorMsg(e.value, req, c, ln)
except InvenioWebSubmitFunctionStop, e:
## For one reason or another, one of the functions has determined that
## the data-processing phase (i.e. the functions execution) should be
## halted and the user should be returned to the form interface once
## more. (NOTE: Redirecting the user to the Web-form interface is
## currently done using JavaScript. The "InvenioWebSubmitFunctionStop"
## exception contains a "value" string, which is effectively JavaScript
## - probably an alert box and a form that is submitted). **THIS WILL
## CHANGE IN THE FUTURE WHEN JavaScript IS REMOVED!**
if e.value is not None:
function_content = e.value
else:
function_content = e
else:
## No function exceptions (InvenioWebSubmitFunctionStop,
## InvenioWebSubmitFunctionError) were raised by the functions. Propose
## the next action (if applicable), and log the submission as finished:
## If the action was mandatory we propose the next
## mandatory action (if any)
if action_score != -1 and last_step == 1:
next_action = Propose_Next_Action(doctype, \
action_score, \
access, \
current_level, \
indir)
## If we are in the last step of an action, we can update
## the "journal of submissions"
if last_step == 1:
if uid_email != "" and uid_email != "guest" and rn != "":
## update the "journal of submission":
## Does the submission already exist in the log?
submission_exists = \
submission_exists_in_log(doctype, act, access, uid_email)
if submission_exists == 1:
## update the rn and status to finished for this submission
## in the log:
update_submission_reference_and_status_in_log(doctype, \
act, \
access, \
uid_email, \
rn, \
"finished")
else:
## Submission doesn't exist in log - create it:
log_new_completed_submission(doctype, \
act, \
access, \
uid_email, \
rn)
## Having executed the functions, create the page that will be displayed
## to the user:
t = websubmit_templates.tmpl_page_endaction(
ln = ln,
- weburl = weburl,
# these fields are necessary for the navigation
file = file,
nextPg = nextPg,
startPg = startPg,
access = access,
curpage = curpage,
nbPg = nbPg,
nbpages = nbpages,
doctype = doctype,
act = act,
docname = docname,
actname = actname,
indir = indir,
mainmenu = mainmenu,
finished = finished,
images = CFG_SITE_URL + '/img',
function_content = function_content,
next_action = next_action,
)
# start display:
req.content_type = "text/html"
req.send_http_header()
p_navtrail = """<a href="/submit" class="navtrail">""" + _("Submit") +\
"""</a>&nbsp;>&nbsp;<a href="/submit?doctype=%(doctype)s" class="navtrail">%(docname)s</a>""" % {
'doctype' : doctype,
'docname' : docname,
}
return page(title= actname,
body = t,
navtrail = p_navtrail,
description="submit documents",
keywords="submit",
uid = uid,
language = ln,
req = req,
navmenuid='submit')
def home(req, c=CFG_SITE_NAME, ln=CFG_SITE_LANG):
"""This function generates the WebSubmit "home page".
Basically, this page contains a list of submission-collections
in WebSubmit, and gives links to the various document-type
submissions.
Document-types only appear on this page when they have been
connected to a submission-collection in WebSubmit.
@param req: (apache request object)
@param c: (string) - defaults to CFG_SITE_NAME
@param ln: (string) - The CDS Invenio interface language of choice.
Defaults to CFG_SITE_LANG (the default language of the installation).
@return: (string) - the Web page to be displayed.
"""
ln = wash_language(ln)
# get user ID:
try:
uid = getUid(req)
except Error, e:
return errorMsg(e, req, c, ln)
# start display:
req.content_type = "text/html"
req.send_http_header()
# load the right message language
_ = gettext_set_language(ln)
finaltext = websubmit_templates.tmpl_submit_home_page(
ln = ln,
catalogues = makeCataloguesTable(ln)
)
return page(title=_("Submit"),
body=finaltext,
navtrail=[],
description="submit documents",
keywords="submit",
uid=uid,
language=ln,
req=req,
navmenuid='submit'
)
def makeCataloguesTable(ln=CFG_SITE_LANG):
"""Build the 'catalogues' (submission-collections) tree for
the WebSubmit home-page. This tree contains the links to
the various document types in WebSubmit.
@param ln: (string) - the language of the interface.
(defaults to 'CFG_SITE_LANG').
@return: (string) - the submission-collections tree.
"""
text = ""
catalogues = []
## Get the submission-collections attached at the top level
## of the submission-collection tree:
top_level_collctns = get_collection_children_of_submission_collection(0)
if len(top_level_collctns) != 0:
## There are submission-collections attatched to the top level.
## retrieve their details for displaying:
for child_collctn in top_level_collctns:
catalogues.append(getCatalogueBranch(child_collctn[0], 1))
text = websubmit_templates.tmpl_submit_home_catalogs(
ln=ln,
catalogs=catalogues
)
else:
text = websubmit_templates.tmpl_submit_home_catalog_no_content(ln=ln)
return text
def getCatalogueBranch(id_father, level):
"""Build up a given branch of the submission-collection
tree. I.e. given a parent submission-collection ID,
build up the tree below it. This tree will include
doctype-children, as well as other submission-
collections and their children.
Finally, return the branch as a dictionary.
@param id_father: (integer) - the ID of the submission-collection
from which to begin building the branch.
@param level: (integer) - the level of the current submission-
collection branch.
@return: (dictionary) - the branch and its sub-branches.
"""
elem = {} ## The dictionary to contain this branch of the tree.
## First, get the submission-collection-details:
collctn_name = get_submission_collection_name(id_father)
if collctn_name is not None:
## Got the submission-collection's name:
elem['name'] = collctn_name
else:
## The submission-collection is unknown to the DB
## set its name as empty:
elem['name'] = ""
elem['id'] = id_father
elem['level'] = level
## Now get details of the doctype-children of this
## submission-collection:
elem['docs'] = [] ## List to hold the doctype-children
## of the submission-collection
doctype_children = \
get_doctype_children_of_submission_collection(id_father)
for child_doctype in doctype_children:
elem['docs'].append(getDoctypeBranch(child_doctype[0]))
## Now, get the collection-children of this submission-collection:
elem['sons'] = []
collctn_children = \
get_collection_children_of_submission_collection(id_father)
for child_collctn in collctn_children:
elem['sons'].append(getCatalogueBranch(child_collctn[0], level + 1))
## Now return this branch of the built-up 'collection-tree':
return elem
def getDoctypeBranch(doctype):
"""Create a document-type 'leaf-node' for the submission-collections
tree. Basically, this leaf is a dictionary containing the name
and ID of the document-type submission to which it links.
@param doctype: (string) - the ID of the document type.
@return: (dictionary) - the document-type 'leaf node'. Contains
the following values:
+ id: (string) - the document-type ID.
+ name: (string) - the (long) name of the document-type.
"""
ldocname = get_longname_of_doctype(doctype)
if ldocname is None:
ldocname = "Unknown Document Type"
return { 'id' : doctype, 'name' : ldocname, }
def displayCatalogueBranch(id_father, level, catalogues):
text = ""
collctn_name = get_submission_collection_name(id_father)
if collctn_name is None:
## If this submission-collection wasn't known in the DB,
## give it the name "Unknown Submission-Collection" to
## avoid errors:
collctn_name = "Unknown Submission-Collection"
## Now, create the display for this submission-collection:
if level == 1:
text = "<LI><font size=\"+1\"><strong>%s</strong></font>\n" \
% collctn_name
else:
## TODO: These are the same (and the if is ugly.) Why?
if level == 2:
text = "<LI>%s\n" % collctn_name
else:
if level > 2:
text = "<LI>%s\n" % collctn_name
## Now display the children document-types that are attached
## to this submission-collection:
## First, get the children:
doctype_children = get_doctype_children_of_submission_collection(id_father)
collctn_children = get_collection_children_of_submission_collection(id_father)
if len(doctype_children) > 0 or len(collctn_children) > 0:
## There is something to display, so open a list:
text = text + "<UL>\n"
## First, add the doctype leaves of this branch:
for child_doctype in doctype_children:
## Add the doctype 'leaf-node':
text = text + displayDoctypeBranch(child_doctype[0], catalogues)
## Now add the submission-collection sub-branches:
for child_collctn in collctn_children:
catalogues.append(child_collctn[0])
text = text + displayCatalogueBranch(child_collctn[0], level+1, catalogues)
## Finally, close up the list if there were nodes to display
## at this branch:
if len(doctype_children) > 0 or len(collctn_children) > 0:
text = text + "</UL>\n"
return text
def displayDoctypeBranch(doctype, catalogues):
text = ""
ldocname = get_longname_of_doctype(doctype)
if ldocname is None:
ldocname = "Unknown Document Type"
text = "<LI><a href=\"\" onmouseover=\"javascript:" \
"popUpTextWindow('%s',true,event);\" onmouseout" \
"=\"javascript:popUpTextWindow('%s',false,event);\" " \
"onClick=\"document.forms[0].doctype.value='%s';" \
"document.forms[0].submit();return false;\">%s</a>\n" \
% (doctype, doctype, doctype, ldocname)
return text
def action(req, c=CFG_SITE_NAME, ln=CFG_SITE_LANG, doctype=""):
# load the right message language
_ = gettext_set_language(ln)
nbCateg = 0
snameCateg = []
lnameCateg = []
actionShortDesc = []
indir = []
actionbutton = []
statustext = []
t = ""
ln = wash_language(ln)
# get user ID:
try:
uid = getUid(req)
uid_email = get_email(uid)
except Error, e:
return errorMsg(e, req, c, ln)
#parses database to get all data
## first, get the list of categories
doctype_categs = get_categories_of_doctype(doctype)
for doctype_categ in doctype_categs:
nbCateg = nbCateg+1
snameCateg.append(doctype_categ[0])
lnameCateg.append(doctype_categ[1])
## Now get the details of the document type:
doctype_details = get_doctype_details(doctype)
if doctype_details is None:
## Doctype doesn't exist - raise error:
return errorMsg (_("Unable to find document type.") + str(doctype), req)
else:
docFullDesc = doctype_details[0]
docShortDesc = doctype_details[1]
description = doctype_details[4]
## Get the details of the actions supported by this document-type:
doctype_actions = get_actions_on_submission_page_for_doctype(doctype)
for doctype_action in doctype_actions:
## Get the details of this action:
action_details = get_action_details(doctype_action[0])
if action_details is not None:
actionShortDesc.append(doctype_action[0])
indir.append(action_details[1])
actionbutton.append(action_details[4])
statustext.append(action_details[5])
## Send the gathered information to the template so that the doctype's
## home-page can be displayed:
t = websubmit_templates.tmpl_action_page(
ln=ln,
uid=uid, guest=(uid_email == "" or uid_email == "guest"),
pid = os.getpid(),
now = time.time(),
doctype = doctype,
description = description,
docfulldesc = docFullDesc,
snameCateg = snameCateg,
lnameCateg = lnameCateg,
actionShortDesc = actionShortDesc,
indir = indir,
# actionbutton = actionbutton,
statustext = statustext,
)
p_navtrail = """<a href="/submit" class="navtrail">%(submit)s</a>""" % {'submit' : _("Submit")}
return page(title = docFullDesc,
body=t,
navtrail=p_navtrail,
description="submit documents",
keywords="submit",
uid=uid,
language=ln,
req=req,
navmenuid='submit'
)
def Request_Print(m, txt):
"""The argumemts to this function are the display mode (m) and the text
to be displayed (txt).
If the argument mode is 'ALL' then the text is unconditionally echoed
m can also take values S (Supervisor Mode) and U (User Mode). In these
circumstances txt is only echoed if the argument mode is the same as
the current mode
"""
global dismode
if m == "A" or m == dismode:
return txt
else:
return ""
def Evaluate_Parameter (field, doctype):
# Returns the literal value of the parameter. Assumes that the value is
# uniquely determined by the doctype, i.e. doctype is the primary key in
# the table
# If the table name is not null, evaluate the parameter
## TODO: The above comment looks like nonesense? This
## function only seems to get the values of parameters
## from the db...
## Get the value for the parameter:
param_val = get_parameter_value_for_doctype(doctype, field)
if param_val is None:
## Couldn't find a value for this parameter for this doctype.
## Instead, try with the default doctype (DEF):
param_val = get_parameter_value_for_doctype("DEF", field)
if param_val is None:
## There was no value for the parameter for the default doctype.
## Nothing can be done about it - return an empty string:
return ""
else:
## There was some kind of value for the parameter; return it:
return param_val
def Get_Parameters (function, doctype):
"""For a given function of a given document type, a dictionary
of the parameter names and values are returned.
@param function: (string) - the name of the function for which the
parameters are to be retrieved.
@param doctype: (string) - the ID of the document type.
@return: (dictionary) - of the parameters of the function.
Keyed by the parameter name, values are of course the parameter
values.
"""
parray = {}
## Get the names of the parameters expected by this function:
func_params = get_parameters_of_function(function)
for func_param in func_params:
## For each of the parameters, get its value for this document-
## type and add it into the dictionary of parameters:
parameter = func_param[0]
parray[parameter] = Evaluate_Parameter (parameter, doctype)
return parray
def get_level(doctype, action):
"""Get the level of a given submission. If unknown, return 0
as the level.
@param doctype: (string) - the ID of the document type.
@param action: (string) - the ID of the action.
@return: (integer) - the level of the submission; 0 otherwise.
"""
subm_details = get_details_of_submission(doctype, action)
if subm_details is not None:
## Return the level of this action
subm_level = subm_details[9]
try:
int(subm_level)
except ValueError:
return 0
else:
return subm_level
else:
return 0
def action_details (doctype, action):
# Prints whether the action is mandatory or optional. The score of the
# action is returned (-1 if the action was optional)
subm_details = get_details_of_submission(doctype, action)
if subm_details is not None:
if subm_details[9] != "0":
## This action is mandatory; return the score:
return subm_details[10]
else:
return -1
else:
return -1
def print_function_calls (req, doctype, action, step, form, start_time, ln=CFG_SITE_LANG):
# Calls the functions required by an "action" action on a "doctype" document
# In supervisor mode, a table of the function calls is produced
global htdocsdir,CFG_WEBSUBMIT_STORAGEDIR,access,CFG_PYLIBDIR,dismode
user_info = collect_user_info(req)
# load the right message language
_ = gettext_set_language(ln)
t = ""
## Get the list of functions to be called
funcs_to_call = get_functions_for_submission_step(doctype, action, step)
## If no functions are found at this step for this doctype,
## get the functions for the DEF(ault) doctype:
if len(funcs_to_call) == 0:
funcs_to_call = get_functions_for_submission_step("DEF", action, step)
if len(funcs_to_call) > 0:
# while there are functions left...
functions = []
for function in funcs_to_call:
function_name = function[0]
function_score = function[1]
currfunction = {
'name' : function_name,
'score' : function_score,
'error' : 0,
'text' : '',
}
if os.path.exists("%s/invenio/websubmit_functions/%s.py" % (CFG_PYLIBDIR, function_name)):
# import the function itself
#function = getattr(invenio.websubmit_functions, function_name)
execfile("%s/invenio/websubmit_functions/%s.py" % (CFG_PYLIBDIR, function_name), globals())
if not globals().has_key(function_name):
currfunction['error'] = 1
else:
function = globals()[function_name]
# Evaluate the parameters, and place them in an array
parameters = Get_Parameters(function_name, doctype)
# Call function:
log_function(curdir, "Start %s" % function_name, start_time)
try:
try:
## Attempt to call the function with 4 arguments:
## ("parameters", "curdir" and "form" as usual),
## and "user_info" - the dictionary of user
## information:
##
## Note: The function should always be called with
## these keyword arguments because the "TypeError"
## except clause checks for a specific mention of
## the 'user_info' keyword argument when a legacy
## function (one that accepts only 'parameters',
## 'curdir' and 'form') has been called and if
## the error string doesn't contain this,
## the TypeError will be considered as a something
## that was incorrectly handled in the function and
## will be propagated as an
## InvenioWebSubmitFunctionError instead of the
## function being called again with the legacy 3
## arguments.
func_returnval = function(parameters=parameters, \
curdir=curdir, \
form=form, \
user_info=user_info)
except TypeError, err:
## If the error contains the string "got an
## unexpected keyword argument", it means that the
## function doesn't accept the "user_info"
## argument. Test for this:
if "got an unexpected keyword argument 'user_info'" in \
str(err).lower():
## As expected, the function doesn't accept
## the user_info keyword argument. Call it
## again with the legacy 3 arguments
## (parameters, curdir, form):
func_returnval = \
function(parameters=parameters, \
curdir=curdir, \
form=form)
else:
## An unexpected "TypeError" was caught.
## It looks as though the function itself didn't
## handle something correctly.
## Convert this error into an
## InvenioWebSubmitFunctionError and raise it:
msg = "Unhandled TypeError caught when " \
"calling [%s] WebSubmit function: " \
"[%s]" % (function_name, str(err))
raise InvenioWebSubmitFunctionError(msg)
except InvenioWebSubmitFunctionWarning, err:
## There was an unexpected behaviour during the
## execution. Log the message into function's log
## and go to next function
log_function(curdir, "***Warning*** from %s: %s" \
% (function_name, str(err)), start_time)
## Reset "func_returnval" to None:
func_returnval = None
log_function(curdir, "End %s" % function_name, start_time)
if func_returnval is not None:
## Append the returned value as a string:
currfunction['text'] = str(func_returnval)
else:
## The function the NoneType. Don't keep that value as
## the currfunction->text. Replace it with the empty
## string.
currfunction['text'] = ""
else:
currfunction['error'] = 1
functions.append(currfunction)
t = websubmit_templates.tmpl_function_output(
ln = ln,
display_on = (dismode == 'S'),
action = action,
doctype = doctype,
step = step,
functions = functions,
)
else :
if dismode == 'S':
t = "<br /><br /><b>" + _("The chosen action is not supported by the document type.") + "</b>"
return t
def Propose_Next_Action (doctype, action_score, access, currentlevel, indir, ln=CFG_SITE_LANG):
global machine, CFG_WEBSUBMIT_STORAGEDIR, act, rn
t = ""
next_submissions = \
get_submissions_at_level_X_with_score_above_N(doctype, currentlevel, action_score)
if len(next_submissions) > 0:
actions = []
first_score = next_submissions[0][10]
for action in next_submissions:
if action[10] == first_score:
## Get the submission directory of this action:
nextdir = get_storage_directory_of_action(action[1])
if nextdir is None:
nextdir = ""
curraction = {
'page' : action[11],
'action' : action[1],
'doctype' : doctype,
'nextdir' : nextdir,
'access' : access,
'indir' : indir,
'name' : action[12],
}
actions.append(curraction)
t = websubmit_templates.tmpl_next_action(
ln = ln,
actions = actions,
)
return t
def specialchars(text):
text = string.replace(text, "&#147;", "\042");
text = string.replace(text, "&#148;", "\042");
text = string.replace(text, "&#146;", "\047");
text = string.replace(text, "&#151;", "\055");
text = string.replace(text, "&#133;", "\056\056\056");
return text
def log_function(curdir, message, start_time, filename="function_log"):
"""Write into file the message and the difference of time
between starttime and current time
@param curdir:(string) path to the destination dir
@param message: (string) message to write into the file
@param starttime: (float) time to compute from
@param filname: (string) name of log file
"""
time_lap = "%.3f" % (time.time() - start_time)
if os.access(curdir, os.F_OK|os.W_OK):
fd = open("%s/%s" % (curdir, filename), "a+")
fd.write("""%s --- %s\n""" % (message, time_lap))
fd.close()
## FIXME: Duplicated
def errorMsg(title, req, c=CFG_SITE_NAME, ln=CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
return page(title = _("Error"),
body = create_error_box(req, title=title, verbose=0, ln=ln),
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='submit')
def warningMsg(title, req, c=CFG_SITE_NAME, ln=CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
return page(title = _("Warning"),
body = title,
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='submit')
diff --git a/modules/websubmit/lib/websubmit_regression_tests.py b/modules/websubmit/lib/websubmit_regression_tests.py
index 8443926ac..39dc2459e 100644
--- a/modules/websubmit/lib/websubmit_regression_tests.py
+++ b/modules/websubmit/lib/websubmit_regression_tests.py
@@ -1,106 +1,106 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSubmit Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class WebSubmitWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebSubmit web pages whether they are up or not."""
def test_submission_pages_availability(self):
"""websubmit - availability of submission pages"""
- baseurl = weburl + '/submit/'
+ baseurl = CFG_SITE_URL + '/submit/'
_exports = ['', 'direct']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_publiline_pages_availability(self):
"""websubmit - availability of approval pages"""
- baseurl = weburl
+ baseurl = CFG_SITE_URL
_exports = ['/approve.py', '/publiline.py',
'/yourapprovals.py']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_your_submissions_pages_availability(self):
"""websubmit - availability of Your Submissions pages"""
- baseurl = weburl
+ baseurl = CFG_SITE_URL
_exports = ['/yoursubmissions.py']
error_messages = []
for url in [baseurl + page for page in _exports]:
error_messages.extend(test_web_page_content(url))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_help_page_availability(self):
"""websubmit - availability of WebSubmit help page"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/submit-guide',
+ test_web_page_content(CFG_SITE_URL + '/help/submit-guide',
expected_text="Submit Guide"))
class WebSubmitTestLegacyURLs(unittest.TestCase):
""" Check that the application still responds to legacy URLs"""
def test_legacy_help_page_link(self):
"""websubmit - legacy Submit Guide page link"""
self.assertEqual([],
- test_web_page_content(weburl + '/help/submit',
+ test_web_page_content(CFG_SITE_URL + '/help/submit',
expected_text="Submit Guide"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/submit/',
+ test_web_page_content(CFG_SITE_URL + '/help/submit/',
expected_text="Submit Guide"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/submit/index.en.html',
+ test_web_page_content(CFG_SITE_URL + '/help/submit/index.en.html',
expected_text="Submit Guide"))
self.assertEqual([],
- test_web_page_content(weburl + '/help/submit/access.en.html',
+ test_web_page_content(CFG_SITE_URL + '/help/submit/access.en.html',
expected_text="Submit Guide"))
test_suite = make_test_suite(WebSubmitWebPagesAvailabilityTest,
WebSubmitTestLegacyURLs)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/websubmit/lib/websubmit_templates.py b/modules/websubmit/lib/websubmit_templates.py
index 85fc21663..93c1ceb9c 100644
--- a/modules/websubmit/lib/websubmit_templates.py
+++ b/modules/websubmit/lib/websubmit_templates.py
@@ -1,2960 +1,2952 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import urllib
import time
import cgi
import gettext
import string
import locale
import re
import operator
import os
from invenio.config import \
CFG_SITE_URL, \
CFG_VERSION, \
- weburl
+ CFG_SITE_URL
from invenio.messages import gettext_set_language
from invenio.dateutils import convert_datetext_to_dategui
from invenio.webmessage_mailutils import email_quoted_txt2html
from invenio.htmlutils import escape_html
class Template:
# Parameters allowed in the web interface for fetching files
files_default_urlargd = {
'version': (str, ""), # version "" means "latest"
'docname': (str, ""), # the docname (optional)
'format' : (str, "") # the format
}
def tmpl_submit_home_page(self, ln, catalogues):
"""
The content of the home page of the submit engine
Parameters:
- 'ln' *string* - The language to display the interface in
- 'catalogues' *string* - The HTML code for the catalogues list
"""
# load the right message language
_ = gettext_set_language(ln)
return """
<script type="text/javascript" language="Javascript1.2">
var allLoaded = 1;
</script>
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(document_types)s:</th>
</tr>
<tr>
<td class="portalboxbody">
<br />
%(please_select)s:
<br /><br />
<table width="100%%">
<tr>
<td width="50%%" class="narrowsearchboxbody">
<form method="get" action="/submit">
<input type="hidden" name="doctype" />
%(catalogues)s
</form>
</td>
</tr>
</table>
</td>
</tr>
</table>""" % {
'document_types' : _("Document types available for submission"),
'please_select' : _("Please select the type of document you want to submit."),
'catalogues' : catalogues,
}
def tmpl_submit_home_catalog_no_content(self, ln):
"""
The content of the home page of submit in case no doctypes are available
Parameters:
- 'ln' *string* - The language to display the interface in
"""
# load the right message language
_ = gettext_set_language(ln)
out = "<h3>" + _("No document types available.") + "</h3>\n"
return out
def tmpl_submit_home_catalogs(self, ln, catalogs):
"""
Produces the catalogs' list HTML code
Parameters:
- 'ln' *string* - The language to display the interface in
- 'catalogs' *array* - The catalogs of documents, each one a hash with the properties:
- 'id' - the internal id
- 'name' - the name
- 'sons' - sub-catalogs
- 'docs' - the contained document types, in the form:
- 'id' - the internal id
- 'name' - the name
There is at least one catalog
"""
# load the right message language
_ = gettext_set_language(ln)
# import pprint
# out = "<pre>" + pprint.pformat(catalogs)
out = ""
for catalog in catalogs:
out += "\n<ul>"
out += self.tmpl_submit_home_catalogs_sub(ln, catalog)
out += "\n</ul>\n"
return out
def tmpl_submit_home_catalogs_sub(self, ln, catalog):
"""
Recursive function that produces a catalog's HTML display
Parameters:
- 'ln' *string* - The language to display the interface in
- 'catalog' *array* - A catalog of documents, with the properties:
- 'id' - the internal id
- 'name' - the name
- 'sons' - sub-catalogs
- 'docs' - the contained document types, in the form:
- 'id' - the internal id
- 'name' - the name
"""
# load the right message language
_ = gettext_set_language(ln)
if catalog['level'] == 1:
out = "<li><font size=\"+1\"><strong>%s</strong></font>\n" % catalog['name']
else:
if catalog['level'] == 2:
out = "<li>%s\n" % catalog['name']
else:
if catalog['level'] > 2:
out = "<li>%s\n" % catalog['name']
if len(catalog['docs']) or len(catalog['sons']):
out += "<ul>\n"
if len(catalog['docs']) != 0:
for row in catalog['docs']:
out += self.tmpl_submit_home_catalogs_doctype(ln, row)
if len(catalog['sons']) != 0:
for row in catalog['sons']:
out += self.tmpl_submit_home_catalogs_sub(ln, row)
if len(catalog['docs']) or len(catalog['sons']):
out += "</ul></li>"
else:
out += "</li>"
return out
def tmpl_submit_home_catalogs_doctype(self, ln, doc):
"""
Recursive function that produces a catalog's HTML display
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doc' *array* - A catalog of documents, with the properties:
- 'id' - the internal id
- 'name' - the name
"""
# load the right message language
_ = gettext_set_language(ln)
return """<li><a href="" onclick="document.forms[0].doctype.value='%(id)s';document.forms[0].submit();return false;">%(name)s</a></li>""" % doc
def tmpl_action_page(self, ln, uid, guest, pid, now, doctype,
description, docfulldesc, snameCateg,
lnameCateg, actionShortDesc, indir,
statustext):
"""
Recursive function that produces a catalog's HTML display
Parameters:
- 'ln' *string* - The language to display the interface in
- 'guest' *boolean* - If the user is logged in or not
- 'pid' *string* - The current process id
- 'now' *string* - The current time (security control features)
- 'doctype' *string* - The selected doctype
- 'description' *string* - The description of the doctype
- 'docfulldesc' *string* - The title text of the page
- 'snameCateg' *array* - The short names of all the categories of documents
- 'lnameCateg' *array* - The long names of all the categories of documents
- 'actionShortDesc' *array* - The short names (codes) for the different actions
- 'indir' *array* - The directories for each of the actions
- 'statustext' *array* - The names of the different action buttons
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
out += """
<script language="JavaScript" type="text/javascript">
var checked = 0;
function tester() {
"""
if (guest):
out += "alert(\"%(please_login_js)s\");return false;\n" % {
'please_login_js' : _("Please log in first.") + '\\n' + _("Use the top-right menu to log in.")
}
out += """
if (checked == 0) {
alert ("%(select_cat)s");
return false;
} else {
return true;
}
}
function clicked() {
checked=1;
}
function selectdoctype(nb) {
document.forms[0].act.value = docname[nb];
}
</script>
<form method="get" action="/submit">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="indir" />
<input type="hidden" name="access" value="%(now)i_%(pid)s" />
<input type="hidden" name="act" />
<input type="hidden" name="startPg" value="1" />
<input type="hidden" name="mainmenu" value="/submit?doctype=%(doctype)s" />
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(docfulldesc)s</th>
</tr>
<tr>
<td class="portalboxbody">%(description)s
<br />
<script language="JavaScript" type="text/javascript">
var nbimg = document.images.length + 1;
</script>
<br />
<table align="center" cellpadding="0" cellspacing="0" border="0">
<tr valign="top">
""" % {
'select_cat' : _("Please select a category"),
'doctype' : doctype,
'now' : now,
'pid' : pid,
'docfulldesc' : docfulldesc,
'description' : description,
}
if len(snameCateg) :
out += """<td align="right">"""
for i in range(0, len(snameCateg)):
out += """<label for="combo%(shortname)s">%(longname)s</label><input type="radio" name="combo%(doctype)s" id="combo%(shortname)s" value="%(shortname)s" onclick="clicked();" />&nbsp;<br />""" % {
'longname' : lnameCateg[i],
'doctype' : doctype,
'shortname' : snameCateg[i],
}
out += "</td>"
else:
out += "<script>checked=1;</script>"
out += """<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>
<table><tr><td>
"""
#display list of actions
for i in range(0,len(actionShortDesc)):
out += """<input type="submit" class="adminbutton" value="%(status)s" onclick="if (tester()) { document.forms[0].indir.value='%(indir)s';document.forms[0].act.value='%(act)s';document.forms[0].submit();}; return false;" /><br />""" % {
'status' : statustext[i],
'indir' : indir[i],
'act' : actionShortDesc[i]
}
out += """ </td></tr></table>
</td>
</tr>
</table>
<br />"""
if len(snameCateg) :
out += """<strong class="headline">%(notice)s:</strong><br />
%(select_cat)s""" % {
'notice' : _("Notice"),
'select_cat' : _("Select a category and then click on an action button."),
}
out += """
<br /><br />
</td>
</tr>
</table>
</form>
<form action="/submit"><hr />
<font color="black"><small>%(continue_explain)s</small></font>
<table border="0" bgcolor="#CCCCCC" width="100%%"><tr>
<td width="100%%">
<small>Access Number: <input size="15" name="AN" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input class="adminbutton" type="submit" value=" %(go)s " />
</small>
</td></tr>
</table>
<hr />
</form>
""" % {
'continue_explain' : _("To continue with a previously interrupted submission, enter an access number into the box below:"),
'doctype' : doctype,
'go' : _("GO"),
}
return out
def tmpl_warning_message(self, ln, msg):
"""
Produces a warning message for the specified text
Parameters:
- 'ln' *string* - The language to display the interface in
- 'msg' *string* - The message to display
"""
# load the right message language
_ = gettext_set_language(ln)
return """<center><font color="red">%s</font></center>""" % msg
def tmpl_page_interface(self, ln, docname, actname, curpage, nbpages, file, nextPg, access, nbPg, doctype, act, indir, fields, javascript, images, mainmenu):
"""
Produces a page with the specified fields (in the submit chain)
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The document type
- 'docname' *string* - The document type name
- 'actname' *string* - The action name
- 'act' *string* - The action
- 'curpage' *int* - The current page of submitting engine
- 'nbpages' *int* - The total number of pages
- 'nextPg' *int* - The next page
- 'access' *string* - The submission number
- 'nbPg' *string* - ??
- 'indir' *string* - the directory of submitting
- 'fields' *array* - the fields to display in the page, with each record having the structure:
- 'fullDesc' *string* - the description of the field
- 'text' *string* - the HTML code of the field
- 'javascript' *string* - if the field has some associated javascript code
- 'type' *string* - the type of field (T, F, I, H, D, S, R)
- 'name' *string* - the name of the field
- 'rows' *string* - the number of rows for textareas
- 'cols' *string* - the number of columns for textareas
- 'val' *string* - the default value of the field
- 'size' *string* - the size for text fields
- 'maxlength' *string* - the maximum length for text fields
- 'htmlcode' *string* - the complete HTML code for user-defined fields
- 'typename' *string* - the long name of the type
- 'javascript' *string* - the javascript code to insert in the page
- 'images' *string* - the path to the images
- 'mainmenu' *string* - the url of the main menu
"""
# load the right message language
_ = gettext_set_language(ln)
# top menu
out = """
<form method="post" action="/submit" enctype="multipart/form-data" onsubmit="return tester();">
<center><table cellspacing="0" cellpadding="0" border="0">
<tr>
<td class="submitHeader"><b>%(docname)s&nbsp;</b></td>
<td class="submitHeader"><small>&nbsp;%(actname)s&nbsp;</small></td>
<td valign="bottom">
<table cellspacing="0" cellpadding="0" border="0" width="100%%">
<tr><td class="submitEmptyPage">&nbsp;&nbsp;</td>
""" % {
'docname' : docname,
'actname' : actname,
}
for i in range(1, nbpages+1):
if i == int(curpage):
out += """<td class="submitCurrentPage"><small>&nbsp;page: %s&nbsp;</small></td>""" % curpage
else:
out += """<td class="submitPage"><small>&nbsp;<a href='' onclick="if (tester2() == 1){document.forms[0].curpage.value=%s;document.forms[0].submit();return false;} else { return false; }">%s</a>&nbsp;</small></td>""" % (i,i)
out += """ <td class="submitEmptyPage">&nbsp;&nbsp;
</td></tr></table>
</td>
<td class="submitHeader" align="right">&nbsp;<a href='' onclick="window.open('/submit/summary?doctype=%(doctype)s&amp;act=%(act)s&amp;access=%(access)s&amp;indir=%(indir)s','summary','scrollbars=yes,menubar=no,width=500,height=250');return false;"><font color="white"><small>%(summary)s(2)</small></font></a>&nbsp;</td>
</tr>
<tr><td colspan="5" class="submitHeader">
<table border="0" cellspacing="0" cellpadding="15" width="100%%" class="submitBody"><tr><td>
<br />
<input type="hidden" name="file" value="%(file)s" />
<input type="hidden" name="nextPg" value="%(nextPg)s" />
<input type="hidden" name="access" value="%(access)s" />
<input type="hidden" name="curpage" value="%(curpage)s" />
<input type="hidden" name="nbPg" value="%(nbPg)s" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="act" value="%(act)s" />
<input type="hidden" name="indir" value="%(indir)s" />
<input type="hidden" name="mode" value="U" />
<input type="hidden" name="step" value="0" />
""" % {
'summary' : _("SUMMARY"),
'doctype' : doctype,
'act' : act,
'access' : access,
'indir' : indir,
'file' : file,
'nextPg' : nextPg,
'curpage' : curpage,
'nbPg' : nbPg,
}
for field in fields:
if field['javascript']:
out += """<script language="JavaScript1.1" type="text/javascript">
%s
</script>
""" % field['javascript'];
# now displays the html form field(s)
out += "%s\n%s\n" % (field['fullDesc'], field['text'])
out += javascript
out += "<br />&nbsp;<br />&nbsp;</td></tr></table></td></tr>\n"
# Display the navigation cell
# Display "previous page" navigation arrows
out += """<tr><td colspan="5"><table border="0" cellpadding="0" cellspacing="0" width="100%%"><tr>"""
if int(curpage) != 1:
out += """ <td class="submitHeader" align="left">&nbsp;
<a href='' onclick="if (tester2() == 1) {document.forms[0].curpage.value=%(prpage)s;document.forms[0].submit();return false;} else { return false; }">
<img src="%(images)s/left-trans.gif" alt="%(prevpage)s" border="0" />
<strong><font color="white">%(prevpage)s</font></strong>
</a>
</td>
""" % {
'prpage' : int(curpage) - 1,
'images' : images,
'prevpage' : _("Previous page"),
}
else:
out += """ <td class="submitHeader">&nbsp;</td>"""
# Display the submission number
out += """ <td class="submitHeader" align="center"><small>%(submission)s: %(access)s</small></td>\n""" % {
'submission' : _("Submission number") + '(1)',
'access' : access,
}
# Display the "next page" navigation arrow
if int(curpage) != int(nbpages):
out += """ <td class="submitHeader" align="right">
<a href='' onclick="if (tester2()){document.forms[0].curpage.value=%(nxpage)s;document.forms[0].submit();return false;} else {return false;}; return false;">
<strong><font color="white">%(nextpage)s</font></strong>
<img src="%(images)s/right-trans.gif" alt="%(nextpage)s" border="0" />
</a>
</td>
""" % {
'nxpage' : int(curpage) + 1,
'images' : images,
'nextpage' : _("Next page"),
}
else:
out += """ <td class="submitHeader">&nbsp;</td>"""
out += """</tr></table></td></tr></table></center></form>
<br />
<br />
<a href="%(mainmenu)s" onclick="return confirm('%(surequit)s')">
<img src="%(images)s/mainmenu.gif" border="0" alt="%(back)s" align="right" /></a>
<br /><br />
<hr />
<small>%(take_note)s</small><br />
<small>%(explain_summary)s</small><br />
""" % {
'surequit' : _("Are you sure you want to quit this submission?"),
'back' : _("Back to main menu"),
'mainmenu' : mainmenu,
'images' : images,
'take_note' : '(1) ' + _("This is your submission access number. It can be used to continue with an interrupted submission in case of problems."),
'explain_summary' : '(2) ' + _("Mandatory fields appear in red in the SUMMARY window."),
}
return out
def tmpl_submit_field(self, ln, field):
"""
Produces the HTML code for the specified field
Parameters:
- 'ln' *string* - The language to display the interface in
- 'field' *array* - the field to display in the page, with the following structure:
- 'javascript' *string* - if the field has some associated javascript code
- 'type' *string* - the type of field (T, F, I, H, D, S, R)
- 'name' *string* - the name of the field
- 'rows' *string* - the number of rows for textareas
- 'cols' *string* - the number of columns for textareas
- 'val' *string* - the default value of the field
- 'size' *string* - the size for text fields
- 'maxlength' *string* - the maximum length for text fields
- 'htmlcode' *string* - the complete HTML code for user-defined fields
- 'typename' *string* - the long name of the type
"""
# load the right message language
_ = gettext_set_language(ln)
# If the field is a textarea
if field['type'] == 'T':
## Field is a textarea:
text="<textarea name=\"%s\" rows=\"%s\" cols=\"%s\">%s</textarea>" \
% (field['name'], field['rows'], field['cols'], cgi.escape(str(field['val']), 1))
# If the field is a file upload
elif field['type'] == 'F':
## the field is a file input:
text = """<input type="file" name="%s" size="%s"%s />""" \
% (field['name'], field['size'], "%s" \
% ((field['maxlength'] in (0, None) and " ") or (""" maxlength="%s\"""" % field['maxlength'])) )
# If the field is a text input
elif field['type'] == 'I':
## Field is a text input:
text = """<input type="text" name="%s" size="%s" value="%s"%s />""" \
% (field['name'], field['size'], field['val'], "%s" \
% ((field['maxlength'] in (0, None) and " ") or (""" maxlength="%s\"""" % field['maxlength'])) )
# If the field is a hidden input
elif field['type'] == 'H':
text="<input type=\"hidden\" name=\"%s\" value=\"%s\" />" % (field['name'], field['val'])
# If the field is user-defined
elif field['type'] == 'D':
text=field['htmlcode']
# If the field is a select box
elif field['type'] == 'S':
text=field['htmlcode']
# If the field type is not recognized
else:
text="%s: unknown field type" % field['typename']
return text
def tmpl_page_interface_js(self, ln, upload, field, fieldhtml, txt, check, level, curdir, values, select, radio, curpage, nbpages, images, returnto):
"""
Produces the javascript for validation and value filling for a submit interface page
Parameters:
- 'ln' *string* - The language to display the interface in
- 'upload' *array* - booleans if the field is a <input type="file"> field
- 'field' *array* - the fields' names
- 'fieldhtml' *array* - the fields' HTML representation
- 'txt' *array* - the fields' long name
- 'check' *array* - if the fields should be checked (in javascript)
- 'level' *array* - strings, if the fields should be filled (M) or not (O)
- 'curdir' *array* - the current directory of the submission
- 'values' *array* - the current values of the fields
- 'select' *array* - booleans, if the controls are "select" controls
- 'radio' *array* - booleans, if the controls are "radio" controls
- 'curpage' *int* - the current page
- 'nbpages' *int* - the total number of pages
- 'images' *int* - the path to the images
- 'returnto' *array* - a structure with 'field' and 'page', if a mandatory field on antoher page was not completed
"""
# load the right message language
_ = gettext_set_language(ln)
nbFields = len(upload)
# if there is a file upload field, we change the encoding type
out = """<script language="JavaScript1.1" type="text/javascript">
"""
for i in range(0,nbFields):
if upload[i] == 1:
out += "document.forms[0].encoding = \"multipart/form-data\";\n"
break
# we don't want the form to be submitted if the user enters 'Return'
# tests if mandatory fields are well filled
out += """function tester(){
return false;
}
function tester2() {
"""
for i in range(0,nbFields):
if re.search("%s\[\]" % field[i],fieldhtml[i]):
fieldname = "%s[]" % field[i]
else:
fieldname = field[i]
out += " el = document.forms[0].elements['%s'];\n" % fieldname
# If the field must be checked we call the checking function
if check[i] != "":
out += """if (%(check)s(el.value) == 0) {
el.focus();
return 0;
} """ % {
'check' : check[i]
}
# If the field is mandatory, we check a value has been selected
if level[i] == 'M':
if select[i] != 0:
# If the field is a select box
out += """if ((el.selectedIndex == -1)||(el.selectedIndex == 0)){
alert("%(field_mandatory)s");
return 0;
} """ % {
'field_mandatory' : _("The field %s is mandatory.") % txt[i] + '\\n' + _("Please make a choice in the select box")
}
elif radio[i] != 0:
# If the field is a radio buttonset
out += """var check=0;
for (var j = 0; j < el.length; j++) {
if (el.options[j].checked){
check++;
}
}
if (check == 0) {
alert("%(press_button)s");
return 0;
}""" % {
'press_button':_("Please press a button.")
}
else:
# If the field is a text input
out += """if (el.value == '') {
alert("%(field_mandatory)s");
return 0;
}""" % {
'field_mandatory' : _("The field %s is mandatory. Please fill it in.") % txt[i]
}
out += """ return 1;
}
<!-- Fill the fields in with the previous saved values-->
"""
# # # # # # # # # # # # # # # # # # # # # # # # #
# Fill the fields with the previously saved values
# # # # # # # # # # # # # # # # # # # # # # # # #
for i in range(0,nbFields):
if re.search("%s\[\]"%field[i],fieldhtml[i]):
fieldname = "%s[]" % field[i]
else:
fieldname = field[i]
text = values[i]
if text != '':
if select[i] != 0:
# If the field is a SELECT element
vals = text.split("\n")
tmp=""
for val in vals:
if tmp != "":
tmp = tmp + " || "
tmp = tmp + "el.options[j].value == \"%s\" || el.options[j].text == \"%s\"" % (val,val)
if tmp != "":
out += """
<!--SELECT field found-->
el = document.forms[0].elements['%(fieldname)s'];
for (var j = 0; j < el.length; j++){
if (%(tmp)s){
el.options[j].selected = true;
}
}""" % {
'fieldname' : fieldname,
'tmp' : tmp,
}
elif radio[i] != 0:
# If the field is a RADIO element
out += """<!--RADIO field found-->
el = document.forms[0].elements['%(fieldname)s'];
if (el.value == "%(text)s"){
el.checked=true;
}""" % {
'fieldname' : fieldname,
'text' : cgi.escape(str(text)),
}
elif upload[i] == 0:
text = text.replace('"','\"')
text = text.replace("\n","\\n")
# If the field is not an upload element
out += """<!--input field found-->
el = document.forms[0].elements['%(fieldname)s'];
el.value="%(text)s";
""" % {
'fieldname' : fieldname,
'text' : cgi.escape(str(text)),
}
out += """<!--End Fill in section-->
"""
# JS function finish
# This function tests each mandatory field in the whole submission and checks whether
# the field has been correctly filled in or not
# This function is called when the user presses the "End
# Submission" button
if int(curpage) == int(nbpages):
out += """function finish() {
"""
if returnto:
out += """alert ("%(msg)s");
document.forms[0].curpage.value="%(page)s";
document.forms[0].submit();
}
""" % {
'msg' : _("The field %(field)s is mandatory.") + '\n' \
+ _("Going back to page") \
+ str(returnto['page']),
'page' : returnto['page']
}
else:
out += """ if (tester2()) {
document.forms[0].action="/submit";
document.forms[0].step.value=1;
document.forms[0].submit();
} else {
return false;
}
}"""
out += """</script>"""
return out
- def tmpl_page_endaction(self, ln, weburl, file, nextPg, startPg, access, curpage, nbPg, nbpages, doctype, act, docname, actname, indir, mainmenu, finished, function_content, next_action, images):
+ def tmpl_page_endaction(self, ln, file, nextPg, startPg, access, curpage, nbPg, nbpages, doctype, act, docname, actname, indir, mainmenu, finished, function_content, next_action, images):
"""
Produces the pages after all the fields have been submitted.
Parameters:
- 'ln' *string* - The language to display the interface in
- - 'weburl' *string* - The url of CDS Invenio
-
- 'doctype' *string* - The document type
- 'act' *string* - The action
- 'docname' *string* - The document type name
- 'actname' *string* - The action name
- 'curpage' *int* - The current page of submitting engine
- 'startPg' *int* - The start page
- 'nextPg' *int* - The next page
- 'access' *string* - The submission number
- 'nbPg' *string* - total number of pages
- 'nbpages' *string* - number of pages (?)
- 'indir' *string* - the directory of submitting
- 'file' *string* - ??
- 'mainmenu' *string* - the url of the main menu
- 'finished' *bool* - if the submission is finished
- 'images' *string* - the path to the images
- 'function_content' *string* - HTML code produced by some function executed
- 'next_action' *string* - if there is another action to be completed, the HTML code for linking to it
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<form ENCTYPE="multipart/form-data" action="/submit" method="post">
<input type="hidden" name="file" value="%(file)s" />
<input type="hidden" name="nextPg" value="%(nextPg)s" />
<input type="hidden" name="startPg" value="%(startPg)s" />
<input type="hidden" name="access" value="%(access)s" />
<input type="hidden" name="curpage" value="%(curpage)s" />
<input type="hidden" name="nbPg" value="%(nbPg)s" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="act" value="%(act)s" />
<input type="hidden" name="indir" value="%(indir)s" />
<input type="hidden" name="fromdir" value="" />
<input type="hidden" name="mainmenu" value="%(mainmenu)s" />
<input type="hidden" name="mode" value="U" />
<input type="hidden" name="step" value="1" />
<input type="hidden" name="deleted" value="no" />
<input type="hidden" name="file_path" value="" />
<input type="hidden" name="userfile_name" value="" />
<center><table cellspacing="0" cellpadding="0" border="0"><tr>
<td class="submitHeader"><b>%(docname)s&nbsp;</b></td>
<td class="submitHeader"><small>&nbsp;%(actname)s&nbsp;</small></td>
<td valign="bottom">
<table cellspacing="0" cellpadding="0" border="0" width="100%%">
<tr><td class="submitEmptyPage">&nbsp;&nbsp;</td>
""" % {
'file' : file,
'nextPg' : nextPg,
'startPg' : startPg,
'access' : access,
'curpage' : curpage,
'nbPg' : nbPg,
'doctype' : doctype,
'act' : act,
'docname' : docname,
'actname' : actname,
'indir' : indir,
'mainmenu' : mainmenu,
}
if finished == 1:
out += """<td class="submitCurrentPage">%(finished)s</td>
<td class="submitEmptyPage">&nbsp;&nbsp;</td>
</tr></table>
</td>
<td class="submitEmptyPage" align="right">&nbsp;</td>
""" % {
'finished' : _("finished!"),
}
else:
for i in range(1, nbpages + 1):
out += """<td class="submitPage"><small>&nbsp;
<a href='' onclick="document.forms[0].curpage.value=%s;document.forms[0].action='/submit';document.forms[0].step.value=0;document.forms[0].submit();return false;">%s</a>&nbsp;</small></td>""" % (i,i)
out += """<td class="submitCurrentPage">%(end_action)s</td><td class="submitEmptyPage">&nbsp;&nbsp;</td></tr></table></td>
<td class="submitHeader" align="right">&nbsp;<a href='' onclick="window.open('/submit/summary?doctype=%(doctype)s&amp;act=%(act)s&amp;access=%(access)s&amp;indir=%(indir)s','summary','scrollbars=yes,menubar=no,width=500,height=250');return false;"><font color="white"><small>%(summary)s(2)</small></font></a>&nbsp;</td>""" % {
'end_action' : _("end of action"),
'summary' : _("SUMMARY"),
'doctype' : doctype,
'act' : act,
'access' : access,
'indir' : indir,
}
out += """</tr>
<tr>
<td colspan="5" class="submitBody">
<small><br /><br />
%(function_content)s
%(next_action)s
<br /><br />
</td>
</tr>
<tr class="submitHeader">
<td class="submitHeader" colspan="5" align="center">""" % {
'function_content' : function_content,
'next_action' : next_action,
}
if finished == 0:
out += """<small>%(submission)s</small>&sup2;:
<small>%(access)s</small>""" % {
'submission' : _("Submission no"),
'access' : access,
}
else:
out += "&nbsp;\n"
out += """
</td>
</tr>
</table>
</center>
</form>
<br />
<br />"""
# Add the "back to main menu" button
if finished == 0:
out += """ <a href="%(mainmenu)s" onclick="return confirm('%(surequit)s')">
<img src="%(images)s/mainmenu.gif" border="0" alt="%(back)s" align="right" /></a>
<br /><br />""" % {
'surequit' : _("Are you sure you want to quit this submission?"),
'back' : _("Back to main menu"),
'images' : images,
'mainmenu' : mainmenu
}
else:
out += """ <a href="%(mainmenu)s">
<img src="%(images)s/mainmenu.gif" border="0" alt="%(back)s" align="right" /></a>
<br /><br />""" % {
'back' : _("Back to main menu"),
'images' : images,
'mainmenu' : mainmenu,
}
return out
def tmpl_function_output(self, ln, display_on, action, doctype, step, functions):
"""
Produces the output of the functions.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'display_on' *bool* - If debug information should be displayed
- 'doctype' *string* - The document type
- 'action' *string* - The action
- 'step' *int* - The current step in submission
- 'functions' *aray* - HTML code produced by functions executed and informations about the functions
- 'name' *string* - the name of the function
- 'score' *string* - the score of the function
- 'error' *bool* - if the function execution produced errors
- 'text' *string* - the HTML code produced by the function
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
if display_on:
out += """<br /><br />%(function_list)s<P>
<table border="1" cellpadding="15">
<tr><th>%(function)s</th><th>%(score)s</th><th>%(running)s</th></tr>
""" % {
'function_list' : _("Here is the %(x_action)s function list for %(x_doctype)s documents at level %(x_step)s") % {
'x_action' : action,
'x_doctype' : doctype,
'x_step' : step,
},
'function' : _("Function"),
'score' : _("Score"),
'running' : _("Running function"),
}
for function in functions:
out += """<tr><td>%(name)s</td><td>%(score)s</td><td>%(result)s</td></tr>""" % {
'name' : function['name'],
'score' : function['score'],
'result' : function['error'] and (_("Function %s does not exist.") % function['name'] + "<br />") or function['text']
}
out += "</table>"
else:
for function in functions:
if not function['error']:
out += function['text']
return out
def tmpl_next_action(self, ln, actions):
"""
Produces the output of the functions.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'actions' *array* - The actions to display, in the structure
- 'page' *string* - the starting page
- 'action' *string* - the action (in terms of submission)
- 'doctype' *string* - the doctype
- 'nextdir' *string* - the path to the submission data
- 'access' *string* - the submission number
- 'indir' *string* - ??
- 'name' *string* - the name of the action
"""
# load the right message language
_ = gettext_set_language(ln)
out = "<br /><br />%(haveto)s<ul>" % {
'haveto' : _("You must now"),
}
i = 0
for action in actions:
if i > 0:
out += " <b>" + _("or") + "</b> "
i += 1
out += """<li><a href="" onclick="document.forms[0].action='/submit';document.forms[0].curpage.value='%(page)s';document.forms[0].startPg.value='%(page)s';document.forms[0].act.value='%(action)s';document.forms[0].doctype.value='%(doctype)s';document.forms[0].indir.value='%(nextdir)s';document.forms[0].access.value='%(access)s';document.forms[0].fromdir.value='%(indir)s';document.forms[0].submit();return false;"> %(name)s </a></li>""" % action
out += "</ul>"
return out
def tmpl_filelist(self, ln, filelist='', recid='', docname='', version=''):
"""
Displays the file list for a record.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'recid' *int* - The record id
- 'docname' *string* - The document name
- 'version' *int* - The version of the document
- 'filelist' *string* - The HTML string of the filelist (produced by the BibDoc classes)
"""
# load the right message language
_ = gettext_set_language(ln)
- title = _("record") + ' #' + '<a href="%s/record/%s">%s</a>' % (weburl, recid, recid)
+ title = _("record") + ' #' + '<a href="%s/record/%s">%s</a>' % (CFG_SITE_URL, recid, recid)
if docname != "":
title += ' ' + _("document") + ' #' + str(docname)
if version != "":
title += ' ' + _("version") + ' #' + str(version)
out = """<div style="width:90%%;margin:auto;min-height:100px;margin-top:10px">
<!--start file list-->
%s
<!--end file list--></div>
""" % (filelist)
return out
def tmpl_bibrecdoc_filelist(self, ln, types):
"""
Displays the file list for a record.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'types' *array* - The different types to display, each record in the format:
- 'name' *string* - The name of the format
- 'content' *array of string* - The HTML code produced by tmpl_bibdoc_filelist, for the right files
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
for mytype in types:
out += "<small><b>%s</b> %s:</small>" % (mytype['name'], _("file(s)"))
out += "<ul>"
for content in mytype['content']:
out += content
out += "</ul>"
return out
- def tmpl_bibdoc_filelist(self, ln, weburl='', versions=[], imageurl='', recid='', docname=''):
+ def tmpl_bibdoc_filelist(self, ln, versions=[], imageurl='', recid='', docname=''):
"""
Displays the file list for a record.
Parameters:
- 'ln' *string* - The language to display the interface in
- - 'weburl' *string* - The url of CDS Invenio
-
- 'versions' *array* - The different versions to display, each record in the format:
- 'version' *string* - The version
- 'content' *string* - The HTML code produced by tmpl_bibdocfile_filelist, for the right file
- 'previous' *bool* - If the file has previous versions
- 'imageurl' *string* - The URL to the file image
- 'recid' *int* - The record id
- 'docname' *string* - The name of the document
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<table border="0" cellspacing="1" class="searchbox">
<tr>
<td align="left" colspan="2" class="portalboxheader">
<img src='%(imageurl)s' border="0" />&nbsp;&nbsp;%(docname)s
</td>
</tr>""" % {
'imageurl' : imageurl,
'docname' : docname
}
for version in versions:
if version['previous']:
- versiontext = """<br />(%(see)s <a href="%(weburl)s/record/%(recID)s/files/?docname=%(docname)s&amp;version=all">%(previous)s</a>)""" % {
+ versiontext = """<br />(%(see)s <a href="%(siteurl)s/record/%(recID)s/files/?docname=%(docname)s&amp;version=all">%(previous)s</a>)""" % {
'see' : _("see"),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'docname' : urllib.quote(docname),
'recID': recid,
'previous': _("previous"),
}
else:
versiontext = ""
out += """<tr>
<td class="portalboxheader">
<font size="-2">%(version)s %(ver)s%(text)s</font>
</td>
<td>
<table>
""" % {
'version' : _("version"),
'ver' : version['version'],
'text' : versiontext,
}
for content in version['content']:
out += content
out += "</table></td></tr>"
out += "</table>"
return out
- def tmpl_bibdocfile_filelist(self, ln, weburl, recid, name, version, format, size):
+ def tmpl_bibdocfile_filelist(self, ln, recid, name, version, format, size):
"""
Displays a file in the file list.
Parameters:
- 'ln' *string* - The language to display the interface in
- - 'weburl' *string* - The url of CDS Invenio
-
- 'recid' *int* - The id of the record
- 'name' *string* - The name of the file
- 'version' *string* - The version
- 'format' *string* - The display format
- 'size' *string* - The size of the file
"""
# load the right message language
_ = gettext_set_language(ln)
return """<tr>
<td valign="top">
- <small><a href="%(weburl)s/record/%(recid)s/files/%(docname)s%(format)s?version=%(version)s">
+ <small><a href="%(siteurl)s/record/%(recid)s/files/%(docname)s%(format)s?version=%(version)s">
%(name)s%(format)s
</a></small>
</td>
<td valign="top">
<font size="-2" color="green">[%(size)s&nbsp;B]</font>
</td></tr>""" % {
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'recid' : recid,
'docname' : name,
'version' : version,
'name' : name,
'format' : format,
'size' : size
}
def tmpl_submit_summary (self, ln, values, images):
"""
Displays the summary for the submit procedure.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'values' *array* - The values of submit. Each of the records contain the following fields:
- 'name' *string* - The name of the field
- 'mandatory' *bool* - If the field is mandatory or not
- 'value' *string* - The inserted value
- 'page' *int* - The submit page on which the field is entered
- 'images' *string* - the path to the images
"""
# load the right message language
_ = gettext_set_language(ln)
out = """<body style="background-image: url(%(images)s/header_background.gif);"><table border="0">""" % \
{ 'images' : images }
for value in values:
if value['mandatory']:
color = "red"
else:
color = ""
out += """<tr>
<td align="right">
<small>
<a href='' onclick="window.opener.document.forms[0].curpage.value='%(page)s';window.opener.document.forms[0].action='/submit';window.opener.document.forms[0].submit();return false;">
<font color="%(color)s">%(name)s</font>
</a>
</small>
</td>
<td>
<i><small><font color="black">%(value)s</font></small></i>
</td>
</tr>""" % {
'color' : color,
'name' : value['name'],
'value' : value['value'],
'page' : value['page']
}
out += "</table>"
return out
- def tmpl_yoursubmissions(self, ln, images, weburl, order, doctypes, submissions):
+ def tmpl_yoursubmissions(self, ln, images, order, doctypes, submissions):
"""
Displays the list of the user's submissions.
Parameters:
- 'ln' *string* - The language to display the interface in
- 'images' *string* - the path to the images
- - 'weburl' *string* - The url of CDS Invenio
-
- 'order' *string* - The ordering parameter
- 'doctypes' *array* - All the available doctypes, in structures:
- 'id' *string* - The doctype id
- 'name' *string* - The display name of the doctype
- 'selected' *bool* - If the doctype should be selected
- 'submissions' *array* - The available submissions, in structures:
- 'docname' *string* - The document name
- 'actname' *string* - The action name
- 'status' *string* - The status of the document
- 'cdate' *string* - Creation date
- 'mdate' *string* - Modification date
- 'id' *string* - The id of the submission
- 'reference' *string* - The display name of the doctype
- 'pending' *bool* - If the submission is pending
- 'act' *string* - The action code
- 'doctype' *string* - The doctype code
"""
# load the right message language
_ = gettext_set_language(ln)
out = ""
out += """
<br />
<form action="">
<input type="hidden" value="%(order)s" name="order" />
<input type="hidden" name="deletedId" />
<input type="hidden" name="deletedDoctype" />
<input type="hidden" name="deletedAction" />
<table class="searchbox" width="100%%" summary="" >
<tr>
<th class="portalboxheader"><small>%(for)s</small>&nbsp;
<select name="doctype" onchange="document.forms[0].submit();">
<option value="">%(alltype)s</option>
""" % {
'order' : order,
'for' : _("For"),
'alltype' : _("all types of document"),
}
for doctype in doctypes:
out += """<option value="%(id)s" %(sel)s>%(name)s</option>""" % {
'id' : doctype['id'],
'name' : doctype['name'],
'sel' : doctype['selected'] and "selected=\"selected\"" or ""
}
out += """ </select>
</th>
</tr>
<tr>
<td class="portalboxbody">
<table>
<tr>
<td></td>
</tr>
"""
num = 0
docname = ""
for submission in submissions:
if submission['docname'] != docname:
docname = submission['docname']
out += """</table>
%(docname)s<br />
<table border="0" class="searchbox" align="left" width="100%%">
<tr>
<th class="headerselected">%(action)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="actiondown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="actionup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
<th class="headerselected">%(status)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="statusdown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="statusup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
<th class="headerselected">%(id)s</th>
<th class="headerselected">%(reference)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="refdown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="refup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
<th class="headerselected">%(first)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="cddown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="cdup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
<th class="headerselected">%(last)s&nbsp;&nbsp;
<a href='' onclick='document.forms[0].order.value="mddown";document.forms[0].submit();return false;'><img src="%(images)s/smalldown.gif" alt="down" border="0" /></a>&nbsp;
<a href='' onclick='document.forms[0].order.value="mdup";document.forms[0].submit();return false;'><img src="%(images)s/smallup.gif" alt="up" border="0" /></a>
</th>
</tr>
""" % {
'docname' : submission['docname'],
'action' : _("Action"),
'status' : _("Status"),
'id' : _("Id"),
'reference' : _("Reference"),
'images' : images,
'first' : _("First access"),
'last' : _("Last access"),
}
if submission['pending']:
idtext = """<a href="submit/sub?access=%(id)s@%(action)s%(doctype)s">%(id)s</a>
&nbsp;<a onclick='if (confirm("%(sure)s")){document.forms[0].deletedId.value="%(id)s";document.forms[0].deletedDoctype.value="%(doctype)s";document.forms[0].deletedAction.value="%(action)s";document.forms[0].submit();return true;}else{return false;}' href=''><img src="%(images)s/smallbin.gif" border="0" alt='%(delete)s' /></a>
""" % {
'images' : images,
'id' : submission['id'],
'action' : submission['act'],
'doctype' : submission['doctype'],
'sure' : _("Are you sure you want to delete this submission?"),
'delete' : _("Delete submission %(x_id)s in %(x_docname)s") % {
'x_id' : str(submission['id']),
'x_docname' : str(submission['docname'])
}
}
else:
idtext = submission['id']
if operator.mod(num,2) == 0:
color = "#e0e0e0"
else:
color = "#eeeeee"
if submission['reference']:
reference = submission['reference']
else:
reference = """<font color="red">%s</font>""" % _("Reference not yet given")
cdate = str(submission['cdate']).replace(" ","&nbsp;")
mdate= str(submission['mdate']).replace(" ","&nbsp;")
out += """
<tr bgcolor="%(color)s">
<td align="center" class="mycdscell">
<small>%(actname)s</small>
</td>
<td align="center" class="mycdscell">
<small>%(status)s</small>
</td>
<td class="mycdscell">
<small>%(idtext)s</small>
</td>
<td class="mycdscell">
<small>&nbsp;%(reference)s</small>
</td>
<td class="mycdscell">
<small>%(cdate)s</small>
</td>
<td class="mycdscell">
<small>%(mdate)s</small>
</td>
</tr>
""" % {
'color' : color,
'actname' : submission['actname'],
'status' : submission['status'],
'idtext' : idtext,
'reference' : reference,
'cdate' : cdate,
'mdate' : mdate,
}
num += 1
out += "</table></td></tr></table></form>"
return out
def tmpl_yourapprovals(self, ln, referees):
"""
Displays the doctypes and categories for which the user is referee
Parameters:
- 'ln' *string* - The language to display the interface in
- 'referees' *array* - All the doctypes for which the user is referee:
- 'doctype' *string* - The doctype
- 'docname' *string* - The display name of the doctype
- 'categories' *array* - The specific categories for which the user is referee:
- 'id' *string* - The category id
- 'name' *string* - The display name of the category
"""
# load the right message language
_ = gettext_set_language(ln)
out = """ <table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(refdocs)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'refdocs' : _("Refereed Documents"),
}
for doctype in referees:
out += """<ul><li><b>%(docname)s</b><ul>""" % doctype
if doctype ['categories'] is None:
out += '''<li><a href="publiline.py?doctype=%(doctype)s">%(generalref)s</a></li>''' % {
'docname' : doctype['docname'],
'doctype' : doctype['doctype'],
'generalref' : _("You are a general referee")}
else:
for category in doctype['categories']:
out += """<li><a href="publiline.py?doctype=%(doctype)s&amp;categ=%(categ)s">%(referee)s</a></li>""" % {
'referee' : _("You are a referee for category:") + ' ' + str(category['name']) + ' (' + str(category['id']) + ')',
'doctype' : doctype['doctype'],
'categ' : category['id']}
out += "</ul><br /></li></ul>"
out += "</td></tr></table>"
return out
def tmpl_publiline_selectdoctype(self, ln, docs):
"""
Displays the doctypes that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'docs' *array* - All the doctypes that the user can select:
- 'doctype' *string* - The doctype
- 'docname' *string* - The display name of the doctype
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(list)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(select)s:
</small>
<blockquote>""" % {
'list' : _("List of refereed types of documents"),
'select' : _("Select one of the following types of documents to check the documents status."),
}
for doc in docs:
out += "<li><a href='publiline.py?doctype=%(doctype)s'>%(docname)s</a></li><br />" % doc
out += """</blockquote>
</td>
</tr>
</table>
<a href="publiline.py?flow=cplx">Go to specific approval workflow.</a>"""
return out
def tmpl_publiline_selectcplxdoctype(self, ln, docs):
"""
Displays the doctypes that the user can select in a complex workflow
Parameters:
- 'ln' *string* - The language to display the interface in
- 'docs' *array* - All the doctypes that the user can select:
- 'doctype' *string* - The doctype
- 'docname' *string* - The display name of the doctype
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(list)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(select)s:
</small>
<blockquote>""" % {
'list' : _("List of refereed types of documents"),
'select' : _("Select one of the following types of documents to check the documents status."),
}
for doc in docs:
out += "<li><a href='publiline.py?flow=cplx&doctype=%(doctype)s'>%(docname)s</a></li><br />" % doc
out += """</blockquote>
</td>
</tr>
</table>
<a href="publiline.py">Go to general approval workflow.</a>"""
return out
def tmpl_publiline_selectcateg(self, ln, doctype, title, categories, images):
"""
Displays the categories from a doctype that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'title' *string* - The doctype name
- 'images' *string* - the path to the images
- 'categories' *array* - All the categories that the user can select:
- 'id' *string* - The id of the category
- 'waiting' *int* - The number of documents waiting
- 'approved' *int* - The number of approved documents
- 'rejected' *int* - The number of rejected documents
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(title)s: %(list_categ)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(choose_categ)s
<blockquote>
<form action="publiline.py" method="get">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="" />
</form>
<table>
<tr>
<td align="left">""" % {
'title' : title,
'doctype' : doctype,
'list_categ' : _("List of refereed categories"),
'choose_categ' : _("Please choose a category"),
}
for categ in categories:
num = categ['waiting'] + categ['approved'] + categ['rejected']
if categ['waiting'] != 0:
classtext = "class=\"blocknote\""
else:
classtext = ""
out += """<a href="" onclick="document.forms[0].categ.value='%(id)s';document.forms[0].submit();return false;"><small %(classtext)s>%(id)s</small></a><small> (%(num)s document(s)""" % {
'id' : categ['id'],
'classtext' : classtext,
'num' : num,
}
if categ['waiting'] != 0:
out += """| %(waiting)s <img alt="%(pending)s" src="%(images)s/waiting_or.gif" border="0" />""" % {
'waiting' : categ['waiting'],
'pending' : _("Pending"),
'images' : images,
}
if categ['approved'] != 0:
out += """| %(approved)s<img alt="%(approved_text)s" src="%(images)s/smchk_gr.gif" border="0" />""" % {
'approved' : categ['approved'],
'approved_text' : _("Approved"),
'images' : images,
}
if categ['rejected'] != 0:
out += """| %(rejected)s<img alt="%(rejected_text)s" src="%(images)s/cross_red.gif" border="0" />""" % {
'rejected' : categ['rejected'],
'rejected_text' : _("Rejected"),
'images' : images,
}
out += ")</small><br />"
out += """
</td>
<td>
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(key)s:</th>
</tr>
<tr>
<td>
<img alt="%(pending)s" src="%(images)s/waiting_or.gif" border="0" /> %(waiting)s<br />
<img alt="%(approved)s" src="%(images)s/smchk_gr.gif" border="0" /> %(already_approved)s<br />
<img alt="%(rejected)s" src="%(images)s/cross_red.gif" border="0" /> %(rejected_text)s<br /><br />
<small class="blocknote">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</small> %(somepending)s<br />
</td>
</tr>
</table>
</td>
</tr>
</table>
</blockquote>
</td>
</tr>
</table>""" % {
'key' : _("Key"),
'pending' : _("Pending"),
'images' : images,
'waiting' : _("Waiting for approval"),
'approved' : _("Approved"),
'already_approved' : _("Already approved"),
'rejected' : _("Rejected"),
'rejected_text' : _("Rejected"),
'somepending' : _("Some documents are pending."),
}
return out
def tmpl_publiline_selectcplxcateg(self, ln, doctype, title, types, images):
"""
Displays the categories from a doctype that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'title' *string* - The doctype name
- 'images' *string* - the path to the images
- 'categories' *array* - All the categories that the user can select:
- 'id' *string* - The id of the category
- 'waiting' *int* - The number of documents waiting
- 'approved' *int* - The number of approved documents
- 'rejected' *int* - The number of rejected documents
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(title)s: %(list_type)s</th>
</tr>
</table><br />
<table class="searchbox" width="100%%" summary="">
<tr>""" % {
'title' : title,
'list_type' : _("List of specific approvals"),
}
columns = []
columns.append ({'apptype' : 'RRP',
'list_categ' : _("List of refereing categories"),
'id_form' : 0,
})
columns.append ({'apptype' : 'RPB',
'list_categ' : _("List of publication categories"),
'id_form' : 1,
})
columns.append ({'apptype' : 'RDA',
'list_categ' : _("List of direct approval categories"),
'id_form' : 2,
})
for column in columns:
out += """
<td>
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(list_categ)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(choose_categ)s
<blockquote>
<form action="publiline.py" method="get">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="" />
<input type="hidden" name="apptype" value="%(apptype)s" />
</form>
<table>
<tr>
<td align="left">""" % {
'doctype' : doctype,
'apptype' : column['apptype'],
'list_categ' : column['list_categ'],
'choose_categ' : _("Please choose a category"),
}
for categ in types[column['apptype']]:
num = categ['waiting'] + categ['approved'] + categ['rejected'] + categ['cancelled']
if categ['waiting'] != 0:
classtext = "class=\"blocknote\""
else:
classtext = ""
out += """<a href="" onclick="document.forms[%(id_form)s].categ.value='%(id)s';document.forms[%(id_form)s].submit();return false;"><small %(classtext)s>%(desc)s</small></a><small> (%(num)s document(s)""" % {
'id' : categ['id'],
'id_form' : column['id_form'],
'classtext' : classtext,
'num' : num,
'desc' : categ['desc'],
}
if categ['waiting'] != 0:
out += """| %(waiting)s <img alt="%(pending)s" src="%(images)s/waiting_or.gif" border="0" />""" % {
'waiting' : categ['waiting'],
'pending' : _("Pending"),
'images' : images,
}
if categ['approved'] != 0:
out += """| %(approved)s<img alt="%(approved_text)s" src="%(images)s/smchk_gr.gif" border="0" />""" % {
'approved' : categ['approved'],
'approved_text' : _("Approved"),
'images' : images,
}
if categ['rejected'] != 0:
out += """| %(rejected)s<img alt="%(rejected_text)s" src="%(images)s/cross_red.gif" border="0" />""" % {
'rejected' : categ['rejected'],
'rejected_text' : _("Rejected"),
'images' : images,
}
if categ['cancelled'] != 0:
out += """| %(cancelled)s<img alt="%(cancelled_text)s" src="%(images)s/smchk_rd.gif" border="0" />""" % {
'cancelled' : categ['cancelled'],
'cancelled_text' : _("Cancelled"),
'images' : images,
}
out += ")</small><br />"
out += """
</td>
</tr>
</table>
</blockquote>
</td>
</tr>
</table>
</td>"""
# Key
out += """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(key)s:</th>
</tr>
<tr>
<td>
<img alt="%(pending)s" src="%(images)s/waiting_or.gif" border="0" /> %(waiting)s<br />
<img alt="%(approved)s" src="%(images)s/smchk_gr.gif" border="0" /> %(already_approved)s<br />
<img alt="%(rejected)s" src="%(images)s/cross_red.gif" border="0" /> %(rejected_text)s<br />
<img alt="%(cancelled)s" src="%(images)s/smchk_rd.gif" border="0" /> %(cancelled_text)s<br /><br />
<small class="blocknote">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</small> %(somepending)s<br />
</td>
</tr>
</table>
</blockquote>
</td>
</tr>
</table>""" % {
'key' : _("Key"),
'pending' : _("Pending"),
'images' : images,
'waiting' : _("Waiting for approval"),
'approved' : _("Approved"),
'already_approved' : _("Already approved"),
'rejected' : _("Rejected"),
'rejected_text' : _("Rejected"),
'cancelled' : _("Cancelled"),
'cancelled_text' : _("Cancelled"),
'somepending' : _("Some documents are pending."),
}
return out
def tmpl_publiline_selectdocument(self, ln, doctype, title, categ, images, docs):
"""
Displays the documents that the user can select in the specified category
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'title' *string* - The doctype name
- 'images' *string* - the path to the images
- 'categ' *string* - the category
- 'docs' *array* - All the categories that the user can select:
- 'RN' *string* - The id of the document
- 'status' *string* - The status of the document
"""
# load the right message language
_ = gettext_set_language(ln)
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(title)s - %(categ)s: %(list)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(choose_report)s
<blockquote>
<form action="publiline.py" method="get">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="" />
</form>
<table class="searchbox">
<tr>
<th class="portalboxheader">%(report_no)s</th>
<th class="portalboxheader">%(pending)s</th>
<th class="portalboxheader">%(approved)s</th>
<th class="portalboxheader">%(rejected)s</th>
</tr>
""" % {
'doctype' : doctype,
'title' : title,
'categ' : categ,
'list' : _("List of refereed documents"),
'choose_report' : _("Click on a report number for more information."),
'report_no' : _("Report Number"),
'pending' : _("Pending"),
'approved' : _("Approved"),
'rejected' : _("Rejected"),
}
for doc in docs:
status = doc ['status']
if status == "waiting":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">
<img alt="check" src="%(images)s/waiting_or.gif" />
</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : images,
}
elif status == "rejected":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/cross_red.gif" /></td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : images,
}
elif status == "approved":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/smchk_gr.gif" /></td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : images,
}
out += """ </table>
</blockquote>
</td>
</tr>
</table>"""
return out
def tmpl_publiline_selectcplxdocument(self, ln, doctype, title, categ, categname, images, docs, apptype):
"""
Displays the documents that the user can select in the specified category
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'title' *string* - The doctype name
- 'images' *string* - the path to the images
- 'categ' *string* - the category
- 'docs' *array* - All the categories that the user can select:
- 'RN' *string* - The id of the document
- 'status' *string* - The status of the document
- 'apptype' *string* - the approval type
"""
# load the right message language
_ = gettext_set_language(ln)
listtype = ""
if apptype == "RRP":
listtype = _("List of refereed documents")
elif apptype == "RPB":
listtype = _("List of publication documents")
elif apptype == "RDA":
listtype = _("List of direct approval documents")
out = """
<table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(title)s - %(categname)s: %(list)s</th>
</tr>
<tr>
<td class="portalboxbody">
%(choose_report)s
<blockquote>
<form action="publiline.py" method="get">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="" />
<input type="hidden" name="apptype" value="%(apptype)s" />
</form>
<table class="searchbox">
<tr>
<th class="portalboxheader">%(report_no)s</th>
<th class="portalboxheader">%(pending)s</th>
<th class="portalboxheader">%(approved)s</th>
<th class="portalboxheader">%(rejected)s</th>
<th class="portalboxheader">%(cancelled)s</th>
</tr>
""" % {
'doctype' : doctype,
'title' : title,
'categname' : categname,
'categ' : categ,
'list' : listtype,
'choose_report' : _("Click on a report number for more information."),
'apptype' : apptype,
'report_no' : _("Report Number"),
'pending' : _("Pending"),
'approved' : _("Approved"),
'rejected' : _("Rejected"),
'cancelled' : _("Cancelled"),
}
for doc in docs:
status = doc ['status']
if status == "waiting":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center"><img alt="check" src="%(images)s/waiting_or.gif" /></td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : images,
}
elif status == "rejected":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/cross_red.gif" /></td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : images,
}
elif status == "approved":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/smchk_gr.gif" /></td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : images,
}
elif status == "cancelled":
out += """<tr>
<td align="center">
<a href="" onclick="document.forms[0].RN.value='%(rn)s';document.forms[0].submit();return false;">%(rn)s</a>
</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center">&nbsp;</td>
<td align="center"><img alt="check" src="%(images)s/smchk_rd.gif" /></td>
</tr>
""" % {
'rn' : doc['RN'],
'images' : images,
}
out += """ </table>
</blockquote>
</td>
</tr>
</table>"""
return out
def tmpl_publiline_displaydoc(self, ln, doctype, docname, categ, rn, status, dFirstReq, dLastReq, dAction, access, images, confirm_send, auth_code, auth_message, authors, title, sysno, newrn):
"""
Displays the categories from a doctype that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'doctype' *string* - The doctype
- 'docname' *string* - The doctype name
- 'categ' *string* - the category
- 'rn' *string* - The document RN (id number)
- 'status' *string* - The status of the document
- 'dFirstReq' *string* - The date of the first approval request
- 'dLastReq' *string* - The date of the last approval request
- 'dAction' *string* - The date of the last action (approval or rejection)
- 'images' *string* - the path to the images
- 'confirm_send' *bool* - must display a confirmation message about sending approval email
- 'auth_code' *bool* - authorised to referee this document
- 'auth_message' *string* - ???
- 'authors' *string* - the authors of the submission
- 'title' *string* - the title of the submission
- 'sysno' *string* - the unique database id for the record
- 'newrn' *string* - the record number assigned to the submission
"""
# load the right message language
_ = gettext_set_language(ln)
if status == "waiting":
image = """<img src="%s/waiting_or.gif" alt="" align="right" />""" % images
elif status == "approved":
image = """<img src="%s/smchk_gr.gif" alt="" align="right" />""" % images
elif status == "rejected":
image = """<img src="%s/iconcross.gif" alt="" align="right" />""" % images
else:
image = ""
out = """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(image)s %(rn)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'image' : image,
'rn' : rn,
}
if confirm_send:
out += """<i><strong class="headline">%(requestsent)s</strong></i><br /><br />""" % {
'requestsent' : _("Your request has been sent to the referee."),
}
out += """<form action="publiline.py">
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<small>""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
}
if title != "unknown":
out += """<strong class="headline">%(title_text)s</strong>%(title)s<br /><br />""" % {
'title_text' : _("Title:"),
'title' : title,
}
if authors != "":
out += """<strong class="headline">%(author_text)s</strong>%(authors)s<br /><br />""" % {
'author_text' : _("Author:"),
'authors' : authors,
}
if sysno != "":
out += """<strong class="headline">%(more)s</strong>
- <a href="%(weburl)s/record/%(sysno)s">%(click)s</a>
+ <a href="%(siteurl)s/record/%(sysno)s">%(click)s</a>
<br /><br />
""" % {
'more' : _("More information:"),
'click' : _("Click here"),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'sysno' : sysno,
}
if status == "waiting":
out += _("This document is still %(x_fmt_open)swaiting for approval%(x_fmt_close)s.") % {'x_fmt_open': '<strong class="headline">',
'x_fmt_close': '</strong>'}
out += "<br /><br />"
out += _("It was first sent for approval on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
if dLastReq == "0000-00-00 00:00:00":
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
else:
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dLastReq) + '</strong><br />'
out += "<br />" + _("You can send an approval request email again by clicking the following button:") + " <br />" +\
"""<input class="adminbutton" type="submit" name="send" value="%(send)s" onclick="return confirm('%(warning)s')" />""" % {
'send' : _("Send Again"),
'warning' : _("WARNING! Upon confirmation, an email will be sent to the referee.")
}
if auth_code == 0:
out += "<br />" + _("As a referee for this document, you may click this button to approve or reject it.") + ":<br />" +\
"""<input class="adminbutton" type="submit" name="approval" value="%(approve)s" onclick="window.location='approve.py?%(access)s';return false;" />""" % {
'approve' : _("Approve/Reject"),
'access' : access
}
if status == "approved":
out += _("This document has been %(x_fmt_open)sapproved%(x_fmt_close)s.") % {'x_fmt_open': '<strong class="headline">',
'x_fmt_close': '</strong>'}
out += '<br />' + _("Its approved reference is:") + ' <strong class="headline">' + str(newrn) + '</strong><br /><br />'
out += _("It was first sent for approval on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
if dLastReq == "0000-00-00 00:00:00":
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
else:
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dLastReq) + '</strong><br />' +\
_("It was approved on:") + ' <strong class="headline">' + str(dAction) + '</strong><br />'
if status == "rejected":
out += _("This document has been %(x_fmt_open)srejected%(x_fmt_close)s.") % {'x_fmt_open': '<strong class="headline">',
'x_fmt_close': '</strong>'}
out += "<br /><br />"
out += _("It was first sent for approval on:") + ' <strong class="headline">' + str(dFirstReq) +'</strong><br />'
if dLastReq == "0000-00-00 00:00:00":
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dFirstReq) + '</strong><br />'
else:
out += _("Last approval email was sent on:") + ' <strong class="headline">' + str(dLastReq) +'</strong><br />'
out += _("It was rejected on:") + ' <strong class="headline">' + str(dAction) + '</strong><br />'
out += """ </small></form>
<br />
</td>
</tr>
</table>"""
return out
def tmpl_publiline_displaycplxdoc(self, ln, doctype, docname, categ, rn, apptype, status, dates, images, isPubCom, isEdBoard, isReferee, isProjectLeader, isAuthor, authors, title, sysno, newrn):
# load the right message language
_ = gettext_set_language(ln)
if status == "waiting":
image = """<img src="%s/waiting_or.gif" alt="" align="right" />""" % images
elif status == "approved":
image = """<img src="%s/smchk_gr.gif" alt="" align="right" />""" % images
elif status == "rejected":
image = """<img src="%s/iconcross.gif" alt="" align="right" />""" % images
elif status == "cancelled":
image = """<img src="%s/smchk_rd.gif" alt="" align="right" />""" % images
else:
image = ""
out = """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(image)s %(rn)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'image' : image,
'rn' : rn,
}
out += """<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="" />
<small>""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
}
if title != "unknown":
out += """<strong class="headline">%(title_text)s</strong>%(title)s<br /><br />""" % {
'title_text' : _("Title:"),
'title' : title,
}
if authors != "":
out += """<strong class="headline">%(author_text)s</strong>%(authors)s<br /><br />""" % {
'author_text' : _("Author:"),
'authors' : authors,
}
if sysno != "":
out += """<strong class="headline">%(more)s</strong>
- <a href="%(weburl)s/record/%(sysno)s">%(click)s</a>
+ <a href="%(siteurl)s/record/%(sysno)s">%(click)s</a>
<br /><br />
""" % {
'more' : _("More information:"),
'click' : _("Click here"),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'sysno' : sysno,
}
out += "<br /><br />"
if apptype == "RRP":
out += _("It has first been asked for refereing process on the ") + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br />'
out += _("Last request e-mail was sent to the publication committee chair on the ") + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br />'
if dates['dRefereeSel'] != None:
out += _("A referee has been selected by the publication committee on the ") + ' <strong class="headline">' + str(dates['dRefereeSel']) + '</strong><br />'
else:
out += _("No referee has been selected yet.")
if (status != "cancelled") and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="RefereeSel", linkText=_("Select a referee"))
out += '<br />'
if dates['dRefereeRecom'] != None:
out += _("The referee has sent his final recommendations to the publication committee on the ") + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br />'
else:
out += _("No recommendation from the referee yet.")
if (status != "cancelled") and (dates['dRefereeSel'] != None) and (isReferee == 0):
out += displaycplxdoc_displayauthaction (action="RefereeRecom", linkText=_("Send a recommendation"))
out += '<br />'
if dates['dPubComRecom'] != None:
out += _("The publication committee has sent his final recommendations to the project leader on the ") + ' <strong class="headline">' + str(dates['dPubComRecom']) + '</strong><br />'
else:
out += _("No recommendation from the publication committee yet.")
if (status != "cancelled") and (dates['dRefereeRecom'] != None) and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="PubComRecom", linkText=_("Send a recommendation"))
out += '<br />'
if status == "cancelled":
out += _("It has been cancelled by the author on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += _("It has been approved by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif status == "rejected":
out += _("It has been rejected by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
else:
out += _("No final decision taken yet.")
if (dates['dPubComRecom'] != None) and (isProjectLeader == 0):
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
out += '<br />'
elif apptype == "RPB":
out += _("It has first been asked for refereing process on the ") + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br />'
out += _("Last request e-mail was sent to the publication committee chair on the ") + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br />'
if dates['dEdBoardSel'] != None:
out += _("An editorial board has been selected by the publication committee on the ") + ' <strong class="headline">' + str(dates['dEdBoardSel']) + '</strong>'
if (status != "cancelled") and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="AddAuthorList", linkText=_("Add an author list"))
out += '<br />'
else:
out += _("No editorial board has been selected yet.")
if (status != "cancelled") and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="EdBoardSel", linkText=_("Select an editorial board"))
out += '<br />'
if dates['dRefereeSel'] != None:
out += _("A referee has been selected by the editorial board on the ") + ' <strong class="headline">' + str(dates['dRefereeSel']) + '</strong><br />'
else:
out += _("No referee has been selected yet.")
if (status != "cancelled") and (dates['dEdBoardSel'] != None) and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="RefereeSel", linkText=_("Select a referee"))
out += '<br />'
if dates['dRefereeRecom'] != None:
out += _("The referee has sent his final recommendations to the editorial board on the ") + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br />'
else:
out += _("No recommendation from the referee yet.")
if (status != "cancelled") and (dates['dRefereeSel'] != None) and (isReferee == 0):
out += displaycplxdoc_displayauthaction (action="RefereeRecom", linkText=_("Send a recommendation"))
out += '<br />'
if dates['dEdBoardRecom'] != None:
out += _("The editorial board has sent his final recommendations to the publication committee on the ") + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br />'
else:
out += _("No recommendation from the editorial board yet.")
if (status != "cancelled") and (dates['dRefereeRecom'] != None) and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="EdBoardRecom", linkText=_("Send a recommendation"))
out += '<br />'
if dates['dPubComRecom'] != None:
out += _("The publication committee has sent his final recommendations to the project leader on the ") + ' <strong class="headline">' + str(dates['dPubComRecom']) + '</strong><br />'
else:
out += _("No recommendation from the publication committee yet.")
if (status != "cancelled") and (dates['dEdBoardRecom'] != None) and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="PubComRecom", linkText=_("Send a recommendation"))
out += '<br />'
if status == "cancelled":
out += _("It has been cancelled by the author on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += _("It has been approved by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif status == "rejected":
out += _("It has been rejected by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
else:
out += _("No final decision taken yet.")
if (dates['dPubComRecom'] != None) and (isProjectLeader == 0):
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
out += '<br />'
elif apptype == "RDA":
out += _("It has first been asked for refereing process on the ") + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br />'
out += _("Last request e-mail was sent to the project leader on the ") + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br />'
if status == "cancelled":
out += _("It has been cancelled by the author on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += _("It has been approved by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif status == "rejected":
out += _("It has been rejected by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
else:
out += _("No final decision taken yet.")
if isProjectLeader == 0:
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
out += '<br />'
out += """ </small></form>
<br />
</td>
</tr>
</table>"""
return out
def tmpl_publiline_displaycplxdocitem(self,
doctype, categ, rn, apptype, action,
comments,
(user_can_view_comments, user_can_add_comment, user_can_delete_comment),
selected_category,
selected_topic, selected_group_id, ln):
_ = gettext_set_language(ln)
if comments and user_can_view_comments:
comments_text = ''
comments_overview = '<ul>'
for comment in comments:
(cmt_uid, cmt_nickname, cmt_title, cmt_body, cmt_date, cmt_priority, cmtid) = comment
comments_overview += '<li><a href="#%s">%s - %s</a> (%s)</li>' % (cmtid, cmt_nickname, cmt_title, convert_datetext_to_dategui (cmt_date))
comments_text += """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr><td class="bsktitle"><a name="%s"></a>%s - %s (%s)</td><td><a href="#top">Top</a></td></tr>
</thead>
<tbody>
<tr><td colspan="2">%s</td></tr>
</tbody>
</table>""" % (cmtid, cmt_nickname, cmt_title, convert_datetext_to_dategui (cmt_date), email_quoted_txt2html(cmt_body))
comments_overview += '</ul>'
else:
comments_text = ''
comments_overview = 'None.'
body = ''
if user_can_view_comments:
body += """<h4>%(comments_label)s</h4>"""
if user_can_view_comments:
body += """%(comments)s"""
if user_can_add_comment:
validation = """
<input type="hidden" name="validate" value="go" />
<input type="submit" class="formbutton" value="%(button_label)s" />""" % {'button_label': _("Add Comment")}
body += self.tmpl_publiline_displaywritecomment (doctype, categ, rn, apptype, action, _("Add Comment"), "", validation, ln)
body %= {
'comments_label': _("Comments"),
'action': action,
'button_label': _("Write a comment"),
'comments': comments_text}
content = '<br />'
out = """
<table class="bskbasket">
<thead class="bskbasketheader">
<tr>
<td class="bsktitle">
<a name="top"></a>
<h4>%(comments_overview_label)s</h4>
%(comments_overview)s
</td>
<td class="bskcmtcol"></td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2" style="padding: 5px;">
%(body)s
</td>
</tr>
</tbody>
</table>""" % {
'comments_overview_label' : _('Comments overview'),
'comments_overview' : comments_overview,
'body' : body,}
return out
def tmpl_publiline_displaywritecomment(self, doctype, categ, rn, apptype, action, write_label, title, validation, ln):
_ = gettext_set_language(ln)
return """
<div style="width:100%%%%">
<hr />
<h2>%(write_label)s</h2>
<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<p class="bsklabel">%(title_label)s:</p>
<input type="text" name="msg_subject" size="80" value="%(title)s"/>
<p class="bsklabel">%(comment_label)s:</p>
<textarea name="msg_body" rows="20" cols="80"></textarea><br />
%(validation)s
</form>
</div>""" % {'write_label': write_label,
'title_label': _("Title"),
'title': title,
'comment_label': _("Comment"),
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'validation' : validation,
}
def tmpl_publiline_displaydocplxaction(self, ln, doctype, categ, rn, apptype, action, status, images, authors, title, sysno, subtitle1, email_user_pattern, stopon1, users, extrausers, stopon2, subtitle2, usersremove, stopon3, validate_btn):
# load the right message language
_ = gettext_set_language(ln)
if status == "waiting":
image = """<img src="%s/waiting_or.gif" alt="" align="right" />""" % images
elif status == "approved":
image = """<img src="%s/smchk_gr.gif" alt="" align="right" />""" % images
elif status == "rejected":
image = """<img src="%s/iconcross.gif" alt="" align="right" />""" % images
else:
image = ""
out = """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(image)s %(rn)s</th>
</tr>
<tr>
<td class="portalboxbody">
<small>""" % {
'image' : image,
'rn' : rn,
}
if title != "unknown":
out += """<strong class="headline">%(title_text)s</strong>%(title)s<br /><br />""" % {
'title_text' : _("Title:"),
'title' : title,
}
if authors != "":
out += """<strong class="headline">%(author_text)s</strong>%(authors)s<br /><br />""" % {
'author_text' : _("Author:"),
'authors' : authors,
}
if sysno != "":
out += """<strong class="headline">%(more)s</strong>
- <a href="%(weburl)s/record/%(sysno)s">%(click)s</a>
+ <a href="%(siteurl)s/record/%(sysno)s">%(click)s</a>
<br /><br />
""" % {
'more' : _("More information:"),
'click' : _("Click here"),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'sysno' : sysno,
}
out += """ </small>
<br />
</td>
</tr>
</table>"""
if ((apptype == "RRP") or (apptype == "RPB")) and ((action == "EdBoardSel") or (action == "RefereeSel")):
out += """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(subtitle)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'subtitle' : subtitle1,
}
out += """<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
}
out += ' <span class="adminlabel">1. %s </span>\n' % _("search for user")
out += ' <input class="admin_wvar" type="text" name="email_user_pattern" value="%s" />\n' % (email_user_pattern, )
out += ' <input class="adminbutton" type="submit" value="%s"/>\n' % (_("search for users"), )
if (stopon1 == "") and (email_user_pattern != ""):
out += ' <br /><span class="adminlabel">2. %s </span>\n' % _("select user")
out += ' <select name="id_user" class="admin_w200">\n'
out += ' <option value="0">*** %s ***</option>\n' % _("select user")
for elem in users:
elem_id = elem[0]
email = elem[1]
out += ' <option value="%s">%s</option>\n' % (elem_id, email)
for elem in extrausers:
elem_id = elem[0]
email = elem[1]
out += ' <option value="%s">%s %s</option>\n' % (elem_id, email, _("connected"))
out += ' </select>\n'
out += ' <input class="adminbutton" type="submit" value="%s" />\n' % (_("add this user"), )
out += stopon2
elif stopon1 != "":
out += stopon1
out += """
</form>
<br />
</td>
</tr>
</table>"""
if action == "EdBoardSel":
out += """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(subtitle)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'subtitle' : subtitle2,
}
out += """<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
}
out += ' <span class="adminlabel">1. %s </span>\n' % _("select user")
out += ' <select name="id_user_remove" class="admin_w200">\n'
out += ' <option value="0">*** %s ***</option>\n' % _("select user")
for elem in usersremove:
elem_id = elem[0]
email = elem[1]
out += ' <option value="%s">%s</option>\n' % (elem_id, email)
out += ' </select>\n'
out += ' <input class="adminbutton" type="submit" value="%s" />\n' % (_("remove this user"), )
out += stopon3
out += """
</form>
<br />
</td>
</tr>
</table>"""
if validate_btn != "":
out += """<form action="publiline.py">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="validate" value="go" />
<input class="adminbutton" type="submit" value="%(validate_btn)s" />
</form>""" % {
'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'validate_btn' : validate_btn,
}
return out
def tmpl_publiline_displaycplxrecom(self, ln, doctype, categ, rn, apptype, action, status, images, authors, title, sysno, msg_to, msg_to_group, msg_subject):
# load the right message language
_ = gettext_set_language(ln)
if status == "waiting":
image = """<img src="%s/waiting_or.gif" alt="" align="right" />""" % images
elif status == "approved":
image = """<img src="%s/smchk_gr.gif" alt="" align="right" />""" % images
elif status == "rejected":
image = """<img src="%s/iconcross.gif" alt="" align="right" />""" % images
else:
image = ""
out = """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">%(image)s %(rn)s</th>
</tr>
<tr>
<td class="portalboxbody">
<small>""" % {
'image' : image,
'rn' : rn,
}
if title != "unknown":
out += """<strong class="headline">%(title_text)s</strong>%(title)s<br /><br />""" % {
'title_text' : _("Title:"),
'title' : title,
}
if authors != "":
out += """<strong class="headline">%(author_text)s</strong>%(authors)s<br /><br />""" % {
'author_text' : _("Author:"),
'authors' : authors,
}
if sysno != "":
out += """<strong class="headline">%(more)s</strong>
- <a href="%(weburl)s/record/%(sysno)s">%(click)s</a>
+ <a href="%(siteurl)s/record/%(sysno)s">%(click)s</a>
<br /><br />
""" % {
'more' : _("More information:"),
'click' : _("Click here"),
- 'weburl' : weburl,
+ 'siteurl' : CFG_SITE_URL,
'sysno' : sysno,
}
out += """ </small>
<br />
</td>
</tr>
</table>"""
# escape forbidden character
msg_to = escape_html(msg_to)
msg_to_group = escape_html(msg_to_group)
msg_subject = escape_html(msg_subject)
write_box = """
<form action="publiline.py" method="post">
<input type="hidden" name="flow" value="cplx" />
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categ" value="%(categ)s" />
<input type="hidden" name="RN" value="%(rn)s" />
<input type="hidden" name="apptype" value="%(apptype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="validate" value="go" />
<div style="float: left; vertical-align:text-top; margin-right: 10px;">
<table class="mailbox">
<thead class="mailboxheader">
<tr>
<td class="inboxheader" colspan="2">
<table class="messageheader">
<tr>
<td class="mailboxlabel">%(to_label)s</td>"""
if msg_to != "":
addr_box = """
<td class="mailboxlabel">%(users_label)s</td>
<td style="width:100%%%%;" class="mailboxlabel">%(to_users)s</td>""" % {'users_label': _("User"),
'to_users' : msg_to,
}
if msg_to_group != "":
addr_box += """
</tr>
<tr>
<td class="mailboxlabel">&nbsp;</td>
<td class="mailboxlabel">%(groups_label)s</td>
<td style="width:100%%%%;" class="mailboxlabel">%(to_groups)s</td>""" % {'groups_label': _("Group"),
'to_groups': msg_to_group,
}
elif msg_to_group != "":
addr_box = """
<td class="mailboxlabel">%(groups_label)s</td>
<td style="width:100%%%%;" class="mailboxlabel">%(to_groups)s</td>""" % {'groups_label': _("Group"),
'to_groups': msg_to_group,
}
else:
addr_box = """
<td class="mailboxlabel">&nbsp;</td>
<td class="mailboxlabel">&nbsp;</td>"""
write_box += addr_box
write_box += """
</tr>
<tr>
<td class="mailboxlabel">&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td class="mailboxlabel">%(subject_label)s</td>
<td colspan="2">
<input class="mailboxinput" type="text" name="msg_subject" value="%(subject)s" />
</td>
</tr>
</table>
</td>
</tr>
</thead>
<tfoot>
<tr>
<td style="height:0px" colspan="2"></td>
</tr>
</tfoot>
<tbody class="mailboxbody">
<tr>
<td class="mailboxlabel">%(message_label)s</td>
<td>
<textarea name="msg_body" rows="10" cols="50"></textarea>
</td>
</tr>
<tr class="mailboxfooter">
<td colspan="2" class="mailboxfoot">
<input type="submit" name="send_button" value="%(send_label)s" class="formbutton"/>
</td>
</tr>
</tbody>
</table>
</div>
</form>
"""
write_box = write_box % {'rn' : rn,
'categ' : categ,
'doctype' : doctype,
'apptype' : apptype,
'action' : action,
'subject' : msg_subject,
'to_label': _("To:"),
'subject_label': _("Subject:"),
'message_label': _("Message:"),
'send_label': _("SEND"),
}
out += write_box
return out
def displaycplxdoc_displayauthaction(action, linkText):
return """ <strong class="headline">(<a href="" onclick="document.forms[0].action.value='%(action)s';document.forms[0].submit();return false;">%(linkText)s</a>)</strong>""" % {
"action" : action,
"linkText" : linkText
}
diff --git a/modules/websubmit/lib/websubmit_webinterface.py b/modules/websubmit/lib/websubmit_webinterface.py
index 40b22ba69..05cd6a646 100644
--- a/modules/websubmit/lib/websubmit_webinterface.py
+++ b/modules/websubmit/lib/websubmit_webinterface.py
@@ -1,560 +1,560 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__lastupdated__ = """$Date$"""
__revision__ = "$Id$"
import string
import os
import time
import types
import re
try:
from mod_python import apache
except ImportError:
pass
import sys
from urllib import quote
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_URL, \
CFG_WEBSUBMIT_STORAGEDIR, \
CFG_VERSION, \
- weburl
+ CFG_SITE_URL
from invenio.dbquery import run_sql, Error
from invenio.access_control_config import VIEWRESTRCOLL
from invenio.access_control_mailcookie import mail_cookie_create_authorize_action
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_admin import acc_is_role
from invenio.webpage import page, create_error_box, pageheaderonly, \
pagefooteronly
from invenio.webuser import getUid, get_email, page_not_authorized, collect_user_info
from invenio.websubmit_config import *
from invenio.webinterface_handler import wash_urlargd, WebInterfaceDirectory
from invenio.urlutils import make_canonical_urlargd, redirect_to_url
from invenio.messages import gettext_set_language
from invenio.search_engine import \
guess_primary_collection_of_a_record, \
get_colID, \
create_navtrail_links, check_user_can_view_record
from invenio.bibdocfile import BibRecDocs, normalize_format, file_strip_ext, \
stream_restricted_icon, BibDoc, InvenioWebSubmitFileError
from invenio.errorlib import register_exception
import invenio.template
websubmit_templates = invenio.template.load('websubmit')
from invenio.websearchadminlib import get_detailed_page_tabs
import invenio.template
webstyle_templates = invenio.template.load('webstyle')
websearch_templates = invenio.template.load('websearch')
class WebInterfaceFilesPages(WebInterfaceDirectory):
def __init__(self,recid):
self.recid = recid
def _lookup(self, component, path):
# after /record/<recid>/files/ every part is used as the file
# name
filename = component
def getfile(req, form):
args = wash_urlargd(form, websubmit_templates.files_default_urlargd)
ln = args['ln']
_ = gettext_set_language(ln)
uid = getUid(req)
user_info = collect_user_info(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE > 1:
return page_not_authorized(req, "../getfile.py/index",
navmenuid='submit')
(auth_code, auth_msg) = check_user_can_view_record(user_info, self.recid)
if auth_code and user_info['email'] == 'guest' and not user_info['apache_user']:
cookie = mail_cookie_create_authorize_action(VIEWRESTRCOLL, {'collection' : guess_primary_collection_of_a_record(self.recid)})
target = '/youraccount/login' + \
make_canonical_urlargd({'action': cookie, 'ln' : ln, 'referer' : \
- weburl + user_info['uri']}, {})
+ CFG_SITE_URL + user_info['uri']}, {})
return redirect_to_url(req, target)
elif auth_code:
return page_not_authorized(req, "../", \
text = auth_msg)
readonly = CFG_ACCESS_CONTROL_LEVEL_SITE == 1
# From now on: either the user provided a specific file
# name (and a possible version), or we return a list of
# all the available files. In no case are the docids
# visible.
try:
bibarchive = BibRecDocs(self.recid)
except InvenioWebSubmitFileError, e:
register_exception(req=req)
msg = "<p>%s</p><p>%s</p>" % (
_("The system has encountered an error in retrieving the list of files for this document."),
_("The error has been logged and will be taken in consideration as soon as possibile."))
return errorMsg(msg, req, CFG_SITE_NAME, ln)
docname = ''
format = ''
version = ''
if filename:
# We know the complete file name, guess which docid it
# refers to
## TODO: Change the extension system according to ext.py from setlink
## and have a uniform extension mechanism...
docname = file_strip_ext(filename)
format = filename[len(docname):]
if format and format[0] != '.':
format = '.' + format
else:
docname = args['docname']
if not format:
format = args['format']
if not version:
version = args['version']
# version could be either empty, or all or an integer
try:
int(version)
except ValueError:
if version != 'all':
version = ''
if version != 'all':
# search this filename in the complete list of files
for doc in bibarchive.list_bibdocs():
if docname == doc.get_docname():
try:
docfile = doc.get_file(format, version)
except InvenioWebSubmitFileError, msg:
register_exception(req=req)
return errorMsg(str(msg), req, CFG_SITE_NAME, ln)
if docfile.get_status() == '':
# The file is not resticted, let's check for
# collection restriction then.
(auth_code, auth_message) = check_user_can_view_record(user_info, self.recid)
if auth_code:
return warningMsg(_("The collection to which this file belong is restricted: ") + auth_message, req, CFG_SITE_NAME, ln)
else:
# The file is probably restricted on its own.
# Let's check for proper authorization then
(auth_code, auth_message) = docfile.is_restricted(req)
if auth_code != 0:
return warningMsg(_("This file is restricted: ") + auth_message, req, CFG_SITE_NAME, ln)
if not readonly:
ip = str(req.get_remote_host(apache.REMOTE_NOLOOKUP))
res = doc.register_download(ip, version, format, uid)
try:
return docfile.stream(req)
except InvenioWebSubmitFileError, msg:
register_exception(req=req)
return errorMsg(str(msg), req, CFG_SITE_NAME, ln)
elif doc.get_icon() is not None and doc.get_icon().docname in filename:
icon = doc.get_icon()
try:
iconfile = icon.get_file('gif', args['version'])
except InvenioWebSubmitFileError, msg:
register_exception(req=req)
return errorMsg(msg, req, CFG_SITE_NAME, ln)
if iconfile.get_status() == '':
# The file is not resticted, let's check for
# collection restriction then.
(auth_code, auth_message) = check_user_can_view_record(user_info, self.recid)
if auth_code:
return stream_restricted_icon(req)
else:
# The file is probably restricted on its own.
# Let's check for proper authorization then
(auth_code, auth_message) = iconfile.is_restricted(req)
if auth_code != 0:
return stream_restricted_icon(req)
if not readonly:
ip = str(req.get_remote_host(apache.REMOTE_NOLOOKUP))
res = doc.register_download(ip, version, format, uid)
try:
return iconfile.stream(req)
except InvenioWebSubmitFileError, msg:
register_exception(req=req)
return errorMsg(msg, req, c, ln)
filelist = bibarchive.display("", args['version'], ln=ln)
t = websubmit_templates.tmpl_filelist(
ln=ln,
recid=self.recid,
docname=args['docname'],
version=args['version'],
filelist=filelist)
cc = guess_primary_collection_of_a_record(self.recid)
unordered_tabs = get_detailed_page_tabs(get_colID(cc), self.recid)
ordered_tabs_id = [(tab_id, values['order']) for (tab_id, values) in unordered_tabs.iteritems()]
ordered_tabs_id.sort(lambda x,y: cmp(x[1],y[1]))
link_ln = ''
if ln != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
tabs = [(unordered_tabs[tab_id]['label'], \
- '%s/record/%s/%s%s' % (weburl, self.recid, tab_id, link_ln), \
+ '%s/record/%s/%s%s' % (CFG_SITE_URL, self.recid, tab_id, link_ln), \
tab_id == 'files',
unordered_tabs[tab_id]['enabled']) \
for (tab_id, order) in ordered_tabs_id
if unordered_tabs[tab_id]['visible'] == True]
t = webstyle_templates.detailed_record_container(t,
self.recid,
tabs,
args['ln'])
title, description, keywords = websearch_templates.tmpl_record_page_header_content(req, self.recid, args['ln'])
return pageheaderonly(title=title,
navtrail=create_navtrail_links(cc=cc, as=0, ln=ln) + \
''' &gt; <a class="navtrail" href="%s/record/%s">%s</a>
&gt; %s''' % \
- (weburl, self.recid, title, _("Access to Fulltext")),
+ (CFG_SITE_URL, self.recid, title, _("Access to Fulltext")),
description="",
keywords="keywords",
uid=uid,
language=ln,
req=req,
navmenuid='search',
navtrail_append_title_p=0) + \
websearch_templates.tmpl_search_pagestart(ln) + \
t + \
websearch_templates.tmpl_search_pageend(ln) + \
pagefooteronly(lastupdated=__lastupdated__, language=ln, req=req)
return getfile, []
def __call__(self, req, form):
"""Called in case of URLs like /record/123/files without
trailing slash.
"""
args = wash_urlargd(form, websubmit_templates.files_default_urlargd)
ln = args['ln']
link_ln = ''
if ln != CFG_SITE_LANG:
link_ln = '?ln=%s' % ln
- return redirect_to_url(req, '%s/record/%s/files/%s' % (weburl, self.recid, link_ln))
+ return redirect_to_url(req, '%s/record/%s/files/%s' % (CFG_SITE_URL, self.recid, link_ln))
def websubmit_legacy_getfile(req, form):
""" Handle legacy /getfile.py URLs """
# FIXME: this should _redirect_ to the proper
# /record/.../files/... URL.
args = wash_urlargd(form, {
'c': (str, CFG_SITE_NAME),
'recid': (str, ''),
'docid': (str, ''),
'version': (str, ''),
'name': (str, ''),
'format': (str, '')
})
def _getfile_py(req,c=CFG_SITE_NAME,ln=CFG_SITE_LANG,recid="",docid="",version="",name="",format=""):
_ = gettext_set_language(ln)
# get user ID:
try:
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../getfile.py/index",
navmenuid='submit')
uid_email = get_email(uid)
except Error, e:
return errorMsg(e.value,req)
filelist=""
# redirect to a canonical URL as far as it is possible (what
# if we only have a docid, and no file supplied?)
if name!="":
if docid=="":
return errorMsg(_("Parameter docid missing"), req, c, ln)
try:
doc = BibDoc(docid=docid)
except InvenioWebSubmitFileError, msg:
register_exception(req=req)
return errorMsg(msg, req, c, ln)
try:
docfile = doc.get_file(format,version)
except InvenioWebSubmitFileError, msg:
register_exception(req=req)
return errorMsg(msg, req, c, ln)
# redirect to this specific file, possibly dropping
# the version if we are referring to the latest one.
target = '%s/record/%d/files/%s%s' % (
- weburl, doc.recid, quote(docfile.name), docfile.format)
+ CFG_SITE_URL, doc.recid, quote(docfile.name), docfile.format)
if version and int(version) == int(doc.getLatestVersion()):
version = ''
target += make_canonical_urlargd({
'version': version}, websubmit_templates.files_default_urlargd)
return redirect_to_url(req, target)
# all files attached to a record
elif recid!="":
- return redirect_to_url(req, '%s/record/%s/files/' % (weburl, recid))
+ return redirect_to_url(req, '%s/record/%s/files/' % (CFG_SITE_URL, recid))
# a precise filename
elif docid!="":
try:
bibdoc = BibDoc(docid=docid)
except InvenioWebSubmitFileError, msg:
register_exception(req=req)
return errorMsg(msg, req, CFG_SITE_NAME, ln)
recid = bibdoc.get_recid()
filelist = bibdoc.display(version, ln=ln)
t = websubmit_templates.tmpl_filelist(
ln = ln,
recid = recid,
docname = name,
version = version,
filelist = filelist,
)
p_navtrail = _("Access to Fulltext")
return page(title="",
body=t,
navtrail = p_navtrail,
description="",
keywords="keywords",
uid=uid,
language=ln,
req=req,
navmenuid='search')
return _getfile_py(req, **args)
# --------------------------------------------------
from invenio.websubmit_engine import home, action, interface, endaction
class WebInterfaceSubmitPages(WebInterfaceDirectory):
_exports = ['summary', 'sub', 'direct', '']
def direct(self, req, form):
args = wash_urlargd(form, {'sub': (str, '')})
sub = args['sub']
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../direct.py/index",
navmenuid='submit')
myQuery = req.args
if sub == "":
return errorMsg("Sorry parameter missing...",req)
res = run_sql("select docname,actname from sbmIMPLEMENT where subname=%s", (sub,))
if len(res)==0:
return errorMsg("Sorry. Cannot analyse parameter",req)
else:
# get document type
doctype = res[0][0]
# get action name
action = res[0][1]
# retrieve other parameter values
params = re.sub("sub=[^&]*","",myQuery)
# find existing access number
result = re.search("access=([^&]*)",params)
if result is not None:
access = result.group(1)
params = re.sub("access=[^&]*","",params)
else:
# create 'unique' access number
pid = os.getpid()
now = time.time()
access = "%i_%s" % (now,pid)
# retrieve 'dir' value
res = run_sql ("select dir from sbmACTION where sactname=%s",(action,))
dir = res[0][0]
try:
mainmenu = req.headers_in['Referer']
except:
mainmenu = ""
url = "/submit?doctype=%s&dir=%s&access=%s&act=%s&startPg=1%s&mainmenu=%s" % (
doctype,dir,access,action,params,quote(mainmenu))
req.err_headers_out.add("Location", url)
raise apache.SERVER_RETURN, apache.HTTP_MOVED_PERMANENTLY
return ""
def sub(self, req, form):
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../sub/",
navmenuid='submit')
myQuery = req.args
if myQuery:
if re.search("@",myQuery):
param = re.sub("@.*","",myQuery)
IN = re.sub(".*@","",myQuery)
else:
IN = myQuery
url = "%s/submit/direct?sub=%s&%s" % (CFG_SITE_URL,IN,param)
req.err_headers_out.add("Location", url)
raise apache.SERVER_RETURN, apache.HTTP_MOVED_PERMANENTLY
return ""
else:
return "<html>Illegal page access</html>"
def summary(self, req, form):
args = wash_urlargd(form, {
'doctype': (str, ''),
'act': (str, ''),
'access': (str, ''),
'indir': (str, '')})
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../summary.py/index",
navmenuid='submit')
t=""
curdir = "%s/%s/%s/%s" % (CFG_WEBSUBMIT_STORAGEDIR,args['indir'],args['doctype'],args['access'])
subname = "%s%s" % (args['act'], args['doctype'])
res = run_sql("select sdesc,fidesc,pagenb,level from sbmFIELD where subname=%s "
"order by pagenb,fieldnb", (subname,))
nbFields = 0
values = []
for arr in res:
if arr[0] != "":
val = {
'mandatory' : (arr[3] == 'M'),
'value' : '',
'page' : arr[2],
'name' : arr[0],
}
if os.path.exists("%s/%s" % (curdir,arr[1])):
fd = open("%s/%s" % (curdir,arr[1]),"r")
value = fd.read()
fd.close()
value = value.replace("\n"," ")
value = value.replace("Select:","")
else:
value = ""
val['value'] = value
values.append(val)
return websubmit_templates.tmpl_submit_summary(
ln = args['ln'],
values = values,
images = CFG_SITE_URL + '/img',
)
def index(self, req, form):
args = wash_urlargd(form, {
'c': (str, CFG_SITE_NAME),
'doctype': (str, ''),
'act': (str, ''),
'startPg': (str, "1"),
'indir': (str, ''),
'access': (str, ''),
'mainmenu': (str, ''),
'fromdir': (str, ''),
'file': (str, ''),
'nextPg': (str, ''),
'nbPg': (str, ''),
'curpage': (str, '1'),
'step': (str, '0'),
'mode': (str, 'U'),
})
req.form = form
def _index(req, c, ln, doctype, act, startPg, indir, access,
mainmenu, fromdir, file, nextPg, nbPg, curpage, step,
mode):
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../submit",
navmenuid='submit')
if doctype=="":
return home(req,c,ln)
elif act=="":
return action(req,c,ln,doctype)
elif int(step)==0:
return interface(req,c,ln, doctype, act, startPg, indir,
access, mainmenu, fromdir, file, nextPg,
nbPg, curpage)
else:
return endaction(req,c,ln, doctype, act, startPg, indir,
access,mainmenu, fromdir, file, nextPg,
nbPg, curpage, step, mode)
return _index(req, **args)
# Answer to both /submit/ and /submit
__call__ = index
def errorMsg(title, req, c=CFG_SITE_NAME, ln=CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
return page(title = _("Error"),
body = create_error_box(req, title=title, verbose=0, ln=ln),
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='submit')
def warningMsg(title, req, c=CFG_SITE_NAME, ln=CFG_SITE_LANG):
# load the right message language
_ = gettext_set_language(ln)
return page(title = _("Warning"),
body = title,
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='submit')
diff --git a/modules/websubmit/lib/websubmitadmin_config.py b/modules/websubmit/lib/websubmitadmin_config.py
index f686e3487..60fc71313 100644
--- a/modules/websubmit/lib/websubmitadmin_config.py
+++ b/modules/websubmit/lib/websubmitadmin_config.py
@@ -1,84 +1,84 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSubmit Admin configuration parameters."""
__revision__ = \
"$Id$"
# pylint: disable-msg=C0301
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
-WEBSUBMITADMIN_WEBURL = "%s/admin/websubmit/websubmitadmin.py" % (weburl,)
-WEBSUBMITADMIN_OLDWEBURL = "%s/admin/websubmit" % (weburl,)
+WEBSUBMITADMINURL = "%s/admin/websubmit/websubmitadmin.py" % (CFG_SITE_URL,)
+WEBSUBMITADMINURL_OLD = "%s/admin/websubmit" % (CFG_SITE_URL,)
class InvenioWebSubmitAdminWarningIOError(Exception):
pass
class InvenioWebSubmitAdminWarningNoUpdate(Exception):
"""Exception used when a no update was made as a result of an action"""
pass
class InvenioWebSubmitAdminWarningDeleteFailed(Exception):
pass
class InvenioWebSubmitAdminWarningInsertFailed(Exception):
pass
class InvenioWebSubmitAdminWarningTooManyRows(Exception):
pass
class InvenioWebSubmitAdminWarningNoRowsFound(Exception):
pass
class InvenioWebSubmitAdminWarningReferentialIntegrityViolation(Exception):
pass
## List of the names of functions for which the parameters are files that can be edited.
## In particular, this applies to the record creation functions that make use of bibconvert.
FUNCTIONS_WITH_FILE_PARAMS = ["Make_Record", "Make_Modify_Record"]
CFG_WEBSUBMITADMIN_WARNING_MESSAGES = \
{
'WRN_WEBSUBMITADMIN_UNABLE_TO_DELETE_FIELD_FROM_SUBMISSION_PAGE' : '_("Unable to delete field at position %s from page %s of submission \'%s\'")',
'WRN_WEBSUBMITADMIN_INVALID_FIELD_NUMBERS_SUPPLIED_WHEN_TRYING_TO_MOVE_FIELD_ON_SUBMISSION_PAGE' : \
'_("Unable to move field at position %s to position %s on page %s of submission \'%s\' - Invalid Field Position Numbers")',
'WRN_WEBSUBMITADMIN_UNABLE_TO_SWAP_TWO_FIELDS_ON_SUBMISSION_PAGE_COULDNT_MOVE_FIELD1_TO_TEMP_POSITION' : \
'_("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")',
'WRN_WEBSUBMITADMIN_UNABLE_TO_SWAP_TWO_FIELDS_ON_SUBMISSION_PAGE_COULDNT_MOVE_FIELD2_TO_FIELD1_POSITION' : \
'_("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")',
'WRN_WEBSUBMITADMIN_UNABLE_TO_SWAP_TWO_FIELDS_ON_SUBMISSION_PAGE_COULDNT_MOVE_FIELD1_TO_POSITION_FIELD2_FROM_TEMPORARY_POSITION' : \
'_("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")',
'WRN_WEBSUBMITADMIN_UNABLE_TO_MOVE_FIELD_TO_NEW_POSITION_ON_SUBMISSION_PAGE_COULDNT_DECREMENT_POSITION_OF_FIELDS_BELOW_FIELD1' : \
'_("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")',
'WRN_WEBSUBMITADMIN_UNABLE_TO_MOVE_FIELD_TO_NEW_POSITION_ON_SUBMISSION_PAGE_COULDNT_INCREMENT_POSITION_OF_FIELDS_AT_AND_BELOW_FIELD2' : \
'_("Unable to move field at position %s to position %s on page %s of submission %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.")',
'WRN_WEBSUBMITADMIN_TOOMANYROWS' : '_("Too many rows found for query [%s]. Expected %s, found %s.")',
'WRN_WEBSUBMITADMIN_NOROWSFOUND' : '_("No Rows found for query [%s].")',
## not warnings per-say, rather events that have taken place:
'WRN_WEBSUBMITADMIN_DELETED_FIELD_FROM_SUBMISSION_PAGE' : '_("Deleted field at position %s from page %s of submission \'%s\'")',
'WRN_WEBSUBMITADMIN_MOVED_FIELD_ON_SUBMISSION_PAGE' : '_("Moved field from position %s to position %s on page %s of submission \'%s\'")',
'WRN_WEBSUBMITADMIN_FIELDUPDATED' : '_("Updated details of field at position %s on page %s of submission \'%s\'")'
}
diff --git a/modules/websubmit/lib/websubmitadmin_regression_tests.py b/modules/websubmit/lib/websubmitadmin_regression_tests.py
index 9664a3cae..beda97c58 100644
--- a/modules/websubmit/lib/websubmitadmin_regression_tests.py
+++ b/modules/websubmit/lib/websubmitadmin_regression_tests.py
@@ -1,71 +1,71 @@
# -*- coding: utf-8 -*-
##
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSubmit Admin Regression Test Suite."""
__revision__ = "$Id$"
import unittest
-from invenio.config import weburl
+from invenio.config import CFG_SITE_URL
from invenio.testutils import make_test_suite, warn_user_about_tests_and_run, \
test_web_page_content, merge_error_messages
class WebSubmitAdminWebPagesAvailabilityTest(unittest.TestCase):
"""Check WebSubmit Admin web pages whether they are up or not."""
def test_websubmit_admin_interface_pages_availability(self):
"""websubmitadmin - availability of WebSubmit Admin interface pages"""
- baseurl = weburl + '/admin/websubmit/websubmitadmin.py/'
+ baseurl = CFG_SITE_URL + '/admin/websubmit/websubmitadmin.py/'
_exports = ['', 'showall', 'doctypelist', 'doctypeadd',
'doctyperemove', 'actionlist', 'jschecklist',
'elementlist', 'functionlist']
error_messages = []
for url in [baseurl + page for page in _exports]:
# first try as guest:
error_messages.extend(test_web_page_content(url,
username='guest',
expected_text=
'Authorization failure'))
# then try as admin:
error_messages.extend(test_web_page_content(url,
username='admin'))
if error_messages:
self.fail(merge_error_messages(error_messages))
return
def test_websubmit_admin_guide_availability(self):
"""websubmitadmin - availability of WebSubmit Admin guide pages"""
- url = weburl + '/help/admin/websubmit-admin-guide'
+ url = CFG_SITE_URL + '/help/admin/websubmit-admin-guide'
error_messages = test_web_page_content(url,
expected_text="WebSubmit Admin Guide")
if error_messages:
self.fail(merge_error_messages(error_messages))
return
test_suite = make_test_suite(WebSubmitAdminWebPagesAvailabilityTest)
if __name__ == "__main__":
warn_user_about_tests_and_run(test_suite)
diff --git a/modules/websubmit/lib/websubmitadmin_templates.py b/modules/websubmit/lib/websubmitadmin_templates.py
index 013dd1c59..02d5fd5f8 100644
--- a/modules/websubmit/lib/websubmitadmin_templates.py
+++ b/modules/websubmit/lib/websubmitadmin_templates.py
@@ -1,3438 +1,3438 @@
# -*- coding: utf-8 -*-
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
import cgi
-from invenio.config import weburl, CFG_SITE_LANG
-from invenio.websubmitadmin_config import WEBSUBMITADMIN_WEBURL, FUNCTIONS_WITH_FILE_PARAMS, WEBSUBMITADMIN_OLDWEBURL
+from invenio.config import CFG_SITE_URL, CFG_SITE_LANG
+from invenio.websubmitadmin_config import WEBSUBMITADMINURL, FUNCTIONS_WITH_FILE_PARAMS, WEBSUBMITADMINURL_OLD
def create_html_table_from_tuple(tableheader=None, tablebody=None, start="", end=""):
"""Create a table from a tuple or a list.
@param header: optional header for the columns (MUST be a list of header titles)
@param tablebody: table body (rows - tuple of tuples)
@param start: text to be added in the beginning, most likely beginning of a form
@param end: text to be added in the end, most likely end of a form.
"""
if type(tableheader) is None:
tableheader = ()
if type(tablebody) is None:
tablebody = ()
## determine table cells alignment based upon first row alignment
align = []
try:
if type(tablebody[0]) in [int, long]:
align = ['admintdright']
elif type(tablebody[0]) in [str, dict]:
align = ['admintdleft']
else:
for item in tablebody[0]:
if type(item) is int:
align.append('admintdright')
else:
align.append('admintdleft')
except IndexError:
## Empty tablebody
pass
## table header row:
tblstr = ""
for hdr in tableheader:
tblstr += """ <th class="adminheader">%s</th>\n""" % (hdr,)
if tblstr != "":
tblstr = """ <tr>\n%s</tr>\n""" % (tblstr, )
tblstr = start + """<table class="admin_wvar_nomargin">\n""" + tblstr
## table body
if len(tablebody) > 0:
for row in tablebody:
tblstr += """ <tr>\n"""
if type(row) not in [int, long, str, dict]:
for i in range(len(row)):
tblstr += """<td class="%s">%s</td>\n""" % (align[i], row[i])
else:
tblstr += """ <td class="%s">%s</td>\n""" % (align[0], row)
tblstr += """ </tr>\n"""
else:
# Empty tuple of table data - display message:
tblstr += """<tr>
<td class="admintdleft" colspan="%s"><span class="info">None</span></td>
</tr>
""" % (len(tableheader),)
tblstr += """</table>\n"""
tblstr += end
return tblstr
def create_html_select_list(select_name, option_list, selected_values="", default_opt="", multiple="", list_size="", css_style="", css_class=""):
"""Make a HTML "select" element from the parameters passed.
@param select_name: Name given to the HTML "select" element
@param option_list: a tuple of tuples containing the options (their values, followed by their
display text). Thus: ( (opt_val, opt_txt), (opt_val, opt_txt) )
It is also possible to provide a tuple of single-element tuples in the case when it is not desirable
to have different option text to the value, thus: ( (opt_val,), (opt_val,) ).
@param selected_value: can be a list/tuple of strings, or a single string/unicode string. Treated as
the "selected" values for the select list's options. E.g. if a value in the "option_list" param was
"test", and the "selected_values" parameter contained "test", then the test option would appear as
follows: '<option value="test" selected>'.
@param default_opt: The default option (value and displayed text) for the select list. If left blank, there
will be no default option, and the first "natural" option will appear first in the list.
If the value of "default_opt" is a string, then this string will be used both as the value and the displayed
text of the default option. If the value of "default_opt" is a tuple/list, then the first option will be
used as the option "value", and the second will be used as the option "displayed text". In the case that
the list/tuple length is only 1, the value will be used for both option "value" and "displayed text".
@param multiple: shall this be a multiple select box? If present, select box will be marked as "multiple".
@param list_size: the size for a multiple select list. If mutiple is present, then this optional size can
be provided. If not provided, the list size attribute will automatically be given the value of the list
length, up to 30.
@param css_style: A string: any additional CSS style information to be placed as the select element's "style"
attribute value.
@param css_class: A string: any class value for CSS.
@return: a string containing the completed HTML Select element
"""
## sanity checking:
if type(css_style) not in (str, unicode):
css_style = ""
if type(option_list) not in (list, tuple):
option_list = ()
txt = """\n <select name="%s"%s""" % ( cgi.escape(select_name, 1),
(multiple != "" and " multiple") or ("")
)
if multiple != "":
## Size attribute for multiple-select list
if (type(list_size) is str and list_size.isdigit()) or type(list_size) is int:
txt += """ size="%s\"""" % (list_size,)
else:
txt += """ size="%s\"""" % ( (len(option_list) <= 30 and str(len(option_list))) or ("30"),)
if css_style != "":
txt += """ style="%s\"""" % (cgi.escape(css_style, 1),)
txt += """>\n"""
if default_opt != "":
if type(default_opt) in (str, unicode):
## default_opt is a string - use its value as both option value and displayed text
txt += """ <option value="%(deflt_opt)s">%(deflt_opt)s</option>\n""" % {'deflt_opt' : cgi.escape(default_opt, 1)}
elif type(default_opt) in (list, tuple):
try:
txt += """ <option value="%(deflt_opt)s">""" % {'deflt_opt' : cgi.escape(default_opt[0], 1) }
try:
txt += """%(deflt_opt)s""" % {'deflt_opt' : cgi.escape(default_opt[1], 1) }
except IndexError:
txt += """%(deflt_opt)s""" % {'deflt_opt' : cgi.escape(default_opt[0], 1) }
txt += """</option>\n"""
except IndexError:
## seems to be an empty list - there will be no default opt
pass
for option in option_list:
try:
txt += """ <option value="%(option_val)s\"""" % { 'option_val' : cgi.escape(option[0], 1) }
if type(selected_values) in (list, tuple):
txt += """%(option_selected)s""" % \
{ 'option_selected' : (option[0] in selected_values and " selected") or ("") }
elif type(selected_values) in (str, unicode) and selected_values != "":
txt += """%(option_selected)s""" % \
{ 'option_selected' : (option[0] == selected_values and " selected") or ("") }
try:
txt += """>%(option_txt)s</option>\n""" % { 'option_txt' : cgi.escape(option[1], 1) }
except IndexError:
txt += """>%(option_txt)s</option>\n""" % { 'option_txt' : cgi.escape(option[0], 1) }
except IndexError:
## empty option tuple - skip
pass
txt += """ </select>\n"""
return txt
class Template:
"""CDS Invenio Template class for creating Web interface"""
def tmpl_navtrail(self, ln=CFG_SITE_LANG):
"""display the navtrail, e.g.:
Home > Admin Area > WebSubmit Administration > Available WebSubmit Actions
@param title: the last part of the navtrail. Is not a link
@param ln: language
return html formatted navtrail
"""
- return '<a class="navtrail" href="%s/help/admin">Admin Area</a> ' % (weburl,)
+ return '<a class="navtrail" href="%s/help/admin">Admin Area</a> ' % (CFG_SITE_URL,)
def _create_adminbox(self, header="", datalist=[], cls="admin_wvar"):
"""Create an adminbox table around the main data on a page - row based.
@param header: the header for the "adminbox".
@param datalist: contents of the "body" to be encapsulated by the "adminbox".
@param cls: css-class to format the look of the table.
@return: the "adminbox" and its contents.
"""
if len(datalist) == 1:
per = "100"
else:
per = "75"
output = """
<table class="%s" width="95%%">
""" % (cls,)
output += """
<thead>
<tr>
<th class="adminheaderleft" colspan="%s">
%s
</th>
</tr>
</thead>
<tbody>""" % (len(datalist), header)
output += """
<tr>
<td style="vertical-align: top; margin-top: 5px; width: %s;">
%s
</td>
""" % (per+'%', datalist[0])
if len(datalist) > 1:
output += """
<td style="vertical-align: top; margin-top: 5px; width: %s;">
%s
</td>""" % ('25%', datalist[1])
output += """
</tr>
</tbody>
</table>
"""
return output
def _create_user_message_string(self, user_msg):
"""Create and return a string containing any message(s) to be shown to the user.
In particular, these messages are generally info/warning messages.
@param user_msg: The message to be shown to the user. This parameter can have either a
string value (in the case where one message is to be shown to the user), or a list/tuple
value, where each value in the list is a string containing the message to be shown to the
user.
@return: EITHER: a string containing a HTML "DIV" section, which contains the message(s) to be
displayed to the user. In the case where there were multiple messages, each message will be
placed on its own line, by means of a "<br />" tag.
OR: an empty string - in the case that the parameter "user_msg" was an empty string.
"""
user_msg_str = ""
user_msg_str_end = ""
if type(user_msg) in (str, unicode):
if user_msg == "":
user_msg = ()
else:
user_msg = (user_msg,)
if len(user_msg) > 0:
user_msg_str += """<div align="center">\n"""
user_msg_str_end = """</div><br />\n"""
for msg in user_msg:
user_msg_str += """<span class="info">%s</span><br />\n""" % (cgi.escape(msg, 1),)
user_msg_str += user_msg_str_end
return user_msg_str
def _create_websubmitadmin_main_menu_header(self):
"""Create the main menu to be displayed on WebSubmit Admin pages."""
menu_body = """
<div>
<table>
<tr>
<td>0.&nbsp;<small><a href="%(adminurl)s/showall">Show all</a></small></td>
<td>&nbsp;1.&nbsp;<small><a href="%(adminurl)s/doctypelist">Available Document Types</a></small></td>
<td>&nbsp;2.&nbsp;<small><a href="%(adminurl)s/doctypeadd">Add New Document Type</a></small></td>
<td>&nbsp;3.&nbsp;<small><a href="%(adminurl)s/doctyperemove">Remove Document Type</a></small></td>
<td>&nbsp;4.&nbsp;<small><a href="%(adminurl)s/actionlist">Available Actions</a></small></td>
<td>&nbsp;5.&nbsp;<small><a href="%(adminurl)s/jschecklist">Available Checks</a></small></td>
</tr>
<tr>
<td>6.&nbsp;<small><a href="%(adminurl)s/elementlist">Available Element descriptions</a></small></td>
<td>&nbsp;7.&nbsp;<small><a href="%(adminurl)s/functionlist">Available Functions</a></small></td>
<td>&nbsp;8.&nbsp;<small><a href="%(adminurl)s/organisesubmissionpage">Organise Main Page</a></small></td>
- <td colspan=2>&nbsp;9.&nbsp;<small><a href="%(weburl)s/help/admin/websubmit-admin-guide">Guide</a></small></td>
+ <td colspan=2>&nbsp;9.&nbsp;<small><a href="%(siteurl)s/help/admin/websubmit-admin-guide">Guide</a></small></td>
</tr>
</table>
</div>
<br />
- """ % { 'adminurl' : WEBSUBMITADMIN_WEBURL, 'weburl': weburl }
+ """ % { 'adminurl' : WEBSUBMITADMINURL, 'siteurl': CFG_SITE_URL }
return self._create_adminbox(header="Main Menu", datalist=[menu_body])
def _element_display_preview_get_element(self,
elname="",
eltype="",
elsize="",
elrows="",
elcols="",
elval="",
elfidesc="",
ellabel=""):
"""Return the raw display-code for an individual element.
@param
"""
preview = "%s" % (ellabel,)
try:
preview += {"D" : """&nbsp;&nbsp;%s&nbsp;&nbsp;""" % (elfidesc,),
"F" : """<input type="file" %sname="dummyfile">""" % \
( (elsize != "" and """size="%s" """ % (cgi.escape(elsize, 1),) ) or (""),),
"H" : """<span class="info">Hidden Input. Contains Following Value: %s</span>""" % (cgi.escape(elval, 1),),
"I" : """<input type="text" %sname="dummyinput" value="%s">""" % \
( (elsize != "" and """size="%s" """ % (cgi.escape(elsize, 1),) ) or (""), cgi.escape(elval, 1)),
"R" : """<span class="info">Cannot Display Response Element - See Element Description</span>""",
"S" : """&nbsp;%s&nbsp;""" % (elfidesc,),
"T" : """<textarea name="dummytextarea" %s%s></textarea>""" % \
( (elrows != "" and """rows="%s" """ % (cgi.escape(elrows, 1),) ) or (""),
(elcols != "" and """cols="%s" """ % (cgi.escape(elcols, 1),) ) or (""),)
}[eltype]
except KeyError:
## Unknown element type - display warning:
preview += """<span class="info">Element Type not Recognised - Cannot Display</span>"""
return preview
def _element_display_preview(self,
elname="",
eltype="",
elsize="",
elrows="",
elcols="",
elval="",
elfidesc=""
):
"""Return a form containing a preview of an element, based on the values of the parameters provided
@param elname: element name
@param eltype: element type (e.g. text, user-defined, etc)
@param elsize: element size (e.g. for text input element)
@param elrows: number of rows (e.g. for textarea element)
@param elcols: number of columns (e.g. for textarea element)
@param elval: value of element (e.g. for text input element)
@param elfidesc: description for element (e.g. for user-defined element)
@return: string of HTML making up a preview of the element in a table
"""
## Open a dummy form and table in which to display a preview of the element
body = """<div><br />
<form name="dummyeldisplay" action="%(adminurl)s/elementlist">
<table class="admin_wvar" align="center">
<thead>
<tr>
<th class="adminheaderleft" colspan="1">
Element Preview:
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<br />&nbsp;&nbsp;
- """ % {'adminurl' : WEBSUBMITADMIN_WEBURL}
+ """ % {'adminurl' : WEBSUBMITADMINURL}
## Based on element type, display a preview of element:
body += self._element_display_preview_get_element(eltype=eltype, elsize=elsize, elrows=elrows, elcols=elcols,
elval=elval, elfidesc=elfidesc)
## Close dummy form and preview table:
body += """&nbsp;&nbsp;<br />
</td>
</tr>
</tbody>
</table>
</form>
</div>"""
return body
def tmpl_display_addelementform(self,
elname="",
elmarccode="",
eltype="",
elsize="",
elrows="",
elcols="",
elmaxlength="",
elval="",
elfidesc="",
elmodifytext="",
elcd="",
elmd="",
perform_act="elementadd",
user_msg="",
el_use_tuple=""
):
"""Display Web form used to add a new element to the database
@param elname: element name
@param elmarccode: marc code of element
@param eltype: element type (e.g. text, user-defined, etc)
@param elsize: element size (e.g. for text input element)
@param elrows: number of rows (e.g. for textarea element)
@param elcols: number of columns (e.g. for textarea element)
@param elmaxlength: maximum length of a text input field
@param elval: value of element (e.g. for text input element)
@param elfidesc: description for element (e.g. for user-defined element)
@param elmodifytext: element's modification text
@param elcd: creation date of element
@param elmd: last modification date of element
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
@param el_use_tuple:
@return: HTML page body.
"""
## First, get a rough preview of the element:
output = ""
etypes = {"D" : "User Defined Input", "F" : "File Input", "H" : "Hidden Input", "I" : "Text Input", \
"R" : "Response", "S" : "Select Box", "T" : "Text Area Element"}
etypeids = etypes.keys()
etypeids.sort()
body_content = ""
output += self._create_user_message_string(user_msg)
if perform_act != "elementadd":
body_content += self._element_display_preview(elname=elname, eltype=eltype, elsize=elsize, \
elrows=elrows, elcols=elcols, elval=elval, elfidesc=elfidesc)
else:
body_content += "<br />"
- body_content += """<form method="post" action="%(websubadmin_url)s/%(perform_action)s">""" \
- % {'websubadmin_url': WEBSUBMITADMIN_WEBURL, 'perform_action': perform_act}
+ body_content += """<form method="post" action="%(adminurl)s/%(perform_action)s">""" \
+ % {'adminurl': WEBSUBMITADMINURL, 'perform_action': perform_act}
body_content += """
<table width="100%%" class="admin_wvar">
<thead>
<tr>
<th class="adminheaderleft" colspan="2">
Enter Element Details:
</th>
</tr>
</thead>
<tbody>
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">&nbsp;</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Element Name:</span></td>
<td width="80%%">"""
if perform_act == "elementadd":
body_content += """
<input type="text" size="30" name="elname" value="%(el_name)s" />""" % {'el_name' : cgi.escape(elname, 1)}
else:
body_content += """<span class="info">%(el_name)s</span><input type="hidden" name="elname" value="%(el_name)s" />""" \
% {'el_name' : cgi.escape(elname, 1)}
body_content += """</td>
</tr>"""
if elcd != "" and elcd is not None:
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Creation Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(elcd), 1),)
if elmd != "" and elmd is not None:
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Last Modification Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(elmd), 1),)
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Modification Text:</span></td>
<td width="80%%"><input type="text" size="90" name="elmodifytext" value="%(el_modifytext)s" /></td>
</tr>""" % {'el_modifytext' : cgi.escape(elmodifytext, 1)}
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Element Type:</span></td>
<td width="80%%">
<select name="eltype">
<option value="NONE_SELECTED">Select:</option>\n"""
for itm in etypeids:
body_content += """ <option value="%s"%s>%s</option>\n""" % \
( itm, (eltype == itm and " selected" ) or (""), cgi.escape(etypes[itm], 1) )
body_content += """ </select>
</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Marc Code:</span></td>
<td width="80%%"><input type="text" size="15" name="elmarccode" value="%(el_marccode)s" /></td>
</tr>
""" % {'el_marccode' : cgi.escape(elmarccode, 1)}
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Size <i><small>(text elements)</small></i>:</span></td>
<td width="80%%"><input type="text" size="10" name="elsize" value="%(el_size)s" /></td>
</tr>
""" % {'el_size' : cgi.escape(elsize, 1)}
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">No. Rows <i><small>(textarea elements)</small></i>:</span></td>
<td width="80%%"><input type="text" size="6" name="elrows" value="%(el_rows)s" /></td>
</tr>
""" % {'el_rows' : cgi.escape(elrows, 1)}
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">No. Columns <i><small>(textarea elements)</small></i>:</span></td>
<td width="80%%"><input type="text" size="6" name="elcols" value="%(el_cols)s" /></td>
</tr>
""" % {'el_cols' : cgi.escape(elcols, 1)}
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Maximum Length <i><small>(text elements)</small></i>:</span></td>
<td width="80%%"><input type="text" size="6" name="elmaxlength" value="%(el_maxlength)s" /></td>
</tr>
""" % {'el_maxlength' : cgi.escape(elmaxlength, 1)}
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Value <i><small>(text/hidden elements)</small></i>:</span></td>
<td width="80%%"><input type="text" size="90" name="elval" value="%(el_val)s" /></td>
</tr>
""" % {'el_val' : cgi.escape(elval, 1)}
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Element Description <i><small>(e.g. user-defined elements)</small></i>:</span></td>
<td width="80%%"><textarea cols="100" rows="30" name="elfidesc" wrap="nowarp">%(el_fidesc)s</textarea></td>
</tr>
""" % {'el_fidesc' : cgi.escape(elfidesc, 1)}
body_content += """
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%"><input name="elcommit" class="adminbutton" type="submit" value="Save Details" /></td>
</tr>
</tbody>
</table>
</form>
"""
## If there is information about which submission pages use this element, display it:
if type(el_use_tuple) is tuple and len(el_use_tuple) > 0:
body_content += """<br /><br />
<table width="100%%" class="admin_wvar">
<thead>
<tr>
<th class="adminheaderleft" colspan="2">
Element Usage:
</th>
</tr>
</thead>
<tbody>
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">"""
for usecase in el_use_tuple:
try:
- body_content += """<small><a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s"""\
+ body_content += """<small><a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s"""\
"""&action=%(action)s&pagenum=%(pageno)s">&nbsp;%(subname)s: Page %(pageno)s</a></small><br />\n"""\
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ % { 'adminurl' : WEBSUBMITADMINURL,
'doctype' : usecase[0],
'action' : usecase[1],
'subname' : "%s%s" % (usecase[1], usecase[0]),
'pageno' : usecase[2]
}
except KeyError, e:
pass
body_content += """&nbsp;</td>
</tr>
</tbody>
</table>
"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Element Details:", datalist=[body_content])
return output
def tmpl_display_submission_page_organisation(self,
submission_collection_tree,
submission_collections,
doctypes,
user_msg=""):
def _build_collection_tree_display(branch, level=0):
outstr = ""
try:
level = int(level)
except TypeError:
level = 0
## open a table in which collection and doctype children will be displayed:
outstr += """<table border ="0" cellspacing="0" cellpadding="0">\n<tr>"""
## Display details of this collection:
if level != 0:
## Button to allow deletion of collection from tree:
- outstr += """<td><a href="%(websubadmin_url)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
- """&deletesbmcollection=1"><img border="0" src="%(weburl)s/img/iconcross.gif" """ \
+ outstr += """<td><a href="%(adminurl)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
+ """&deletesbmcollection=1"><img border="0" src="%(siteurl)s/img/iconcross.gif" """ \
"""title="Remove submission collection from tree"></a></td>""" \
- % { 'weburl' : cgi.escape(weburl, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'collection_id' : cgi.escape(str(branch['collection_id']), 1),
}
## does this collection have a collection brother above it?
if branch['has_brother_above'] == 1:
## Yes it does - add 'up' arrow:
- outstr += """<td><a href="%(websubadmin_url)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
- """&movesbmcollectionup=1"><img border="0" src="%(weburl)s/img/smallup.gif" """\
+ outstr += """<td><a href="%(adminurl)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
+ """&movesbmcollectionup=1"><img border="0" src="%(siteurl)s/img/smallup.gif" """\
"""title="Move submission collection up"></a></td>""" \
- % { 'weburl' : cgi.escape(weburl, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'collection_id' : cgi.escape(str(branch['collection_id']), 1),
}
else:
## No it doesn't - no 'up' arrow:
- outstr += """<td><img border="0" src="%(weburl)s/img/white_field.gif"></td>"""\
- % { 'weburl' : cgi.escape(weburl, 1), }
+ outstr += """<td><img border="0" src="%(siteurl)s/img/white_field.gif"></td>"""\
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1), }
## does this collection have a collection brother below it?
if branch['has_brother_below'] == 1:
## Yes it does - add 'down' arrow:
- outstr += """<td><a href="%(websubadmin_url)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
- """&movesbmcollectiondown=1"><img border="0" src="%(weburl)s/img/smalldown.gif" """\
+ outstr += """<td><a href="%(adminurl)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
+ """&movesbmcollectiondown=1"><img border="0" src="%(siteurl)s/img/smalldown.gif" """\
"""title="Move submission collection down"></a></td>""" \
- % { 'weburl' : cgi.escape(weburl, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'collection_id' : cgi.escape(str(branch['collection_id']), 1),
}
else:
## No it doesn't - no 'down' arrow:
- outstr += """<td><img border="0" src="%(weburl)s/img/white_field.gif"></td>"""\
- % { 'weburl' : cgi.escape(weburl, 1), }
+ outstr += """<td><img border="0" src="%(siteurl)s/img/white_field.gif"></td>"""\
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1), }
## Display the collection name:
outstr += """<td>&nbsp;<span style="color: green; font-weight: bold;">%s</span></td>""" \
% branch['collection_name']
else:
outstr += "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>"
outstr += "</tr>\n"
## If there are doctype children attached to this collection, display them:
num_doctype_children = len(branch['doctype_children'])
if num_doctype_children > 0:
outstr += """<tr><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>""" \
"""<table border ="0" cellspacing="0" cellpadding="0">\n"""
for child_num in xrange(0, num_doctype_children):
outstr += """<tr>\n"""
## Button to allow doctype to be detached from tree:
- outstr += """<td><a href="%(websubadmin_url)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
+ outstr += """<td><a href="%(adminurl)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
"""&doctype=%(doctype)s&catscore=%(catalogueorder)s&deletedoctypefromsbmcollection=1"><img border="0" """\
- """src="%(weburl)s/img/iconcross.gif" title="Remove doctype from branch"></a></td>""" \
- % { 'weburl' : cgi.escape(weburl, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ """src="%(siteurl)s/img/iconcross.gif" title="Remove doctype from branch"></a></td>""" \
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'collection_id' : cgi.escape(str(branch['collection_id']), 1),
'doctype' : cgi.escape(branch['doctype_children'][child_num]['doctype_id']),
'catalogueorder' : cgi.escape(str(branch['doctype_children'][child_num]['catalogue_order']), 1),
}
## Does this doctype have a brother above it?
if child_num > 0:
## Yes it does - add an 'up' arrow:
- outstr += """<td><a href="%(websubadmin_url)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
+ outstr += """<td><a href="%(adminurl)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
"""&doctype=%(doctype)s&catscore=%(catalogueorder)s&movedoctypeupinsbmcollection=1"><img border="0" """ \
- """src="%(weburl)s/img/smallup.gif" title="Move doctype up"></a></td>""" \
- % { 'weburl' : cgi.escape(weburl, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ """src="%(siteurl)s/img/smallup.gif" title="Move doctype up"></a></td>""" \
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'collection_id' : cgi.escape(str(branch['collection_id']), 1),
'doctype' : cgi.escape(branch['doctype_children'][child_num]['doctype_id']),
'catalogueorder' : cgi.escape(str(branch['doctype_children'][child_num]['catalogue_order']), 1),
}
else:
## No it doesn't - no 'up' arrow:
- outstr += """<td><img border="0" src="%(weburl)s/img/white_field.gif"></td>"""\
- % { 'weburl' : cgi.escape(weburl, 1), }
+ outstr += """<td><img border="0" src="%(siteurl)s/img/white_field.gif"></td>"""\
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1), }
## Does this doctype have a brother below it?
if child_num < num_doctype_children - 1:
## Yes it does - add a 'down' arrow:
- outstr += """<td><a href="%(websubadmin_url)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
+ outstr += """<td><a href="%(adminurl)s/organisesubmissionpage?sbmcolid=%(collection_id)s""" \
"""&doctype=%(doctype)s&catscore=%(catalogueorder)s&movedoctypedowninsbmcollection=1"><img border="0" """ \
- """src="%(weburl)s/img/smalldown.gif" title="Move doctype down"></a></td>""" \
- % { 'weburl' : cgi.escape(weburl, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ """src="%(siteurl)s/img/smalldown.gif" title="Move doctype down"></a></td>""" \
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'collection_id' : cgi.escape(str(branch['collection_id']), 1),
'doctype' : cgi.escape(branch['doctype_children'][child_num]['doctype_id']),
'catalogueorder' : cgi.escape(str(branch['doctype_children'][child_num]['catalogue_order']), 1),
}
else:
## No it doesn't - no 'down' arrow:
- outstr += """<td><img border="0" src="%(weburl)s/img/white_field.gif"></td>"""\
- % { 'weburl' : cgi.escape(weburl, 1), }
+ outstr += """<td><img border="0" src="%(siteurl)s/img/white_field.gif"></td>"""\
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1), }
## Display the document type details:
- outstr += """<td>&nbsp;<small><a href="%(websubadmin_url)s/doctypeconfigure?doctype=%(doctype)s">"""\
+ outstr += """<td>&nbsp;<small><a href="%(adminurl)s/doctypeconfigure?doctype=%(doctype)s">"""\
"""%(doctype_name)s [%(doctype)s]</a></small></td>""" \
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ % { 'adminurl' : WEBSUBMITADMINURL,
'doctype' : cgi.escape(branch['doctype_children'][child_num]['doctype_id'], 1),
'doctype_name' : cgi.escape(branch['doctype_children'][child_num]['doctype_lname'], 1),
}
outstr += "</tr>\n"
## If there were doctype children attached to this collection, they have been displayed,
## so close up the row:
if num_doctype_children > 0:
outstr += "</table>\n</td></tr>"
## Display Lower branches of tree:
for lower_branch in branch['collection_children']:
outstr += "<tr><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>"
outstr += _build_collection_tree_display(branch=lower_branch, level=level+1)
outstr += "</td></tr>\n"
outstr += "</table>"
return outstr
## begin display:
output = ""
body_content = """<br />
<table class="admin_wvar" width="100%%">
<thead>
<tr>
<th class="adminheaderleft">
Submission Page Organisational Hierarchy:
</th>
</tr>
</thead>
<tbody>
<tr>
<td><br />"""
body_content += _build_collection_tree_display(submission_collection_tree)
body_content += """</td>
</tr>"""
body_content += """
<tr>
<td><br /></td>
</tr>
<tr>
<td><br />"""
## Form to allow user to add a new submission-collection:
body_content += """
- <form method="post" action="%(websubadmin_url)s/organisesubmissionpage">
+ <form method="post" action="%(adminurl)s/organisesubmissionpage">
<span class="adminlabel">You can add a new Submission-Collection:</span><br />
<small style="color: navy;">Name:</small>&nbsp;&nbsp;
<input type="text" name="addsbmcollection" style="margin: 5px 10px 5px 10px;" />
&nbsp;&nbsp;<small style="color: navy;">Attached to:</small>&nbsp;&nbsp;""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1), }
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1), }
if len(submission_collections) > 0:
body_content += """
%(submission_collections)s""" \
% { 'submission_collections' : \
create_html_select_list(select_name="addtosbmcollection",
option_list=submission_collections,
css_style="margin: 5px 10px 5px 10px;")
}
else:
body_content += """<input type="hidden" name="addtosbcollection" value="0" />
<span style="color: green;">Top Level</span>"""
body_content += """<input name="sbmcollectionadd" class="adminbutton" type="submit" """ \
"""value="Add" />
</form>"""
body_content += """</td>
</tr>
<tr>
<td><br /><br /></td>
</tr>"""
## if there are doctypes in the system, provide a form to enable the user to
## connect a document type to the submission-collection tree:
if len(submission_collections) > 1 and len(doctypes) > 0:
body_content += """<tr><td>
- <form method="post" action="%(websubadmin_url)s/organisesubmissionpage">
+ <form method="post" action="%(adminurl)s/organisesubmissionpage">
<span class="adminlabel">You can attach a Document Type to a Submission-Collection:</span><br />
<small style="color: navy;">Document Type Name:</small><br />
%(doctypes)s
<br /><small style="color: navy;">Attached to:</small>&nbsp;&nbsp;""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctypes' : create_html_select_list(select_name="adddoctypes",
option_list=doctypes,
css_style="margin: 5px 10px 5px 10px;",
multiple=1,
list_size=5)
}
body_content += """
%(submission_collections)s""" \
% { 'submission_collections' : \
create_html_select_list(select_name="addtosbmcollection",
option_list=submission_collections[1:],
css_style="margin: 5px 10px 5px 10px;")
}
body_content += """<input name="submissioncollectionadd" class="adminbutton" type="submit" """ \
"""value="Add" />
</form></td>
</tr>"""
body_content += """</tbody>
</table>"""
output += self._create_user_message_string(user_msg)
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Submission-Collections of Submission Page:", datalist=[body_content])
return output
def tmpl_display_addactionform(self,
actid="",
actname="",
working_dir="",
status_text="",
perform_act = "actionadd",
cd="",
md="",
user_msg=""):
"""Display web form used to add a new action to Websubmit.
@param actid: Value of the "sactname" (action id) parameter of the Websubmit action.
@param actname: Value of the "lactname" (long action name) parameter of the Websubmit action.
@param working_dir: Value of the "dir" (action working/archive directory) parameter of the Websubmit action.
@param status_text: Value of the "statustext" (action status text) parameter of the WebSubmit action.
@param perform_act: action for form (minus websubmitadmin base url)
@param cd: Creation date of action.
@param md: Modification date of action.
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
@return: HTML page body.
"""
output = ""
output += self._create_user_message_string(user_msg)
- body_content = """<form method="post" action="%(websubadmin_url)s/%(perform_action)s">""" \
- % {'websubadmin_url': WEBSUBMITADMIN_WEBURL, 'perform_action': perform_act}
+ body_content = """<form method="post" action="%(adminurl)s/%(perform_action)s">""" \
+ % {'adminurl': WEBSUBMITADMINURL, 'perform_action': perform_act}
body_content += """
<table width="90%%">
<tr>
<td width="20%%"><span class="adminlabel">Action Code:</span></td>
<td width="80%%">"""
if perform_act == "actionadd":
body_content += """
<input type="text" size="6" name="actid" value="%(ac_id)s" />""" % {'ac_id' : cgi.escape(actid, 1)}
else:
body_content += """<span class="info">%(ac_id)s</span><input type="hidden" name="actid" value="%(ac_id)s" />""" \
% {'ac_id' : cgi.escape(actid, 1)}
body_content += """</td>
</tr>"""
if "" not in (cd, md):
if cd is not None:
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Creation Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(cd), 1),)
if md is not None:
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Last Modification Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(md), 1), )
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Action Description:</span></td>
<td width="80%%"><input type="text" size="60" name="actname" value="%(ac_name)s" /></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Action dir:</span></td>
<td width="80%%"><input type="text" size="40" name="working_dir" value="%(w_dir)s" /></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Action Status Text:</span></td>
<td width="80%%"><input type="text" size="60" name="status_text" value="%(s_txt)s" /></td>
</tr>""" % {'ac_name' : cgi.escape(actname, 1), 'w_dir' : cgi.escape(working_dir, 1), \
's_txt' : cgi.escape(status_text, 1)}
body_content += """
<tr>
<td colspan="2">
<table>
<tr>
<td>
<input name="actcommit" class="adminbutton" type="submit" value="Save Details" />
</form>
</td>
<td>
<br />
- <form method="post" action="%(websubadmin_url)s/actionlist">
+ <form method="post" action="%(adminurl)s/actionlist">
<input name="actcommitcancel" class="adminbutton" type="submit" value="Cancel" />
</form>
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
- """ % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL }
+ """ % { 'adminurl' : WEBSUBMITADMINURL }
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Enter Action Details:", datalist=[body_content])
return output
def tmpl_display_addjscheckform(self,
chname="",
chdesc="",
perform_act = "jscheckadd",
cd="",
md="",
user_msg=""):
"""Display web form used to add a new Check to Websubmit.
@param chname: Value of the "chname" (check ID/name) parameter of the WebSubmit Check.
@param chdesc: Value of the "chdesc" (check description - i.e. JS code) parameter of the
WebSubmit Check.
@param perform_act: action for form (minus websubmitadmin base url)
@param cd: Creation date of check.
@param md: Modification date of check.
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
@return: HTML page body.
"""
output = ""
output += self._create_user_message_string(user_msg)
- body_content = """<form method="post" action="%(websubadmin_url)s/%(perform_action)s">""" \
- % {'websubadmin_url': WEBSUBMITADMIN_WEBURL, 'perform_action': perform_act}
+ body_content = """<form method="post" action="%(adminurl)s/%(perform_action)s">""" \
+ % {'adminurl': WEBSUBMITADMINURL, 'perform_action': perform_act}
body_content += """
<table width="90%%">
<tr>
<td width="20%%"><span class="adminlabel">Check Name:</span></td>
<td width="80%%">"""
if perform_act == "jscheckadd":
body_content += """
<input type="text" size="15" name="chname" value="%(ch_name)s" />""" % {'ch_name' : cgi.escape(chname, 1)}
else:
body_content += """<span class="info">%(ch_name)s</span><input type="hidden" name="chname" value="%(ch_name)s" />""" \
% {'ch_name' : cgi.escape(chname, 1)}
body_content += """</td>
</tr>"""
if "" not in (cd, md):
if cd is not None:
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Creation Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(cd), 1),)
if md is not None:
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Last Modification Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(md), 1),)
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Check Description:</span></td>
<td width="80%%">
<textarea cols="90" rows="22" name="chdesc">%(ch_descr)s</textarea>
</td>
</tr>
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%"><input name="chcommit" class="adminbutton" type="submit" value="Save Details" /></td>
</tr>
</table>
</form>
""" % {'ch_descr' : cgi.escape(chdesc, 1)}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Enter Action Details:", datalist=[body_content])
return output
def tmpl_display_addfunctionform(self,
funcname="",
funcdescr="",
func_parameters=None,
all_websubmit_func_parameters=None,
perform_act="functionadd",
user_msg=""):
"""Display web form used to add a new function to Websubmit.
@param funcname: Value of the "function" (unique function name) parameter
@param chdesc: Value of the "description" (function textual description) parameter
@param perform_act: action for form (minus websubmitadmin base url)
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
@return: HTML page body.
"""
if type(func_parameters) not in (list, tuple):
## bad parameters list - reset
func_parameters = ()
if type(all_websubmit_func_parameters) not in (list, tuple):
## bad list of function parameters - reset
all_websubmit_func_parameters = ()
output = ""
output += self._create_user_message_string(user_msg)
- body_content = """<form method="post" action="%(websubadmin_url)s/%(perform_action)s">""" \
- % {'websubadmin_url' : WEBSUBMITADMIN_WEBURL, 'perform_action': perform_act}
+ body_content = """<form method="post" action="%(adminurl)s/%(perform_action)s">""" \
+ % {'adminurl' : WEBSUBMITADMINURL, 'perform_action': perform_act}
## Function Name and description:
body_content += """<br />
<table width="100%%" class="admin_wvar">
<thead>
<tr>
<th class="adminheaderleft" colspan="2">
%sFunction Details:
</th>
</tr>
</thead>
<tbody>
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">&nbsp;</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Function Name:</span></td>
<td width="80%%">""" % ((funcname != "" and cgi.escape(funcname, 1) + " ") or (""), )
if perform_act == "functionadd" and funcname == "":
body_content += """
<input type="text" size="30" name="funcname" />"""
else:
body_content += """<span class="info">%(func_name)s</span><input type="hidden" name="funcname" value="%(func_name)s" />""" \
% {'func_name' : cgi.escape(funcname, 1)}
body_content += """</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Function Description:</span></td>
<td width="80%%"><input type="text" size="90" name="funcdescr" value="%(func_descr)s" />
</tr>""" % {'func_descr' : cgi.escape(funcdescr, 1)}
body_content += """
<tr>
<td width="20%%" colspan="2">&nbsp;</td>
</tr>
<tr>
<td width="20%%" colspan="2"><input name="%s" class="adminbutton" type="submit" value="Save Details" /></td>
</tr>
</tbody>
</table>""" % ( ((perform_act == "functionadd" and funcname == "") and "funcaddcommit") or ("funcdescreditcommit"), )
if funcname not in ("", None):
body_content += """<br />
<table width="100%%" class="admin_wvar">
<thead>
<tr>
<th class="adminheaderleft">
Parameters for Function %(func_name)s:
</th>
</tr>
</thead>
<tbody>
<tr>
<td><br />""" % {'func_name' : cgi.escape(funcname, 1)}
params_tableheader = ["Parameter", "&nbsp;"]
params_tablebody = []
for parameter in func_parameters:
params_tablebody.append( ("<small>%s</small>" % (cgi.escape(parameter[0], 1),),
- """<small><a href="%(websubadmin_url)s/functionedit?funcparamdelcommit=funcparamdelcommit""" \
+ """<small><a href="%(adminurl)s/functionedit?funcparamdelcommit=funcparamdelcommit""" \
"""&amp;funcname=%(func_name)s&amp;funceditdelparam=%(delparam_name)s">delete</a></small>""" \
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ % { 'adminurl' : WEBSUBMITADMINURL,
'func_name' : cgi.escape(funcname, 1),
'delparam_name' : cgi.escape(parameter[0], 1)
}
) )
body_content += create_html_table_from_tuple(tableheader=params_tableheader, tablebody=params_tablebody)
body_content += """</td>
</tr>
</tbody>
</table>
<br />"""
## Add a parameter?
body_content += """<table width="100%%" class="admin_wvar">
<thead>
<tr>
<th class="adminheaderleft" colspan="2">
Add Parameter to Function %(func_name)s:
</th>
</tr>
</thead>
<tbody>""" % {'func_name' : cgi.escape(funcname, 1)}
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Add Parameter:</span></td>
<td width="80%%"><small>Select a parameter to add to function:</small>&nbsp;%s&nbsp;&nbsp;""" \
% (create_html_select_list(select_name="funceditaddparam", option_list=all_websubmit_func_parameters),)
body_content += """<small>-Or-</small>&nbsp;&nbsp;<small>Enter a new parameter:</small>&nbsp;&nbsp;<input type="text" """ \
+ """name="funceditaddparamfree" size="15" /><input name="funcparamaddcommit" class="adminbutton" """ \
+ """type="submit" value="Add" /></td>
</tr>
</tbody>
</table>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Enter Function Details:", datalist=[body_content])
return output
def tmpl_display_function_usage(self, funcname, func_usage, user_msg=""):
"""Display a table containing the details of a function's usage in the various actions of the various doctypes.
Displayed will be information about the document type and action, and the score and step at which
the function is called within that action.
@param funcname: (string) function name.
@param func_usage: (tuple) A tuple of tuples, each containing details of the function usage:
(doctype, docname, function-step, function-score, action id, action name)
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
@return: (string) HTML page body.
"""
output = ""
body_content = ""
header = ["Doctype", "&nbsp;", "Action", "&nbsp;", "Score", "Step", "Show Details"]
tbody = []
output += self._create_user_message_string(user_msg)
body_content += "<br />"
for usage in func_usage:
tbody.append( ("<small>%s</small>" % (cgi.escape(usage[0], 1),),
"<small>%s</small>" % (cgi.escape(usage[1], 1),),
"<small>%s</small>" % (cgi.escape(usage[2], 1),),
"<small>%s</small>" % (cgi.escape(usage[3], 1),),
"<small>%s</small>" % (cgi.escape(usage[4], 1),),
"<small>%s</small>" % (cgi.escape(usage[5], 1),),
"""<small><a href="%s/doctypeconfiguresubmissionfunctions?doctype=%s&action=%s"""\
"""&viewSubmissionFunctions=true">Show</a></small>"""\
- % (WEBSUBMITADMIN_WEBURL, cgi.escape(usage[0], 1), cgi.escape(usage[2], 1))
+ % (WEBSUBMITADMINURL, cgi.escape(usage[0], 1), cgi.escape(usage[2], 1))
)
)
body_content += create_html_table_from_tuple(tableheader=header, tablebody=tbody)
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="""Usage of the "%s" Function:""" % (cgi.escape(funcname, 1),), datalist=[body_content])
return output
def tmpl_display_allactions(self,
actions,
user_msg=""):
"""Create the page body used for displaying all Websubmit actions.
@param actions: A tuple of tuples containing the action id, and the action name (actid, actname).
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
@return: HTML page body.
"""
output = ""
output += self._create_user_message_string(user_msg)
body_content = """<div>
<table>
"""
for action in actions:
body_content += """<tr>
<td align="left">&nbsp;&nbsp;<a href="%s/actionedit?actid=%s">%s: %s</a></td>
</tr>
-""" % (WEBSUBMITADMIN_WEBURL, cgi.escape(action[0], 1), cgi.escape(action[0], 1), cgi.escape(action[1], 1))
+""" % (WEBSUBMITADMINURL, cgi.escape(action[0], 1), cgi.escape(action[0], 1), cgi.escape(action[1], 1))
body_content += """</table>"""
## Button to create new action:
body_content += """<br /><form action="%s/actionadd" METHOD="post"><input class="adminbutton" type="submit" value="Add Action" /></form>""" \
- % (WEBSUBMITADMIN_WEBURL,)
+ % (WEBSUBMITADMINURL,)
body_content += """</div>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Select an Action:", datalist=[body_content])
return output
def tmpl_display_alldoctypes(self,
doctypes,
user_msg = ""):
"""Create the page body used for displaying all Websubmit document types.
@param doctypes: A tuple of tuples containing the doctype id, and the doctype name (docid, docname).
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
return: HTML page body.
"""
output = ""
output += self._create_user_message_string(user_msg)
body_content = """<div>
<table>
"""
for doctype in doctypes:
body_content += """<tr>
<td align="left">&nbsp;&nbsp;<a href="%s/doctypeconfigure?doctype=%s">%s&nbsp;&nbsp;[%s]</a></td>
</tr>
-""" % (WEBSUBMITADMIN_WEBURL, cgi.escape(doctype[0], 1), cgi.escape(doctype[1], 1), cgi.escape(doctype[0], 1))
+""" % (WEBSUBMITADMINURL, cgi.escape(doctype[0], 1), cgi.escape(doctype[1], 1), cgi.escape(doctype[0], 1))
body_content += """</table>"""
## Button to create new action:
body_content += """<br /><form action="%s/doctypeadd" METHOD="post"><input class="adminbutton" type="submit" value="Add New Doctype" /></form>""" \
- % (WEBSUBMITADMIN_WEBURL,)
+ % (WEBSUBMITADMINURL,)
body_content += """</div>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Select a Document Type:", datalist=[body_content])
return output
def tmpl_display_alljschecks(self,
jschecks,
user_msg = ""):
"""Create the page body used for displaying all Websubmit JavaScript Checks.
@param jschecks: A tuple of tuples containing the check name (chname, which is unique for
each check.)
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
return: HTML page body.
"""
output = ""
output += self._create_user_message_string(user_msg)
body_content = """<div>
<table>
"""
for jscheck in jschecks:
body_content += """<tr>
<td align="left">&nbsp;&nbsp;<a href="%s/jscheckedit?chname=%s">%s</a></td>
</tr>
-""" % (WEBSUBMITADMIN_WEBURL, cgi.escape(jscheck[0], 1), cgi.escape(jscheck[0], 1))
+""" % (WEBSUBMITADMINURL, cgi.escape(jscheck[0], 1), cgi.escape(jscheck[0], 1))
body_content += """</table>"""
## Button to create new action:
body_content += """<br /><form action="%s/jscheckadd" METHOD="post"><input class="adminbutton" type="submit" value="Add Check" /></form>""" \
- % (WEBSUBMITADMIN_WEBURL,)
+ % (WEBSUBMITADMINURL,)
body_content += """</div>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Select a Checking Function:", datalist=[body_content])
return output
def tmpl_display_allfunctions(self,
functions,
user_msg = ""):
"""Create the page body used for displaying all Websubmit functions.
@param functions: A tuple of tuples containing the function name, and the function
description (function, description).
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
return: HTML page body.
"""
output = ""
header = ["Function Name", "View Usage", "Edit Details"]
output += self._create_user_message_string(user_msg)
body_content = """<div><br />\n"""
tbody = []
for function in functions:
tbody.append(("&nbsp;&nbsp;%s" % (cgi.escape(function[0], 1),),
"""<small><a href="%s/functionusage?funcname=%s">View Usage</a></small>""" % \
- (WEBSUBMITADMIN_WEBURL, cgi.escape(function[0], 1)),
+ (WEBSUBMITADMINURL, cgi.escape(function[0], 1)),
"""<small><a href="%s/functionedit?funcname=%s">Edit Details</a></small>""" % \
- (WEBSUBMITADMIN_WEBURL, cgi.escape(function[0], 1))
+ (WEBSUBMITADMINURL, cgi.escape(function[0], 1))
))
button_newfunc = """<form action="%s/functionadd" METHOD="post">
<input class="adminbutton" type="submit" value="Add New Function" />
- </form>""" % (WEBSUBMITADMIN_WEBURL,)
+ </form>""" % (WEBSUBMITADMINURL,)
body_content += create_html_table_from_tuple(tableheader=header, tablebody=tbody, end=button_newfunc)
body_content += """</div>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="WebSubmit Functions:", datalist=[body_content])
return output
def tmpl_display_allelements(self,
elements,
user_msg = ""):
"""Create the page body used for displaying all Websubmit elements.
@param elements: A tuple of tuples containing the element name (name).
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
return: HTML page body.
"""
output = ""
output += self._create_user_message_string(user_msg)
body_content = """<div>
<table style="align:center;">
"""
for element in elements:
body_content += """<tr>
<td align="left">&nbsp;&nbsp;<a href="%s/elementedit?elname=%s">%s</a></td>
</tr>
-""" % (WEBSUBMITADMIN_WEBURL, cgi.escape(element[0], 1), cgi.escape(element[0], 1))
+""" % (WEBSUBMITADMINURL, cgi.escape(element[0], 1), cgi.escape(element[0], 1))
body_content += """</table>"""
## Button to create new action:
body_content += """<br /><form action="%s/elementadd" METHOD="post"><input class="adminbutton" type="submit" value="Add New Element" /></form>""" \
- % (WEBSUBMITADMIN_WEBURL,)
+ % (WEBSUBMITADMINURL,)
body_content += """</div>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Select an Element:", datalist=[body_content])
return output
def tmpl_display_delete_doctype_form(self, doctype="", alldoctypes="", user_msg=""):
"""TODO: DOCSTRING"""
output = ""
output += self._create_user_message_string(user_msg)
body_content = "<div>"
if doctype not in ("", None) and type(doctype) in (str, unicode):
## Display the confirmation message:
- body_content += """<form method="get" action="%(websubadmin_url)s/doctyperemove">""" \
+ body_content += """<form method="get" action="%(adminurl)s/doctyperemove">""" \
"""<input type="hidden" name="doctype" value="%(doc_type)s" />\n""" \
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL, 'doc_type' : cgi.escape(doctype, 1) }
+ % { 'adminurl' : WEBSUBMITADMINURL, 'doc_type' : cgi.escape(doctype, 1) }
body_content += """<div><span class="info"><i>Really</i> remove document type "%s" and all of its configuration details?</span> <input name="doctypedeleteconfirm" class="adminbutton\""""\
"""type="submit" value="Confirm" /></div>\n</form>\n""" % (cgi.escape(doctype,) )
else:
## just display the list of document types to delete:
if type(alldoctypes) not in (list, tuple):
## bad list of document types - reset
alldoctypes = ()
- body_content += """<form method="get" action="%(websubadmin_url)s/doctyperemove">""" \
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL }
+ body_content += """<form method="get" action="%(adminurl)s/doctyperemove">""" \
+ % { 'adminurl' : WEBSUBMITADMINURL }
body_content += """
<table width="100%%" class="admin_wvar">
<thead>
<tr>
<th class="adminheaderleft">
Select a Document Type to Remove:
</th>
</tr>
</thead>
<tbody>
<tr>
<td>&nbsp;&nbsp;%s&nbsp;&nbsp;<input name="doctypedelete" class="adminbutton" type="submit" value="Remove" /></td>
</tr>
</table>
</form>""" \
% (create_html_select_list(select_name="doctype", option_list=alldoctypes),)
body_content += """</div>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Remove a Document Type:", datalist=[body_content])
return output
## DOCTYPE CONFIGURE
def tmpl_display_submission_clone_form(self,
doctype,
action,
clonefrom_list,
user_msg=""
):
if type(clonefrom_list) not in (list, tuple):
clonefrom_list = ()
output = ""
output += self._create_user_message_string(user_msg)
- body_content = """<form method="get" action="%(websubadmin_url)s/%(formaction)s">""" \
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL , 'formaction' : cgi.escape("doctypeconfigure", 1) }
+ body_content = """<form method="get" action="%(adminurl)s/%(formaction)s">""" \
+ % { 'adminurl' : WEBSUBMITADMINURL , 'formaction' : cgi.escape("doctypeconfigure", 1) }
body_content += """
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="action" value="%(action)s" />
<table width="90%%">
<tr>
<td width="20%%"><span class="adminlabel">Clone from Document Type:</span></td>
<td width="80%%">
%(clonefrom)s
</td>
</tr>
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">
<input name="doctypesubmissionaddclonechosen" class="adminbutton" type="submit" value="Continue" />&nbsp;
<input name="doctypesubmissionaddclonechosencancel" class="adminbutton" type="submit" value="Cancel" />&nbsp;
</td>
</tr>
</table>
</form>""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'clonefrom' : create_html_select_list(select_name="doctype_cloneactionfrom",
option_list=clonefrom_list,
default_opt=("None", "Do not clone from another Document Type/Submission"),
css_style="margin: 5px 10px 5px 10px;"
)
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Add Submission '%s' to Document Type '%s':" % (action, doctype),
datalist=[body_content])
return output
def tmpl_display_delete_doctypesubmission_form(self, doctype="", action="", user_msg=""):
"""TODO: DOCSTRING"""
output = ""
output += self._create_user_message_string(user_msg)
body_content = """<div>"""
## Display the confirmation message:
- body_content += """<form method="get" action="%(websubadmin_url)s/%(formaction)s">""" \
+ body_content += """<form method="get" action="%(adminurl)s/%(formaction)s">""" \
"""<input type="hidden" name="doctype" value="%(doctype)s" />\n""" \
"""<input type="hidden" name="action" value="%(action)s" />\n""" \
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ % { 'adminurl' : WEBSUBMITADMINURL,
'formaction' : "doctypeconfigure",
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1)
}
body_content += """<div><span class="info"><i>Really</i> remove the Submission "%s" and all related details from Document Type "%s"?</span> <input name="doctypesubmissiondeleteconfirm" class="adminbutton" """ \
"""type="submit" value="Confirm" /></div>\n</form>\n""" % (cgi.escape(action, 1), cgi.escape(doctype, 1) )
body_content += """</div>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="""Delete Submission "%s" from Document Type "%s"\"""" % (action, doctype), datalist=[body_content])
return output
def tmpl_display_submissiondetails_form(self,
doctype,
action,
displayed="",
buttonorder="",
statustext="",
level="",
score="",
stpage="",
endtxt="",
cd="",
md="",
user_msg="",
perform_act="doctypeconfigure",
saveaction="edit"
):
output = ""
output += self._create_user_message_string(user_msg)
- body_content = """<form method="get" action="%(websubadmin_url)s/%(action)s">""" \
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL , 'action' : cgi.escape(perform_act, 1) }
+ body_content = """<form method="get" action="%(adminurl)s/%(action)s">""" \
+ % { 'adminurl' : WEBSUBMITADMINURL , 'action' : cgi.escape(perform_act, 1) }
body_content += """
<table width="90%%">"""
if cd not in ("", None):
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Creation Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(cd), 1),)
if md not in ("", None):
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Last Modification Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(md), 1),)
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Submission Displayed on Start Page:</span></td>
<td width="80%%">
<select name="displayed">
<option value="Y"%s>Yes</option>
<option value="N"%s>No</option>
</select>
</td>
</tr>""" % ( (displayed == "Y" and " selected") or (""),
(displayed == "N" and " selected") or ("")
)
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Button Order:</span></td>
<td width="80%%">
<input type="text" size="4" name="buttonorder" value="%(buttonorder)s" />
</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Status Text:</span></td>
<td width="80%%">
<input type="text" size="35" name="statustext" value="%(statustext)s" />
</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Level:</span></td>
<td width="80%%">
<input type="text" size="4" name="level" value="%(level)s" />
</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Score:</span></td>
<td width="80%%">
<input type="text" size="4" name="score" value="%(score)s" />
</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Stpage:</span></td>
<td width="80%%">
<input type="text" size="4" name="stpage" value="%(stpage)s" />
</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">End Text:</span></td>
<td width="80%%">
<input type="text" size="35" name="endtxt" value="%(endtxt)s" />
</td>
</tr>
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input name="%(savebutton)s" class="adminbutton" type="submit" value="Save Details" />
&nbsp;
<input name="doctypesubmissiondetailscancel" class="adminbutton" type="submit" value="Cancel" />
</td>
</tr>
</table>
</form>
""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'displayed' : cgi.escape(displayed, 1),
'buttonorder' : cgi.escape(buttonorder, 1),
'statustext' : cgi.escape(statustext, 1),
'level' : cgi.escape(level, 1),
'score' : cgi.escape(score, 1),
'stpage' : cgi.escape(stpage, 1),
'endtxt' : cgi.escape(endtxt, 1),
'cd' : cgi.escape(cd, 1),
'md' : cgi.escape(md, 1),
'savebutton' : ((saveaction == "edit" and "doctypesubmissioneditdetailscommit") or ("doctypesubmissionadddetailscommit"))
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Enter Details of '%s' Submission of '%s' Document Type:" % (action, doctype),
datalist=[body_content])
return output
def tmpl_display_doctypedetails_form(self, doctype="", doctypename="", doctypedescr="", cd="", md="", clonefrom="", \
alldoctypes="", user_msg="", perform_act="doctypeadd"):
"""TODO : DOCSTRING"""
output = ""
body_content = ""
if perform_act == "doctypeadd":
formheader = "Add a new Document Type:"
else:
formheader = "Edit Document Type Details:"
output += self._create_user_message_string(user_msg)
if type(alldoctypes) not in (list, tuple):
## bad list of document types - reset
alldoctypes = ()
- body_content += """<form method="get" action="%(websubadmin_url)s/%(action)s">""" \
- % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL , 'action' : cgi.escape(perform_act, 1) }
+ body_content += """<form method="get" action="%(adminurl)s/%(action)s">""" \
+ % { 'adminurl' : WEBSUBMITADMINURL , 'action' : cgi.escape(perform_act, 1) }
body_content += """
<table width="90%%">
<tr>
<td width="20%%"><span class="adminlabel">Document Type ID:</span></td>
<td width="80%%">"""
if perform_act == "doctypeadd":
body_content += """<input type="text" size="15" name="doctype" value="%(doctype_id)s" />""" \
% {'doctype_id' : cgi.escape(doctype, 1)}
else:
body_content += """<span class="info">%(doctype_id)s</span><input type="hidden" name="doctype" value="%(doctype_id)s" />""" \
% {'doctype_id' : cgi.escape(doctype, 1)}
body_content += """</td>
</tr>"""
if perform_act != "doctypeadd":
if cd not in ("", None):
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Creation Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(cd), 1),)
if md not in ("", None):
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Last Modification Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(md), 1), )
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Document Type Name:</span></td>
<td width="80%%"><input type="text" size="60" name="doctypename" value="%(doctype_name)s" /></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Document Type Description:</span></td>
<td width="80%%"><textarea name="doctypedescr" cols="60" rows="8">%(doctype_description)s</textarea></td>
</tr>""" % { 'doctype_name' : cgi.escape(doctypename, 1),
'doctype_description' : "%s" % ((doctypedescr is not None and cgi.escape(str(doctypedescr), 1)) or ("")),
}
if perform_act == "doctypeadd":
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Document Type to Clone:</span></td>
<td width="80%%">%(doctype_select_list)s</td>
</tr>""" % { 'doctype_select_list' :
create_html_select_list(select_name="clonefrom",
option_list=alldoctypes,
selected_values=clonefrom,
default_opt=('None', 'Select:')
)
}
body_content += """
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">
<input name="doctypedetailscommit" class="adminbutton" type="submit" value="Save Details" />"""
if perform_act != "doctypeadd":
## add a cancel button if this is not a call to add a new document type:
body_content += """
&nbsp;
<input name="doctypedetailscommitcancel" class="adminbutton" type="submit" value="cancel" />"""
body_content += """
</td>
</tr>
</table>
</form>\n"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header=formheader, datalist=[body_content])
return output
def _tmpl_configire_doctype_overview_create_doctype_details(self, doctype="", doctypename="", doctypedescr="",
doctype_cdate="", doctype_mdate="", perform_act="doctypeconfigure"
):
"""Display the details of a document type"""
txt = """
<table class="admin_wvar" rules="rows" width="100%%">
<thead>
<tr style="border-bottom: hidden">
<th class="adminheaderleft" colspan="2">
%(doctype_id)s Document Type Details:
</th>
</tr>
</thead>
<tbody>
<tr style="border-top: hidden; border-bottom: hidden">
<td width="20%%">&nbsp;</td>
<td width="80%%">&nbsp;</td>
</tr>
<tr>
<td width="20%%" style="border-bottom: hidden"><span class="adminlabel">Document Type ID:</span></td>
<td width="80%%"><span class="info">%(doctype_id)s</span></td>
</tr>
<tr>
<td width="20%%" style="border-bottom: hidden"><span class="adminlabel">Creation Date:</span></td>
<td width="80%%"><span class="info">%(doctype_cdate)s</span></td>
</tr>
<tr>
<td width="20%%" style="border-bottom: hidden"><span class="adminlabel">Modification Date:</span></td>
<td width="80%%"><span class="info">%(doctype_mdate)s</span></td>
</tr>
<tr>
<td width="20%%" style="border-top: hidden; border-bottom: hidden"><span class="adminlabel">Document Type Name:</span></td>
<td width="80%%"><span>%(doctype_name)s</span></td>
</tr>
<tr style="border-bottom: hidden">
<td width="20%%" style="border-top: hidden"><span class="adminlabel">Document Type Description:</span></td>
<td width="80%%"><span>%(doctype_descr)s</span></td>
</tr>
<tr style="border-top: hidden">
<td colspan="2">
- <form method="post" action="%(websubadmin_url)s/%(performaction)s">
+ <form method="post" action="%(adminurl)s/%(performaction)s">
<input name="doctype" type="hidden" value="%(doctype_id)s" />
<input name="doctypedetailsedit" class="adminbutton" type="submit" value="Edit Details" />
</form>
</td>
</tr>
</tbody>
</table>\n""" % { 'doctype_id' : cgi.escape(doctype, 1),
'doctype_cdate' : "%s" % ((doctype_cdate not in ("", None) and cgi.escape(str(doctype_cdate), 1)) or (""),),
'doctype_mdate' : "%s" % ((doctype_mdate not in ("", None) and cgi.escape(str(doctype_mdate), 1)) or (""),),
'doctype_name' : cgi.escape(doctypename, 1),
'doctype_descr' : doctypedescr,
'performaction' : cgi.escape(perform_act, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1)
}
return txt
def _tmpl_configure_doctype_overview_create_categories_view(self,
doctype="",
doctype_categories="",
jumpcategout="",
perform_act="doctypeconfigure"
):
"""Display the details of the categories for a given document type"""
## sanity checking for categories list:
if type(doctype_categories) not in (list, tuple):
doctype_categories = ()
txt = """
<table class="admin_wvar" width="100%%">
<thead>
<tr>
<th class="adminheaderleft">
Categories of Document Type %(doctype_id)s:
</th>
</tr>
</thead>
<tbody>
<tr>
<td><br />""" % { 'doctype_id' : cgi.escape(doctype, 1) }
modify_categ_txt = ""
try:
categs_tableheader = ["Categ ID", "Description", "&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;"]
categs_tablebody = []
num_categs = len(doctype_categories)
for i in range(0, num_categs):
this_categname = doctype_categories[i][0]
this_categdescr = doctype_categories[i][1]
this_categscore = doctype_categories[i][2]
t_row = ["""&nbsp;&nbsp;%s""" % cgi.escape(this_categname, 1),
"""&nbsp;&nbsp;%s""" % cgi.escape(this_categdescr, 1)]
## up arrow:
if i != 0:
- t_row += ["""<a href="%(websubadmin_url)s/%(performaction)s?doctype=%(doctype)s&categid=%(categid)s&"""\
+ t_row += ["""<a href="%(adminurl)s/%(performaction)s?doctype=%(doctype)s&categid=%(categid)s&"""\
"""movecategup=1">"""\
- """<img border="0" src="%(weburl)s/img/smallup.gif" title="Move Category Up" /></a>""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ """<img border="0" src="%(siteurl)s/img/smallup.gif" title="Move Category Up" /></a>""" \
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'categid' : cgi.escape(str(this_categname), 1),
}
]
else:
## this is the first category - don't provide an arrow to move it up
t_row += ["&nbsp;"]
## down arrow:
if i != num_categs - 1:
- t_row += ["""<a href="%(websubadmin_url)s/%(performaction)s?doctype=%(doctype)s&categid=%(categid)s&"""\
+ t_row += ["""<a href="%(adminurl)s/%(performaction)s?doctype=%(doctype)s&categid=%(categid)s&"""\
"""movecategdown=1">"""\
- """<img border="0" src="%(weburl)s/img/smalldown.gif" title="Move Category Down" /></a>""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ """<img border="0" src="%(siteurl)s/img/smalldown.gif" title="Move Category Down" /></a>""" \
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'categid' : cgi.escape(str(this_categname), 1),
}
]
else:
## this is the first function - don't provide an arrow to move it up
t_row += ["&nbsp;"]
## 'jump-out' arrow:
if jumpcategout in ("", None):
## provide "move from" arrows for all categories:
if num_categs > 1:
- t_row += ["""<a href="%(websubadmin_url)s/%(performaction)s?doctype=%(doctype)s&jumpcategout=%(categid)s">"""\
- """<img border="0" src="%(weburl)s/img/move_from.gif" title="Move category [%(categid)s] """\
+ t_row += ["""<a href="%(adminurl)s/%(performaction)s?doctype=%(doctype)s&jumpcategout=%(categid)s">"""\
+ """<img border="0" src="%(siteurl)s/img/move_from.gif" title="Move category [%(categid)s] """\
"""from score %(categscore)s" /></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'categid' : cgi.escape(str(this_categname), 1),
'categscore' : cgi.escape(str(this_categscore), 1),
}
]
else:
t_row += ["&nbsp;"]
else:
## there is a value for "jumpcategout", so a "moveto" button must be provided
if num_categs > 1:
## is this the categ that will be moved?
if jumpcategout == this_categname:
## yes it is - no "move-to" arrow here
t_row += ["&nbsp;"]
else:
## no it isn't - "move-to" arrow here
- t_row += ["""<a href="%(websubadmin_url)s/%(performaction)s?doctype=%(doctype)s"""\
+ t_row += ["""<a href="%(adminurl)s/%(performaction)s?doctype=%(doctype)s"""\
"""&jumpcategout=%(jumpcategout)s&jumpcategin=%(categid)s">"""\
- """<img border="0" src="%(weburl)s/img/move_to.gif" title="Move category"""\
+ """<img border="0" src="%(siteurl)s/img/move_to.gif" title="Move category"""\
""" [%(jumpcategout)s] to this location" /></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'categid' : cgi.escape(str(this_categname), 1),
'jumpcategout' : cgi.escape(str(jumpcategout), 1),
}
]
else:
## there is only 1 category - cannot perform a "move"
t_row += ["&nbsp;"]
## 'edit' button:
- t_row += ["""<form class="hyperlinkform" method="post" action="%(websubadmin_url)s/%(performaction)s">""" \
+ t_row += ["""<form class="hyperlinkform" method="post" action="%(adminurl)s/%(performaction)s">""" \
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type""" \
"""="hidden" />""" \
"""<input class="hyperlinkformHiddenInput" name="categid" value="%(category)s" type""" \
"""="hidden" />""" \
"""<input type="submit" name="doctypecategoryedit" value="edit" """\
"""class="hyperlinkformSubmitButton" />""" \
"""</form>""" % { 'doctype' : cgi.escape(doctype, 1),
'category' : cgi.escape(str(this_categname), 1),
'performaction' : cgi.escape(perform_act, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
}
]
## 'delete' button:
- t_row += ["""<form class="hyperlinkform" method="post" action="%(websubadmin_url)s/%(performaction)s">""" \
+ t_row += ["""<form class="hyperlinkform" method="post" action="%(adminurl)s/%(performaction)s">""" \
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type""" \
"""="hidden" />""" \
"""<input class="hyperlinkformHiddenInput" name="categid" value="%(category)s" type""" \
"""="hidden" />""" \
"""<input type="submit" name="doctypecategorydelete" value="delete" """\
"""class="hyperlinkformSubmitButton" />""" \
"""</form>""" % { 'doctype' : cgi.escape(doctype, 1),
'category' : cgi.escape(str(this_categname), 1),
'performaction' : cgi.escape(perform_act, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
}
]
## 'jumping-out from' arrow:
if jumpcategout not in ("", None):
if jumpcategout == this_categname and num_categs > 1:
- t_row += ["""<img border="0" src="%(weburl)s/img/move_from.gif" title="Moving category """\
+ t_row += ["""<img border="0" src="%(siteurl)s/img/move_from.gif" title="Moving category """\
"""[%(categid)s] from this location (score %(categscore)s)" />"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'categid' : cgi.escape(str(this_categname), 1),
'categscore' : cgi.escape(str(this_categscore), 1),
}
]
else:
t_row += ["&nbsp;"]
else:
t_row += ["&nbsp;"]
## finally, append the newly created row to the tbody list:
categs_tablebody.append(t_row)
txt += create_html_table_from_tuple(tableheader=categs_tableheader, tablebody=categs_tablebody)
except IndexError:
## categs tuple was not in expected format ((sname, lname), (sname, lname)[, ...])
txt += """<span class="info">Unable to correctly display categories</span>"""
txt += """</td>
</tr>
<tr>
<td><br />
</td>
</tr>"""
## form to add a new category:
txt += """
<tr>
<td>
<span class="adminlabel">Add a new Category:</span><br />
- <form method="post" action="%(websubadmin_url)s/%(formaction)s">
+ <form method="post" action="%(adminurl)s/%(formaction)s">
<input name="doctype" type="hidden" value="%(doctype)s" />
<small style="color: navy;">ID:&nbsp;</small>
<input style="margin: 5px 10px 5px 10px;" name="categid" type="text" size="10" />&nbsp;
<small style="color: navy;">Description:&nbsp;</small>
<input style="margin: 5px 10px 5px 10px;" name="categdescr" type="text" size="25" />&nbsp;
<input name="doctypecategoryadd" class="adminbutton" type="submit" value="Add Category" />
</form>
</td>
</tr>
</tbody>
</table>""" % { 'formaction' : cgi.escape(perform_act, 1),
'doctype' : cgi.escape(doctype, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1)
}
return txt
def _tmpl_configure_doctype_overview_create_submissions_view(self,
doctype="",
doctype_submissions="",
add_actions_list=None,
perform_act="doctypeconfigure"
):
"""Display the details of the submissions for a given document type"""
## sanity checking for submissions list:
if type(doctype_submissions) not in (list, tuple):
doctype_submissions = ()
if type(add_actions_list) not in (list, tuple):
add_actions_list = ()
txt = """
<table class="admin_wvar" width="100%%">
<thead>
<tr>
<th class="adminheaderleft">
Submissions of Document Type %(doctype_id)s:
</th>
</tr>
</thead>
<tbody>
<tr>
<td><br />""" % { 'doctype_id' : cgi.escape(doctype, 1) }
try:
submissions_tableheader = ["Action", "Creation<br />Date", "Modification<br />Date", "Displayed?", "No.<br />Pages", \
"Button<br />Order", "Status<br />Text", "Level", "Score", "Stpage", "End<br />Text", \
"View Submission<br />Interface", "View Submission<br />Functions", \
"Edit Submission<br />Details", "Delete<br />Submission"]
submissions_tablebody = []
for subm in doctype_submissions:
submissions_tablebody.append( ("%s" % (cgi.escape(str(subm[2]), 1),),
"%s" % (cgi.escape(str(subm[5]), 1),),
"%s" % (cgi.escape(str(subm[6]), 1),),
"%s" % (cgi.escape(str(subm[3]), 1),),
"%s" % (cgi.escape(str(subm[4]), 1),),
"%s" % (cgi.escape(str(subm[7]), 1),),
"%s" % (cgi.escape(str(subm[8]), 1),),
"%s" % (cgi.escape(str(subm[9]), 1),),
"%s" % (cgi.escape(str(subm[10]), 1),),
"%s" % (cgi.escape(str(subm[11]), 1),),
"%s" % (cgi.escape(str(subm[12]), 1),),
- """<form class="hyperlinkform" method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionpages">""" \
+ """<form class="hyperlinkform" method="get" action="%(adminurl)s/doctypeconfiguresubmissionpages">""" \
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type""" \
"""="hidden" />""" \
"""<input class="hyperlinkformHiddenInput" name="action" value="%(action)s" type""" \
"""="hidden" />""" \
"""<input type="submit" name="viewSubmissionInterface" value="view interface" """\
"""class="hyperlinkformSubmitButton" />""" \
"""</form>""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(str(subm[2]), 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1)
},
- """<form class="hyperlinkform" method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctions">""" \
+ """<form class="hyperlinkform" method="get" action="%(adminurl)s/doctypeconfiguresubmissionfunctions">""" \
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type""" \
"""="hidden" />""" \
"""<input class="hyperlinkformHiddenInput" name="action" value="%(action)s" type""" \
"""="hidden" />""" \
"""<input type="submit" name="viewSubmissionFunctions" value="view functions" """\
"""class="hyperlinkformSubmitButton" />""" \
"""</form>""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(str(subm[2]), 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1)
},
- """<form class="hyperlinkform" method="get" action="%(websubadmin_url)s/%(formaction)s">""" \
+ """<form class="hyperlinkform" method="get" action="%(adminurl)s/%(formaction)s">""" \
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type""" \
"""="hidden" />""" \
"""<input class="hyperlinkformHiddenInput" name="action" value="%(action)s" type""" \
"""="hidden" />""" \
"""<input type="submit" name="doctypesubmissionedit" value="edit submission" """\
"""class="hyperlinkformSubmitButton" />""" \
"""</form>""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(str(subm[2]), 1),
'formaction' : cgi.escape(perform_act, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1)
},
- """<form class="hyperlinkform" method="get" action="%(websubadmin_url)s/%(formaction)s">""" \
+ """<form class="hyperlinkform" method="get" action="%(adminurl)s/%(formaction)s">""" \
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type""" \
"""="hidden" />""" \
"""<input class="hyperlinkformHiddenInput" name="action" value="%(action)s" type""" \
"""="hidden" />""" \
"""<input type="submit" name="doctypesubmissiondelete" value="delete submission" """\
"""class="hyperlinkformSubmitButton" />""" \
"""</form>""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(str(subm[2]), 1),
'formaction' : cgi.escape(perform_act, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1)
}
) )
txt += create_html_table_from_tuple(tableheader=submissions_tableheader, tablebody=submissions_tablebody)
except IndexError:
## submissions tuple was not in expected format
txt += """<span class="info">Unable to correctly display details of submissions</span>"""
txt += """</td>
</tr>"""
## now, display a list of actions that can be added
txt += """
<tr>
<td>
<span class="adminlabel">Add a new Submission:</span><br />"""
if len(add_actions_list) > 0:
txt += """
- <form method="get" action="%(websubadmin_url)s/%(performaction)s">
+ <form method="get" action="%(adminurl)s/%(performaction)s">
<input type="hidden" name="doctype" value="%(doctype)s" />
%(submissions_list)s
<input name="doctypesubmissionadd" class="adminbutton" type="submit" value="Add Submission" />
</form>""" \
% { 'doctype' : cgi.escape(doctype, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'submissions_list' : create_html_select_list(select_name="action", option_list=add_actions_list,
css_style="margin: 5px 10px 5px 10px;")
}
else:
txt += """
<br />
<span class="info">No Available Actions to Add</span>"""
txt += """
</td>
</tr>
</tbody>
</table>"""
return txt
def _tmpl_configure_doctype_overview_create_referees_view(self,
doctype="",
doctype_referees="",
perform_act="doctypeconfigure"
):
"""Display the details of the referees of the various categories of a given document type"""
## sanity checking for doctype_referees:
if type(doctype_referees) is not dict:
doctype_referees = {}
txt = """
<table class="admin_wvar" width="100%%">
<thead>
<tr>
<th class="adminheaderleft">
Manage Referees for Document Type %(doctype_id)s:
</th>
</tr>
</thead>
<tbody>
<tr>
<td><br />""" % { 'doctype_id' : cgi.escape(doctype, 1) }
try:
referees_tableheader = ["Referee"]
referees_tablebody = []
referee_roles = doctype_referees.keys()
referee_roles.sort()
for role in referee_roles:
if doctype_referees[role][0] == "*":
referees_tablebody.append( ("""<span style="color: navy;">%s</span>""" % (cgi.escape(doctype_referees[role][1], 1)),
"&nbsp;") )
else:
referees_tablebody.append( ("""<span style="color: navy;">%s (%s)</span>""" % (cgi.escape(doctype_referees[role][0], 1), \
cgi.escape(doctype_referees[role][1], 1)),
"&nbsp;") )
for referee in doctype_referees[role][2]:
referees_tablebody.append( ("""<small>%s</small>""" % (cgi.escape(referee[1], 1),),))
txt += create_html_table_from_tuple(tableheader=referees_tableheader, tablebody=referees_tablebody)
except IndexError:
## referees dictionary was not in expected format
txt += """<span class="info">Unable to correctly display details of referees</span>"""
txt += """
</td>
</tr>
<tr>
<td>
- <form method="post" action="%(websubadmin_url)s/referees.py">
+ <form method="post" action="%(adminurl)s/referees.py">
<input type="hidden" name="doctype" value="%(doctype_id)s" />
<input name="managerefereesdoctype" class="adminbutton" type="submit" value="Manage Referees" />
</form>
</td>
</tr>
</tbody>
</table>""" % { 'doctype_id' : cgi.escape(doctype, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_OLDWEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL_OLD, 1)
}
return txt
def tmpl_configure_doctype_overview(self, doctype="", doctypename="", doctypedescr="", doctype_cdate="", doctype_mdate="", \
doctype_categories="", jumpcategout="", doctype_submissions="", \
doctype_referees="", user_msg="", add_actions_list=None, perform_act="doctypeconfigure"):
"""TODO : DOCSTRING"""
## sanity checking:
if type(doctype_categories) not in (list, tuple):
doctype_categories = ()
if type(doctype_submissions) not in (list, tuple):
doctype_submissions = ()
if type(add_actions_list) not in (list, tuple):
add_actions_list = ()
output = ""
body_content = ""
output += self._create_user_message_string(user_msg)
## table containing document type details:
body_content += """<br />%s""" % (self._tmpl_configire_doctype_overview_create_doctype_details(doctype=doctype,
doctypename=doctypename,
doctypedescr=doctypedescr,
doctype_cdate=doctype_cdate,
doctype_mdate=doctype_mdate,
perform_act=perform_act
)
)
body_content += """<hr style="width: 80%%;" />"""
## this document type's submissions:
body_content += """<br />%s""" % (self._tmpl_configure_doctype_overview_create_submissions_view(doctype=doctype,
doctype_submissions=doctype_submissions,
add_actions_list=add_actions_list,
perform_act=perform_act
)
)
body_content += """<hr style="width: 80%%;" />"""
## table containing document type's categories:
body_content += """<br />%s""" % (self._tmpl_configure_doctype_overview_create_categories_view(doctype=doctype,
doctype_categories=doctype_categories,
jumpcategout=jumpcategout,
perform_act=perform_act
)
)
body_content += """<hr style="width: 80%%;" />"""
## button for allocation of referees:
body_content += """<br />%s""" % (self._tmpl_configure_doctype_overview_create_referees_view(doctype=doctype,
doctype_referees=doctype_referees,
perform_act=perform_act
)
)
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Configure Document Type:", datalist=[body_content])
return output
def tmpl_display_edit_category_form(self, doctype, categid, categdescr, user_msg="", perform_act="doctypeconfigure"):
output = ""
body_content = "<div>"
output += self._create_user_message_string(user_msg)
body_content += """
- <form method="get" action="%(websubadmin_url)s/%(performaction)s">
+ <form method="get" action="%(adminurl)s/%(performaction)s">
<table width="90%%">
<tr>
<td width="20%%"><span class="adminlabel">Category Name:</span></td>
<td width="80%%"><span class="info">%(categid)s</span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Category Description:</span></td>
<td width="80%%"><input type="text" size="60" name="categdescr" value="%(categdescr)s" /></td>
</tr>
<tr>
<td width="20%%">&nbsp;&nbsp;</td>
<td width="80%%">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="categid" value="%(categid)s" />
<input name="doctypecategoryeditcommit" class="adminbutton" type="submit" value="Save Details" />
&nbsp;
<input name="doctypecategoryeditcancel" class="adminbutton" type="submit" value="Cancel" />
</td>
</tr>
</table>
</form>
""" % {
'categid' : cgi.escape(categid, 1),
'doctype' : cgi.escape(doctype, 1),
'categdescr' : cgi.escape(categdescr, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1)
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Edit Details of '%(categid)s' Category of '%(doctype)s' Document Type:" \
% { 'doctype' : cgi.escape(doctype, 1), 'categid' : cgi.escape(categid, 1) },
datalist=[body_content])
return output
def tmpl_configuredoctype_add_submissionfunction(self,
doctype,
action,
cursubmissionfunctions,
allWSfunctions,
addfunctionname="",
addfunctionstep="",
addfunctionscore="",
perform_act="doctypeconfiguresubmissionfunctions",
user_msg=""):
## sanity checking:
if type(cursubmissionfunctions) not in (list, tuple):
submissionfunctions = ()
if type(allWSfunctions) not in (list, tuple):
allWSfunctions = ()
output = ""
output += self._create_user_message_string(user_msg)
## display a form to add a function to the submission:
body_content = """
<br />
<table class="admin_wvar" width="55%%">
<thead>
<tr>
<th class="adminheaderleft" colspan="2">
Add function:
</th>
</tr>
</thead>
<tbody>
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">&nbsp;
- <form method="get" action="%(websubadmin_url)s/%(performaction)s">
+ <form method="get" action="%(adminurl)s/%(performaction)s">
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
</td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Function Name:</span></td>
<td width="80%%"><span class="info">%(allWSfunctions)s</span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Step:</span></td>
<td width="80%%"><span class="info"><input name="addfunctionstep" type="text" value="%(step)s" size="5" /></span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Score:</span></td>
<td width="80%%"><span class="info"><input name="addfunctionscore" type="text" value="%(score)s" size="5" /></span></td>
</tr>
<tr>
<td colspan="2">
<table>
<tr>
<td>
<input name="configuresubmissionaddfunctioncommit" class="adminbutton" type="submit" value="Save Details" />
</form>
</td>
<td>
<br />
- <form method="post" action="%(websubadmin_url)s/%(performaction)s">
+ <form method="post" action="%(adminurl)s/%(performaction)s">
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="configuresubmissionaddfunctioncancel" class="adminbutton" type="submit" value="Cancel" />
</form>
</td>
</tr>
</table>
</td>
</tr>
</table>""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
'step' : cgi.escape(addfunctionstep, 1),
'score' : cgi.escape(addfunctionscore, 1),
'allWSfunctions' : create_html_select_list(select_name="addfunctionname",
option_list=allWSfunctions,
selected_values=addfunctionname,
default_opt=("", "Select function to add:"))
}
## build a table of the functions currently associated with the submission:
body_content += """<hr />\n"""
header = ["Function Name", "Step", "Score"]
tbody = []
for functn in cursubmissionfunctions:
thisfunctionname = functn[0]
thisfunctionstep = str(functn[1])
thisfunctionscore = str(functn[2])
## function name:
t_row = ["""&nbsp;&nbsp;%s""" % (cgi.escape(thisfunctionname, 1),)]
## function step:
t_row += ["""%s""" % (cgi.escape(thisfunctionstep, 1),) ]
## function score:
t_row += ["""%s""" % (cgi.escape(thisfunctionscore, 1),) ]
## finally, append the newly created row to the tbody list:
tbody.append(t_row)
body_content += """
<table class="admin_wvar" width="55%%">
<thead>
<tr>
<th class="adminheaderleft">
Current submission functions configuration:
</th>
</tr>
</thead>
<tbody>
<tr>
<td width="100%%">&nbsp;</td>
</tr>
<tr>
<td width="100%%">"""
body_content += create_html_table_from_tuple(tableheader=header, tablebody=tbody)
body_content += """
</td>
</tr>
</table>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="""Add a function to the [%s] submission of the [%s] document type""" \
% (cgi.escape(action, 1), cgi.escape(doctype, 1)), datalist=[body_content])
return output
def tmpl_configuredoctype_display_submissionfunctions(self,
doctype,
action,
submissionfunctions,
movefromfunctionname="",
movefromfunctionstep="",
movefromfunctionscore="",
perform_act="doctypeconfiguresubmissionfunctions",
user_msg=""):
"""Create the page body used for displaying all Websubmit functions.
@param functions: A tuple of tuples containing the function name, and the function
description (function, description).
@param user_msg: Any message to be displayed on screen, such as a status report for the last task, etc.
return: HTML page body.
"""
## sanity checking:
if type(submissionfunctions) not in (list, tuple):
submissionfunctions = ()
output = ""
output += self._create_user_message_string(user_msg)
body_content = """<div><br />\n"""
header = ["Function Name", "&nbsp;", "&nbsp;", "&nbsp;", "Step", "Score", "View Parameters", "Delete", "&nbsp;"]
tbody = []
num_functions = len(submissionfunctions)
for i in range(0, num_functions):
thisfunctionname = submissionfunctions[i][0]
thisfunctionstep = str(submissionfunctions[i][1])
thisfunctionscore = str(submissionfunctions[i][2])
t_row = ["""&nbsp;&nbsp;%s""" % (cgi.escape(thisfunctionname, 1),)]
## up arrow:
if i != 0:
- t_row += ["""<a href="%(websubadmin_url)s/%(performaction)s?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/%(performaction)s?doctype=%(doctype)s&action=%(action)s&"""\
"""moveupfunctionname=%(func)s&moveupfunctionstep=%(step)s&moveupfunctionscore=%(score)s">"""\
- """<img border="0" src="%(weburl)s/img/smallup.gif" title="Move Function Up" /></a>""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ """<img border="0" src="%(siteurl)s/img/smallup.gif" title="Move Function Up" /></a>""" \
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'func' : cgi.escape(thisfunctionname, 1),
'step' : cgi.escape(thisfunctionstep, 1),
'score' : cgi.escape(thisfunctionscore, 1)
}
]
else:
## this is the first function - don't provide an arrow to move it up
t_row += ["&nbsp;"]
## down arrow:
if num_functions > 1 and i < num_functions - 1:
- t_row += ["""<a href="%(websubadmin_url)s/%(performaction)s?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/%(performaction)s?doctype=%(doctype)s&action=%(action)s&"""\
"""movedownfunctionname=%(func)s&movedownfunctionstep=%(step)s&movedownfunctionscore=%(score)s">"""\
- """<img border="0" src="%(weburl)s/img/smalldown.gif" title="Move Function Down" /></a>""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ """<img border="0" src="%(siteurl)s/img/smalldown.gif" title="Move Function Down" /></a>""" \
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'func' : cgi.escape(thisfunctionname, 1),
'step' : cgi.escape(thisfunctionstep, 1),
'score' : cgi.escape(thisfunctionscore, 1)
}
]
else:
t_row += ["&nbsp;"]
if movefromfunctionname in ("", None):
## provide "move from" arrows for all functions
if num_functions > 1:
- t_row += ["""<a href="%(websubadmin_url)s/%(performaction)s?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/%(performaction)s?doctype=%(doctype)s&action=%(action)s&"""\
"""movefromfunctionname=%(func)s&movefromfunctionstep=%(step)s&movefromfunctionscore=%(score)s">"""\
- """<img border="0" src="%(weburl)s/img/move_from.gif" title="Move %(func)s (step %(step)s, score %(score)s)"""\
+ """<img border="0" src="%(siteurl)s/img/move_from.gif" title="Move %(func)s (step %(step)s, score %(score)s)"""\
""" from this location" /></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'func' : cgi.escape(thisfunctionname, 1),
'step' : cgi.escape(thisfunctionstep, 1),
'score' : cgi.escape(thisfunctionscore, 1)
}
]
else:
t_row += ["&nbsp;"]
else:
## there is a value for "movefromfunctionname", so a "moveto" button must be provided
if num_functions > 1:
## is this the function that will be moved?
if movefromfunctionname == thisfunctionname and \
movefromfunctionstep == thisfunctionstep and \
movefromfunctionscore == thisfunctionscore:
## yes it is - no "move-to" arrow here
t_row += ["&nbsp;"]
else:
## no it isn't - "move-to" arrow here
- t_row += ["""<a href="%(websubadmin_url)s/%(performaction)s?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/%(performaction)s?doctype=%(doctype)s&action=%(action)s&"""\
"""movefromfunctionname=%(fromfunc)s&movefromfunctionstep=%(fromstep)s&movefromfunctionscore=%(fromscore)s&"""\
"""movetofunctionname=%(tofunc)s&movetofunctionstep=%(tostep)s&movetofunctionscore=%(toscore)s">"""\
- """<img border="0" src="%(weburl)s/img/move_to.gif" title="Move %(fromfunc)s (step %(fromstep)s, score %(fromscore)s)"""\
+ """<img border="0" src="%(siteurl)s/img/move_to.gif" title="Move %(fromfunc)s (step %(fromstep)s, score %(fromscore)s)"""\
""" to this location (step %(tostep)s, score %(toscore)s)" /></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'fromfunc' : cgi.escape(movefromfunctionname, 1),
'fromstep' : cgi.escape(movefromfunctionstep, 1),
'fromscore' : cgi.escape(movefromfunctionscore, 1),
'tofunc' : cgi.escape(thisfunctionname, 1),
'tostep' : cgi.escape(thisfunctionstep, 1),
'toscore' : cgi.escape(thisfunctionscore, 1)
}
]
else:
## there is only 1 function - cannot perform a "move"!
t_row += ["&nbsp;"]
## function step:
t_row += ["""%s""" % (cgi.escape(thisfunctionstep, 1),) ]
## function score:
t_row += ["""%s""" % (cgi.escape(thisfunctionscore, 1),) ]
## "view parameters" link:
- t_row += ["""<form class="hyperlinkform" method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctionsparameters">"""\
+ t_row += ["""<form class="hyperlinkform" method="get" action="%(adminurl)s/doctypeconfiguresubmissionfunctionsparameters">"""\
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="action" value="%(action)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="functionname" value="%(thisfunctionname)s" type="hidden" />"""\
"""<input type="submit" name="viewfunctionparameters" value="view parameters" class="hyperlinkformSubmitButton" />"""\
"""</form>\n"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'thisfunctionname' : cgi.escape(thisfunctionname, 1)
} ]
## "delete function" link:
- t_row += ["""<form class="hyperlinkform" method="get" action="%(websubadmin_url)s/%(performaction)s">"""\
+ t_row += ["""<form class="hyperlinkform" method="get" action="%(adminurl)s/%(performaction)s">"""\
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="action" value="%(action)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="deletefunctionname" value="%(thisfunctionname)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="deletefunctionstep" value="%(step)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="deletefunctionscore" value="%(score)s" type="hidden" />"""\
"""<input type="submit" name="deletefunction" value="delete" class="hyperlinkformSubmitButton" />"""\
"""</form>\n"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'thisfunctionname' : cgi.escape(thisfunctionname, 1),
'step' : cgi.escape(thisfunctionstep, 1),
'score' : cgi.escape(thisfunctionscore, 1)
} ]
## final column containing "jumping-out from" image when moving a function:
if movefromfunctionname not in ("", None):
if movefromfunctionname == thisfunctionname and \
movefromfunctionstep == thisfunctionstep and \
movefromfunctionscore == thisfunctionscore and \
num_functions > 1:
- t_row += ["""<img border="0" src="%(weburl)s/img/move_from.gif" title="Moving %(fromfunc)s (step %(fromstep)s, """\
+ t_row += ["""<img border="0" src="%(siteurl)s/img/move_from.gif" title="Moving %(fromfunc)s (step %(fromstep)s, """\
"""score %(fromscore)s) from this location" />"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'fromfunc' : cgi.escape(movefromfunctionname, 1),
'fromstep' : cgi.escape(movefromfunctionstep, 1),
'fromscore' : cgi.escape(movefromfunctionscore, 1)
}
]
else:
t_row += ["&nbsp;"]
else:
t_row += ["&nbsp;"]
## finally, append the newly created row to the tbody list:
tbody.append(t_row)
body_content += create_html_table_from_tuple(tableheader=header, tablebody=tbody)
body_content += """</div>"""
## buttons for "add a function" and "finished":
body_content += """
<table>
<tr>
<td>
<br />
- <form method="post" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctions">
+ <form method="post" action="%(adminurl)s/doctypeconfiguresubmissionfunctions">
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="configuresubmissionaddfunction" class="adminbutton" type="submit" value="Add a Function" />
</form>
</td>
<td>
<br />
- <form method="post" action="%(websubadmin_url)s/doctypeconfigure">
+ <form method="post" action="%(adminurl)s/doctypeconfigure">
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="funishedviewsubmissionfunctions" class="adminbutton" type="submit" value="Finished" />
</form>
</td>
</tr>
</table>""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1)
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1)
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="""Functions of the "%s" Submission of the "%s" Document Type:""" \
% (cgi.escape(action, 1), cgi.escape(doctype, 1)), datalist=[body_content])
return output
def _tmpl_configuredoctype_submissionfield_display_changeable_fields(self,
fieldtext="",
fieldlevel="",
fieldshortdesc="",
fieldcheck="",
allchecks=""):
"""Used when displaying the details of a submission field that is to be edited or inserted onto a
submission page.
This function creates the form elements for the values that can be edited by the user, such as the field's
label, short description, etc. (Examples of details of the submission field that could not be edited by the
user and are therefore not included in this function, are the creation-date/modification-date of the field,
etc.
@param fieldtext: (string) the label used for a field
@param fieldlevel: (char) 'M' or 'O' - whether a field is Mandatory or Optional
@param fieldshortdesc: (string) the short description of a field
@param fieldcheck: (string) the JavaScript checking function applied to a field
@param allchecks: (tuple of strings) the names of all WebSubmit JavaScript checks
@return: (string) a section of a form
"""
## sanity checking
if type(allchecks) not in (tuple, list):
allchecks = []
## make form-section
txt = """
<tr>
<td width="20%%"><span class="adminlabel">Field Label:</span></td>
<td width="80%%"><br /><textarea name="fieldtext" rows="5" cols="50">%(fieldtext)s</textarea><br /><br /></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Field Level:</span></td>
<td width="80%%"><span>%(fieldlevel)s</span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Field Short Description:</span></td>
<td width="80%%"><br /><input type="text" size="35" name="fieldshortdesc" value="%(fieldshortdesc)s" /><br /><br /></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">JavaScript Check:</span></td>
<td width="80%%"><span>%(fieldcheck)s</span></td>
</tr>""" % { 'fieldtext' : cgi.escape(fieldtext, 1),
'fieldlevel' : create_html_select_list(select_name="fieldlevel",
option_list=(("M", "Mandatory"), ("O", "Optional")),
selected_values=fieldlevel
),
'fieldshortdesc' : cgi.escape(fieldshortdesc, 1),
'fieldcheck' : create_html_select_list(select_name="fieldcheck",
option_list=allchecks,
selected_values=fieldcheck,
default_opt=("", "--NO CHECK--")
)
}
return txt
def tmpl_configuredoctype_add_submissionfield(self,
doctype="",
action="",
pagenum="",
fieldname="",
fieldtext="",
fieldlevel="",
fieldshortdesc="",
fieldcheck="",
allchecks="",
allelements="",
user_msg="",
perform_act="doctypeconfiguresubmissionpageelements"):
## sanity checking
if type(allelements) not in (tuple, list):
allelements = []
## begin template:
output = ""
body_content = ""
output += self._create_user_message_string(user_msg)
body_content += """
<table class="admin_wvar" width="95%%">
<thead>
<tr>
<th class="adminheaderleft" colspan="2">
Add a field to page %(pagenum)s of submission %(submission)s
</th>
</tr>
</thead>
<tbody>
<tr>
<td width="20%%">&nbsp;</td>
- <td width="80%%">&nbsp;<form method="get" action="%(websubadmin_url)s/%(performaction)s"></td>
+ <td width="80%%">&nbsp;<form method="get" action="%(adminurl)s/%(performaction)s"></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Page Number:</span></td>
<td width="80%%"><span class="info">%(pagenum)s</span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Field Name:</span></td>
<td width="80%%">%(fieldname)s</td>
- </tr>""" % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ </tr>""" % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'fieldname' : create_html_select_list(select_name="fieldname",
option_list=allelements,
selected_values=fieldname,
default_opt=("", "Select a Field:")
),
'pagenum' : cgi.escape(pagenum, 1),
'submission' : cgi.escape("%s%s" % (action, doctype), 1),
'performaction' : cgi.escape(perform_act, 1)
}
body_content += self._tmpl_configuredoctype_submissionfield_display_changeable_fields(fieldtext=fieldtext,
fieldlevel=fieldlevel,
fieldshortdesc=fieldshortdesc,
fieldcheck=fieldcheck,
allchecks=allchecks)
body_content += """
<tr>
<td colspan="2">
<table>
<tr>
<td>
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="pagenum" type="hidden" value="%(pagenum)s" />
<input name="addfieldcommit" class="adminbutton" type="submit" value="Add Field" />
</form>
</td>
<td>
<br />
- <form method="post" action="%(websubadmin_url)s/%(performaction)s">
+ <form method="post" action="%(adminurl)s/%(performaction)s">
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="pagenum" type="hidden" value="%(pagenum)s" />
<input name="canceladdsubmissionfield" class="adminbutton" type="submit" value="Cancel" />
</form>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
- </table>\n""" % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ </table>\n""" % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1)
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Field Details:", datalist=[body_content])
return output
def tmpl_configuredoctype_edit_submissionfield(self,
doctype="",
action="",
pagenum="",
fieldnum="",
fieldname="",
fieldtext="",
fieldlevel="",
fieldshortdesc="",
fieldcheck="",
cd="",
md="",
allchecks="",
user_msg="",
perform_act="doctypeconfiguresubmissionpageelements"):
## begin template:
output = ""
body_content = ""
output += self._create_user_message_string(user_msg)
body_content += """
<table class="admin_wvar" width="95%%">
<thead>
<tr>
<th class="adminheaderleft" colspan="2">
Details of the %(fieldname)s field as it appears at position %(fieldnum)s on Page %(pagenum)s of the %(submission)s Submission:
</th>
</tr>
</thead>
<tbody>
<tr>
<td width="20%%">&nbsp;</td>
- <td width="80%%">&nbsp;<form method="get" action="%(websubadmin_url)s/%(performaction)s"></td>
+ <td width="80%%">&nbsp;<form method="get" action="%(adminurl)s/%(performaction)s"></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Page Number:</span></td>
<td width="80%%"><span class="info">%(pagenum)s</span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Field Number:</span></td>
<td width="80%%"><span class="info">%(fieldnum)s</span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Field Name:</span></td>
<td width="80%%"><span class="info">%(fieldname)s</span></td>
- </tr>""" % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ </tr>""" % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'pagenum' : cgi.escape(pagenum, 1),
'fieldnum' : cgi.escape(fieldnum, 1),
'fieldname' : cgi.escape(fieldname, 1),
'submission' : cgi.escape("%s%s" % (action, doctype), 1),
'performaction' : cgi.escape(perform_act, 1)
}
## field creation date:
if cd not in ("", None):
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Creation Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(cd), 1),)
## field last-modified date:
if md not in ("", None):
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Last Modification Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(md), 1), )
body_content += self._tmpl_configuredoctype_submissionfield_display_changeable_fields(fieldtext=fieldtext,
fieldlevel=fieldlevel,
fieldshortdesc=fieldshortdesc,
fieldcheck=fieldcheck,
allchecks=allchecks)
body_content += """
<tr>
<td colspan="2">
<table>
<tr>
<td>
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="pagenum" type="hidden" value="%(pagenum)s" />
<input name="editfieldposn" type="hidden" value="%(fieldnum)s" />
<input name="editfieldposncommit" class="adminbutton" type="submit" value="Save Changes" />
</form>
</td>
<td>
<br />
- <form method="post" action="%(websubadmin_url)s/%(performaction)s">
+ <form method="post" action="%(adminurl)s/%(performaction)s">
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="pagenum" type="hidden" value="%(pagenum)s" />
<input name="canceleditsubmissionfield" class="adminbutton" type="submit" value="Cancel" />
</form>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>\n""" % { 'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'fieldnum' : cgi.escape(fieldnum, 1),
- 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'performaction' : cgi.escape(perform_act, 1)
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Field Details:", datalist=[body_content])
return output
def tmpl_configuredoctype_display_submissionpage_preview(self, doctype, action, pagenum, fields, user_msg=""):
"""Create a page displaying a simple preview of a submission page
@param doctype: (string) the unique ID of a document type
@param action: (string) the unique ID of an action
@param pagenum: (string) the number of the page that is to be previewed
@param fields: a tuple of tuples, whereby each tuple contains the details of a field on the submission page:
(fieldname, check-name, field-type, size, rows, cols, field-description)
@param user_msg: a tuple or string, containing any message(s) to be displayed to the user
@return: a string, which makes up the page body
"""
## Sanity Checking of elements:
if type(fields) not in (list, tuple):
fields = ()
try:
if type(fields[0]) not in (tuple, list):
fields = ()
except IndexError:
pass
## begin template:
output = ""
body_content = ""
output += self._create_user_message_string(user_msg)
## hyperlink back to page details:
body_content += """
<div style="text-align: center;">
- <a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&pagenum=%(pagenum)s">
+ <a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&pagenum=%(pagenum)s">
Return to details of page [%(pagenum)s] of submission [%(submission)s]</a>
</div>
- <hr />""" % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ <hr />""" % { 'adminurl' : WEBSUBMITADMINURL,
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'submission' : cgi.escape("%s%s" % (action, doctype), 1)
}
body_content += """<div><br />
- <form name="dummyeldisplay" action="%(websubadmin_url)s">
+ <form name="dummyeldisplay" action="%(adminurl)s">
<table class="admin_wvar" align="center">
<thead>
<tr>
<th class="adminheaderleft" colspan="1">
Page Preview:
</th>
</tr>
</thead>
<tbody>
<tr bgcolor="#f1f1f1">
<td>
<br />&nbsp;&nbsp;
- """ % {'websubadmin_url' : WEBSUBMITADMIN_WEBURL}
+ """ % {'adminurl' : WEBSUBMITADMINURL}
for field in fields:
body_content += self._element_display_preview_get_element(elname=field[0], eltype=field[3], elsize=field[4],
elrows=field[5], elcols=field[6], elval=field[8],
elfidesc=field[7], ellabel=field[1])
body_content += "\n"
body_content += """&nbsp;&nbsp;<br />
</td>
</tr>
</tbody>
</table>
</form>
</div>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Preview of Page %s of Submission %s:" \
% (pagenum, "%s%s" % (action, doctype)), datalist=[body_content])
return output
def tmpl_configuredoctype_list_submissionelements(self,
doctype,
action,
pagenum,
page_elements,
movefieldfromposn="",
user_msg=""):
## Sanity Checking of elements:
if type(page_elements) not in (list, tuple):
page_elements = ()
try:
if type(page_elements[0]) not in (tuple, list):
page_elements = ()
except IndexError:
pass
try:
int(movefieldfromposn)
except ValueError:
movefieldfromposn = ""
## begin template:
output = ""
body_content = ""
output += self._create_user_message_string(user_msg)
number_elements = len(page_elements)
if number_elements > 0:
body_content += """
<table width="100%%" class="admin_wvar">
<tbody>
<tr>
<td style="text-align: center;">
<br />
- <form method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionpagespreview">
+ <form method="get" action="%(adminurl)s/doctypeconfiguresubmissionpagespreview">
<input type="hidden" name="doctype" value="%(doctype_id)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="pagenum" value="%(pagenum)s" />
<input name="viewsubmissionpagepreview" class="adminbutton" type="submit" value="View Page Preview" />
</form>
</td>
</tr>
- </table>""" % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ </table>""" % { 'adminurl' : WEBSUBMITADMINURL,
'doctype_id' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1)
}
t_header = ["&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;", "Name", "Element Label",
"Level", "Short Descr.", "Check", "Creation Date", "Modification Date", "&nbsp;",
"&nbsp;", "&nbsp;", "&nbsp;"]
t_body = []
for i in range(0, number_elements):
## Field number:
t_row = ["""%s""" % (cgi.escape(page_elements[i][1], 1),) ]
## Move a field from posn - to posn arrows:
if movefieldfromposn in ("", None):
## provide "move from" arrow for all element
if number_elements > 1:
- t_row += ["""<a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&movefieldfromposn=%(fieldnum)s">"""\
- """<img border="0" src="%(weburl)s/img/move_from.gif" title="Move field at position %(fieldnum)s"""\
+ """<img border="0" src="%(siteurl)s/img/move_from.gif" title="Move field at position %(fieldnum)s"""\
""" from this location" /></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'fieldnum' : cgi.escape(page_elements[i][1], 1)
}
]
else:
t_row += ["&nbsp;"]
else:
## there is a value for "movefieldfromposn", so a "moveto" button must be provided
if number_elements > 1:
## is this the field that will be moved?
if movefieldfromposn == page_elements[i][1]:
## yes it is - no "move-to" arrow here
t_row += ["&nbsp;"]
else:
## no it isn't - "move-to" arrow here
- t_row += ["""<a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&movefieldfromposn=%(movefieldfromposn)s&movefieldtoposn=%(fieldnum)s">"""\
- """<img border="0" src="%(weburl)s/img/move_to.gif" title="Move field at position %(movefieldfromposn)s"""\
+ """<img border="0" src="%(siteurl)s/img/move_to.gif" title="Move field at position %(movefieldfromposn)s"""\
""" to this location at position %(fieldnum)s" /></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'fieldnum' : cgi.escape(page_elements[i][1], 1),
'movefieldfromposn' : cgi.escape(movefieldfromposn, 1)
}
]
else:
## there is only 1 field - cannot perform a "move"!
t_row += ["&nbsp;"]
## up arrow:
if i != 0:
- t_row += ["""<a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&movefieldfromposn=%(fieldnum)s&movefieldtoposn=%(previousfield)s">"""\
- """<img border="0" src="%(weburl)s/img/smallup.gif" title="Move Element Up" /></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ """<img border="0" src="%(siteurl)s/img/smallup.gif" title="Move Element Up" /></a>"""\
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'fieldnum' : cgi.escape(page_elements[i][1], 1),
'previousfield' : cgi.escape(str(int(page_elements[i][1])-1), 1)
}
]
else:
## first element - don't provide up arrow:
t_row += ["&nbsp;"]
## down arrow:
if number_elements > 1 and i < number_elements - 1:
- t_row += ["""<a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&movefieldfromposn=%(fieldnum)s&movefieldtoposn=%(nextfield)s">"""\
- """<img border="0" src="%(weburl)s/img/smalldown.gif" title="Move Element Down" /></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ """<img border="0" src="%(siteurl)s/img/smalldown.gif" title="Move Element Down" /></a>"""\
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'fieldnum' : cgi.escape(page_elements[i][1], 1),
'nextfield' : cgi.escape(str(int(page_elements[i][1])+1), 1)
}
]
else:
t_row += ["&nbsp;"]
## Element Name:
t_row += ["""<span class="info">%s</span>""" % (cgi.escape(str(page_elements[i][2]), 1),) ]
## Element Label:
t_row += ["""%s""" % (cgi.escape(str(page_elements[i][3]), 1),) ]
## Level:
t_row += ["""%s""" % (cgi.escape(str(page_elements[i][4]), 1),) ]
## Short Descr:
t_row += ["""%s""" % (cgi.escape(str(page_elements[i][5]), 1),) ]
## Check:
t_row += ["""%s""" % (cgi.escape(str(page_elements[i][6]), 1),) ]
## Creation Date:
if page_elements[i][7] not in ("", None):
t_row += ["%s" % (cgi.escape(str(page_elements[i][7]), 1),)]
else:
t_row += ["&nbsp;"]
## Modification Date:
if page_elements[i][8] not in ("", None):
t_row += ["%s" % (cgi.escape(str(page_elements[i][8]), 1),)]
else:
t_row += ["&nbsp;"]
## View/Edit field:
- t_row += ["""<a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&editfieldposn=%(fieldnum)s"><small>edit</small></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'fieldnum' : cgi.escape(page_elements[i][1], 1)
}
]
## Delete Element from page:
- t_row += ["""<a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&deletefieldposn=%(fieldnum)s"><small>delete</small></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'fieldnum' : cgi.escape(page_elements[i][1], 1)
}
]
## View/Edit Element Definition:
- t_row += ["""<a href="%(websubadmin_url)s/elementedit?elname=%(elementname)s&doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/elementedit?elname=%(elementname)s&doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s"><small>element</small></a>"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1),
'elementname' : cgi.escape(page_elements[i][2], 1)
}
]
## Jump element out-from:
t_row += ["&nbsp;"]
## final column containing "jumping-out from" image when moving a field:
if movefieldfromposn not in ("", None):
if movefieldfromposn == page_elements[i][1] and number_elements > 1:
- t_row += ["""<img border="0" src="%(weburl)s/img/move_from.gif" title="Move field at position %(fieldnum)s"""\
+ t_row += ["""<img border="0" src="%(siteurl)s/img/move_from.gif" title="Move field at position %(fieldnum)s"""\
""" from this location" />"""\
- % { 'weburl' : cgi.escape(weburl, 1),
+ % { 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'fieldnum' : cgi.escape(page_elements[i][1], 1)
}
]
else:
t_row += ["&nbsp;"]
else:
t_row += ["&nbsp;"]
## finally, append the newly created row to the tbody list:
t_body.append(t_row)
## now create the table and include it into the page body:
body_content += """
<table width="100%%">
<tr>
<td colspan="2"><br />"""
body_content += create_html_table_from_tuple(tableheader=t_header, tablebody=t_body)
body_content += """
<br />
</td>
</tr>"""
body_content += """
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">
<table>
<tr>
<td>
- <form method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements">
+ <form method="get" action="%(adminurl)s/doctypeconfiguresubmissionpageelements">
<input type="hidden" name="doctype" value="%(doctype_id)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="pagenum" value="%(pagenum)s" />
<input name="addfield" class="adminbutton" type="submit" value="Add a Field" />
</form>
</td>
<td>
- <form method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionpages">
+ <form method="get" action="%(adminurl)s/doctypeconfiguresubmissionpages">
<input type="hidden" name="doctype" value="%(doctype_id)s" />
<input type="hidden" name="action" value="%(action)s" />
<input name="finishedviewfields" class="adminbutton" type="submit" value="Finished" />
</form>
</td>
</tr>
</table>
</td>
- </tr>""" % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ </tr>""" % { 'adminurl' : WEBSUBMITADMINURL,
'doctype_id' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(pagenum, 1)
}
body_content += """
</table>"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Submission Page Details:", datalist=[body_content])
return output
def tmpl_configuredoctype_edit_functionparameter_file(self, doctype, action, function, paramfilename,
paramfilecontent, paramname="", user_msg=""):
## begin template:
output = ""
body_content = ""
output += self._create_user_message_string(user_msg)
body_content += """
<table class="admin_wvar" width="95%%">
<tbody>
<tr>
<td width="20%%">&nbsp;</td>
- <td width="80%%">&nbsp;<form method="post" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctionsparameters"></td>
+ <td width="80%%">&nbsp;<form method="post" action="%(adminurl)s/doctypeconfiguresubmissionfunctionsparameters"></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Parameter Value:</span></td>
<td width="80%%"><textarea cols="115" rows="22" name="paramfilecontent">%(paramfilecontent)s</textarea></td>
</tr>
<tr>
<td colspan="2">
<table>
<tr>
<td>
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="functionname" type="hidden" value="%(function)s" />
<input name="paramname" type="hidden" value="%(paramname)s" />
<input name="paramfilename" type="hidden" value="%(paramfilename)s" />
<input name="editfunctionparameterfilecommit" class="adminbutton" type="submit" value="Save Changes" />
</form>
</td>
<td>
<br />
- <form method="post" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctionsparameters">
+ <form method="post" action="%(adminurl)s/doctypeconfiguresubmissionfunctionsparameters">
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="functionname" type="hidden" value="%(function)s" />
<input name="editfunctionparameterfilecancel" class="adminbutton" type="submit" value="Cancel" />
</form>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
- </table>\n""" % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ </table>\n""" % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'function' : cgi.escape(function, 1),
'paramname' : cgi.escape(paramname, 1),
'paramfilename' : cgi.escape(paramfilename, 1),
'paramfilecontent' : cgi.escape(paramfilecontent, 1)
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Edit the [%s] parameter file:" % (paramfilename,), datalist=[body_content])
return output
def tmpl_configuredoctype_edit_functionparameter_value(self,
doctype,
action,
function,
paramname,
paramval,
user_msg=""):
## begin template:
output = ""
body_content = ""
output += self._create_user_message_string(user_msg)
body_content += """
<table class="admin_wvar" width="95%%">
<tbody>
<tr>
<td width="20%%">&nbsp;</td>
- <td width="80%%">&nbsp;<form method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctionsparameters"></td>
+ <td width="80%%">&nbsp;<form method="get" action="%(adminurl)s/doctypeconfiguresubmissionfunctionsparameters"></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Parameter Value:</span></td>
<td width="80%%"><input type="text" size="35" name="paramval" value="%(paramval)s" /></td>
</tr>
<tr>
<td colspan="2">
<table>
<tr>
<td>
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="functionname" type="hidden" value="%(function)s" />
<input name="paramname" type="hidden" value="%(paramname)s" />
<input name="editfunctionparametervaluecommit" class="adminbutton" type="submit" value="Save Changes" />
</form>
</td>
<td>
<br />
- <form method="post" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctionsparameters">
+ <form method="post" action="%(adminurl)s/doctypeconfiguresubmissionfunctionsparameters">
<input name="doctype" type="hidden" value="%(doctype)s" />
<input name="action" type="hidden" value="%(action)s" />
<input name="functionname" type="hidden" value="%(function)s" />
<input name="editfunctionparametervaluecancel" class="adminbutton" type="submit" value="Cancel" />
</form>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
- </table>\n""" % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ </table>\n""" % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'function' : cgi.escape(function, 1),
'paramname' : cgi.escape(paramname, 1),
'paramval' : cgi.escape(paramval, 1)
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Edit the value of the %s Parameter:" % (paramname,), datalist=[body_content])
return output
def tmpl_configuredoctype_list_functionparameters(self,
doctype,
action,
function,
params,
user_msg=""):
"""Display the parameters and their values for a given function as applied to a given document type
"""
linktoparamfile = 0
## sanity checking:
if type(params) not in (list, tuple):
params = ()
## make table of function parameters:
if function in FUNCTIONS_WITH_FILE_PARAMS:
linktoparamfile = 1
t_header = ["Parameter Name", "Parameter Value", "Edit Parameter", "%s" \
% ((linktoparamfile == 1 and "Edit File") or ("&nbsp;"),)]
t_body = []
num_params = len(params)
for i in range(0, num_params):
thisparamname = params[i][0]
thisparamval = params[i][1]
## parameter name:
t_row = ["""&nbsp;&nbsp;%s""" % (cgi.escape(thisparamname, 1),)]
## parameter value:
t_row += ["""&nbsp;&nbsp;<span class="info">%s</span>""" % (cgi.escape(thisparamval, 1),)]
## button to edit parameter value:
- t_row += ["""<form class="hyperlinkform" method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctionsparameters">"""\
+ t_row += ["""<form class="hyperlinkform" method="get" action="%(adminurl)s/doctypeconfiguresubmissionfunctionsparameters">"""\
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="action" value="%(action)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="functionname" value="%(function)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="paramname" value="%(thisparamname)s" type="hidden" />"""\
"""<input type="submit" name="editfunctionparametervalue" value="edit value" class="hyperlinkformSubmitButton" />"""\
"""</form>\n"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'function' : cgi.escape(function, 1),
'thisparamname' : cgi.escape(thisparamname, 1)
} ]
## button to edit the value of a parameter's file:
- editstr = """<form class="hyperlinkform" method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctionsparameters">"""\
+ editstr = """<form class="hyperlinkform" method="get" action="%(adminurl)s/doctypeconfiguresubmissionfunctionsparameters">"""\
"""<input class="hyperlinkformHiddenInput" name="doctype" value="%(doctype)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="action" value="%(action)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="functionname" value="%(function)s" type="hidden" />"""\
"""<input class="hyperlinkformHiddenInput" name="paramname" value="%(thisparamname)s" type="hidden" />"""\
"""<input type="submit" name="editfunctionparameterfile" value="edit file" class="hyperlinkformSubmitButton" />"""\
"""</form>\n"""\
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'function' : cgi.escape(function, 1),
'thisparamname' : cgi.escape(thisparamname, 1)
}
t_row += ["%s" % ((linktoparamfile == 1 and editstr) or ("&nbsp;"),)]
## finally, append the newly created row to the tbody list:
t_body.append(t_row)
## create display of page
output = ""
output += self._create_user_message_string(user_msg)
body_content = """
<table class="admin_wvar" width="100%%">
<tbody>
<tr>
<td>
<br />
%(paramstable)s
<br />
</td>
</tr>
<tr>
<td>
- <form method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionfunctions">
+ <form method="get" action="%(adminurl)s/doctypeconfiguresubmissionfunctions">
<input type="hidden" name="doctype" value="%(doctype)s" />
<input type="hidden" name="action" value="%(action)s" />
<input name="finishedviewfields" class="adminbutton" type="submit" value="Finished" />
</form>
</td>
</tr>
- </table>""" % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ </table>""" % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'paramstable' : create_html_table_from_tuple(tableheader=t_header, tablebody=t_body)
}
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="""Parameters of the %(function)s Function, belonging to the %(doctype)s Document Type:"""\
% { 'function' : cgi.escape(function, 1), 'doctype' : cgi.escape(doctype, 1) },
datalist=[body_content])
return output
def tmpl_configuredoctype_list_submissionpages(self,
doctype,
action,
number_pages,
cd="",
md="",
deletepagenum="",
user_msg=""):
## sanity checking:
try:
number_pages = int(number_pages)
except ValueError:
number_pages = 0
deletepagenum = str(deletepagenum)
output = ""
body_content = ""
output += self._create_user_message_string(user_msg)
body_content += """
<table width="90%%">
<tr>
<td width="20%%"><span class="adminlabel">Document Type ID:</span></td>
<td width="80%%"><span class="info">%(doctype_id)s</span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Submission ID:</span></td>
<td width="80%%"><span class="info">%(action)s</span></td>
</tr>
<tr>
<td width="20%%"><span class="adminlabel">Number of Pages:</span></td>
<td width="80%%"><span class="info">%(num_pages)s</span></td>
</tr>""" % { 'doctype_id' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'num_pages' : cgi.escape(str(number_pages), 1)
}
if cd not in ("", None):
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Creation Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(cd), 1),)
if md not in ("", None):
body_content += """
<tr>
<td width="20%%"><span class="adminlabel">Last Modification Date:</span></td>
<td width="80%%"><span class="info">%s</span></td>
</tr>""" % (cgi.escape(str(md), 1), )
## EITHER: Make a table of links to each page -OR-
## prompt for confirmation of deletion of a page:
if deletepagenum == "":
## This is a normal visit to display details of a submission's pages
## make a table of links to each page:
t_header = ["Page", "&nbsp;", "&nbsp;", "View Page", "Delete"]
t_body = []
for i in range(1, number_pages + 1):
t_row = ["""Page %d""" % (i,)]
## up arrow:
if i != 1:
- t_row += ["""<a href="%(websubadmin_url)s/doctypeconfiguresubmissionpages?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/doctypeconfiguresubmissionpages?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&movepage=true&movepagedirection=up">"""\
- """<img border="0" src="%(weburl)s/img/smallup.gif" title="Move Page Up" /></a>""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ """<img border="0" src="%(siteurl)s/img/smallup.gif" title="Move Page Up" /></a>""" \
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(str(i), 1)
}
]
else:
## this is the first function - don't provide an arrow to move it up
t_row += ["&nbsp;"]
## down arrow:
if number_pages > 1 and i < number_pages:
- t_row += ["""<a href="%(websubadmin_url)s/doctypeconfiguresubmissionpages?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<a href="%(adminurl)s/doctypeconfiguresubmissionpages?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&movepage=true&movepagedirection=down">"""\
- """<img border="0" src="%(weburl)s/img/smalldown.gif" title="Move Page Down" /></a>""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
- 'weburl' : cgi.escape(weburl, 1),
+ """<img border="0" src="%(siteurl)s/img/smalldown.gif" title="Move Page Down" /></a>""" \
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
+ 'siteurl' : cgi.escape(CFG_SITE_URL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(str(i), 1)
}
]
else:
t_row += ["&nbsp;"]
## "view page" link:
- t_row += ["""<small><a href="%(websubadmin_url)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<small><a href="%(adminurl)s/doctypeconfiguresubmissionpageelements?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s">view page</a></small>""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(str(i), 1)
}
]
## "delete page" link:
- t_row += ["""<small><a href="%(websubadmin_url)s/doctypeconfiguresubmissionpages?doctype=%(doctype)s&action=%(action)s&"""\
+ t_row += ["""<small><a href="%(adminurl)s/doctypeconfiguresubmissionpages?doctype=%(doctype)s&action=%(action)s&"""\
"""pagenum=%(pagenum)s&deletepage=true">delete page</a></small>""" \
- % { 'websubadmin_url' : cgi.escape(WEBSUBMITADMIN_WEBURL, 1),
+ % { 'adminurl' : cgi.escape(WEBSUBMITADMINURL, 1),
'doctype' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(str(i), 1)
}
]
## finally, append the newly created row to the tbody list:
t_body.append(t_row)
## now create the table and include it into the page body:
body_content += """
<tr>
<td colspan="2"><br />"""
body_content += create_html_table_from_tuple(tableheader=t_header, tablebody=t_body)
body_content += """
<br />
</td>
</tr>"""
body_content += """
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">
<table>
<tr>
<td>
- <form method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionpages">
+ <form method="get" action="%(adminurl)s/doctypeconfiguresubmissionpages">
<input type="hidden" name="doctype" value="%(doctype_id)s" />
<input type="hidden" name="action" value="%(action)s" />
<input name="addpage" class="adminbutton" type="submit" value="Add a Page" />
</form>
</td>
<td>
- <form method="get" action="%(websubadmin_url)s/doctypeconfigure">
+ <form method="get" action="%(adminurl)s/doctypeconfigure">
<input type="hidden" name="doctype" value="%(doctype_id)s" />
<input name="finishedviewpages" class="adminbutton" type="submit" value="Finished" />
</form>
</td>
</tr>
</table>
</td>
- </tr>""" % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ </tr>""" % { 'adminurl' : WEBSUBMITADMINURL,
'doctype_id' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1)
}
else:
## user has requested the deletion of a page from the current submission, and this visit should
## simply prompt them for confirmation:
body_content += """
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%"><br /><span class="info">REALLY delete page %(pagenum)s and all of its associated interface elements from """\
"""this submission? You CANNOT undo this!</span></td>
</tr>
<tr>
<td width="20%%">&nbsp;</td>
<td width="80%%">
<table>
<tr>
<td>
- <form method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionpages">
+ <form method="get" action="%(adminurl)s/doctypeconfiguresubmissionpages">
<input type="hidden" name="doctype" value="%(doctype_id)s" />
<input type="hidden" name="action" value="%(action)s" />
<input type="hidden" name="deletepage" value="true" />
<input type="hidden" name="pagenum" value="%(pagenum)s" />
<input name="deletepageconfirm" class="adminbutton" type="submit" value="Confirm" />
</form>
</td>
<td>
- <form method="get" action="%(websubadmin_url)s/doctypeconfiguresubmissionpages">
+ <form method="get" action="%(adminurl)s/doctypeconfiguresubmissionpages">
<input type="hidden" name="doctype" value="%(doctype_id)s" />
<input type="hidden" name="action" value="%(action)s" />
<input name="cancelpagedelete" class="adminbutton" type="submit" value="No! Stop!" />
</form>
</td>
</tr>
</table>
</td>
- </tr>""" % { 'websubadmin_url' : WEBSUBMITADMIN_WEBURL,
+ </tr>""" % { 'adminurl' : WEBSUBMITADMINURL,
'doctype_id' : cgi.escape(doctype, 1),
'action' : cgi.escape(action, 1),
'pagenum' : cgi.escape(deletepagenum, 1)
}
body_content += """
</table>
"""
output += self._create_websubmitadmin_main_menu_header()
output += self._create_adminbox(header="Submission Page Details:", datalist=[body_content])
return output
diff --git a/modules/websubmit/web/admin/referees.py b/modules/websubmit/web/admin/referees.py
index 7b233c612..db47c6e39 100644
--- a/modules/websubmit/web/admin/referees.py
+++ b/modules/websubmit/web/admin/referees.py
@@ -1,244 +1,244 @@
## $Id$
##
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
"""WebSubmit interface for the management of referees."""
__revision__ = "$Id$"
## import interesting modules:
import types
import re
from invenio.config import \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_URL, \
- weburl
+ CFG_SITE_URL
from invenio.dbquery import run_sql, Error
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_admin import \
acc_delete_user_role, \
acc_get_role_id, \
acc_add_role, \
acc_add_action,\
acc_add_role_action_arguments, \
acc_add_argument, \
acc_get_user_roles, \
acc_add_user_role, \
acc_get_action_id, \
acc_get_all_roles, \
acc_get_role_users
from invenio.webpage import page, create_error_box
from invenio.webuser import getUid, get_email, list_registered_users, page_not_authorized
from invenio.messages import wash_language
def index(req, c=CFG_SITE_NAME, ln=CFG_SITE_LANG, todo="", id="", doctype="",
categ="", addusers="", warningText="", role=""):
"""Main entry point for the management of referees."""
ln = wash_language(ln)
# get user ID:
try:
uid = getUid(req)
except Error, e:
return errorMsg(str(e), req, ln=ln)
(auth_code, auth_message) = acc_authorize_action(req, "cfgwebsubmit", verbose=0)
if auth_code != 0:
## user is not authorised to use WebSubmit Admin:
return page_not_authorized(req=req, text=auth_message)
# request for deleting a user
if todo == "deleteuser":
acc_delete_user_role(id, name_role=role)
# request for adding user(s)
if todo == "adduser":
role = "referee_%s_%s" % (doctype, categ[1])
roleId = acc_get_role_id(role)
# if the role does not exists, we create it
if roleId == 0:
if acc_add_role(role, "referees for document type %s category %s" % (doctype, categ[1])) == 0:
return errorMsg("Cannot create referee role", req)
else:
roleId = acc_get_role_id(role)
# if the action does not exist, we create it
actionId = acc_get_action_id("referee")
if actionId == 0:
if acc_add_action("referee", "", "no", ("doctype","categ")) == 0:
return errorMsg("Cannot create action 'referee'", req)
else:
actionId = acc_get_action_id("referee")
#create arguments
arg1Id = acc_add_argument("doctype", doctype)
arg2Id = acc_add_argument("categ", categ[1])
# then link the role with the action
if acc_add_role_action_arguments(roleId, actionId, -1, 0, 0, [arg1Id, arg2Id]) == 0:
return errorMsg("Cannot link role with action", req)
roleId = acc_get_role_id(role)
# For each id in the array
if isinstance(addusers, types.ListType):
for adduser in addusers:
# First check whether this id is not already associated with this rule
myRoles = acc_get_user_roles(adduser)
if not roleId in myRoles:
# Actually add the role to the user
acc_add_user_role(adduser, roleId)
else:
warningText = '<span style="color:#f00">Sorry... This user is already a referee for this category.</span>'
else:
# First check whether this id is not already associated with this rule
myRoles = acc_get_user_roles(addusers)
if not roleId in myRoles:
# Actually add the role to the user
acc_add_user_role(addusers, roleId)
else:
warningText = '<span style="color:#f00">Sorry... This user is already a referee for this category.</span>'
return page(title="websubmit admin - referee selection",
body=displayRefereesPage(doctype, warningText),
description="",
keywords="",
uid=uid,
language=ln,
req=req)
def displayRefereesPage(doctype, warningText):
"""Output the list of refeeres as well as the controls to add/remove them"""
t = ""
if doctype in ['', '*']:
doctype = '*'
docname = "all catalogues"
else:
res = run_sql("SELECT * FROM sbmDOCTYPE WHERE sdocname=%s", (doctype,))
docname = res[0][0]
t += warningText
t += """
<form action="referees.py" method="post">
<input type="hidden" name="todo" value="" />
<input type="hidden" name="id" value="" />
<input type="hidden" name="doctype" value="%s" />
<input type="hidden" name="categ" value="" />
<input type="hidden" name="role" value="" />
<!-- Role: referee -->
<table><tr><td valign="top">""" % doctype
# call the function to display the table containing the list of associated emails
t += displayUserTable(doctype)
t += """
</td>
<td valign="top">"""
# call the function to display the form allowing the manager to add new users
t += displayAddUser(doctype)
- end_url = "%s/admin/websubmit/websubmitadmin.py/doctypeconfigure?doctype=%s" % (weburl, doctype)
+ end_url = "%s/admin/websubmit/websubmitadmin.py/doctypeconfigure?doctype=%s" % (CFG_SITE_URL, doctype)
if doctype in ['', '*']:
- end_url = "%s/admin/websubmit/websubmitadmin.py/" % weburl
+ end_url = "%s/admin/websubmit/websubmitadmin.py/" % CFG_SITE_URL
t += """
</td></tr></table>
<!-- End submissionuser rule -->
<a href="%s">Finished</a>
</form>""" % end_url
return t
def displayUserTable(doctype):
"""Display the list of referees for the given doctype, as well as
the control to remove them"""
t = ""
# start displaying the table which will contain the list of email addresses.
t += """
<table class="searchbox">
<tr>
<th class="portalboxheader" colspan="2">Referees</th>
</tr>"""
roles = acc_get_all_roles()
referees = {}
for role in roles:
role_name = role[1]
role_id = role[0]
if re.match("^referee_%s_" % doctype, role_name):
# Try to retrieve the referee's email from the referee's database
if acc_get_role_users(role_id) is not None:
referees[role_name] = acc_get_role_users(role_id)
if len(referees) == 0:
t += '<tr><td align="center" colspan="2"><img src="%s/img/noway.gif" height="16px" width="16px" alt="Empty"/></td></tr>' % CFG_SITE_URL
i = 0
for role in referees.keys():
categ = re.match("referee_%s_(.*)" % doctype, role).group(1)
res = run_sql("SELECT lname FROM sbmCATEGORIES WHERE sname=%s and doctype=%s", (categ, doctype,))
if len(res) > 0:
categname = "Referee(s) for category: %s" % res[0][0]
else:
categname = "General Referee(s)"
t += '<tr><td colspan="2"><small><b>%s</b> </small></td></tr>' % categname
for referee in referees[role]:
if int(i/2) == i/2:
bgcolor = "#eeeeee"
else:
bgcolor = "#dddddd"
t += '<tr bgcolor="%s">' % bgcolor
t += '<td align="right"><small>'
t += referee[1]
t += '</small></td>'
t += '''<td><a href="" onclick="if (confirm('Are you sure you want to delete this referee?')){document.forms[0].todo.value='deleteuser';document.forms[0].id.value='%s';document.forms[0].role.value='%s';document.forms[0].submit();return false;}else{return false;}">''' % (referee[0], role)
t += '<img src="%s/img/iconcross.gif" border="0" alt="Remove" /></a>' % CFG_SITE_URL
t += '</td>'
t += '</tr>'
i += 1
# close table
t += "</table>"
return t
def displayAddUser(doctype):
"""Display controls for adding users"""
t = ""
# start displaying the table which will contain the add form
t += """
<table class="searchbox" summary="">
<tr>
<th class="portalboxheader">Add</th>
</tr>
<tr>
<td>
User:<br/>"""
users = list_registered_users()
if len(users) < 20:
numrows = len(users)
else:
numrows = 20
t += '<select multiple="multiple" name="addusers" size="%s">' % numrows
for user in users:
if user[1] != "":
t += '<option value="%s">%s</option>' % (user[0], user[1])
t += '</select><br/>'
t += '<select name="categ">'
t += '<option value="*">All categories</option>'
res = run_sql("select lname,sname FROM sbmCATEGORIES WHERE doctype=%s ORDER BY lname", (doctype,))
for row in res:
t += '<option value="%s">%s</option>' % (row[1], row[0])
t += '</select><br/>'
t += '''<input class="adminbutton" type="button" onclick="document.forms[0].todo.value='adduser';document.forms[0].submit();" value="ADD" />'''
t += '</td></tr></table>'
return t
def errorMsg(title, req, uid, c=CFG_SITE_NAME, ln=CFG_SITE_LANG):
"""Prints the error page."""
return page(title="error",
body = create_error_box(req, title=title,verbose=0, ln=ln),
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
language=ln,
uid=uid,
req=req)
diff --git a/modules/websubmit/web/yoursubmissions.py b/modules/websubmit/web/yoursubmissions.py
index 4db3d60be..20a6ba3d3 100644
--- a/modules/websubmit/web/yoursubmissions.py
+++ b/modules/websubmit/web/yoursubmissions.py
@@ -1,218 +1,216 @@
## $Id$
## This file is part of CDS Invenio.
## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN.
##
## CDS 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.
##
## CDS 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 CDS Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
__revision__ = "$Id$"
## import interesting modules:
import string
import os
import sys
import time
import types
import re
import shutil
import operator
from invenio.config import \
CFG_ACCESS_CONTROL_LEVEL_SITE, \
CFG_SITE_LANG, \
CFG_SITE_NAME, \
CFG_SITE_URL, \
CFG_WEBSUBMIT_STORAGEDIR, \
CFG_SITE_SECURE_URL, \
- CFG_VERSION, \
- weburl
+ CFG_VERSION
from invenio.dbquery import run_sql, Error
from invenio.access_control_engine import acc_authorize_action
from invenio.access_control_admin import *
from invenio.webpage import page, create_error_box
from invenio.webuser import getUid, get_email, list_registered_users, page_not_authorized
from invenio.messages import gettext_set_language, wash_language
from invenio.websubmit_config import *
from invenio.search_engine import search_pattern
import invenio.template
websubmit_templates = invenio.template.load('websubmit')
def index(req,c=CFG_SITE_NAME,ln=CFG_SITE_LANG,order="",doctype="",deletedId="",deletedAction="",deletedDoctype=""):
global uid
ln = wash_language(ln)
# load the right message language
_ = gettext_set_language(ln)
t=""
# get user ID:
try:
uid = getUid(req)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../yoursubmissions.py/index",
navmenuid='yoursubmissions')
u_email = get_email(uid)
except Error, e:
return errorMsg(str(e), req, ln=ln)
if u_email == "guest" or u_email == "":
return warningMsg(websubmit_templates.tmpl_warning_message(
ln = ln,
msg = _("Sorry, you must log in to perform this action."),
),req, ln = ln)
if deletedId != "":
t += deleteSubmission(deletedId,deletedAction,deletedDoctype,u_email)
# doctypes
res = run_sql("select ldocname,sdocname from sbmDOCTYPE order by ldocname")
doctypes = []
for row in res:
doctypes.append({
'id' : row[1],
'name' : row[0],
'selected' : (doctype == row[1]),
})
# submissions
# request order default value
reqorder = "sbmSUBMISSIONS.md DESC, lactname"
# requested value
if order == "actiondown":
reqorder = "lactname ASC, sbmSUBMISSIONS.md DESC"
elif order == "actionup":
reqorder = "lactname DESC, sbmSUBMISSIONS.md DESC"
elif order == "refdown":
reqorder = "reference ASC, sbmSUBMISSIONS.md DESC, lactname DESC"
elif order == "refup":
reqorder = "reference DESC, sbmSUBMISSIONS.md DESC, lactname DESC"
elif order == "cddown":
reqorder = "sbmSUBMISSIONS.cd DESC, lactname"
elif order == "cdup":
reqorder = "sbmSUBMISSIONS.cd ASC, lactname"
elif order == "mddown":
reqorder = "sbmSUBMISSIONS.md DESC, lactname"
elif order == "mdup":
reqorder = "sbmSUBMISSIONS.md ASC, lactname"
elif order == "statusdown":
reqorder = "sbmSUBMISSIONS.status DESC, lactname"
elif order == "statusup":
reqorder = "sbmSUBMISSIONS.status ASC, lactname"
if doctype != "":
docselect = " and doctype='%s' " % doctype
else:
docselect = ""
res = run_sql("SELECT sbmSUBMISSIONS.* FROM sbmSUBMISSIONS,sbmACTION WHERE sactname=action and email=%s and id!='' "+docselect+" ORDER BY doctype,"+reqorder,(u_email,))
currentdoctype = ""
currentaction = ""
currentstatus = ""
submissions = []
for row in res:
if currentdoctype != row[1]:
currentdoctype = row[1]
currentaction = ""
currentstatus = ""
res2 = run_sql("SELECT ldocname FROM sbmDOCTYPE WHERE sdocname=%s",(currentdoctype,))
if res2:
ldocname = res2[0][0]
else:
ldocname = """***Unknown Document Type - (%s)""" % (currentdoctype,)
if currentaction != row[2]:
currentaction = row[2]
res2 = run_sql("SELECT lactname FROM sbmACTION WHERE sactname=%s",(currentaction,))
if res2:
lactname = res2[0][0]
else:
lactname = "\""
else:
lactname = "\""
if currentstatus != row[3]:
currentstatus = row[3]
status=row[3]
else:
status = "\""
submissions.append({
'docname' : ldocname,
'actname' : lactname,
'status' : status,
'cdate' : row[6],
'mdate' : row[7],
'reference' : row[5],
'id' : row[4],
'act' : currentaction,
'doctype' : currentdoctype,
'pending' : (row[3] == "pending")
})
# display
t += websubmit_templates.tmpl_yoursubmissions(
ln = ln,
- weburl = weburl,
images = CFG_SITE_URL + '/img',
order = order,
doctypes = doctypes,
submissions = submissions,
)
return page(title=_("Your Submissions"),
navtrail= """<a class="navtrail" href="%(sitesecureurl)s/youraccount/display">%(account)s</a>""" % {
'sitesecureurl' : CFG_SITE_SECURE_URL,
'account' : _("Your Account"),
},
body=t,
description="",
keywords="",
uid=uid,
language=ln,
req=req,
navmenuid='yoursubmissions')
def deleteSubmission(id, action, doctype, u_email):
global CFG_WEBSUBMIT_STORAGEDIR
run_sql("delete from sbmSUBMISSIONS WHERE doctype=%s and action=%s and email=%s and status='pending' and id=%s",(doctype,action,u_email,id,))
res = run_sql("select dir from sbmACTION where sactname=%s",(action,))
dir = res[0][0]
if not ('..' in doctype or '..' in id) and id != "":
full = os.path.join(CFG_WEBSUBMIT_STORAGEDIR, dir, doctype, id)
if os.path.isdir(full):
shutil.rmtree(full)
return ""
def warningMsg(title,req,c=CFG_SITE_NAME,ln=CFG_SITE_LANG):
return page(title="warning",
body = title,
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='yoursubmissions')
def errorMsg(title,req,c=CFG_SITE_NAME,ln=CFG_SITE_LANG):
return page(title="error",
body = create_error_box(req, title=title,verbose=0, ln=ln),
description="%s - Internal Error" % c,
keywords="%s, Internal Error" % c,
uid = getUid(req),
language=ln,
req=req,
navmenuid='yoursubmissions')

Event Timeline