<a name="3"></a><h2>3. Edit multiple records via web interface</h2>
-<p>The purpose of the <a href="<CFG_SITE_URL>/record/multiedit/">Multi-Record Editor Web interface</a> is to allow cataloguers to easily edit more than one record in one go.</p>
+<p>The purpose of the <a href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/multiedit/">Multi-Record Editor Web interface</a> is to allow cataloguers to easily edit more than one record in one go.</p>
<p>The Multi-Record Editor allows cataloguers to easily look up various records in
the system in order to find record sets upon which to operate, and then to allow
some easy replacement procedures on these records in one go, e.g. a substring
substitution of some field value in some field tags.
</p>
<a name="3.1"></a><h3>3.1 Multi-Record Editor user guide</h3>
<p>While working with the Multi-Record Editor, the first step is to filter the set
of records that are going to be modified.<p>
<p>In order to do that, three options are available in the interface:<p>
<blockquote>
<pre>
Search criteria: [ ]
Filter collection: [ ]
Output tags: [ ]
[Search]
</pre>
</blockquote>
<p>
<ul>
<li>Search criteria allows to search records using the
same syntax offered by Invenio's web search.
<li>These records can be filtered by the desired collection, thus narrowing the
search results.
<li>Finally, for convenience, the tags displayed for each record can be specified.
The tags have to be separated by commas.
</ul>
</p>
<p>After clicking the <code>Search</code> button, the set of records that will
be affected by the changes will be visible at the bottom of the interface. It is
possible to specify whether to visualize them in <code>MARC</code> format or in
<code>HTML Brief</code> format.</p>
<p>The next step is to specify the desired changes to be made on the records.
When defining a new field action, the field tag and its indicators (if necessary)
have to be specified and one of the three actions (Add field, Delete field,
Update field) selected.</p>
<blockquote>
<pre>
Field
[ tag ][ind1][ind2] [Select action[V]]
</pre>
</blockquote>
<p>After that, as many actions on subfields as needed can be defined. The subfield
tag has to be specified and one action (<code>Add subfield, Delete subfield, Replace
full content, Replace substring</code>) selected. Depending on the field action
selected some actions for subfields will not be available.</p>
<p>The difference between <code>Replace full content</code> and <code>Replace
substring</code> resides in that the former deletes all the content present in
a subfield and writes the specified value on it whereas the latter looks for a string
and substitutes it by a new string.</p>
<p>All subfield actions have the <code>Apply only to specific field instances</code>
option. This is useful, for example, in cases where there are multiple authors
(<code>700__</code> tags) and we do not want to act in all of them.</p>
<p>In that case one could add the condition that only fields where the tag
<code>$a</code> is equal to <code>Ellis A.</code> should be modified.</p>
<blockquote>
<pre>
700__ Update Field
[u] [Replace full content]
[Ellis J.]
when other subfield [u] is equal to [Ellis A.]
</pre>
</blockquote>
<p>Every subfield action defined has to be saved using the correspondent button
before applying the changes.</p>
<p>Once all the actions for fields and subfields have been specified the modifications
can be previewed using the corresponding button.</p>
<p>Finally, when clicking on the <code>Apply changes</code> button all modifications
will be sent to the server and will be visible after some time.</p>
<a name="4"></a><h2>4. Edit records via command line</h2>
<p>The idea is to download record in XML MARC format, edit it by using
any editor, and upload the changes back. Note that you can edit any
number of records at the same time: for example, you can download all
records written by <code>Qllis, J</code>, open the file in your
favourite text editor, and change globally the author name to the
proper form <code>Ellis, J</code>.</p>
<p>You therefore continue as follows:</p>
<ol>
<li> Download the record in XML MARC. For example, download record ID 1234:
"text_search_criteria_explanation" : _("""Specify the criteria you'd like to use for filtering records that will be changed. Use "Search" to see which records would have been filtered using these criteria."""),
""" % {"warning_msg": "Due to the amount of records to be modified, you need 'superadmin' rights to send the modifications. If it is not the case, your changes will be saved once you click the 'Apply Changes' button and you will be able to contact the admin to apply them"
"""Returns the scripts that should be imported."""
scripts_jquery = ["jquery.min.js",
"json2.js"]
scripts = ["bibeditmulti.js"]
result = ""
for script in scripts_jquery:
result += '<script type="text/javascript" src="%s/js/jquery/%s">' \
'</script>\n' % (CFG_SITE_URL, script)
for script in scripts:
result += '<script type="text/javascript" src="%s/js/%s">' \
'</script>\n' % (CFG_SITE_URL, script)
return result
def changes_applied(self, status, file_path):
""" returns html message when changes sent to server """
if status == 0:
body = """
- <div class="clean-ok"><div>Changes have been sent to the server. It will take some time before they are applied. You can <a href=%s/record/multiedit>reset </a> the editor.</div>
- """ % (CFG_SITE_URL)
+ <div class="clean-ok"><div>Changes have been sent to the server. It will take some time before they are applied. You can <a href=%s/%s/multiedit>reset </a> the editor.</div>
+ """ % (CFG_SITE_URL, CFG_SITE_RECORD)
elif status in [1, 2]:
body = """
<div class="clean-ok">You are submitting a file that manipulates more than %s records. Your job will therefore be processed only during <strong>%s</strong>. <br /><br />
If you are not happy about this, please contact %s, quoting your file <strong>%s</strong> <br /><br />
- You can <a href=%s/record/multiedit>reset</a> the editor.</div>
+ You can <a href=%s/%s/multiedit>reset</a> the editor.</div>
""" % (CFG_BIBEDITMULTI_LIMIT_INSTANT_PROCESSING,
CFG_BIBEDITMULTI_LIMIT_DELAYED_PROCESSING_TIME,
CFG_SITE_ADMIN_EMAIL,
file_path,
- CFG_SITE_URL)
+ CFG_SITE_URL,
+ CFG_SITE_RECORD)
else:
body = """
<div class="clean-error">Sorry, you are submitting a file that manipulates more than %s records. You don't have enough rights for this.
<br /> <br />
If you are not happy about this, please contact %s, quoting your file %s <br /><br />
- You can <a href=%s/record/multiedit>reset</a> the editor.</div>
+ You can <a href=%s/%s/multiedit>reset</a> the editor.</div>
<description>Output the various actions available on a record</description>
<ul class="detailedrecordactions">
<li><a href="<BFE_SERVER_INFO var='CFG_SITE_URL'/>yourbaskets/add?ln=<BFE_CLIENT_INFO var='ln'/>&recid=<BFE_RECORD_ID/>">_(Add to personal basket)_</a></li>
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_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>0$ have also been studied.</small><br />'''
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>'''
<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>
@param var: the name of the desired variable. Can be one of: CFG_SITE_NAME, CFG_SITE_NAME_INTL, CFG_SITE_LANG, CFG_VERSION, CFG_SITE_ADMIN_EMAIL, CFG_SITE_SUPPORT_EMAIL, CFG_SITE_URL, searchurl, recurl
CFG_SITE_NAME: the name of the server
CFG_SITE_NAME_INTL: internationalized name
CFG_SITE_LANG: the default language of the server
CFG_VERSION: the software version
CFG_SITE_ADMIN_EMAIL: the admin email
CFG_SITE_SUPPORT_EMAIL: the support email
CFG_SITE_URL: the base url for the server
searchurl: the search url for the server
recurl: the base url for the record
'''
recID = bfo.recID
if var == '':
out = ''
elif var in ['name', 'CFG_SITE_NAME']:
out = CFG_SITE_NAME
elif var in ['i18n_name', 'CFG_SITE_NAME_INTL']:
out = CFG_SITE_NAME_INTL.get(bfo.lang, CFG_SITE_NAME)
elif var in ['lang', 'CFG_SITE_LANG']:
out = CFG_SITE_LANG
elif var == 'CFG_VERSION':
out = 'Invenio v' + str(CFG_VERSION)
elif var in ['email', 'admin_email', 'CFG_SITE_ADMIN_EMAIL']:
out = CFG_SITE_ADMIN_EMAIL
elif var in ['support_email', 'CFG_SITE_SUPPORT_EMAIL']:
text += bibharvest_templates.tmpl_print_brs(ln, 2)
output += createhiddenform(action="editsource#1",
text=text,
button="Modify",
oai_src_id=oai_src_id,
ln=ln,
confirm=1)
if confirm in [1, "1"] and not oai_src_name:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a name for the source.")
elif confirm in [1, "1"] and not oai_src_prefix:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a metadata prefix.")
elif confirm in [1, "1"] and not oai_src_baseurl:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a base url.")
elif confirm in [1, "1"] and not oai_src_frequency:
output += bibharvest_templates.tmpl_print_warning(ln, "Please choose a frequency of harvesting")
elif confirm in [1, "1"] and "c" in oai_src_post and (not oai_src_config or validatefile(oai_src_config) != 0):
output += bibharvest_templates.tmpl_print_warning(ln, "You selected a postprocess mode which involves conversion.")
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a valid name of or a full path to a BibConvert config file or change postprocess mode.")
elif confirm in [1, "1"] and "f" in oai_src_post and (not oai_src_bibfilter or validatefile(oai_src_bibfilter) != 0):
output += bibharvest_templates.tmpl_print_warning(ln, "You selected a postprocess mode which involves filtering.")
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a valid name of or a full path to a BibFilter script or change postprocess mode.")
link_label=_("Go back to the OAI sources overview"))
if confirm in [1, "1"] and not oai_src_name:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a name for the source.")
elif confirm in [1, "1"] and not oai_src_prefix:
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a metadata prefix.")
elif confirm in [1, "1"] and not oai_src_frequency:
output += bibharvest_templates.tmpl_print_warning(ln, "Please choose a frequency of harvesting")
elif confirm in [1, "1"] and not oai_src_lastrun:
output += bibharvest_templates.tmpl_print_warning(ln, "Please choose the harvesting starting date")
elif confirm in [1, "1"] and "c" in oai_src_post and (not oai_src_config or validatefile(oai_src_config) != 0):
output += bibharvest_templates.tmpl_print_warning(ln, "You selected a postprocess mode which involves conversion.")
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a valid name of or a full path to a BibConvert config file or change postprocess mode.")
elif confirm in [1, "1"] and "f" in oai_src_post and (not oai_src_bibfilter or validatefile(oai_src_bibfilter) != 0):
output += bibharvest_templates.tmpl_print_warning(ln, "You selected a postprocess mode which involves filtering.")
output += bibharvest_templates.tmpl_print_warning(ln, "Please enter a valid name of or a full path to a BibFilter script or change postprocess mode.")
<subfield code="b"><!--HTML--><p class="articleHeader">This great island lay over against the Pillars of Heracles, in extent greater than Libya and Asia put together, and was the passage to other islands and to a great ocean of which the Mediterranean sea was only the harbour; and within the Pillars the empire of Atlantis reached in Europe to Tyrrhenia and in Libya to Egypt.</p><p>This mighty power was arrayed against Egypt and Hellas and all the countries</p><div class="phrwithcaption"><div class="imageScale"><img src="http://invenio-software.org/download/invenio-demo-site-files/icon-journal_Athanasius_Kircher_Atlantis_image.gif" alt="" /></div><p>Representation of Atlantis by Athanasius Kircher (1669)</p></div>bordering on the Mediterranean. Then your city did bravely, and won renown over the whole earth. For at the peril of her own existence, and when the other Hellenes had deserted her, she repelled the invader, and of her own accord gave liberty to all the nations within the Pillars. A little while afterwards there were great earthquakes and floods, and your warrior race all sank into the earth; and the great island of Atlantis also disappeared in the sea. This is the explanation of the shallows which are found in that part of the Atlantic ocean. <p></p>(Excerpt from TIMAEUS, By Plato, translated By Jowett, Benjamin)<br /></subfield>
<subfield code="a">A brief overview of hep-th/0201028 prepared for NATO Advanced Study Institute and EC Summer School on Progress in String, Field and Particle Theory, Cargese, Corsica, France, 25 June - 11 July 2002.</subfield>
<subfield code="u">National Technical University of Athens</subfield>
</datafield>
<datafield tag="245" ind1=" " ind2=" ">
<subfield code="a">Quasi normal Modes of Electromagnetic Perturbations of Four-Dimensional Topological Black Holes</subfield>
</datafield>
<datafield tag="260" ind1=" " ind2=" ">
<subfield code="c">2006</subfield>
</datafield>
<datafield tag="269" ind1=" " ind2=" ">
<subfield code="c">10 Jun 2006</subfield>
</datafield>
<datafield tag="300" ind1=" " ind2=" ">
<subfield code="a">17 p</subfield>
</datafield>
<datafield tag="520" ind1=" " ind2=" ">
<subfield code="a">We study the perturbative behaviour of topological black holes with scalar hair. We calculate both analytically and numerically the quasi-normal modes of the electromagnetic perturbations. In the case of small black holes we find clear evidence of a second-order phase transition of a topological black hole to a hairy configuration. We also find evidence of a second-order phase transition of the AdS vacuum solution to a topological black hole.</subfield>
<td align="left" colspan="3" bgcolor="#e6e6fa"><h2>Metadata</h2> <font color="red"><b>Warning:</b> modification(s) will not be saved on the %(CFG_SITE_NAME)s</font>
class BibUploadReferencesModeTest(GenericBibUploadTest):
"""Testing references mode."""
def setUp(self):
"""Initialize the MARCXML variable"""
GenericBibUploadTest.setUp(self)
self.test_insert = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">CERN</subfield>
</datafield>
</record>"""
self.test_reference = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag =\"""" + bibupload.CFG_BIBUPLOAD_REFERENCE_TAG + """\" ind1="C" ind2="5">
<subfield code="m">M. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,</subfield>
<subfield code="s">J. High Energy Phys. 07 (2004) 014</subfield>
</datafield>
</record>"""
self.test_reference_expected_xm = """<record>
<controlfield tag="001">123456789</controlfield>
<datafield tag ="100" ind1=" " ind2=" ">
<subfield code="a">Tester, T</subfield>
<subfield code="u">CERN</subfield>
</datafield>
<datafield tag =\"""" + bibupload.CFG_BIBUPLOAD_REFERENCE_TAG + """\" ind1="C" ind2="5">
<subfield code="m">M. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,</subfield>
<subfield code="s">J. High Energy Phys. 07 (2004) 014</subfield>
</datafield>
</record>"""
self.test_insert_hm = """
001__ 123456789
100__ $$aTester, T$$uCERN
"""
self.test_reference_expected_hm = """
001__ 123456789
100__ $$aTester, T$$uCERN
%(reference_tag)sC5 $$mM. Lüscher and P. Weisz, String excitation energies in SU(N) gauge theories beyond the free-string approximation,$$sJ. High Energy Phys. 07 (2004) 014
print >> sys.stderr, "Table %s has been modified: before was [%s], after was [%s]" % (table, pprint.pformat(before[table]), pprint.pformat(after[table]))
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))
enable_recstruct_cache = enable_recstruct_cache in ('True', '1')
pid = server_pid(ping_the_process=False)
if pid:
print >> sys.stderr, "ERROR: bibsched seems to run with pid %d, according to %s." % (pid, pidfile)
print >> sys.stderr, " Please stop bibsched before running this procedure."
sys.exit(1)
if enable_recstruct_cache:
print ">>> Searching records which need recstruct cache resetting; this may take a while..."
all_recids = intbitset(run_sql("SELECT id FROM bibrec"))
good_recids = intbitset(run_sql("SELECT bibrec.id FROM bibrec JOIN bibfmt ON bibrec.id = bibfmt.id_bibrec WHERE format='recstruct' AND modification_date < last_updated"))
recids = all_recids - good_recids
print ">>> Generating recstruct cache..."
tot = len(recids)
count = 0
for recid in recids:
value = serialize_via_marshal(get_record(recid))
run_sql("DELETE FROM bibfmt WHERE id_bibrec=%s AND format='recstruct'", (recid, ))
run_sql("INSERT INTO bibfmt(id_bibrec, format, last_updated, value) VALUES(%s, 'recstruct', NOW(), %s)", (recid, value))
('basketusers', 'Users who can use baskets', 'deny email "hyde@cds.cern.ch"\nallow any'),
('claimpaperusers', 'Users who can perform changes to their own paper attributions without the need for an operator\'s approval', 'deny email "hyde@cds.cern.ch"\nallow any'),
('submit_DEMOJRN_*', 'Users who can submit (and modify) "Atlantis Times" articles', 'deny all'),
('atlantiseditor', 'Users who can configure "Atlantis Times" journal', 'deny all'),
('commentmoderator', 'Users who can moderate comments', 'deny all'),
('poetrycommentreader', 'Users who can view comments in Poetry collection', 'deny all'))
0: 'Try to <a href="%s/youraccount/login?referer=%%s">login</a> with another account.' % (CFG_SITE_SECURE_URL),
1: '<br />If you think this is not correct, please contact: <a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL),
2: '<br />If you have any questions, please write to <a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL),
3: 'Guest users are not allowed, please <a href="%s/youraccount/login">login</a>.' % CFG_SITE_SECURE_URL,
4: 'The site is temporarily closed for maintenance. Please come back soon.',
5: 'Authorization failure',
6: '%s temporarily closed' % CFG_SITE_NAME,
7: 'This functionality is temporarily closed due to server maintenance. Please use only the search engine in the meantime.',
8: 'Functionality temporarily closed'
}
CFG_WEBACCESS_WARNING_MSGS = {
0: 'Authorization granted',
1: 'You are not authorized to perform this action.',
2: 'You are not authorized to perform any action.',
3: 'The action %s does not exist.',
4: 'Unexpected error occurred.',
5: 'Missing mandatory keyword argument(s) for this action.',
6: 'Guest accounts are not authorized to perform this action.',
7: 'Not enough arguments, user ID and action name required.',
8: 'Incorrect keyword argument(s) for this action.',
9: """Account '%s' is not yet activated.""",
10: """You were not authorized by the authentication method '%s'.""",
11: """The selected login method '%s' is not the default method for this account, please try another one.""",
12: """Selected login method '%s' does not exist.""",
13: """Could not register '%s' account.""",
14: """Could not login using '%s', because this user is unknown.""",
15: """Could not login using your '%s' account, because you have introduced a wrong password.""",
16: """External authentication troubles using '%s' (maybe temporary network problems).""",
17: """You have not yet confirmed the email address for the '%s' authentication method.""",
18: """The administrator has not yet activated your account for the '%s' authentication method.""",
19: """The site is having troubles in sending you an email for confirming your email address. The error has been logged and will be taken care of as soon as possible.""",
20: """No roles are authorized to perform action %s with the given parameters."""
- '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 + '">',
</table>""" % {'header_label': _("Adding %i items to your baskets") % (colid == -1 and 1 or len(recids)),
'create_new_basket': _("Please choose a basket: %(x_basket_selection_box)s %(x_fmt_open)s(or %(x_url_open)screate a new one%(x_url_close)s first)%(x_fmt_close)s") % \
unsubscribe = """<a href="%s">%s%s</a>""" % (unsubscribe_url, unsubscribe_logo, _("Unsubscribe from basket"))
out = """
<thead>
<tr>
<td class="bskbasketheader"%(optional_colspan)s>
<table>
<tr>
<td class="bskbasketheadertitle">
<strong>
%(name)s
</strong>
<small>
%(records_field)s%(comments_field)s
%(last_update_field)s
</small>
</td>
<td class="bskbasketheaderoptions">
%(subscribe_unsubscribe_basket)s
</td>
</table>
</td>
</tr>
</thead>"""
out %= {'optional_colspan': optional_colspan,
'name': name,
'nb_items': nb_items,
'records_field': records_field,
'comments_field': comments_field,
'last_update_field': last_update_field,
'subscribe_unsubscribe_basket': subscription_status > 0 and unsubscribe or subscription_status < 0 and subscribe or not subscription_status and ' '}
return out
def tmpl_public_basket_footer(self,
bskid,
nb_items,
id_owner,
subscription_status,
ln=CFG_SITE_LANG):
"""Template for public basket footer display."""
_ = gettext_set_language(ln)
optional_colspan = nb_items and ' colspan="3"' or ''
display_owner_url = """%s/yourmessages/write?msg_to=%s""" % (CFG_SITE_URL, nickname or str(uid))
display_owner_text = _("This public basket belongs to the user ")
display_owner = """%s<a href="%s">%s</a>.""" % (display_owner_text, display_owner_url, nickname or display_name)
out = """
<tfoot>
<tr>
<td class="bskbasketfooter"%(optional_colspan)s>
<table>
<tr>
<td class="bskbasketfootertitle">
<small>
%(display_owner)s
</small>
</td>
<td class="bskbasketfooteroptions">
%(subscribe_unsubscribe_basket)s
</td>
</tr>
</table>
</td>
</tr>
</tfoot>"""
out %= {'optional_colspan': optional_colspan,
'display_owner': subscription_status and display_owner or _('This public basket belongs to you.'),
'subscribe_unsubscribe_basket': subscription_status > 0 and unsubscribe or subscription_status < 0 and subscribe or not subscription_status and ' '}
'file_size_limit_msg': CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE > 0 and _("Max %(x_nb_bytes)s per file") % {'x_nb_bytes': (CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE < 1024*1024 and (str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/1024) + 'KB') or (str(CFG_WEBCOMMENT_MAX_ATTACHMENT_SIZE/(1024*1024)) + 'MB'))} or ''}
editor = get_html_text_editor(name='msg',
content=msg,
textual_content=textual_msg,
width='100%',
height='400px',
enabled=CFG_WEBCOMMENT_USE_RICH_TEXT_EDITOR,
file_upload_url=file_upload_url,
toolbar_set = "WebComment")
subscribe_to_discussion = ''
if not user_is_subscribed_to_discussion:
# Offer to subscribe to discussion
subscribe_to_discussion = '<small><input type="checkbox" name="subscribe" id="subscribe"/><label for="subscribe">%s</label></small>' % _("Send me an email when a new comment is posted")
form = """<div id="comment-write"><h2>%(add_comment)s</h2>
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.") % \
<p>Usually, only the necessary files are copied (<a href="http://docs.fckeditor.net/FCKeditor_2.x/Developers_Guide/Deployment">Check which files need to be deployed</a>) and none are modified.</p>
Additional files from Invenio are needed to support the editor
they are basically the only files that interface with the FCKeditor,
and must adapt to modifications of FCKeditor APIs.</p>
<h3><a name="2.3">Configuration</a></h3>
<p>The configuration of FCKeditor (colors, size, behaviour) can be
done when instantiating the editor ("inline", in
<code>htmlutils.get_html_text_editor(..)</code> function) or via a
Javascript config file placed in a web accessible location. Read
FCKeditor documentation to learn more about these options.<br/>
The current solution is to have a maximum of the configuration made in
<code>htmlutils.get_html_text_editor(..)</code>, such that it is easy
to customize the editor directly from the source code, without having
to change any Javascript config file.</p>
<p>For the moment a single Javascript file
(<code>invenio-fckeditor-config.js</code>) is used, mainly to define
the toolbar sets, that cannot be defined "inline".</p>
<p><strong>It is to be thought if it would not be better to have the
configuration for each call of the function (or each Invenio
module) in different config files. That would make the customization
of each instance possible by admin users.</strong></p>
<h3><a name="2.4">Javscript vs Python Integration</a></h3>
<p>FCKeditor can be integrated into pages either via Javascript, or
using the Python wrapper. The current way of doing is to use the
Python wrapper.</p>
<strong>Pro and cons of using the Python wrapper:</strong>
<ul style="list-style-type:none;">
<li><strong>+</strong> easier to read and maintain</li>
<li><strong>+</strong> can be pylinted</li>
<li><strong>+</strong> faster on client-side?</li>
<li><strong>+</strong> can partly hide changes in FCKeditor APIs?</li>
<li><strong>-</strong> cannot check if client has enabled Javascript
(can only check
user-agent for compatibility)<strong><a href="#aboutCheckingClientJavascript">*</a></strong></li>
<li><strong>-</strong> might be dropped at some point?</li>
</ul>
<a name="aboutCheckingClientJavascript"></a><strong>*</strong> A trick is applied to check if client has enabled Javascript when editor is integrated via Python: the complete instantiation code is written via <code>document.write(..)</code> (in Javascript) and a <code><noscript></code> tag is used to fall back on a regular <code><textarea></code>.
<h2><a name="3.">APIs</a></h2>
<h3><a name="3.1">Basic Integration</a></h3>
<p>To integrate the FCKeditor, please exclusively use the following method:</p>
<pre>
from htmlutils import get_html_text_editor
[...]
out += get_html_text_editor('myeditor')
</pre>
<p>Refer to <code>htmlutils.py</code> for more information about the
function and its parameters.</p>
<p>It is wise to always use the above method and never directly rely
on any FCKeditor file. You have to expect that the editor is not
always installed on the server, or that the client might not support
it (eg. Javascript disabled). In these cases, a basic
<code><textarea/></code> is used instead.<br/>
If you need to know what type of input form (<code>textarea</code> or
FCKeditor) was used by the client, you can catch the value of the form
variable <code>editor_type</code>, which is submitted at the same time
as other elements of your form.</p>
<h3><a name="3.2">File Upload Support</a></h3>
<p>In order to support file upload rigth from FCKeditor, you must call
<code>get_html_text_editor(..)</code> with its <code>file_upload_url</code>
parameter set to the URL to which the file will be uploaded.</p>
<p>The second step is to implement the URL handler
<code>file_upload_url</code> so that that it understands the
"commands" sent by FCKeditor, does something with the file (eg. moves
it to a chosen location) and sends a correct reply.</p>
<p>To do so, the easiest is to instantiate an
<code>FCKeditorConnectorInvenio</code> object with the input
parameters, and sends back the value returned by its
<code>doResponse()</code> function. Note that you have to correctly
set the response headers by reading the object <code>headers</code>
member and <strong>implement yourself restrictions checking in your
code</strong>, as this is not managed by the FCKeditorConnectorInvenio
class </p>
<p>You can use the following parameters when instantiating the connector:
Success message if a user applied the "regenerate" link. Links back to
the regenerated journal.
"""
_ = gettext_set_language(ln)
out = '''
The issue number %(issue)s for the %(journal_name)s journal has been successfully
regenerated. <br/>
Look at your changes: >> <a href="%(CFG_SITE_URL)s/journal/%(journal_name)s/%(issue_year)s/%(issue_number)s"> %(journal_name)s </a> <br/> or go back to this journal <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">administration interface</a>.
<td><input tabindex="2" type="text" name="img_url" value="" id="image_url" size="60"/><em><br/><small>Image displayed along the featured record</small></em></td>
or go back to the <a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/administrate?journal_name=%(journal_name)s">administration interface</a>''' % {'CFG_SITE_URL': CFG_SITE_URL,
('<em>not released</em>' + (as_editor and '<br/><a href="%s/admin/webjournal/webjournaladmin.py/issue_control?journal_name=%s">>release now</a>' % (CFG_SITE_URL, journal_name) or '')) or
'released on: %s' % released_on.strftime("%d.%m.%Y"),
(not announced_on)
and ('<em>not announced</em>' + (as_editor and '<br/><a href="%s/admin/webjournal/webjournaladmin.py/alert?journal_name=%s&issue=%s">>announce now</a>' % (CFG_SITE_URL, journal_name, issue) or '')) or
'announced on: %s <br/><a href="%s/admin/webjournal/webjournaladmin.py/alert?journal_name=%s&issue=%s">>re-announce</a>' % (announced_on.strftime("%d.%m.%Y"), CFG_SITE_URL, journal_name, issue),
out += '''<tr style="background-color:#%(color)s">
<td class="admintdleft" colspan="3" style="padding: 5px 10px;"><a href="%(CFG_SITE_URL)s/admin/webjournal/webjournaladmin.py/configure?action=add">Add new journal</a></td>
return '<span style="color:#f00">Configuration could not be read. Please check that %s/webjournal/%s/%s-config.xml exists and can be read by the server.</span><br/>' % (CFG_ETCDIR, journal_name, journal_name)
Go to the <a href="%(CFG_SITE_URL)s/journal/%(name)s">%(name)s journal</a> to
see the result.</span>''' % {'CFG_SITE_URL': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'name': journal_name,
'recid': recid}
elif result == 1:
- msg = '''<span style="color:#f00"><a href="%(CFG_SITE_URL)s/record/%(recid)s">record %(recid)s</a> is already featured. Choose another one or remove it first.</span>''' % \
+ msg = '''<span style="color:#f00"><a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)s">record %(recid)s</a> is already featured. Choose another one or remove it first.</span>''' % \
{'CFG_SITE_URL': CFG_SITE_URL,
+ 'CFG_SITE_RECORD': CFG_SITE_RECORD,
'recid': recid}
else:
msg = '''<span style="color:#f00">Record could not be featured. Check file permission.</span>'''
- <legend>Remove featured record</legend><span style="color:#f00">Are you sure you want to remove <a href="%(CFG_SITE_URL)s/record/%(recid)s">record %(recid)s</a> from the list of featured record?
+ <legend>Remove featured record</legend><span style="color:#f00">Are you sure you want to remove <a href="%(CFG_SITE_URL)s/%(CFG_SITE_RECORD)s/%(recid)s">record %(recid)s</a> from the list of featured record?
Add a new journal or configure the settings of an existing journal.
Parameters:
journal_name - the journal to configure, or name of the new journal
xml_config - the xml configuration of the journal (string)
action - One of ['edit', 'editDone', 'add', 'addDone']
ln - language
"""
msg = None
if action == 'edit':
# Read existing config
if journal_name is not None:
if not can_read_xml_config(journal_name):
return '<span style="color:#f00">Configuration could not be read. Please check that %s/webjournal/%s/%s-config.xml exists and can be read by the server.</span><br/>' % (CFG_ETCDIR, journal_name, journal_name)
return '<span style="color:#f00">You must specify a journal name</span>'
if action in ['editDone', 'addDone']:
# Save config
if action == 'addDone':
res = add_journal(journal_name, xml_config)
if res == -1:
msg = '<span style="color:#f00">A journal with that name already exists. Please choose another name.</span>'
action = 'add'
elif res == -2:
msg = '<span style="color:#f00">Configuration could not be written (no permission). Please manually copy your config to %s/webjournal/%s/%s-config.xml</span><br/>' % (CFG_ETCDIR, journal_name, journal_name)
action = 'edit'
elif res == -4:
msg = '<span style="color:#f00">Cache file could not be written (no permission). Please manually create directory %s/webjournal/%s/ and make it writable for your Apache user</span><br/>' % (CFG_CACHEDIR, journal_name)
<a name="3.11"></a><h3>3.11 Configuration of related external collections</h3>
<p>You can customize each collection to provide your users an
additional source of information external to your repository: in a
<i>book</i> collection you might want for example to provide a link to
<i>Amazon</i> items corresponding to the user's query. Futhermore, for
some external services only, you can set the collection to display the
results directly in Invenio search results page.
<p>The following settings are available:
<dl>
<dt>Disabled</dt>
<dd>The external collection is not shown to the user.<dd>
<dt>See also</dt>
<dd>A link to the external collection listing the items corresponding to user's query is displayed (only once a query has been performed).</dd>
<dt>External search</dt>
<dd>User can ask to perform a search in parallel on your repository and on the external collection. Results are shown in the Invenio search results page. Not available for all external collections.<dd>
<dt>External search checked</dt>
<dd>Same as above, but the external collection is searched by default. Not available for all external collections.</dd>
<dl>
<p>You can also apply the settings to sub-collections, by checking the
"<i>Apply also to daughter collections</i>" checkboxes when you apply
your modifications.
<p>Note that in case you have defined an external hosted collection and you are
in fact configuring its related external collections there is no restriction on
setting even itself as "<em>See also</em>", "<em>External search</em>" or
"<em>External search checked</em>"; directly or recursively via the "<i>Apply
also to daughter collections</i>" option. It is up entirely to the admin to
keep a clean and consistent installation (for more detailed information see <a
href="#4">section 4</a>).
<a name="3.12"></a><h3>3.12 Detailed record page options</h3>
<p>These settings let you define how the detailed view (such as <a
-href="<CFG_SITE_URL>/record/1"><CFG_SITE_URL>/record/1</a>) of records in this
+href="<CFG_SITE_URL>/<CFG_SITE_RECORD>/1"><CFG_SITE_URL>/<CFG_SITE_RECORD>/1</a>) of records in this
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))
# combine the current hitset with resulting hitset using the current operator
if current_operator == '+':
result_hitset = result_hitset & current_hitset
elif current_operator == '-':
result_hitset = result_hitset - current_hitset
elif current_operator == '|':
result_hitset = result_hitset | current_hitset
else:
assert False, "Unknown operator in search_pattern_parenthesised()"
return result_hitset
# If searching with parenteses fails, perform search ignoring parentheses
except SyntaxError:
print_warning(req, _("Search syntax misunderstood. Ignoring all parentheses in the query. If this doesn't help, please check your search and try again."))
# remove the parentheses in the query. Current implementation removes all the parentheses,
# but it could be improved to romove only these that are not inside quotes
print_warning(req, _("No match found in collection %(x_collection)s. Other public collections gave %(x_url_open)s%(x_nb_hits)d hits%(x_url_close)s.") %\
{'x_collection': '<em>' + string.join([get_coll_i18nname(coll, ln, False) for coll in colls], ', ') + '</em>',
"""Sort records in 'recIDs' list according sort field 'sort_field' in order 'sort_order'.
If more than one instance of 'sort_field' is found for a given record, try to choose that that is given by
'sort pattern', for example "sort by report number that starts by CERN-PS".
Note that 'sort_field' can be field code like 'author' or MARC tag like '100__a' directly."""
_ = gettext_set_language(ln)
## check arguments:
if not sort_field:
return recIDs
if len(recIDs) > CFG_WEBSEARCH_NB_RECORDS_TO_SORT:
if of.startswith('h'):
print_warning(req, _("Sorry, sorting is allowed on sets of up to %d records only. Using default sort order.") % CFG_WEBSEARCH_NB_RECORDS_TO_SORT, "Warning")
return recIDs
sort_fields = string.split(sort_field, ",")
recIDs_dict = {}
recIDs_out = []
## first deduce sorting MARC tag out of the 'sort_field' argument:
tags = []
for sort_field in sort_fields:
if sort_field and str(sort_field[0:2]).isdigit():
# sort_field starts by two digits, so this is probably a MARC tag already
tags.append(sort_field)
else:
# let us check the 'field' table
query = """SELECT DISTINCT(t.value) FROM tag AS t, field_tag AS ft, field AS f
WHERE f.code=%s AND ft.id_field=f.id AND t.id=ft.id_tag
ORDER BY ft.score DESC"""
res = run_sql(query, (sort_field, ))
if res:
for row in res:
tags.append(row[0])
else:
if of.startswith('h'):
print_warning(req, _("Sorry, %s does not seem to be a valid sort option. Choosing title sort instead.") % cgi.escape(sort_field), "Error")
tags.append("245__a")
if verbose >= 3:
print_warning(req, "Sorting by tags %s." % cgi.escape(repr(tags)))
if sort_pattern:
print_warning(req, "Sorting preferentially by %s." % cgi.escape(sort_pattern))
## check if we have sorting tag defined:
if tags:
# fetch the necessary field values:
for recID in recIDs:
val = "" # will hold value for recID according to which sort
vals = [] # will hold all values found in sorting tag for recID
for tag in tags:
if CFG_CERN_SITE and tag == '773__c':
# CERN hack: journal sorting
# 773__c contains page numbers, e.g. 3-13, and we want to sort by 3, and numerically:
vals.extend(["%050s" % x.split("-",1)[0] for x in get_fieldvalues(recID, tag)])
else:
vals.extend(get_fieldvalues(recID, tag))
if sort_pattern:
# try to pick that tag value that corresponds to sort pattern
bingo = 0
for v in vals:
if v.lower().startswith(sort_pattern.lower()): # bingo!
bingo = 1
val = v
break
if not bingo: # sort_pattern not present, so add other vals after spaces
val = sort_pattern + " " + string.join(vals)
else:
# no sort pattern defined, so join them all together
val = string.join(vals)
val = strip_accents(val.lower()) # sort values regardless of accents and case
print_warning(req, "Hosted collections (perform_search_request): there were no hosted collections to be searched")
## let's define some useful boolean variables:
# True means there are actual or potential hosted collections results to be printed
hosted_colls_actual_or_potential_results_p = not (not hosted_colls or not ((hosted_colls_results and hosted_colls_true_results) or hosted_colls_timeouts))
# True means there are hosted collections timeouts to take care of later
# (useful for more accurate printing of results later)
hosted_colls_potential_results_p = not (not hosted_colls or not hosted_colls_timeouts)
# True means we only have hosted collections to deal with
only_hosted_colls_actual_or_potential_results_p = not colls_to_search and hosted_colls_actual_or_potential_results_p
expected_text='Search term <em><SCRIPT>alert("XSS");</SCRIPT></em> inside index <em><script>alert("xss");</script></em> did not match any record.'))
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)
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)
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])
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 />"
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]])
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 += """<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 />
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]])
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])
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")))
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 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}
<td>1. <small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&ln=%s&mtype=perform_addcollection">Create new collection</a></small></td>
<td>2. <small><a href="%s/admin/websearch/websearchadmin.py?colID=%s&ln=%s&mtype=perform_addcollectiontotree">Attach collection to tree</a></small></td>
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&ln=%s&move_up=%s&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 += """ """
text += "</td><td>"
if down == 1 and edit:
text += """<a href="%s/admin/websearch/websearchadmin.py/modifycollectiontree?colID=%s&ln=%s&move_down=%s&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 += """ """
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&ln=%s&move_from=%s&move_to=%s%s&rtype=%s#tree"><img border="0" src="%s/img/move_to.gif" title="Move '%s' to '%s'"></a>
<dd>When deleting a collection, you also deletes all data related to the collection like translations, relations to other collections and information about which rank methods to use.
<br />For more information, please go to the <a title="See guide" href="%s/help/admin/websearch-admin-guide">WebSearch guide</a> and read the section regarding deleting a collection.</dd>
</dl>
</strong>
</span>
""" % CFG_SITE_URL
col_dict = dict(get_def_name('', "collection"))
if colID != 1 and colID and col_dict.has_key(int(colID)):
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>"""
<td>11. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_manage_external_collections#11">Configuration of related external collections</a></small></td>
<td>12. <small><a href="editcollection?colID=%s&ln=%s&mtype=perform_showdetailedrecordoptions#12">Detailed record page options</a></small></td>
if collection_table_update_time > collection_web_update_time:
output += """<br /><b><span class="info">Warning: The collections have been modified since last time Webcoll was executed, to process the changes, Webcoll must be executed.</span></b><br />"""
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='webcoll' and runtime< now() ORDER by runtime")
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='bibindex' and runtime< now() ORDER by runtime")
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='webcoll' and runtime > now() ORDER by runtime")
res = run_sql("select id, proc, host, user, runtime, sleeptime, arguments, status, progress from schTASK where proc='bibindex' and runtime > now() ORDER by runtime")
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 />"""
output += """<br /><span class=info>New collection \"%s\" has been added to the database table \"externalcollection\".</span><br />""" % (collection)
else:
output += """<br /><span class=info>Collection \"%s\" has already been added to the database table \"externalcollection\" or was already there.</span><br />""" % (collection)
elif update == "del":
# icl : the "inconsistent list" comes as a string, it has to be converted back into a list
icl = eval(icl)
#icl = icl[1:-1].split(',')
for collection in icl:
#collection = str(collection[1:-1])
query_select = "SELECT id FROM externalcollection WHERE name like '%(name)s';" % {'name': collection}
results_select = run_sql(query_select)
if results_select:
query_delete = "DELETE FROM externalcollection WHERE id like '%(id)s';" % {'id': results_select[0][0]}
query_delete_states = "DELETE FROM collection_externalcollection WHERE id_externalcollection like '%(id)s';" % {'id': results_select[0][0]}
run_sql(query_delete)
run_sql(query_delete_states)
output += """<br /><span class=info>Collection \"%s\" has been deleted from the database table \"externalcollection\".</span><br />""" % (collection)
else:
output += """<br /><span class=info>Collection \"%s\" has already been delete from the database table \"externalcollection\" or was never there.</span><br />""" % (collection)
external_collections_file = []
external_collections_db = []
for coll in external_collections_dictionary.values():
external_collections_file.append(coll.name)
external_collections_file.sort()
query = """SELECT name from externalcollection"""
results = run_sql(query)
for result in results:
external_collections_db.append(result[0])
external_collections_db.sort()
number_file = len(external_collections_file)
number_db = len(external_collections_db)
if external_collections_file == external_collections_db:
output += """<br /><span class="info">External collections are consistent.</span><br /><br />
- database table \"externalcollection\" has %(number_db)s collections<br />
- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections""" % {
Click here</a> to force remove the extra collections from your database (warning: use with caution!). If the problem persists please check your configuration manually.""" % {
"number_db" : number_db,
"number_file" : number_file,
"diff" : external_collections_diff,
"site_url" : CFG_SITE_URL,
"colID" : colID,
"ln" : ln}
else:
output += """<br /><span class="warning">There is an inconsistency:</span><br /><br />
- database table \"externalcollection\" has %(number_db)s collections<br />
- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections
<br /><br /><span class="warning">The external collections do not match.</span>
<br />To fix the problem please check your configuration manually.""" % {
"number_db" : number_db,
"number_file" : number_file}
else:
output += """<br /><span class="warning">There is an inconsistency:</span><br /><br />
- database table \"externalcollection\" has %(number_db)s collections<br />
- configuration file \"websearch_external_collections_config.py\" has %(number_file)s collections
<br /><br /><span class="warning">The number of external collections is the same but the collections do not match.</span>
<br />To fix the problem please check your configuration manually.""" % {
tree = tree[0:ssize] + ntree + tree[ssize:len(tree)]
return tree
except StandardError, e:
register_exception()
return ()
def add_col_dad_son(add_dad, add_son, rtype):
"""Add a son to a collection (dad)
add_dad - add to this collection id
add_son - add this collection id
rtype - either regular or virtual"""
try:
res = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s ORDER BY score ASC", (add_dad, ))
highscore = 0
for score in res:
if int(score[0]) > highscore:
highscore = int(score[0])
highscore += 1
res = run_sql("INSERT INTO collection_collection(id_dad,id_son,score,type) values(%s,%s,%s,%s)", (add_dad, add_son, highscore, rtype))
return (1, highscore)
except StandardError, e:
register_exception()
return (0, e)
def compare_on_val(first, second):
"""Compare the two values"""
return cmp(first[1], second[1])
def get_col_fld(colID=-1, type = '', id_field=''):
"""Returns either all portalboxes associated with a collection, or based on either colID or language or both.
colID - collection id
ln - language id"""
sql = "SELECT id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue, field WHERE id_field=field.id"
params = []
if colID > -1:
sql += " AND id_collection=%s"
params.append(colID)
if id_field:
sql += " AND id_field=%s"
params.append(id_field)
if type:
sql += " AND type=%s"
params.append(type)
sql += " ORDER BY type, score desc, score_fieldvalue desc"
res = run_sql(sql, tuple(params))
return res
def get_col_pbx(colID=-1, ln='', position = ''):
"""Returns either all portalboxes associated with a collection, or based on either colID or language or both.
colID - collection id
ln - language id"""
sql = "SELECT id_portalbox, id_collection, ln, score, position, title, body FROM collection_portalbox, portalbox WHERE id_portalbox = portalbox.id"
params = []
if colID > -1:
sql += " AND id_collection=%s"
params.append(colID)
if ln:
sql += " AND ln=%s"
params.append(ln)
if position:
sql += " AND position=%s"
params.append(position)
sql += " ORDER BY position, ln, score desc"
res = run_sql(sql, tuple(params))
return res
def get_col_fmt(colID=-1):
"""Returns all formats currently associated with a collection, or for one specific collection
colID - the id of the collection"""
if colID not in [-1, "-1"]:
res = run_sql("SELECT id_format, id_collection, code, score FROM collection_format, format WHERE id_format = format.id AND id_collection=%s ORDER BY score desc", (colID, ))
else:
res = run_sql("SELECT id_format, id_collection, code, score FROM collection_format, format WHERE id_format = format.id ORDER BY score desc")
return res
def get_col_rnk(colID, ln):
""" Returns a list of the rank methods the given collection is attached to
colID - id from collection"""
try:
res1 = dict(run_sql("SELECT id_rnkMETHOD, '' FROM collection_rnkMETHOD WHERE id_collection=%s", (colID, )))
res2 = get_def_name('', "rnkMETHOD")
result = filter(lambda x: res1.has_key(x[0]), res2)
return result
except StandardError, e:
return ()
def get_pbx():
"""Returns all portalboxes"""
res = run_sql("SELECT id, title, body FROM portalbox ORDER by title,body")
return res
def get_fld_value(fldvID = ''):
"""Returns fieldvalue"""
sql = "SELECT id, name, value FROM fieldvalue"
params = []
if fldvID:
sql += " WHERE id=%s"
params.append(fldvID)
sql += " ORDER BY name"
res = run_sql(sql, tuple(params))
return res
def get_pbx_pos():
"""Returns a list of all the positions for a portalbox"""
position = {}
position["rt"] = "Right Top"
position["lt"] = "Left Top"
position["te"] = "Title Epilog"
position["tp"] = "Title Prolog"
position["ne"] = "Narrow by coll epilog"
position["np"] = "Narrow by coll prolog"
return position
def get_sort_nametypes():
"""Return a list of the various translationnames for the fields"""
type = {}
type['soo'] = 'Sort options'
type['seo'] = 'Search options'
type['sew'] = 'Search within'
return type
def get_fmt_nametypes():
"""Return a list of the various translationnames for the output formats"""
type = []
type.append(('ln', 'Long name'))
return type
def get_fld_nametypes():
"""Return a list of the various translationnames for the fields"""
type = []
type.append(('ln', 'Long name'))
return type
def get_col_nametypes():
"""Return a list of the various translationnames for the collections"""
type = []
type.append(('ln', 'Long name'))
return type
def find_last(tree, start_son):
"""Find the previous collection in the tree with the same father as start_son"""
id_dad = tree[start_son][3]
while start_son > 0:
start_son -= 1
if tree[start_son][3] == id_dad:
return start_son
def find_next(tree, start_son):
"""Find the next collection in the tree with the same father as start_son"""
id_dad = tree[start_son][3]
while start_son < len(tree):
start_son += 1
if tree[start_son][3] == id_dad:
return start_son
def remove_col_subcol(id_son, id_dad, type):
"""Remove a collection as a son of another collection in the tree, if collection isn't used elsewhere in the tree, remove all registered sons of the id_son.
id_son - collection id of son to remove
id_dad - the id of the dad"""
try:
if id_son != id_dad:
tree = get_col_tree(id_son)
run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s", (id_son, id_dad))
else:
tree = get_col_tree(id_son, type)
run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s and type=%s", (id_son, id_dad, type))
if not run_sql("SELECT id_dad,id_son,type,score from collection_collection WHERE id_son=%s and type=%s", (id_son, type)):
for (id, up, down, dad, rtype) in tree:
run_sql("DELETE FROM collection_collection WHERE id_son=%s and id_dad=%s", (id, dad))
return (1, "")
except StandardError, e:
return (0, e)
def check_col(add_dad, add_son):
"""Check if the collection can be placed as a son of the dad without causing loops.
add_dad - collection id
add_son - collection id"""
try:
stack = [add_dad]
res = run_sql("SELECT id_dad FROM collection_collection WHERE id_dad=%s AND id_son=%s", (add_dad, add_son))
if res:
raise StandardError
while len(stack) > 0:
colID = stack.pop()
res = run_sql("SELECT id_dad FROM collection_collection WHERE id_son=%s", (colID, ))
for id in res:
if int(id[0]) == int(add_son):
# raise StandardError # this was the original but it didnt work
return(0)
else:
stack.append(id[0])
return (1, "")
except StandardError, e:
return (0, e)
def attach_rnk_col(colID, rnkID):
"""attach rank method to collection
rnkID - id from rnkMETHOD table
colID - id of collection, as in collection table """
try:
res = run_sql("INSERT INTO collection_rnkMETHOD(id_collection, id_rnkMETHOD) values (%s,%s)", (colID, rnkID))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def detach_rnk_col(colID, rnkID):
"""detach rank method from collection
rnkID - id from rnkMETHOD table
colID - id of collection, as in collection table """
try:
res = run_sql("DELETE FROM collection_rnkMETHOD WHERE id_collection=%s AND id_rnkMETHOD=%s", (colID, rnkID))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def switch_col_treescore(col_1, col_2):
try:
res1 = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s and id_son=%s", (col_1[3], col_1[0]))
res2 = run_sql("SELECT score FROM collection_collection WHERE id_dad=%s and id_son=%s", (col_2[3], col_2[0]))
res = run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s and id_son=%s", (res2[0][0], col_1[3], col_1[0]))
res = run_sql("UPDATE collection_collection SET score=%s WHERE id_dad=%s and id_son=%s", (res1[0][0], col_2[3], col_2[0]))
score - decides which portalbox is the most important
position - position on page the portalbox should appear."""
try:
if score:
res = run_sql("INSERT INTO collection_portalbox(id_portalbox, id_collection, ln, score, position) values (%s,%s,'%s',%s,%s)", (pbxID, colID, ln, score, position))
else:
res = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and ln=%s and position=%s ORDER BY score desc, ln, position", (colID, ln, position))
if res:
score = int(res[0][0])
else:
score = 0
res = run_sql("INSERT INTO collection_portalbox(id_portalbox, id_collection, ln, score, position) values (%s,%s,%s,%s,%s)", (pbxID, colID, ln, (score + 1), position))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def add_col_fmt(colID, fmtID, score=''):
"""Add a output format to the collection.
colID - the id of the collection involved
fmtID - the id of the format.
score - the score of the format, decides sorting, if not given, place the format on top"""
try:
if score:
res = run_sql("INSERT INTO collection_format(id_format, id_collection, score) values (%s,%s,%s)", (fmtID, colID, score))
else:
res = run_sql("SELECT score FROM collection_format WHERE id_collection=%s ORDER BY score desc", (colID, ))
if res:
score = int(res[0][0])
else:
score = 0
res = run_sql("INSERT INTO collection_format(id_format, id_collection, score) values (%s,%s,%s)", (fmtID, colID, (score + 1)))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def add_col_fld(colID, fldID, type, fldvID=''):
"""Add a sort/search/field to the collection.
colID - the id of the collection involved
fldID - the id of the field.
fldvID - the id of the fieldvalue.
type - which type, seo, sew...
score - the score of the format, decides sorting, if not given, place the format on top"""
try:
if fldvID and fldvID not in [-1, "-1"]:
run_sql("DELETE FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s and id_fieldvalue is NULL", (colID, fldID, type))
res = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s ORDER BY score desc", (colID, fldID, type))
if res:
score = int(res[0][0])
res = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s AND id_field=%s and type=%s ORDER BY score_fieldvalue desc", (colID, fldID, type))
else:
res = run_sql("SELECT score FROM collection_field_fieldvalue WHERE id_collection=%s and type=%s ORDER BY score desc", (colID, type))
if res:
score = int(res[0][0]) + 1
else:
score = 1
res = run_sql("SELECT id_collection,id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue where id_field=%s and id_collection=%s and type=%s and id_fieldvalue=%s", (fldID, colID, type, fldvID))
if not res:
run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=score_fieldvalue+1 WHERE id_field=%s AND id_collection=%s and type=%s", (fldID, colID, type))
res = run_sql("INSERT INTO collection_field_fieldvalue(id_field, id_fieldvalue, id_collection, type, score, score_fieldvalue) values (%s,%s,%s,%s,%s,%s)", (fldID, fldvID, colID, type, score, 1))
else:
return (0, (1, "Already exists"))
else:
res = run_sql("SELECT id_collection,id_field,id_fieldvalue,type,score,score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s AND type=%s and id_field=%s and id_fieldvalue is NULL", (colID, type, fldID))
if res:
return (0, (1, "Already exists"))
else:
run_sql("UPDATE collection_field_fieldvalue SET score=score+1")
res = run_sql("INSERT INTO collection_field_fieldvalue(id_field, id_collection, type, score,score_fieldvalue) values (%s,%s,%s,%s, 0)", (fldID, colID, type, 1))
return (1, "")
except StandardError, e:
register_exception()
return (0, e)
def modify_dbquery(colID, dbquery=None):
"""Modify the dbquery of an collection.
colID - the id of the collection involved
dbquery - the new dbquery"""
# BTW, sometimes '' is passed instead of None, so change it to None
if not dbquery:
dbquery = None
try:
res = run_sql("UPDATE collection SET dbquery=%s WHERE id=%s", (dbquery, colID))
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (colID, id_1, fldvID_1))
res2 = run_sql("SELECT score_fieldvalue FROM collection_field_fieldvalue WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (colID, id_1, fldvID_2))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
else:
res = run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (res2[0][0], colID, id_1, fldvID_1))
res = run_sql("UPDATE collection_field_fieldvalue SET score_fieldvalue=%s WHERE id_collection=%s and id_field=%s and id_fieldvalue=%s", (res1[0][0], colID, id_1, fldvID_2))
return (1, "")
except Exception, e:
register_exception()
return (0, e)
def switch_pbx_score(colID, id_1, id_2, sel_ln):
"""Switch the scores of id_1 and id_2 in the table given by the argument.
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and id_portalbox=%s and ln=%s", (colID, id_1, sel_ln))
res2 = run_sql("SELECT score FROM collection_portalbox WHERE id_collection=%s and id_portalbox=%s and ln=%s", (colID, id_2, sel_ln))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (res2[0][0], colID, id_1, sel_ln))
res = run_sql("UPDATE collection_portalbox SET score=%s WHERE id_collection=%s and id_portalbox=%s and ln=%s", (res1[0][0], colID, id_2, sel_ln))
return (1, "")
except Exception, e:
register_exception()
return (0, e)
def switch_score(colID, id_1, id_2, table):
"""Switch the scores of id_1 and id_2 in the table given by the argument.
colID - collection the id_1 or id_2 is connected to
id_1/id_2 - id field from tables like format..portalbox...
table - name of the table"""
try:
res1 = run_sql("SELECT score FROM collection_%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (colID, id_1))
res2 = run_sql("SELECT score FROM collection_%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (colID, id_2))
if res1[0][0] == res2[0][0]:
return (0, (1, "Cannot rearrange the selected fields, either rearrange by name or use the mySQL client to fix the problem."))
res = run_sql("UPDATE collection_%s SET score=%%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (res2[0][0], colID, id_1))
res = run_sql("UPDATE collection_%s SET score=%%s WHERE id_collection=%%s and id_%s=%%s" % (table, table), (res1[0][0], colID, id_2))
(re.compile(r"/search"), "<p>" + _("This collection is restricted. If you think you have right to access it, please authenticate yourself.") + "</p>"),
- (re.compile(r"/record/\d+/files/.+"), "<p>" + _("This file is restricted. If you think you have right to access it, please authenticate yourself.") + "</p>"),
+ (re.compile(r"/%s/\d+/files/.+" % CFG_SITE_RECORD), "<p>" + _("This file is restricted. If you think you have right to access it, please authenticate yourself.") + "</p>"),
)
msg = ""
for regexp, txt in login_referrer2msg:
if regexp.search(referer):
msg = txt
break
internal = None
for system in CFG_EXTERNAL_AUTHENTICATION.keys():
if CFG_EXTERNAL_AUTHENTICATION[system] is None:
internal = system
break
register_available = CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS <= 1 and internal
## Let's retrieve all the login method that are not dedicated to robots
methods = [method[0] for method in CFG_EXTERNAL_AUTHENTICATION.iteritems() if not method[1] or not method[1].robot_login_method_p()]
methods.sort()
return websession_templates.tmpl_login_form(
ln = ln,
referer = referer,
internal = internal,
register_available = register_available,
methods = methods,
selected_method = CFG_EXTERNAL_AUTH_DEFAULT,
msg = msg,
)
# perform_logout: display the message of not longer authorized,
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 += "<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">' \
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>"
- '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.")
'basket_explain' : _("With baskets you can define specific collections of items, store interesting records you want to access later or share with others."),
}
if guest and CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
out += self.tmpl_warning_guest_user(ln = ln, type = "baskets")
'explain_alerts' : _("Subscribe to a search which will be run periodically by our service. The result can be sent to you via Email or stored in one of your baskets."),
}
if guest and CFG_WEBSESSION_DIFFERENTIATE_BETWEEN_GUESTS:
out += self.tmpl_warning_guest_user(type="alerts", ln = ln)
'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.") %\
'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.""") % \
- 'ln' *string* - The language to display the interface in
- 'referer' *string* - The referer URL - will be redirected upon after login
- 'internal' *boolean* - If we are producing an internal authentication
- 'register_available' *boolean* - If users can register freely in the system
- 'methods' *array* - The available authentication methods
- 'selected_method' *string* - The default authentication method
- 'msg' *string* - The message to print before the form, if needed
"""
# load the right message language
_ = gettext_set_language(ln)
if msg is "":
out = "<p>%(please_login)s</p>" % {
'please_login' : _("If you already have an account, please login using the form below.")
}
if CFG_CERN_SITE:
out += "<p>" + _("If you don't own a CERN account yet, you can register a %(x_url_open)snew CERN lightweight account%(x_url_close)s.") % {'x_url_open' : '<a href="https://www.cern.ch/lightweightregistration/RegisterAccount.aspx">', 'x_url_close' : '</a>'} + "</p>"
else:
if register_available:
out += "<p>"+_("If you don't own an account yet, please %(x_url_open)sregister%(x_url_close)s an internal account.") %\
'password_contain' : _("The password phrase may contain punctuation, spaces, etc."),
'retype' : _("Retype Password"),
'register' : _("register"),
'explain_acc' : _("Please do not use valuable passwords such as your Unix, AFS or NICE passwords with this service. Your email address will stay strictly confidential and will not be disclosed to any third party. It will be used to identify you for personal services of %s. For example, you may set up an automatic alert search that will look for new preprints and will notify you daily of new arrivals by email.") % CFG_SITE_NAME,
}
else:
# level >=2, so users cannot register accounts
out += "<p>" + _("It is not possible to create an account yourself. Contact %s if you want an account.") % ('<a href="mailto:%s">%s</a>' % (CFG_SITE_SUPPORT_EMAIL, CFG_SITE_SUPPORT_EMAIL)) + "</p>"
@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).")
display information to the admin user about possible
ssecurity problems in the system.
"""
message = ""
_ = gettext_set_language(ln)
#Try and connect to the mysql database with the default invenio password
if "warning_mysql_password_equal_to_invenio_password" in warning_list:
message += "<p><font color=red>"
message += _("Warning: The password set for MySQL root user is the same as the default Invenio password. For security purposes, you may want to change the password.")
message += "</font></p>"
#Try and connect to the invenio database with the default invenio password
if "warning_invenio_password_equal_to_default" in warning_list:
message += "<p><font color=red>"
message += _("Warning: The password set for the Invenio MySQL user is the same as the shipped default. For security purposes, you may want to change the password.")
message += "</font></p>"
#Check if the admin password is empty
if "warning_empty_admin_password" in warning_list:
message += "<p><font color=red>"
message += _("Warning: The password set for the Invenio admin user is currently empty. For security purposes, it is strongly recommended that you add a password.")
message += "</font></p>"
#Check if the admin email has been changed from the default
if "warning_site_support_email_equal_to_default" in warning_list:
message += "<p><font color=red>"
message += _("Warning: The email address set for support email is currently set to info@invenio-software.org. It is recommended that you change this to your own address.")
message += "</font></p>"
#Check for a new release
if "note_new_release_available" in warning_list:
message += "<p><font color=red>"
message += _("A newer version of Invenio is available for download. You may want to visit ")
if activated == 1: # Ok we consider the user as logged in :-)
setUid(req, uid)
return 0
def updateDataUser(uid, email, nickname):
"""
Update user data. Used when a user changed his email or password
or nickname.
"""
email = email.lower()
if email == 'guest':
return 0
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 2:
run_sql("update user set email=%s where id=%s", (email, uid))
if nickname and nickname != '':
run_sql("update user set nickname=%s where id=%s", (nickname, uid))
return 1
def updatePasswordUser(uid, password):
"""Update the password of a user."""
if CFG_ACCESS_CONTROL_LEVEL_ACCOUNTS < 3:
run_sql("update user set password=AES_ENCRYPT(email,%s) where id=%s", (password, uid))
return 1
def loginUser(req, p_un, p_pw, login_method):
"""It is a first simple version for the authentication of user. It returns the id of the user,
for checking afterwards if the login is correct
"""
# p_un passed may be an email or a nickname:
p_email = get_email_from_username(p_un)
# go on with the old stuff based on p_email:
if not login_method in CFG_EXTERNAL_AUTHENTICATION:
return ([], p_email, p_pw, 12)
if CFG_EXTERNAL_AUTHENTICATION[login_method]: # External Authenthication
try:
p_email = CFG_EXTERNAL_AUTHENTICATION[login_method].auth_user(p_email, p_pw, req) or CFG_EXTERNAL_AUTHENTICATION[login_method].auth_user(p_un, p_pw, req) ## We try to login with either the email of the nickname
if p_email:
p_email = p_email.lower()
else:
return([], p_email, p_pw, 15)
except InvenioWebAccessExternalAuthError:
register_exception(req=req, alert_admin=True)
raise
if p_email: # Authenthicated externally
query_result = run_sql("SELECT id from user where email=%s", (p_email,))
if not query_result: # First time user
p_pw_local = int(random.random() * 1000000)
p_nickname = ''
if CFG_EXTERNAL_AUTHENTICATION[login_method].enforce_external_nicknames:
return self.sendError(1, "This connector couldn\'t access to local user\'s files directories. Please check the UserFilesAbsolutePath in \"editor/filemanager/connectors/py/config.py\" and try again. ")
# File upload doesn't have to return XML, so intercept here
<p>Figure 6 also shows the possibility to select among various
categories prior to jumping into one of the available actions. These
categories usually don't have a direct impact on the chosen
workflow. Think of them simply as a simple WebSubmit element place on
the first page, that is common to all the actions of your submission
(indeed you could set up your submissions to have such categories
inside your submission actions pages, but that would require
additional work).</p>
<p>Last, but not least, a submission is usually referred to by a short
name (at most 5 letters), reused in many places in the WebSubmit admin
interface.</p>
To summarize:
<ul>
<li>A <b>submission</b> is made of different actions</li>
<li>An <b>action</b> is a workflow made of pages, checks and a flow of functions.</li>
<li>A <b>page</b> contains several WebSubmit elements, usually input elements with some label.</li>
<li>A <b>WebSubmit element</b> is a control on the interface to input or display values.</li>
<li>Javacript <b>checks</b> can be attached to WebSubmit elements, in order to validate the input data before going to a futher step of the submission.</li>
<li>A <b>function</b> performs some post-processing operations, usually
on data collected thanks to WebSubmit elements. Functions can have side-effects and outputs</li>
<li>Functions are organized in <b>steps</b>, blocks of functions</li>
</ul>
<p style="font-style: italic;">Another concept remains to be explained, but this functionality tends to disappear from submissions, and might be deprecated at some point. We provide the explanation about it below only for completeness, but it is strongly discouraged to go that way:<br/>
<blockquote>
It is possible to group actions in <b>sets</b>: an action set is a succession of actions which should be done in a given order when a user starts.<br/>
For example the submission of a document can be composed of two actions: Submission of Bibliographic Information (SBI) and Fulltext Transfer (FTT) which should be done one after the other.<br/>
When the user starts the submission, we want the submission to get him first in SBI and when he finishes SBI to carry him to FTT. SBI and FTT are in this case in the same action set. They will both have a level of 1 ("level" is a bad name, it should be "action set number"), SBI will have a score of 1, and FTT a score of 2 (which means it will be started after SBI). If you set the stpage of FTT to 2, the user will be directly carried to the 2nd page of the FTT web form. This value is usually set to 1.<br/>
The endtxt field contains the text which will be displayed to the user at the end of the first action (here it could be "you now have to transfer your files")<br/>
A single action like "Modify Bibliographic Information" should have the 3 columns to 0,0 and 1.
</blockquote>
</p>
<h3><a name="behindthescenes">1.2 Behind the scenes</a></h3>
<p>This section highlights a few key behaviours of WebSubmit which are
particularly important to understand when designing a submission.</p>
<p>When a user starts a new submission, a working directory is created
on disk in order to store all the collected values. This working
directory is usually called the "<code>curdir</code>". It is located
in a subdirectory
of <code>/opt/invenio/var/data/submit/storage/</code><small><em>{action
<p>To circumvent this limitation (as well as the impossibility to
delete files), you might combine this technique with one of the
techniques described below (For eg: with
the <code>Move_Revised_Files_To_Storage</code> function detailed in
the <a href="#2.2revise">Revising/deleting files</a> section of
the <a href="#2.2">File Input element + Move_Files_To_Storage
function</a> technique) </p>
<a name="5.2"></a><h3>5.2 File Input element + Move_Files_To_Storage function</h3>
<p>This way of doing is similar to the <a href="#2.1">technique
described above</a>. The main difference is that it leaves the job of
actually uploading/revisings the file(s) to a WebSubmit functions,
instead of the FFT in the uploaded MARCXML.</p>
<b>Limitations:</b>
<ul>
<li>revision of files requires
well-defined <code>doctype</code>. The consequence is that you can
have only one file per doctype (1 "Main", 1 "Additionnal",
etc.)</li>
<li>cannot easily delete files</li>
<li>does not support setting some additional file attributes (description, name, etc.)</li>
<li>uploaded doctypes must inherit the names of their <code>File Input</code> elements. For eg. "DEMO_FILE", instead of "Main", "Additional", "Figure", etc.</li>
</ul>
<p><b>1-4)</b> Add a file input field to your submission page as
describe in <a href="#2.1">previous technique</a>.</p>
<p>As before, the file is uploaded to the server once the user ends
the submission, but it is not attached to the created record. The
solution is to rely on the "<code>Move_Files_To_Storage</code>" function:</p>
<p><b>5)</b> Add the "<code>Move_Files_To_Storage</code>" function to your submission
functions. It is suggested to insert it after the function
"Insert_Record".</p>
<p><b>6)</b> Configure the <code>Move_Files_To_Storage</code>
function. The key parameter is <code>paths_and_suffixes</code>, which
must contain your <code>File Input</code> element names, and possibly
map to some suffixes to be added to the corresponding uploaded
files.<br/> For example, add <code>{'DEMO_FILE':'',
'DEMO_FILE2':'_additional'}</code> to have the files uploaded with
DEMO_FILE and DEMO_FILE2 elements attached to the record (with the
DEMO_FILE2 filename suffixed with
"_additional"). The <code>paths_and_restriction</code> works similarly
to set the files restrictions.
</p>
<p>Each file is simply attached to the record, with its document type
(<code>doctype</code>) being the name of your input file element (for e.g. file
uploaded with the "<code>DEMO_FILE</code>" element is attached with document type
"<code>DEMO_FILE</code>"). The filenames are kept.</p>
<p>The "<code>Move_Revised_Files_To_Storage</code>" must be added to your modification
workflow ("MBI"). It will use the file uploaded with your "<code>DEMO_FILE</code>"
input element to revise the file with <code>doctype</code> "<code>DEMO_FILE</code>", the file
from "<code>DEMO_FILE2</code>" input element to revise file with <code>doctype</code>
"<code>DEMO_FILE2</code>", etc.</p>
<p><b>1)</b> Go to your modification workflow (MBI), and add
<code>Move_Revised_Files_To_Storage</code> to your submission
functions (usually after the "<code>Insert_Modify_Record</code>").</p>
<p><b>2)</b> Set up the <code>elementNameToDoctype</code> parameter of
this function so it maps your <code>File Input</code> field name to
the doctype to revise. For eg: "<code>DEMO_FILE=Main</code>" so that
file uploaded using the <code>DEMO_FILE</code> input field will be
used to replace the file with <code>doctype</code> "Main". This makes
the assumption that you indeed previously uploaded (for eg. with an
FFT during an SBI step) a file with this doctype.<br/> You can define
several mappings, by using character <code>|</code> as separator. For
eg:
<code>DEMO_FILE=Main|DEMO_FILE2=Additional</code>.<br/> If you have
initially uploaded your files with
the <code>Move_Files_To_Storage</code> function, you will for
eg. configure the parameter with "<code>DEMO_FILE=DEMO_FILE</code>",
so that file uploaded with <code>DEMO_FILE</code> input field will
replace the files that have been previously uploaded with doctype
"DEMO_FILE".
</p>
<p>Note that function <code>Move_Revised_Files_To_Storage</code> can
be used in combination with other techniques, as long as the mapping
in <code>elementNameToDoctype</code> can be done unambiguously.</p>
<p>Check
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Move_Revised_Files_to_Storage"><code>Move_Revised_Files_To_Storage</code>
function documentation for more detailed information.</p>
<p>This option offers a full-featured file manager, that can be
easily configured to support file upload, revision, deletion,
commenting, restrictions, etc. It can handle an "unlimited" number of
files.</p>
<p>The strategy consists in adding a WebSubmit function
("<code>Create_Upload_Files_Interface</code>") to your submission functions list,
in order to display a file submission interface. The interface will
therefore only show up after all the submission pages have been filled
in and submitted. Once displayed, the interface lets the user upload
new/revised files: the function refreshes the interface for each
upload (runs through the functions list again and stops on the
<code>Create_Upload_Files_Interface</code>). When the user applies the
modifications, the submission "step" is incremented and executes the
submissions function of step 2, skipping the display of the
interface. In this step 2 you can perform the usual tasks of your
submission. You also must add an additional function
(<code>Move_Uploaded_Files_To_Storage</code>) to run at step 2 in order to attach
the files that have been submitted at step 1.</p>
<p>These functions are incompatible with function
"Create_Modify_Interface". It is therefore suggested to create a
dedicated submission action (in addition to "SBI" and "MBI") to let
your users edit the files independently of the bibliographic data. An
example of such setup can be found in DEMOPIC submission.</p>
<b>Limitations:</b>
<ul>
<li>Use of a WebSubmit function to draw the interface, which prevents
the interface to be used inside a submission form (is displayed at a
later step). Not as integrated as a simple input file form
element.</li>
<li>Requires Javascript to be enabled user-side (is applicable to all
submissions anyway.</li>
</ul>
<p><b>1)</b> Go to your submission in WebSubmit admin, and add a new
submission action (for e.g. "[SRV] Submit New File"). If necessary,
create your own action in <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/actionlist">WebSubmit admin "Available WebSubmit Actions"</a>
page. You can clone from another existing action (in that case move to
point 4 below), or simply create an empty action.</p>
<p><b>2)</b> Go to the new SRV action interface ("View Interface"), add a
page, open it and add fields that will allow users to specify the record
to update. Typically you will add a "<code>DEMO_RN</code>" field to enter the
report number, and "<code>DEMO_CONTINUE</code>" button to submit the form.</p>
<p><b>3)</b> Go the the new SRV action functions ("View" functions) and add
the necessary functions: for e.g. at step 1, "<code>Get_Report_Number</code>",
"<code>Get_Recid</code>" and "<code>Create_Upload_Files_Interface</code>". At step 2,
"<code>Get_Recid</code>", "<code>Move_Uploaded_Files_to_Storage</code>" and "<code>Print_Success</code>".</p>
<p><b>4)</b> Configure the <code>Create_Upload_Files_Interface</code> parameters. There
are many options available. Briefly, the most important one is the
"<code>doctype</code>" parameter, which lets you specify the document types users
are allowed to submit. Use "<code>|</code>" to separate doctypes, and "<code>=</code>" to
separate <code>doctype</code> and <code>doctype</code> description. For e.g. input "<code>Main=Main
File|Additional=Additional Document</code>" to let users choose either Main
or Additional types (which will show as "Main File" and "Additional
Document" to users). Other parameters will let you define for which
<code>doctype</code> users can revise or delete files (for e.g. specify for
<code>canDeleteDoctypes</code> "Additional" so that only these
documents can be deleted once they have been uploaded). Use
"<code>*</code>" to specify "any declared doctype", and
"<code>|</code>" as separator (for all <code>can_*_doctypes</code>
parameters).</p>
<p>To read more about the parameters available for this function, check the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Create_Upload_Files_Interface"><code>Create_Upload_Files_Interface</code> function documentation</a>.</p>
<p><b>5)</b> Configure the <code>Move_Uploaded_Files_To_Storage</code>. There are less options
than in <code>Create_Upload_Files_Interface</code> function. Specify for e.g. in
<code>createIconDoctypes</code> for which doctypes icons will be
created, or in "<code>forceFileRevision</code>" if revisions of file
attributes trigger a new file revision. For an up-to-date
documentation check
the <a href="<CFG_SITE_URL>/admin/websubmit/websubmitadmin.py/functionedit?funcname=Move_Uploaded_Files_to_Storage"><code>Move_Uploaded_Files_to_Storage</code>
function documentation</a>.</p>
<h4>Revising/deleting files</h4>
<p>File revisions and deletions comes for free with the
functions. Simply allow deletion or revision of files when
<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/>
<span class="guideheader">E</span>ven once created, a document type will not appear automatically on this page. To configure the list of catalogues and document
types displayed on this page, the administrator shall go to the <a target=top href="<CFG_SITE_URL>/admin/websubmit/editCatalogues.php">edit catalogues</a>
page. (see the <a href="#catalogues">guide section</a>)<br clear="all"/>
<h3>The user can then click on the document type he is interested in.</h3>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-menu_doc.png" alt="Document type Page" class="guideimg" align="left">
<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/>
<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/>
<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/>
<span class="guideheader">Y</span>ou can also change the order of the buttons, by going to the <a target=top href="websubmit-admin">
main page</a>, click on the title of the document type then on the icon in the "Edit Submission" column and in the
line of the desired action. There you can set the "buttonorder" form field.<br/><br clear="all"/>
<h3>The user now may choose a category, then click on the action button he wishes.<br/>The submission starts, the first page of the web form appears.</h3>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-form.png" alt="Document type Page" class="guideimg" align="left">
<span class="guideheader">T</span>his web form is composed of several pages, on each of these
pages form fields can be found. To modify the number of pages, add or withdraw form fields and modify
the texts before each form field, you shall go to the <a target=top href="websubmit-admin">main page</a>,
click on the title of the document type then on the icon in the "Edit Submission Pages" column and in the line of the
desired action. (see the <a href="#actionimplement">guide section</a>)<br/><br clear="all"/>
<h3>On the last page of the submission, there should be a button like in the following image which will
trigger the end script</h3>
<IMG src="<CFG_SITE_URL>/img/admin/websubmit-admin-guide-end_action.png" alt="Document type End Page" class="guideimg" align="left">
<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/>
<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"/>
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/>
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.
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.
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><PA>categ</PA></b>": in this case this string is replaced with the content of the file [altrnin]<br/>
"<b><PA>yy</PA></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><PA>file:<i>name_of_file</i></PA></b>": in this case, this string is replaced by the first line of the given file<br />
"<b><PA>file*:<i>name_of_file</i></PA></b>": in this case, this string is replaced by all the lines of the given file, separated by a dash ('-') character.
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><PA>categ</PA></b>": in this case this string is replaced with the content of the file [altrnin]<br/>
"<b><PA>yy</PA></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><PA>file:<i>name_of_file</i></PA></b>": in this case, this string is replaced by the first line of the given file<br />
"<b><PA>file*:<i>name_of_file</i></PA></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 <PA>categ</PA> in the reference format or in the counter
email addresses of the people who will receive this email (comma separated list). this parameter may contain the <b><CATEG></b> string. In which case the variable computed from the [categformatDAM] parameter replaces this string.<br/>
contains a regular expression used to compute the category of the document given the reference of the document.<br/>
eg.: if [categformatAFP]="TEST-<CATEG>-.*" and the reference of the document is "TEST-CATEGORY1-2001-001", then the computed category equals "CATEGORY1"
email addresses of the people who will receive this email (comma separated list). this parameter may contain the <b><CATEG></b> string. In which case the variable computed from the [categformatDAM] parameter replaces this string.<br/>
The subdirectory g0 is used to split the documents accross the filesystem. The CFG_FILE_DIR_SIZE variable from
invenio.conf determines how many documents will be stored under one subdirectory.<br/><br/>
Several files may be stored under the same document directory: they are the different formats and versions of the
same document. Versions are indicated by a string of the form ";1.0" concatenated to the name of the file.<br/><br/>
Please see the <href="/help/admin/howto-fulltext">HOWTO Manage Fulltext Files</a> for more information on the administrative command line tools available to manipulate fulltext files.
Returns the list all bibdocs object belonging to a recid.
If C{doctype} is set, it returns just the bibdocs of that doctype.
@param doctype: the optional doctype.
@type doctype: string
@return: the list of bibdocs.
@rtype: list of BibDoc
"""
if not doctype:
return self.bibdocs
else:
return [bibdoc for bibdoc in self.bibdocs if doctype == bibdoc.doctype]
def get_bibdoc_names(self, doctype=''):
"""
Returns all the names of the documents associated with the bibdoc.
If C{doctype} is set, restrict the result to all the matching doctype.
@param doctype: the optional doctype.
@type doctype: string
@return: the list of document names.
@rtype: list of string
"""
return [bibdoc.docname for bibdoc in self.list_bibdocs(doctype)]
def check_file_exists(self, path):
"""
Check if a file with the same content of the file pointed in C{path}
is already attached to this record.
@param path: the file to be checked against.
@type path: string
@return: True if a file with the requested content is already attached
to the record.
@rtype: bool
"""
size = os.path.getsize(path)
# Let's consider all the latest files
files = self.list_latest_files()
# Let's consider all the latest files with same size
potential = [afile for afile in files if afile.get_size() == size]
if potential:
checksum = calculate_md5(path)
# Let's consider all the latest files with the same size and the
# same checksum
potential = [afile for afile in potential if afile.get_checksum() == checksum]
if potential:
potential = [afile for afile in potential if filecmp.cmp(afile.get_full_path(), path)]
if potential:
return True
else:
# Gosh! How unlucky, same size, same checksum but not same
# content!
pass
return False
def propose_unique_docname(self, docname):
"""
Given C{docname}, return a new docname that is not already attached to
the record.
@param docname: the reference docname.
@type docname: string
@return: a docname not already attached.
@rtype: string
"""
docname = normalize_docname(docname)
goodname = docname
i = 1
while goodname in self.get_bibdoc_names():
i += 1
goodname = "%s_%s" % (docname, i)
return goodname
def merge_bibdocs(self, docname1, docname2):
"""
This method merge C{docname2} into C{docname1}.
1. Given all the formats of the latest version of the files
attached to C{docname2}, these files are added as new formats
into C{docname1}.
2. C{docname2} is marked as deleted.
@raise InvenioWebSubmitFileError: if at least one format in C{docname2}
already exists in C{docname1}. (In this case the two bibdocs are
preserved)
@note: comments and descriptions are also copied.
@note: if C{docname2} has a I{restriction}(i.e. if the I{status} is
set) and C{docname1} doesn't, the restriction is imported.
"""
bibdoc1 = self.get_bibdoc(docname1)
bibdoc2 = self.get_bibdoc(docname2)
## Check for possibility
for bibdocfile in bibdoc2.list_latest_files():
format = bibdocfile.get_format()
if bibdoc1.format_already_exists_p(format):
raise InvenioWebSubmitFileError('Format %s already exists in bibdoc %s of record %s. It\'s impossible to merge bibdoc %s into it.' % (format, docname1, self.id, docname2))
Algorithm that transform a broken/old bibdoc into a coherent one.
Think of it as being the fsck of BibDocs.
- All the files in the bibdoc directory will be renamed according
to the document name. Proper .recid, .type, .md5 files will be
created/updated.
- In case of more than one file with the same format version a new
bibdoc will be created in order to put does files.
@param docname: the document name that need to be fixed.
@type docname: string
@return: the list of newly created bibdocs if any.
@rtype: list of BibDoc
@raise InvenioWebSubmitFileError: in case of issues that can not be
fixed automatically.
"""
bibdoc = self.get_bibdoc(docname)
versions = {}
res = []
new_bibdocs = [] # List of files with the same version/format of
# existing file which need new bibdoc.
counter = 0
zero_version_bug = False
if os.path.exists(bibdoc.basedir):
for filename in os.listdir(bibdoc.basedir):
if filename[0] != '.' and ';' in filename:
name, version = filename.split(';')
try:
version = int(version)
except ValueError:
# Strange name
register_exception()
raise InvenioWebSubmitFileError, "A file called %s exists under %s. This is not a valid name. After the ';' there must be an integer representing the file version. Please, manually fix this file either by renaming or by deleting it." % (filename, bibdoc.basedir)
raise InvenioWebSubmitFileError, "Error in renaming '%s' to '%s': '%s'" % ('%s/%s' % (bibdoc.basedir, filename), '%s/%s' % (bibdoc.basedir, new_name), e)
if versions[version].has_key(format):
new_bibdocs.append((new_name, version))
else:
versions[version][format] = new_name
counter += 1
elif filename[0] != '.':
# Strange name
register_exception()
raise InvenioWebSubmitFileError, "A file called %s exists under %s. This is not a valid name. There should be a ';' followed by an integer representing the file version. Please, manually fix this file either by renaming or by deleting it." % (filename, bibdoc.basedir)
raise InvenioWebSubmitFileError, "The docid %s does not exist." % docid
# else it is a new document
else:
if not docname:
raise InvenioWebSubmitFileError, "You should specify the docname when creating a new bibdoc"
else:
self.recid = recid
self.doctype = doctype
self.docname = docname
self.status = ''
if recid:
res = run_sql("SELECT b.id FROM bibrec_bibdoc bb JOIN bibdoc b on bb.id_bibdoc=b.id WHERE bb.id_bibrec=%s AND b.docname=%s LIMIT 1", (recid, docname), 1)
if res:
raise InvenioWebSubmitFileError, "A bibdoc called %s already exists for recid %s" % (docname, recid)
self.id = run_sql("INSERT INTO bibdoc (status,docname,creation_date,modification_date) "
raise InvenioWebSubmitFileError, "A file for docname '%s' for the recid '%s' already exists for the format '%s'" % (self.docname, self.recid, docfile.get_format())
my_new_bibdoc.add_file_new_version(CFG_PREFIX + '/lib/webtest/invenio/test.jpg', description= 'the new version', comment=None, format=None, flags=["PERFORM_HIDE_PREVIOUS"])
#epilog="""With <query> you select the range of record/docnames/single files to work on. Note that some actions e.g. delete, append, revise etc. works at the docname level, while others like --set-comment, --set-description, at single file level and other can be applied in an iterative way to many records in a single run. Note that specifing docid(2) takes precedence over recid(2) which in turns takes precedence over pattern/collection search.""",
query_options.add_option('-a', '--all', action='store_true', dest='all', help='Select all the records')
query_options.add_option("--with-deleted-recs", choices=['yes', 'no', 'only'], type="choice", dest="deleted_recs", help="'Yes' to also match deleted records, 'no' to exclude them, 'only' to match only deleted ones", metavar="yes/no/only", default='no')
query_options.add_option("--with-deleted-docs", choices=['yes', 'no', 'only'], type="choice", dest="deleted_docs", help="'Yes' to also match deleted documents, 'no' to exclude them, 'only' to match only deleted ones (e.g. for undeletion)", metavar="yes/no/only", default='no')
query_options.add_option("--with-empty-recs", choices=['yes', 'no', 'only'], type="choice", dest="empty_recs", help="'Yes' to also match records without attached documents, 'no' to exclude them, 'only' to consider only such records (e.g. for statistics)", metavar="yes/no/only", default='no')
query_options.add_option("--with-empty-docs", choices=['yes', 'no', 'only'], type="choice", dest="empty_docs", help="'Yes' to also match documents without attached files, 'no' to exclude them, 'only' to consider only such documents (e.g. for sanity checking)", metavar="yes/no/only", default='no')
query_options.add_option("--with-record-modification-date", action="callback", callback=_date_range_callback, dest="md_rec", nargs=1, type="string", default=(None, None), help="matches records modified date1 and date2; dates can be expressed relatively, e.g.:\"-5m,2030-2-23 04:40\" # matches records modified since 5 minutes ago until the 2030...", metavar="date1,date2")
query_options.add_option("--with-record-creation-date", action="callback", callback=_date_range_callback, dest="cd_rec", nargs=1, type="string", default=(None, None), help="matches records created between date1 and date2; dates can be expressed relatively", metavar="date1,date2")
query_options.add_option("--with-document-modification-date", action="callback", callback=_date_range_callback, dest="md_doc", nargs=1, type="string", default=(None, None), help="matches documents modified between date1 and date2; dates can be expressed relatively", metavar="date1,date2")
query_options.add_option("--with-document-creation-date", action="callback", callback=_date_range_callback, dest="cd_doc", nargs=1, type="string", default=(None, None), help="matches documents created between date1 and date2; dates can be expressed relatively", metavar="date1,date2")
- query_options.add_option("--url", dest="url", help='matches the document referred by the URL, e.g. "%s/record/1/files/foobar.pdf?version=2"' % CFG_SITE_URL)
+ query_options.add_option("--url", dest="url", help='matches the document referred by the URL, e.g. "%s/%s/1/files/foobar.pdf?version=2"' % (CFG_SITE_URL, CFG_SITE_RECORD))
query_options.add_option("--path", dest="path", help='matches the document referred by the internal filesystem path, e.g. %s/g0/1/foobar.pdf\\;1' % CFG_WEBSUBMIT_FILEDIR)
query_options.add_option("--with-docname", dest="docname", help='matches documents with the given docname (accept wildcards)')
query_options.add_option("--with-doctype", dest="doctype", help='matches documents with the given doctype')
query_options.add_option('-p', '--pattern', dest='pattern', help='matches records by pattern')
query_options.add_option('-c', '--collection', dest='collection', help='matches records by collection')
query_options.add_option('--force', dest='force', help='force an action even when it\'s not necessary e.g. textify on an already textified bibdoc.', action='store_true', default=False)
parser.add_option_group(query_options)
getting_information_options = OptionGroup(parser, 'Actions for getting information')
getting_information_options.add_option('--get-info', dest='action', action='store_const', const='get-info', help='print all the informations about the matched record/documents')
getting_information_options.add_option('--get-disk-usage', dest='action', action='store_const', const='get-disk-usage', help='print disk usage statistics of the matched documents')
getting_information_options.add_option('--get-history', dest='action', action='store_const', const='get-history', help='print the matched documents history')
revising_options = OptionGroup(parser, 'Action for revising content')
revising_options.add_option("--append", dest='append_path', help='specify the URL/path of the file that will appended to the bibdoc (implies --with-empty-recs=yes)', metavar='PATH/URL')
revising_options.add_option("--revise", dest='revise_path', help='specify the URL/path of the file that will revise the bibdoc', metavar='PATH/URL')
revising_options.add_option("--revert", dest='action', action='store_const', const='revert', help='reverts a document to the specified version')
revising_options.add_option("--delete", action='store_const', const='delete', dest='action', help='soft-delete the matched documents')
revising_options.add_option("--hard-delete", action='store_const', const='hard-delete', dest='action', help='hard-delete the single matched document with a specific format and a specific revision (this operation is not revertible)')
revising_options.add_option("--purge", action='store_const', const='purge', dest='action', help='purge (i.e. hard-delete any format of any version prior to the latest version of) the matched documents')
revising_options.add_option("--expunge", action='store_const', const='expunge', dest='action', help='expunge (i.e. hard-delete any version and formats of) the matched documents')
revising_options.add_option("--with-versions", dest="version", help="specifies the version(s) to be used with hard-delete, hide, revert, e.g.: 1-2,3 or all")
revising_options.add_option("--with-format", dest="format", help='to specify a format when appending/revising/deleting/reverting a document, e.g. "pdf"', metavar='FORMAT')
housekeeping_options = OptionGroup(parser, 'Actions for housekeeping')
housekeeping_options.add_option("--check-md5", action='store_const', const='check-md5', dest='action', help='check md5 checksum validity of files')
housekeeping_options.add_option("--check-format", action='store_const', const='check-format', dest='action', help='check if any format-related inconsistences exists')
housekeeping_options.add_option("--check-duplicate-docnames", action='store_const', const='check-duplicate-docnames', dest='action', help='check for duplicate docnames associated with the same record')
housekeeping_options.add_option("--update-md5", action='store_const', const='update-md5', dest='action', help='update md5 checksum of files')
housekeeping_options.add_option("--fix-all", action='store_const', const='fix-all', dest='action', help='fix inconsistences in filesystem vs database vs MARC')
housekeeping_options.add_option("--fix-marc", action='store_const', const='fix-marc', dest='action', help='synchronize MARC after filesystem/database')
housekeeping_options.add_option("--fix-format", action='store_const', const='fix-format', dest='action', help='fix format related inconsistences')
housekeeping_options.add_option("--fix-duplicate-docnames", action='store_const', const='fix-duplicate-docnames', dest='action', help='fix duplicate docnames associated with the same record')
parser.add_option_group(housekeeping_options)
experimental_options = OptionGroup(parser, 'Experimental options (do not expect to find them in the next release)')
experimental_options.add_option('--textify', dest='action', action='store_const', const='textify', help='extract text from matched documents and store it for later indexing')
experimental_options.add_option('--with-ocr', dest='perform_ocr', action='store_true', default=False, help='when used with --textify, wether to perform OCR')
parser.add_option('-H', '--human-readable', dest='human_readable', action='store_true', default=False, help='print sizes in human readable format (e.g., 1KB 234MB 2GB)')
parser.add_option('--yes-i-know', action='store_true', dest='yes-i-know', help='use with care!')
return parser
def print_info(recid, docid, info):
"""Nicely print info about a recid, docid pair."""
wait_for_user("WARNING: a document with name %s and format %s already exists for recid %s. A new document with name %s will be created instead." % (repr(docname), repr(format), repr(recid), repr(new_docname)))
docname = new_docname
ffts = {recid: [{
'docname' : docname,
'comment' : comment,
'description' : description,
'restriction' : restriction,
'doctype' : doctype,
'format' : format,
'url' : url
}]}
return bibupload_ffts(ffts, append=True)
def cli_revise(options, revise_path):
"""Create a bibupload FFT task submission for appending a format."""
"""Check if any format-related inconsistences exists."""
count = 0
tot = 0
duplicate = False
for recid in cli_recids_iterator(options):
tot += 1
bibrecdocs = BibRecDocs(recid)
if not bibrecdocs.check_duplicate_docnames():
print >> sys.stderr, "recid %s has duplicate docnames!"
broken = True
duplicate = True
else:
broken = False
for docname in bibrecdocs.get_bibdoc_names():
if not bibrecdocs.check_format(docname):
print >> sys.stderr, "recid %s with docname %s need format fixing" % (recid, docname)
broken = True
if broken:
count += 1
if count:
result = "%d out of %d records need their formats to be fixed." % (count, tot)
else:
result = "All records appear to be correct with respect to formats."
if duplicate:
result += " Note however that at least one record appear to have duplicate docnames. You should better fix this situation by using --fix-duplicate-docnames."
email_txt = "The document %s\nTitle: %s\nAuthor(s): %s\n\nhas been correctly received\n\n" % (fullrn,m_title,m_author)
# The user is either informed that the document has been added to the database, or sent for approval
if parameters['status'] == "APPROVAL":
email_txt = email_txt + "An email has been sent to the referee. You will be warned by email as soon as the referee takes his/her decision regarding your document.\n\n"
elif parameters['status'] == "ADDED":
- email_txt = email_txt + "It will be soon added to our Document Server.\n\nOnce inserted, you will be able to check the bibliographic information and the quality of the electronic documents at this URL:\n<%s/record/%s>\nIf you detect an error please let us know by sending an email to %s. \n\n" % (CFG_SITE_URL,sysno,CFG_SITE_SUPPORT_EMAIL)
+ email_txt = email_txt + "It will be soon added to our Document Server.\n\nOnce inserted, you will be able to check the bibliographic information and the quality of the electronic documents at this URL:\n<%s/%s/%s>\nIf you detect an error please let us know by sending an email to %s. \n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno,CFG_SITE_SUPPORT_EMAIL)
email_txt += "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
- mail_referee +="To access the document(s), select the file(s) from the location:<%s/record/%s/files/>\n\n" % (CFG_SITE_URL,sysno)
+ mail_referee +="To access the document(s), select the file(s) from the location:<%s/%s/%s/files/>\n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
mail_referee +="To approve/reject the document, you should go to this URL:\n<%s/publiline.py?doctype=%s&categ=%s&RN=%s>\n" % (CFG_SITE_URL,doctype,category,rn)
#sth = run_sql("SELECT access FROM sbmAPPROVAL WHERE rn=%s", (rn,))
#if len(sth) >0:
#access = sth[0][0]
# Build referee's email address
refereeaddress = ""
# Try to retrieve the publication committee chair's email from the role database
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_%s" % (doctype,category))):
refereeaddress += user[1] + ","
# And if there are general referees
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_*" % doctype)):
refereeaddress += user[1] + ","
refereeaddress = re.sub(",$","",refereeaddress)
# Creation of the mail for the referee
addresses = ""
if refereeaddress != "":
addresses = refereeaddress + ","
if otheraddresses != "":
addresses += otheraddresses
else:
addresses = re.sub(",$","",addresses)
title_referee = "Request for publication of %s" % rn
mail_referee = "The document %s has been asked for publication to the %s Server..\nYour have to select an editorial board for it.\n\n" % (rn,CFG_SITE_NAME)
- mail_referee +="To access the document(s), select the file(s) from the location:<%s/record/%s/files/>\n\n" % (CFG_SITE_URL,sysno)
+ mail_referee +="To access the document(s), select the file(s) from the location:<%s/%s/%s/files/>\n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
mail_referee +="To select an editorial board, you should go to this URL:\n<%s/publiline.py?doctype=%s&categ=%s&RN=%s>\n" % (CFG_SITE_URL,doctype,category,rn)
#sth = run_sql("SELECT access FROM sbmAPPROVAL WHERE rn=%s", (rn,))
#if len(sth) >0:
#access = sth[0][0]
# Build referee's email address
refereeaddress = ""
# Try to retrieve the publication committee chair's email from the role database
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_%s" % (doctype,category))):
refereeaddress += user[1] + ","
# And if there are general referees
for user in acc_get_role_users(acc_get_role_id("pubcomchair_%s_*" % doctype)):
refereeaddress += user[1] + ","
refereeaddress = re.sub(",$","",refereeaddress)
# Creation of the mail for the referee
addresses = ""
if refereeaddress != "":
addresses = refereeaddress + ","
if otheraddresses != "":
addresses += otheraddresses
else:
addresses = re.sub(",$","",addresses)
title_referee = "Request for refereeing process of %s" % rn
mail_referee = "The document %s has been asked for refereing process to the %s Server..\nYour have to select an editorial board for it.\n\n" % (rn,CFG_SITE_NAME)
- mail_referee +="To access the document(s), select the file(s) from the location:<%s/record/%s/files/>\n\n" % (CFG_SITE_URL,sysno)
+ mail_referee +="To access the document(s), select the file(s) from the location:<%s/%s/%s/files/>\n\n" % (CFG_SITE_URL,CFG_SITE_RECORD,sysno)
mail_referee +="To select an editorial board, you should go to this URL:\n<%s/publiline.py?doctype=%s&categ=%s&RN=%s>\n" % (CFG_SITE_URL,doctype,category,rn)
- message = "A revised version of document %s has been submitted.\n\nTitle: %s\nAuthor(s): %s\nURL: <%s/record/%s>%s" % (rn,title,author,CFG_SITE_URL,sysno,note)
+ message = "A revised version of document %s has been submitted.\n\nTitle: %s\nAuthor(s): %s\nURL: <%s/%s/%s>%s" % (rn,title,author,CFG_SITE_URL,CFG_SITE_RECORD,sysno,note)
Displays the doctypes and categories for which the user is referee
Parameters:
- 'ln' *string* - The language to display the interface in
- 'referees' *array* - All the doctypes for which the user is referee:
- 'doctype' *string* - The doctype
- 'docname' *string* - The display name of the doctype
- 'categories' *array* - The specific categories for which the user is referee:
- 'id' *string* - The category id
- 'name' *string* - The display name of the category
"""
# load the right message language
_ = gettext_set_language(ln)
out = """ <table class="searchbox" width="100%%" summary="">
<tr>
<th class="portalboxheader">%(refdocs)s</th>
</tr>
<tr>
<td class="portalboxbody">""" % {
'refdocs' : _("Refereed Documents"),
}
for doctype in referees:
out += """<ul><li><b>%(docname)s</b><ul>""" % doctype
if doctype ['categories'] is None:
out += '''<li><a href="publiline.py?doctype=%(doctype)s%(ln_link)s">%(generalref)s</a></li>''' % {
'docname' : doctype['docname'],
'doctype' : doctype['doctype'],
'generalref' : _("You are a general referee"),
'ln_link': '&ln=' + ln}
else:
for category in doctype['categories']:
out += """<li><a href="publiline.py?doctype=%(doctype)s&categ=%(categ)s%(ln_link)s">%(referee)s</a></li>""" % {
'referee' : _("You are a referee for category:") + ' ' + str(category['name']) + ' (' + str(category['id']) + ')',
'doctype' : doctype['doctype'],
'categ' : category['id'],
'ln_link': '&ln=' + ln}
out += "</ul><br /></li></ul>"
out += "</td></tr></table>"
out += '''<p>To see the status of documents for which approval has been requested, click <a href=\"%(url)s/publiline.py?flow=cplx\">here</a></p>''' % {'url' : CFG_SITE_URL}
return out
def tmpl_publiline_selectdoctype(self, ln, docs):
"""
Displays the doctypes that the user can select
Parameters:
- 'ln' *string* - The language to display the interface in
- 'docs' *array* - All the doctypes that the user can select:
- 'doctype' *string* - The doctype
- 'docname' *string* - The display name of the doctype
out += _("It has first been asked for refereing process on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br /></td></tr>'
out += "<tr><td width='400px'>"
out += _("Last request e-mail was sent to the publication committee chair on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br /></td></tr>'
if dates['dRefereeSel'] != None:
out += "<tr><td width='400px'>"
out += _("A referee has been selected by the publication committee on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dRefereeSel']) + '</strong><br /></td></tr>'
else:
out += "<tr><td width='400px'>"
out += _("No referee has been selected yet.") + "</td><td>"
if (status != "cancelled") and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="RefereeSel", linkText=_("Select a referee"))
out += '<br /></td></tr>'
if dates['dRefereeRecom'] != None:
out += "<tr><td width='400px'>"
out += _("The referee has sent his final recommendations to the publication committee on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br /></td></tr>'
else:
out += "<tr><td width='400px'>"
out += _("No recommendation from the referee yet.") + "</td><td>"
if (status != "cancelled") and (dates['dRefereeSel'] != None) and (isReferee == 0):
out += displaycplxdoc_displayauthaction (action="RefereeRecom", linkText=_("Send a recommendation"))
out += '<br /></td></tr>'
if dates['dPubComRecom'] != None:
out += "<tr><td width='400px'>"
out += _("The publication committee has sent his final recommendations to the project leader on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dPubComRecom']) + '</strong><br /></td></tr>'
else:
out += "<tr><td width='400px'>"
out += _("No recommendation from the publication committee yet.") + "</td><td>"
if (status != "cancelled") and (dates['dRefereeRecom'] != None) and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="PubComRecom", linkText=_("Send a recommendation"))
out += '<br /></td></tr>'
if status == "cancelled":
out += "<tr><td width='400px'>"
out += _("It has been cancelled by the author on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br /></td></tr>'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += "<tr><td width='400px'>"
out += _("It has been approved by the project leader on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br /></td></tr>'
elif status == "rejected":
out += "<tr><td width='400px'>"
out += _("It has been rejected by the project leader on the ") + "</td><td>" + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br /></td></tr>'
else:
out += "<tr><td width='400px'>"
out += _("No final decision taken yet.") + "</td><td>"
if (dates['dPubComRecom'] != None) and (isProjectLeader == 0):
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
out += '<br /></table>'
elif apptype == "RPB":
out += _("It has first been asked for refereing process on the ") + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br />'
out += _("Last request e-mail was sent to the publication committee chair on the ") + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br />'
if dates['dEdBoardSel'] != None:
out += _("An editorial board has been selected by the publication committee on the ") + ' <strong class="headline">' + str(dates['dEdBoardSel']) + '</strong>'
if (status != "cancelled") and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="AddAuthorList", linkText=_("Add an author list"))
out += '<br />'
else:
out += _("No editorial board has been selected yet.")
if (status != "cancelled") and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="EdBoardSel", linkText=_("Select an editorial board"))
out += '<br />'
if dates['dRefereeSel'] != None:
out += _("A referee has been selected by the editorial board on the ") + ' <strong class="headline">' + str(dates['dRefereeSel']) + '</strong><br />'
else:
out += _("No referee has been selected yet.")
if (status != "cancelled") and (dates['dEdBoardSel'] != None) and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="RefereeSel", linkText=_("Select a referee"))
out += '<br />'
if dates['dRefereeRecom'] != None:
out += _("The referee has sent his final recommendations to the editorial board on the ") + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br />'
else:
out += _("No recommendation from the referee yet.")
if (status != "cancelled") and (dates['dRefereeSel'] != None) and (isReferee == 0):
out += displaycplxdoc_displayauthaction (action="RefereeRecom", linkText=_("Send a recommendation"))
out += '<br />'
if dates['dEdBoardRecom'] != None:
out += _("The editorial board has sent his final recommendations to the publication committee on the ") + ' <strong class="headline">' + str(dates['dRefereeRecom']) + '</strong><br />'
else:
out += _("No recommendation from the editorial board yet.")
if (status != "cancelled") and (dates['dRefereeRecom'] != None) and (isEdBoard == 0):
out += displaycplxdoc_displayauthaction (action="EdBoardRecom", linkText=_("Send a recommendation"))
out += '<br />'
if dates['dPubComRecom'] != None:
out += _("The publication committee has sent his final recommendations to the project leader on the ") + ' <strong class="headline">' + str(dates['dPubComRecom']) + '</strong><br />'
else:
out += _("No recommendation from the publication committee yet.")
if (status != "cancelled") and (dates['dEdBoardRecom'] != None) and (isPubCom == 0):
out += displaycplxdoc_displayauthaction (action="PubComRecom", linkText=_("Send a recommendation"))
out += '<br />'
if status == "cancelled":
out += _("It has been cancelled by the author on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += _("It has been approved by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif status == "rejected":
out += _("It has been rejected by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
else:
out += _("No final decision taken yet.")
if (dates['dPubComRecom'] != None) and (isProjectLeader == 0):
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
out += '<br />'
elif apptype == "RDA":
out += _("It has first been asked for refereing process on the ") + ' <strong class="headline">' + str(dates['dFirstReq']) + '</strong><br />'
out += _("Last request e-mail was sent to the project leader on the ") + ' <strong class="headline">' + str(dates['dLastReq']) + '</strong><br />'
if status == "cancelled":
out += _("It has been cancelled by the author on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif dates['dProjectLeaderAction'] != None:
if status == "approved":
out += _("It has been approved by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
elif status == "rejected":
out += _("It has been rejected by the project leader on the ") + ' <strong class="headline">' + str(dates['dProjectLeaderAction']) + '</strong><br />'
else:
out += _("No final decision taken yet.")
if isProjectLeader == 0:
out += displaycplxdoc_displayauthaction (action="ProjectLeaderDecision", linkText=_("Take a decision"))
if isAuthor == 0:
out += displaycplxdoc_displayauthaction (action="AuthorCancel", linkText=_("Cancel"))
return warningMsg(_("An error has happened in trying to retrieve the requested file."), req, CFG_SITE_NAME, ln)
else:
return warningMsg(_('Not enough information to retrieve the document'), req, CFG_SITE_NAME, ln)
else:
if not name and docid:
## Let's obtain the name from the docid
try:
bibdoc = BibDoc(docid)
name = bibdoc.get_docname()
except InvenioWebSubmitFileError, e:
return warningMsg(_("An error has happened in trying to retrieving the requested file."), req, CFG_SITE_NAME, ln)
format = normalize_format(format)
- redirect_to_url(req, '%s/record/%s/files/%s%s?ln=%s%s' % (CFG_SITE_URL, recid, name, format, ln, version and 'version=%s' % version or ''), apache.HTTP_MOVED_PERMANENTLY)
+ redirect_to_url(req, '%s/%s/%s/files/%s%s?ln=%s%s' % (CFG_SITE_URL, CFG_SITE_RECORD, recid, name, format, ln, version and 'version=%s' % version or ''), apache.HTTP_MOVED_PERMANENTLY)
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../sub/",
navmenuid='submit')
try:
raise DeprecationWarning, 'submit/sub handler has been used. Please use submit/direct. e.g. "submit/sub?RN=123@SBIFOO" -> "submit/direct?RN=123&sub=SBIFOO"'
if uid == -1 or CFG_ACCESS_CONTROL_LEVEL_SITE >= 1:
return page_not_authorized(req, "../submit",
navmenuid='submit')
if CFG_CERN_SITE:
## HACK BEGIN: this is a hack for CMS and ATLAS draft
from invenio.webuser import collect_user_info
user_info = collect_user_info(req)
if doctype == 'CMSPUB' and 'cds-admin [CERN]' not in user_info['group'] and not user_info['email'].lower() == 'cds.support@cern.ch':
if 'cms-publication-committee-chair [CERN]' not in user_info['group']:
return page_not_authorized(req, "../submit", text="In order to access this submission interface you need to be member of the CMS Publication Committee Chair.",
navmenuid='submit')
elif doctype == 'ATLPUB' and 'cds-admin [CERN]' not in user_info['group'] and not user_info['email'].lower() == 'cds.support@cern.ch':
if 'atlas-gen [CERN]' not in user_info['group']:
return page_not_authorized(req, "../submit", text="In order to access this submission interface you need to be member of ATLAS.",
return run_sql("SELECT COUNT(*) FROM sbmCPLXAPPROVAL WHERE doctype=%s AND categ=%s AND status=%s AND type=%s",(doctype,categ,status,apptype,))[0][0]
def __db_get_infos (key):
return run_sql("SELECT status,id_group,id_bskBASKET,id_EdBoardGroup,dFirstReq,dLastReq,dEdBoardSel,dRefereeSel,dRefereeRecom,dEdBoardRecom,dPubComRecom,dProjectLeaderAction FROM sbmCPLXAPPROVAL WHERE rn=%s and type=%s", key)
def __db_set_EdBoardSel_time (key):
run_sql("UPDATE sbmCPLXAPPROVAL SET dEdBoardSel=NOW() WHERE rn=%s and type=%s", key)
Scientific Note approval for document %s has been submitted to the CERN Document Server.
Your approval is requested for this document. Once you have received recommendations from both the referee and the publication committee chair, you will be able to make your decision.
Requested subcategory: %s
Title: %s
Author(s): %s
To access the document(s), select the file(s) from the location:
- <%s/record/%s>
+ <%s/%s/%s>
The %s has made a recommendation for the document. He/she said the following:
%s
You can approve this document by visiting this page:
<%s>
You can also check the status of the document from: